diff -Nru linux/MAINTAINERS linux-2.4.19-pre5-mjc/MAINTAINERS --- linux/MAINTAINERS Mon Apr 8 22:26:52 2002 +++ linux-2.4.19-pre5-mjc/MAINTAINERS Mon Apr 8 22:31:21 2002 @@ -1498,10 +1498,11 @@ L: linux-net@vger.kernel.org S: Maintained -SOUND -P: Alan Cox -M: alan@redhat.com -S: Maintained for 2.2 only +SOUND - ALSA +P: Jaroslav Kysela +M: perex@suse.cz +L: alsa-devel@alsa-project.org +S: Maintained SPARC: P: David S. Miller diff -Nru linux/Makefile linux-2.4.19-pre5-mjc/Makefile --- linux/Makefile Mon Apr 8 22:26:52 2002 +++ linux-2.4.19-pre5-mjc/Makefile Mon Apr 8 22:31:21 2002 @@ -130,7 +130,7 @@ NETWORKS =net/network.o LIBS =$(TOPDIR)/lib/lib.a -SUBDIRS =kernel drivers mm fs net ipc lib +SUBDIRS =kernel drivers mm fs net ipc lib sound DRIVERS-n := DRIVERS-y := @@ -165,6 +165,7 @@ endif DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o +DRIVERS-$(CONFIG_SOUND_ALSA) += sound/sound.o DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o @@ -210,6 +211,7 @@ drivers/pci/devlist.h drivers/pci/classlist.h drivers/pci/gen-devlist \ drivers/zorro/devlist.h drivers/zorro/gen-devlist \ drivers/sound/bin2hex drivers/sound/hex2hex \ + sound/oss/bin2hex sound/oss/hex2hex \ drivers/atm/fore200e_mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2} \ drivers/scsi/aic7xxx/aicasm/aicasm_gram.c \ drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \ @@ -236,6 +238,11 @@ drivers/sound/pndsperm.c \ drivers/sound/pndspini.c \ drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw \ + sound/oss/*_boot.h sound/oss/.*.boot \ + sound/oss/msndinit.c \ + sound/oss/msndperm.c \ + sound/oss/pndsperm.c \ + sound/oss/pndspini.c \ .version .config* config.in config.old \ scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \ scripts/lxdialog/*.o scripts/lxdialog/lxdialog \ @@ -352,7 +359,7 @@ init/do_mounts.o: init/do_mounts.c include/config/MARKER $(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $*.o $< -fs lib mm ipc kernel drivers net: dummy +fs lib mm ipc kernel drivers net sound: dummy $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@) TAGS: dummy diff -Nru linux/arch/i386/config.in linux-2.4.19-pre5-mjc/arch/i386/config.in --- linux/arch/i386/config.in Mon Apr 8 22:26:42 2002 +++ linux-2.4.19-pre5-mjc/arch/i386/config.in Mon Apr 8 22:31:21 2002 @@ -416,14 +416,25 @@ endmenu fi +if [ "$CONFIG_SOUND" != "y" ] && [ "$CONFIG_SOUND" != "m" ]; then + mainmenu_option next_comment + comment 'ALSA Sound' + tristate 'Sound card support' CONFIG_SOUND_ALSA + if [ "$CONFIG_SOUND_ALSA" != "n" ]; then + source sound/Config.in + fi + endmenu +fi + +if [ "$CONFIG_SOUND_ALSA" != "y" ] && [ "$CONFIG_SOUND_ALSA" != "m" ]; then mainmenu_option next_comment comment 'Sound' - tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then source drivers/sound/Config.in fi endmenu +fi source drivers/usb/Config.in diff -Nru linux/arch/ppc/config.in linux-2.4.19-pre5-mjc/arch/ppc/config.in --- linux/arch/ppc/config.in Mon Apr 8 22:26:10 2002 +++ linux-2.4.19-pre5-mjc/arch/ppc/config.in Mon Apr 8 22:31:21 2002 @@ -372,6 +372,18 @@ source fs/Config.in +if [ "$CONFIG_SOUND" != "y" ] && [ "$CONFIG_SOUND" != "m" ]; then + mainmenu_option next_comment + comment 'ALSA Sound' + tristate 'Sound card support' CONFIG_SOUND_ALSA + if [ "$CONFIG_SOUND_ALSA" != "n" ]; then + source sound/oss/dmasound/Config.in + source sound/Config.in + fi + endmenu +fi + +if [ "$CONFIG_SOUND_ALSA" != "y" ] && [ "$CONFIG_SOUND_ALSA" != "m" ]; then mainmenu_option next_comment comment 'Sound' tristate 'Sound card support' CONFIG_SOUND @@ -379,8 +391,8 @@ source drivers/sound/dmasound/Config.in source drivers/sound/Config.in fi - endmenu +fi if [ "$CONFIG_8xx" = "y" ]; then source arch/ppc/8xx_io/Config.in diff -Nru linux/include/asm-i386/io.h linux-2.4.19-pre5-mjc/include/asm-i386/io.h --- linux/include/asm-i386/io.h Mon Apr 8 22:25:53 2002 +++ linux-2.4.19-pre5-mjc/include/asm-i386/io.h Mon Apr 8 22:31:24 2002 @@ -160,6 +160,13 @@ extern void bt_iounmap(void *addr, unsigned long size); /* + * ISA I/O bus memory addresses are 1:1 with the physical address. + */ +#define isa_virt_to_bus virt_to_phys +#define isa_page_to_bus page_to_phys +#define isa_bus_to_virt phys_to_virt + +/* * IO bus memory addresses are also 1:1 with the physical address */ #define virt_to_bus virt_to_phys diff -Nru linux/include/linux/proc_fs.h linux-2.4.19-pre5-mjc/include/linux/proc_fs.h --- linux/include/linux/proc_fs.h Mon Apr 8 22:25:57 2002 +++ linux-2.4.19-pre5-mjc/include/linux/proc_fs.h Mon Apr 8 22:31:24 2002 @@ -209,4 +209,26 @@ #endif /* CONFIG_PROC_FS */ +struct proc_inode { + struct task_struct *task; + int type; + union { + int (*proc_get_link)(struct inode *, struct dentry **, struct vfsmount **); + int (*proc_read)(struct task_struct *task, char *page); + } op; + struct file *file; + struct proc_dir_entry *pde; + struct inode vfs_inode; +}; + +static inline struct proc_inode *PROC_I(const struct inode *inode) +{ + return list_entry(inode, struct proc_inode, vfs_inode); +} + +static inline struct proc_dir_entry *PDE(const struct inode *inode) +{ + return PROC_I(inode)->pde; +} + #endif /* _LINUX_PROC_FS_H */ diff -Nru linux/include/sound/ac97_codec.h linux-2.4.19-pre5-mjc/include/sound/ac97_codec.h --- linux/include/sound/ac97_codec.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ac97_codec.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,187 @@ +#ifndef __SOUND_AC97_CODEC_H +#define __SOUND_AC97_CODEC_H + +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "info.h" + +/* + * AC'97 codec registers + */ + +#define AC97_RESET 0x00 /* Reset */ +#define AC97_MASTER 0x02 /* Master Volume */ +#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ +#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ +#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ +#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ +#define AC97_PHONE 0x0c /* Phone Volume (optional) */ +#define AC97_MIC 0x0e /* MIC Volume */ +#define AC97_LINE 0x10 /* Line In Volume */ +#define AC97_CD 0x12 /* CD Volume */ +#define AC97_VIDEO 0x14 /* Video Volume (optional) */ +#define AC97_AUX 0x16 /* AUX Volume (optional) */ +#define AC97_PCM 0x18 /* PCM Volume */ +#define AC97_REC_SEL 0x1a /* Record Select */ +#define AC97_REC_GAIN 0x1c /* Record Gain */ +#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ +#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ +#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ +#define AC97_RESERVED 0x24 /* Reserved */ +#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ +/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ +#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ +#define AC97_SPDIF 0x3a /* S/PDIF control */ +/* range 0x3c-0x58 - MODEM */ +/* range 0x5a-0x7b - Vendor Specific */ +#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ +#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF Enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0040 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0080 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0100 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ +#define AC97_EA_SLOT_MASK 0xffcf /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0xcfff /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* specific - SigmaTel */ +#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ +#define AC97_SIGMATEL_DAC2INVERT 0x6e +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */ +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 + +/* specific - Analog Devices */ +#define AC97_AD_TEST 0x5a /* test register */ +#define AC97_AD_CODEC_CFG 0x70 /* codec configuration */ +#define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */ +#define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ +#define AC97_AD_MISC 0x76 /* Misc Control Bits */ + +/* ac97->scaps */ +#define AC97_SCAP_SURROUND_DAC (1<<0) /* surround L&R DACs are present */ +#define AC97_SCAP_CENTER_LFE_DAC (1<<1) /* center and LFE DACs are present */ + +/* ac97->flags */ +#define AC97_HAS_PC_BEEP (1<<0) +#define AC97_AD_MULTI (1<<1) /* Analog Devices - multi codecs */ + +/* + + */ + +typedef struct _snd_ac97 ac97_t; + +struct _snd_ac97 { + void (*write) (ac97_t *ac97, unsigned short reg, unsigned short val); + unsigned short (*read) (ac97_t *ac97, unsigned short reg); + void (*wait) (ac97_t *ac97); + void (*init) (ac97_t *ac97); + snd_info_entry_t *proc_entry; + snd_info_entry_t *proc_regs_entry; + void *private_data; + void (*private_free) (ac97_t *ac97); + /* --- */ + snd_card_t *card; + spinlock_t reg_lock; + unsigned short num; /* number of codec: 0 = primary, 1 = secondary */ + unsigned short addr; /* physical address of codec [0-3] */ + unsigned int id; /* identification of codec */ + unsigned short caps; /* capabilities (register 0) */ + unsigned short ext_id; /* extended feature identification (register 28) */ + unsigned int scaps; /* driver capabilities */ + unsigned int flags; /* specific code */ + unsigned int clock; /* AC'97 clock (usually 48000Hz) */ + unsigned int rates_front_dac; + unsigned int rates_surr_dac; + unsigned int rates_lfe_dac; + unsigned int rates_adc; + unsigned int rates_mic_adc; + unsigned int spdif_status; + unsigned short regs[0x80]; /* register cache */ + unsigned char reg_accessed[0x80 / 8]; /* bit flags */ + union { /* vendor specific code */ + struct { + unsigned short unchained[3]; // 0 = C34, 1 = C79, 2 = C69 + unsigned short chained[3]; // 0 = C34, 1 = C79, 2 = C69 + unsigned short id[3]; // codec IDs (lower 16-bit word) + unsigned short pcmreg[3]; // PCM registers + struct semaphore mutex; + } ad18xx; + } spec; +}; + +int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97); + +void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value); +unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg); +void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value); +int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value); +int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value); +int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate); +#ifdef CONFIG_PM +void snd_ac97_suspend(ac97_t *ac97); +void snd_ac97_resume(ac97_t *ac97); +#endif + +#endif /* __SOUND_AC97_CODEC_H */ diff -Nru linux/include/sound/ad1816a.h linux-2.4.19-pre5-mjc/include/sound/ad1816a.h --- linux/include/sound/ad1816a.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ad1816a.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,174 @@ +#ifndef __SOUND_AD1816A_H +#define __SOUND_AD1816A_H + +/* + ad1816a.h - definitions for ADI SoundPort AD1816A chip. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "control.h" +#include "pcm.h" +#include "timer.h" + +#define AD1816A_REG(r) (chip->port + r) + +#define AD1816A_CHIP_STATUS 0x00 +#define AD1816A_INDIR_ADDR 0x00 +#define AD1816A_INTERRUPT_STATUS 0x01 +#define AD1816A_INDIR_DATA_LOW 0x02 +#define AD1816A_INDIR_DATA_HIGH 0x03 +#define AD1816A_PIO_DEBUG 0x04 +#define AD1816A_PIO_STATUS 0x05 +#define AD1816A_PIO_DATA 0x06 +#define AD1816A_RESERVED_7 0x07 +#define AD1816A_PLAYBACK_CONFIG 0x08 +#define AD1816A_CAPTURE_CONFIG 0x09 +#define AD1816A_RESERVED_10 0x0a +#define AD1816A_RESERVED_11 0x0b +#define AD1816A_JOYSTICK_RAW_DATA 0x0c +#define AD1816A_JOYSTICK_CTRL 0x0d +#define AD1816A_JOY_POS_DATA_LOW 0x0e +#define AD1816A_JOY_POS_DATA_HIGH 0x0f + +#define AD1816A_LOW_BYTE_TMP 0x00 +#define AD1816A_INTERRUPT_ENABLE 0x01 +#define AD1816A_EXTERNAL_CTRL 0x01 +#define AD1816A_PLAYBACK_SAMPLE_RATE 0x02 +#define AD1816A_CAPTURE_SAMPLE_RATE 0x03 +#define AD1816A_VOICE_ATT 0x04 +#define AD1816A_FM_ATT 0x05 +#define AD1816A_I2S_1_ATT 0x06 +#define AD1816A_I2S_0_ATT 0x07 +#define AD1816A_PLAYBACK_BASE_COUNT 0x08 +#define AD1816A_PLAYBACK_CURR_COUNT 0x09 +#define AD1816A_CAPTURE_BASE_COUNT 0x0a +#define AD1816A_CAPTURE_CURR_COUNT 0x0b +#define AD1816A_TIMER_BASE_COUNT 0x0c +#define AD1816A_TIMER_CURR_COUNT 0x0d +#define AD1816A_MASTER_ATT 0x0e +#define AD1816A_CD_GAIN_ATT 0x0f +#define AD1816A_SYNTH_GAIN_ATT 0x10 +#define AD1816A_VID_GAIN_ATT 0x11 +#define AD1816A_LINE_GAIN_ATT 0x12 +#define AD1816A_MIC_GAIN_ATT 0x13 +#define AD1816A_PHONE_IN_GAIN_ATT 0x13 +#define AD1816A_ADC_SOURCE_SEL 0x14 +#define AD1816A_ADC_PGA 0x14 +#define AD1816A_CHIP_CONFIG 0x20 +#define AD1816A_DSP_CONFIG 0x21 +#define AD1816A_FM_SAMPLE_RATE 0x22 +#define AD1816A_I2S_1_SAMPLE_RATE 0x23 +#define AD1816A_I2S_0_SAMPLE_RATE 0x24 +#define AD1816A_RESERVED_37 0x25 +#define AD1816A_PROGRAM_CLOCK_RATE 0x26 +#define AD1816A_3D_PHAT_CTRL 0x27 +#define AD1816A_PHONE_OUT_ATT 0x27 +#define AD1816A_RESERVED_40 0x28 +#define AD1816A_HW_VOL_BUT 0x29 +#define AD1816A_DSP_MAILBOX_0 0x2a +#define AD1816A_DSP_MAILBOX_1 0x2b +#define AD1816A_POWERDOWN_CTRL 0x2c +#define AD1816A_TIMER_CTRL 0x2c +#define AD1816A_VERSION_ID 0x2d +#define AD1816A_RESERVED_46 0x2e + +#define AD1816A_READY 0x80 + +#define AD1816A_PLAYBACK_IRQ_PENDING 0x80 +#define AD1816A_CAPTURE_IRQ_PENDING 0x40 +#define AD1816A_TIMER_IRQ_PENDING 0x20 + +#define AD1816A_PLAYBACK_ENABLE 0x01 +#define AD1816A_PLAYBACK_PIO 0x02 +#define AD1816A_CAPTURE_ENABLE 0x01 +#define AD1816A_CAPTURE_PIO 0x02 + +#define AD1816A_FMT_LINEAR_8 0x00 +#define AD1816A_FMT_ULAW_8 0x08 +#define AD1816A_FMT_LINEAR_16_LIT 0x10 +#define AD1816A_FMT_ALAW_8 0x18 +#define AD1816A_FMT_LINEAR_16_BIG 0x30 +#define AD1816A_FMT_ALL 0x38 +#define AD1816A_FMT_STEREO 0x04 + +#define AD1816A_PLAYBACK_IRQ_ENABLE 0x8000 +#define AD1816A_CAPTURE_IRQ_ENABLE 0x4000 +#define AD1816A_TIMER_IRQ_ENABLE 0x2000 +#define AD1816A_TIMER_ENABLE 0x0080 + +#define AD1816A_SRC_LINE 0x00 +#define AD1816A_SRC_OUT 0x10 +#define AD1816A_SRC_CD 0x20 +#define AD1816A_SRC_SYNTH 0x30 +#define AD1816A_SRC_VIDEO 0x40 +#define AD1816A_SRC_MIC 0x50 +#define AD1816A_SRC_MONO 0x50 +#define AD1816A_SRC_PHONE_IN 0x60 +#define AD1816A_SRC_MASK 0x70 + +#define AD1816A_CAPTURE_NOT_EQUAL 0x1000 +#define AD1816A_WSS_ENABLE 0x8000 + +typedef struct _snd_ad1816a ad1816a_t; + +struct _snd_ad1816a { + unsigned long port; + struct resource *res_port; + int irq; + int dma1; + int dma2; + + unsigned short hardware; + unsigned short version; + + spinlock_t lock; + + unsigned short mode; + + snd_card_t *card; + snd_pcm_t *pcm; + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; + + snd_timer_t *timer; +}; + + +#define AD1816A_HW_AUTO 0 +#define AD1816A_HW_AD1816A 1 +#define AD1816A_HW_AD1815 2 +#define AD1816A_HW_AD18MAX10 3 + +#define AD1816A_MODE_PLAYBACK 0x01 +#define AD1816A_MODE_CAPTURE 0x02 +#define AD1816A_MODE_TIMER 0x04 +#define AD1816A_MODE_OPEN (AD1816A_MODE_PLAYBACK | \ + AD1816A_MODE_CAPTURE | \ + AD1816A_MODE_TIMER) + + +extern int snd_ad1816a_create(snd_card_t *card, unsigned long port, + int irq, int dma1, int dma2, + ad1816a_t **chip); + +extern int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm); +extern int snd_ad1816a_mixer(ad1816a_t *chip); + +#endif /* __SOUND_AD1816A_H */ diff -Nru linux/include/sound/ad1848.h linux-2.4.19-pre5-mjc/include/sound/ad1848.h --- linux/include/sound/ad1848.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ad1848.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,192 @@ +#ifndef __SOUND_AD1848_H +#define __SOUND_AD1848_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for AD1847/AD1848/CS4248 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" + +/* IO ports */ + +#define AD1848P( codec, x ) ( (chip) -> port + c_d_c_AD1848##x ) + +#define c_d_c_AD1848REGSEL 0 +#define c_d_c_AD1848REG 1 +#define c_d_c_AD1848STATUS 2 +#define c_d_c_AD1848PIO 3 + +/* codec registers */ + +#define AD1848_LEFT_INPUT 0x00 /* left input control */ +#define AD1848_RIGHT_INPUT 0x01 /* right input control */ +#define AD1848_AUX1_LEFT_INPUT 0x02 /* left AUX1 input control */ +#define AD1848_AUX1_RIGHT_INPUT 0x03 /* right AUX1 input control */ +#define AD1848_AUX2_LEFT_INPUT 0x04 /* left AUX2 input control */ +#define AD1848_AUX2_RIGHT_INPUT 0x05 /* right AUX2 input control */ +#define AD1848_LEFT_OUTPUT 0x06 /* left output control register */ +#define AD1848_RIGHT_OUTPUT 0x07 /* right output control register */ +#define AD1848_DATA_FORMAT 0x08 /* clock and data format - playback/capture - bits 7-0 MCE */ +#define AD1848_IFACE_CTRL 0x09 /* interface control - bits 7-2 MCE */ +#define AD1848_PIN_CTRL 0x0a /* pin control */ +#define AD1848_TEST_INIT 0x0b /* test and initialization */ +#define AD1848_MISC_INFO 0x0c /* miscellaneaous information */ +#define AD1848_LOOPBACK 0x0d /* loopback control */ +#define AD1848_DATA_UPR_CNT 0x0e /* playback/capture upper base count */ +#define AD1848_DATA_LWR_CNT 0x0f /* playback/capture lower base count */ + +/* definitions for codec register select port - CODECP( REGSEL ) */ + +#define AD1848_INIT 0x80 /* CODEC is initializing */ +#define AD1848_MCE 0x40 /* mode change enable */ +#define AD1848_TRD 0x20 /* transfer request disable */ + +/* definitions for codec status register - CODECP( STATUS ) */ + +#define AD1848_GLOBALIRQ 0x01 /* IRQ is active */ + +/* definitions for AD1848_LEFT_INPUT and AD1848_RIGHT_INPUT registers */ + +#define AD1848_ENABLE_MIC_GAIN 0x20 + +#define AD1848_MIXS_LINE1 0x00 +#define AD1848_MIXS_AUX1 0x40 +#define AD1848_MIXS_LINE2 0x80 +#define AD1848_MIXS_ALL 0xc0 + +/* definitions for clock and data format register - AD1848_PLAYBK_FORMAT */ + +#define AD1848_LINEAR_8 0x00 /* 8-bit unsigned data */ +#define AD1848_ALAW_8 0x60 /* 8-bit A-law companded */ +#define AD1848_ULAW_8 0x20 /* 8-bit U-law companded */ +#define AD1848_LINEAR_16 0x40 /* 16-bit twos complement data - little endian */ +#define AD1848_STEREO 0x10 /* stereo mode */ +/* bits 3-1 define frequency divisor */ +#define AD1848_XTAL1 0x00 /* 24.576 crystal */ +#define AD1848_XTAL2 0x01 /* 16.9344 crystal */ + +/* definitions for interface control register - AD1848_IFACE_CTRL */ + +#define AD1848_CAPTURE_PIO 0x80 /* capture PIO enable */ +#define AD1848_PLAYBACK_PIO 0x40 /* playback PIO enable */ +#define AD1848_CALIB_MODE 0x18 /* calibration mode bits */ +#define AD1848_AUTOCALIB 0x08 /* auto calibrate */ +#define AD1848_SINGLE_DMA 0x04 /* use single DMA channel */ +#define AD1848_CAPTURE_ENABLE 0x02 /* capture enable */ +#define AD1848_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* definitions for pin control register - AD1848_PIN_CTRL */ + +#define AD1848_IRQ_ENABLE 0x02 /* enable IRQ */ +#define AD1848_XCTL1 0x40 /* external control #1 */ +#define AD1848_XCTL0 0x80 /* external control #0 */ + +/* definitions for test and init register - AD1848_TEST_INIT */ + +#define AD1848_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ +#define AD1848_DMA_REQUEST 0x10 /* DMA request in progress */ + +/* defines for codec.mode */ + +#define AD1848_MODE_NONE 0x0000 +#define AD1848_MODE_PLAY 0x0001 +#define AD1848_MODE_CAPTURE 0x0002 +#define AD1848_MODE_TIMER 0x0004 +#define AD1848_MODE_OPEN (AD1848_MODE_PLAY|AD1848_MODE_CAPTURE|AD1848_MODE_TIMER) +#define AD1848_MODE_RUNNING 0x0010 + +/* defines for codec.hardware */ + +#define AD1848_HW_DETECT 0x0000 /* let AD1848 driver detect chip */ +#define AD1848_HW_AD1847 0x0001 /* AD1847 chip */ +#define AD1848_HW_AD1848 0x0002 /* AD1848 chip */ +#define AD1848_HW_CS4248 0x0003 /* CS4248 chip */ +#define AD1848_HW_CMI8330 0x0004 /* CMI8330 chip */ + +struct _snd_ad1848 { + unsigned long port; /* i/o port */ + struct resource *res_port; + int irq; /* IRQ line */ + int dma; /* data DMA */ + unsigned short version; /* version of CODEC chip */ + unsigned short mode; /* see to AD1848_MODE_XXXX */ + unsigned short hardware; /* see to AD1848_HW_XXXX */ + unsigned short single_dma:1; /* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */ + + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_card_t *card; + + unsigned char image[32]; /* SGalaxy needs an access to extended registers */ + int mce_bit; + int calibrate_mute; + int dma_size; + + spinlock_t reg_lock; + struct semaphore open_mutex; +}; + +typedef struct _snd_ad1848 ad1848_t; + +/* exported functions */ + +void snd_ad1848_out(ad1848_t *chip, unsigned char reg, unsigned char value); +void snd_ad1848_dout(ad1848_t *chip, unsigned char reg, unsigned char value); +unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg); +void snd_ad1848_mce_up(ad1848_t *chip); +void snd_ad1848_mce_down(ad1848_t *chip); + +int snd_ad1848_create(snd_card_t * card, + unsigned long port, + int irq, int dma, + unsigned short hardware, + ad1848_t ** chip); + +int snd_ad1848_pcm(ad1848_t * chip, int device, snd_pcm_t **rpcm); +int snd_ad1848_mixer(ad1848_t * chip); +void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ad1848_info_single, \ + get: snd_ad1848_get_single, put: snd_ad1848_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ad1848_info_double, \ + get: snd_ad1848_get_double, put: snd_ad1848_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#ifdef CONFIG_SND_DEBUG +void snd_ad1848_debug(ad1848_t *chip); +#endif + +#endif /* __SOUND_AD1848_H */ diff -Nru linux/include/sound/ainstr_fm.h linux-2.4.19-pre5-mjc/include/sound/ainstr_fm.h --- linux/include/sound/ainstr_fm.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ainstr_fm.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,132 @@ +/* + * Advanced Linux Sound Architecture + * + * FM (OPL2/3) Instrument Format + * Copyright (c) 2000 Uros Bizjak + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_FM_H +#define __SOUND_AINSTR_FM_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define FM_SHARE_FILE 0 + +/* + * FM operator + */ + +typedef struct fm_operator { + unsigned char am_vib; + unsigned char ksl_level; + unsigned char attack_decay; + unsigned char sustain_release; + unsigned char wave_select; +} fm_operator_t; + +/* + * Instrument + */ + +#define FM_PATCH_OPL2 0x01 /* OPL2 2 operators FM instrument */ +#define FM_PATCH_OPL3 0x02 /* OPL3 4 operators FM instrument */ + +typedef struct { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned char type; /* instrument type */ + + fm_operator_t op[4]; + unsigned char feedback_connection[2]; + + unsigned char echo_delay; + unsigned char echo_atten; + unsigned char chorus_spread; + unsigned char trnsps; + unsigned char fix_dur; + unsigned char modes; + unsigned char fix_key; +} fm_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * fm_xinstrument FM_STRU_INSTR + * + */ + +#define FM_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * FM operator + */ + +typedef struct fm_xoperator { + __u8 am_vib; + __u8 ksl_level; + __u8 attack_decay; + __u8 sustain_release; + __u8 wave_select; +} fm_xoperator_t; + +/* + * Instrument + */ + +typedef struct fm_xinstrument { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u8 type; /* instrument type */ + + fm_xoperator_t op[4]; /* fm operators */ + __u8 feedback_connection[2]; + + __u8 echo_delay; + __u8 echo_atten; + __u8 chorus_spread; + __u8 trnsps; + __u8 fix_dur; + __u8 modes; + __u8 fix_key; +} fm_xinstrument_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_fm_id; + +int snd_seq_fm_init(snd_seq_kinstr_ops_t * ops, + snd_seq_kinstr_ops_t * next); + +#endif + +#endif /* __SOUND_AINSTR_FM_H */ diff -Nru linux/include/sound/ainstr_gf1.h linux-2.4.19-pre5-mjc/include/sound/ainstr_gf1.h --- linux/include/sound/ainstr_gf1.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ainstr_gf1.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,227 @@ +/* + * Advanced Linux Sound Architecture + * + * GF1 (GUS) Patch Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_GF1_H +#define __SOUND_AINSTR_GF1_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define GF1_SHARE_FILE 0 + +/* + * wave formats + */ + +#define GF1_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define GF1_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define GF1_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define GF1_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define GF1_WAVE_LOOP 0x0008 /* loop mode */ +#define GF1_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define GF1_WAVE_STEREO 0x0100 /* stereo mode */ +#define GF1_WAVE_ULAW 0x0200 /* uLaw compression mode */ + +/* + * Wavetable definitions + */ + +typedef struct gf1_wave { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this instrument */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + + unsigned char flags; /* GF1 patch flags */ + unsigned char pad; + unsigned int sample_rate; /* sample rate in Hz */ + unsigned int low_frequency; /* low frequency range */ + unsigned int high_frequency; /* high frequency range */ + unsigned int root_frequency; /* root frequency range */ + signed short tune; + unsigned char balance; + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + unsigned short scale_frequency; + unsigned short scale_factor; /* 0-2048 or 0-2 */ + + struct gf1_wave *next; +} gf1_wave_t; + +/* + * Instrument + */ + +#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */ +#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */ +#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define IWFFFF_EFFECT_NONE 0 +#define IWFFFF_EFFECT_REVERB 1 +#define IWFFFF_EFFECT_CHORUS 2 +#define IWFFFF_EFFECT_ECHO 3 + +typedef struct { + unsigned short exclusion; + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ + + gf1_wave_t *wave; /* first waveform */ +} gf1_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * gf1_xinstrument IWFFFF_STRU_INSTR + * +gf1_xwave IWFFFF_STRU_WAVE + * + */ + +#define GF1_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E') +#define GF1_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Wavetable definitions + */ + +typedef struct gf1_xwave { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u32 format; /* wave format */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + + __u8 flags; /* GF1 patch flags */ + __u8 pad; + __u32 sample_rate; /* sample rate in Hz */ + __u32 low_frequency; /* low frequency range */ + __u32 high_frequency; /* high frequency range */ + __u32 root_frequency; /* root frequency range */ + __s16 tune; + __u8 balance; + __u8 envelope_rate[6]; + __u8 envelope_offset[6]; + __u8 tremolo_sweep; + __u8 tremolo_rate; + __u8 tremolo_depth; + __u8 vibrato_sweep; + __u8 vibrato_rate; + __u8 vibrato_depth; + __u16 scale_frequency; + __u16 scale_factor; /* 0-2048 or 0-2 */ +} gf1_xwave_t; + +/* + * Instrument + */ + +typedef struct gf1_xinstrument { + __u32 stype; + + __u16 exclusion; + __u16 exclusion_group; /* 0 - none, 1-65535 */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} gf1_xinstrument_t; + +/* + * Instrument info + */ + +#define GF1_INFO_ENVELOPE (1<<0) +#define GF1_INFO_TREMOLO (1<<1) +#define GF1_INFO_VIBRATO (1<<2) + +typedef struct gf1_info { + unsigned char flags; /* supported wave flags */ + unsigned char pad[3]; + unsigned int features; /* supported features */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} gf1_info_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_gf1_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, gf1_info_t *info); + int (*put_sample)(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, gf1_wave_t *wave, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_gf1_ops_t; + +int snd_seq_gf1_init(snd_gf1_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_GF1_H */ diff -Nru linux/include/sound/ainstr_iw.h linux-2.4.19-pre5-mjc/include/sound/ainstr_iw.h --- linux/include/sound/ainstr_iw.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ainstr_iw.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,375 @@ +/* + * Advanced Linux Sound Architecture + * + * InterWave FFFF Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_IW_H +#define __SOUND_AINSTR_IW_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define IWFFFF_SHARE_FILE 0 + +/* + * wave formats + */ + +#define IWFFFF_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define IWFFFF_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define IWFFFF_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define IWFFFF_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define IWFFFF_WAVE_LOOP 0x0008 /* loop mode */ +#define IWFFFF_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define IWFFFF_WAVE_ULAW 0x0020 /* uLaw compressed wave */ +#define IWFFFF_WAVE_RAM 0x0040 /* wave is _preloaded_ in RAM (it is used for ROM simulation) */ +#define IWFFFF_WAVE_ROM 0x0080 /* wave is in ROM */ +#define IWFFFF_WAVE_STEREO 0x0100 /* wave is stereo */ + +/* + * Wavetable definitions + */ + +typedef struct iwffff_wave { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this wave */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + unsigned int sample_ratio; /* sample ratio (44100 * 1024 / rate) */ + unsigned char attenuation; /* 0 - 127 (no corresponding midi controller) */ + unsigned char low_note; /* lower frequency range for this waveform */ + unsigned char high_note; /* higher frequency range for this waveform */ + unsigned char pad; + + struct iwffff_wave *next; +} iwffff_wave_t; + +/* + * Layer + */ + +#define IWFFFF_LFO_SHAPE_TRIANGLE 0 +#define IWFFFF_LFO_SHAPE_POSTRIANGLE 1 + +typedef struct iwffff_lfo { + unsigned short freq; /* (0-2047) 0.01Hz - 21.5Hz */ + signed short depth; /* volume +- (0-255) 0.48675dB/step */ + signed short sweep; /* 0 - 950 deciseconds */ + unsigned char shape; /* see to IWFFFF_LFO_SHAPE_XXXX */ + unsigned char delay; /* 0 - 255 deciseconds */ +} iwffff_lfo_t; + +#define IWFFFF_ENV_FLAG_RETRIGGER 0x0001 /* flag - retrigger */ + +#define IWFFFF_ENV_MODE_ONE_SHOT 0x0001 /* mode - one shot */ +#define IWFFFF_ENV_MODE_SUSTAIN 0x0002 /* mode - sustain */ +#define IWFFFF_ENV_MODE_NO_SUSTAIN 0x0003 /* mode - no sustain */ + +#define IWFFFF_ENV_INDEX_VELOCITY 0x0001 /* index - velocity */ +#define IWFFFF_ENV_INDEX_FREQUENCY 0x0002 /* index - frequency */ + +typedef struct iwffff_env_point { + unsigned short offset; + unsigned short rate; +} iwffff_env_point_t; + +typedef struct iwffff_env_record { + unsigned short nattack; + unsigned short nrelease; + unsigned short sustain_offset; + unsigned short sustain_rate; + unsigned short release_rate; + unsigned char hirange; + unsigned char pad; + struct iwffff_env_record *next; + /* points are stored here */ + /* count of points = nattack + nrelease */ +} iwffff_env_record_t; + +typedef struct iwffff_env { + unsigned char flags; + unsigned char mode; + unsigned char index; + unsigned char pad; + struct iwffff_env_record *record; +} iwffff_env_t; + +#define IWFFFF_LAYER_FLAG_RETRIGGER 0x0001 /* retrigger */ + +#define IWFFFF_LAYER_VELOCITY_TIME 0x0000 /* velocity mode = time */ +#define IWFFFF_LAYER_VELOCITY_RATE 0x0001 /* velocity mode = rate */ + +#define IWFFFF_LAYER_EVENT_KUP 0x0000 /* layer event - key up */ +#define IWFFFF_LAYER_EVENT_KDOWN 0x0001 /* layer event - key down */ +#define IWFFFF_LAYER_EVENT_RETRIG 0x0002 /* layer event - retrigger */ +#define IWFFFF_LAYER_EVENT_LEGATO 0x0003 /* layer event - legato */ + +typedef struct iwffff_layer { + unsigned char flags; + unsigned char velocity_mode; + unsigned char layer_event; + unsigned char low_range; /* range for layer based */ + unsigned char high_range; /* on either velocity or frequency */ + unsigned char pan; /* pan offset from CC1 (0 left - 127 right) */ + unsigned char pan_freq_scale; /* position based on frequency (0-127) */ + unsigned char attenuation; /* 0-127 (no corresponding midi controller) */ + iwffff_lfo_t tremolo; /* tremolo effect */ + iwffff_lfo_t vibrato; /* vibrato effect */ + unsigned short freq_scale; /* 0-2048, 1024 is equal to semitone scaling */ + unsigned char freq_center; /* center for keyboard frequency scaling */ + unsigned char pad; + iwffff_env_t penv; /* pitch envelope */ + iwffff_env_t venv; /* volume envelope */ + + iwffff_wave_t *wave; + struct iwffff_layer *next; +} iwffff_layer_t; + +/* + * Instrument + */ + +#define IWFFFF_EXCLUDE_NONE 0x0000 /* exclusion mode - none */ +#define IWFFFF_EXCLUDE_SINGLE 0x0001 /* exclude single - single note from the instrument group */ +#define IWFFFF_EXCLUDE_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define IWFFFF_LAYER_NONE 0x0000 /* not layered */ +#define IWFFFF_LAYER_ON 0x0001 /* layered */ +#define IWFFFF_LAYER_VELOCITY 0x0002 /* layered by velocity */ +#define IWFFFF_LAYER_FREQUENCY 0x0003 /* layered by frequency */ + +#define IWFFFF_EFFECT_NONE 0 +#define IWFFFF_EFFECT_REVERB 1 +#define IWFFFF_EFFECT_CHORUS 2 +#define IWFFFF_EFFECT_ECHO 3 + +typedef struct { + unsigned short exclusion; + unsigned short layer_type; + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ + + iwffff_layer_t *layer; /* first layer */ +} iwffff_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * iwffff_xinstrument IWFFFF_STRU_INSTR + * +iwffff_xlayer IWFFFF_STRU_LAYER + * *iwffff_xenv_record IWFFFF_STRU_ENV_RECT (tremolo) + * *iwffff_xenv_record IWFFFF_STRU_EVN_RECT (vibrato) + * +iwffff_xwave IWFFFF_STRU_WAVE + * + */ + +#define IWFFFF_STRU_WAVE __cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E') +#define IWFFFF_STRU_ENV_RECP __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P') +#define IWFFFF_STRU_ENV_RECV __cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V') +#define IWFFFF_STRU_LAYER __cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R') +#define IWFFFF_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Wavetable definitions + */ + +typedef struct iwffff_xwave { + __u32 stype; /* structure type */ + + __u32 share_id[4]; /* share id - zero = no sharing */ + + __u32 format; /* wave format */ + __u32 offset; /* offset to ROM (address) */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + __u32 sample_ratio; /* sample ratio (44100 * 1024 / rate) */ + __u8 attenuation; /* 0 - 127 (no corresponding midi controller) */ + __u8 low_note; /* lower frequency range for this waveform */ + __u8 high_note; /* higher frequency range for this waveform */ + __u8 pad; +} iwffff_xwave_t; + +/* + * Layer + */ + +typedef struct iwffff_xlfo { + __u16 freq; /* (0-2047) 0.01Hz - 21.5Hz */ + __s16 depth; /* volume +- (0-255) 0.48675dB/step */ + __s16 sweep; /* 0 - 950 deciseconds */ + __u8 shape; /* see to ULTRA_IW_LFO_SHAPE_XXXX */ + __u8 delay; /* 0 - 255 deciseconds */ +} iwffff_xlfo_t; + +typedef struct iwffff_xenv_point { + __u16 offset; + __u16 rate; +} iwffff_xenv_point_t; + +typedef struct iwffff_xenv_record { + __u32 stype; + __u16 nattack; + __u16 nrelease; + __u16 sustain_offset; + __u16 sustain_rate; + __u16 release_rate; + __u8 hirange; + __u8 pad; + /* points are stored here.. */ + /* count of points = nattack + nrelease */ +} iwffff_xenv_record_t; + +typedef struct iwffff_xenv { + __u8 flags; + __u8 mode; + __u8 index; + __u8 pad; +} iwffff_xenv_t; + +typedef struct iwffff_xlayer { + __u32 stype; + __u8 flags; + __u8 velocity_mode; + __u8 layer_event; + __u8 low_range; /* range for layer based */ + __u8 high_range; /* on either velocity or frequency */ + __u8 pan; /* pan offset from CC1 (0 left - 127 right) */ + __u8 pan_freq_scale; /* position based on frequency (0-127) */ + __u8 attenuation; /* 0-127 (no corresponding midi controller) */ + iwffff_xlfo_t tremolo; /* tremolo effect */ + iwffff_xlfo_t vibrato; /* vibrato effect */ + __u16 freq_scale; /* 0-2048, 1024 is equal to semitone scaling */ + __u8 freq_center; /* center for keyboard frequency scaling */ + __u8 pad; + iwffff_xenv_t penv; /* pitch envelope */ + iwffff_xenv_t venv; /* volume envelope */ +} iwffff_xlayer_t; + +/* + * Instrument + */ + +typedef struct iwffff_xinstrument { + __u32 stype; + + __u16 exclusion; + __u16 layer_type; + __u16 exclusion_group; /* 0 - none, 1-65535 */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} iwffff_xinstrument_t; + +/* + * ROM support + * InterWave ROMs are Little-Endian (x86) + */ + +#define IWFFFF_ROM_HDR_SIZE 512 + +typedef struct { + __u8 iwave[8]; + __u8 revision; + __u8 series_number; + __u8 series_name[16]; + __u8 date[10]; + __u16 vendor_revision_major; + __u16 vendor_revision_minor; + __u32 rom_size; + __u8 copyright[128]; + __u8 vendor_name[64]; + __u8 description[128]; +} iwffff_rom_header_t; + +/* + * Instrument info + */ + +#define IWFFFF_INFO_LFO_VIBRATO (1<<0) +#define IWFFFF_INFO_LFO_VIBRATO_SHAPE (1<<1) +#define IWFFFF_INFO_LFO_TREMOLO (1<<2) +#define IWFFFF_INFO_LFO_TREMOLO_SHAPE (1<<3) + +typedef struct iwffff_info { + unsigned int format; /* supported format bits */ + unsigned int effects; /* supported effects (1 << IWFFFF_EFFECT*) */ + unsigned int lfos; /* LFO effects */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} iwffff_info_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_iwffff_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, iwffff_info_t *info); + int (*put_sample)(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, iwffff_wave_t *wave, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_iwffff_ops_t; + +int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_IW_H */ diff -Nru linux/include/sound/ainstr_simple.h linux-2.4.19-pre5-mjc/include/sound/ainstr_simple.h --- linux/include/sound/ainstr_simple.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ainstr_simple.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,158 @@ +/* + * Advanced Linux Sound Architecture + * + * Simple (MOD player) Instrument Format + * Copyright (c) 1994-99 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_AINSTR_SIMPLE_H +#define __SOUND_AINSTR_SIMPLE_H + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * share types (share ID 1) + */ + +#define SIMPLE_SHARE_FILE 0 + +/* + * wave formats + */ + +#define SIMPLE_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define SIMPLE_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define SIMPLE_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define SIMPLE_WAVE_BACKWARD 0x0004 /* backward mode (maybe used for reverb or ping-ping loop) */ +#define SIMPLE_WAVE_LOOP 0x0008 /* loop mode */ +#define SIMPLE_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define SIMPLE_WAVE_STEREO 0x0100 /* stereo wave */ +#define SIMPLE_WAVE_ULAW 0x0200 /* uLaw compression mode */ + +/* + * instrument effects + */ + +#define SIMPLE_EFFECT_NONE 0 +#define SIMPLE_EFFECT_REVERB 1 +#define SIMPLE_EFFECT_CHORUS 2 +#define SIMPLE_EFFECT_ECHO 3 + +/* + * instrument info + */ + +typedef struct simple_instrument_info { + unsigned int format; /* supported format bits */ + unsigned int effects; /* supported effects (1 << SIMPLE_EFFECT_*) */ + unsigned int max8_len; /* maximum 8-bit wave length */ + unsigned int max16_len; /* maximum 16-bit wave length */ +} simple_instrument_info_t; + +/* + * Instrument + */ + +typedef struct { + unsigned int share_id[4]; /* share id - zero = no sharing */ + unsigned int format; /* wave format */ + + struct { + unsigned int number; /* some other ID for this instrument */ + unsigned int memory; /* begin of waveform in onboard memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } address; + + unsigned int size; /* size of waveform in samples */ + unsigned int start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop end offset in samples * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + + unsigned char effect1; /* effect 1 */ + unsigned char effect1_depth; /* 0-127 */ + unsigned char effect2; /* effect 2 */ + unsigned char effect2_depth; /* 0-127 */ +} simple_instrument_t; + +/* + * + * Kernel <-> user space + * Hardware (CPU) independent section + * + * * = zero or more + * + = one or more + * + * simple_xinstrument SIMPLE_STRU_INSTR + * + */ + +#define SIMPLE_STRU_INSTR __cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T') + +/* + * Instrument + */ + +typedef struct simple_xinstrument { + __u32 stype; + + __u32 share_id[4]; /* share id - zero = no sharing */ + __u32 format; /* wave format */ + + __u32 size; /* size of waveform in samples */ + __u32 start; /* start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_start; /* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u32 loop_end; /* loop start offset in samples * 16 (lowest 4 bits - fraction) */ + __u16 loop_repeat; /* loop repeat - 0 = forever */ + + __u8 effect1; /* effect 1 */ + __u8 effect1_depth; /* 0-127 */ + __u8 effect2; /* effect 2 */ + __u8 effect2_depth; /* 0-127 */ +} simple_xinstrument_t; + +#ifdef __KERNEL__ + +#include "seq_instr.h" + +extern char *snd_seq_simple_id; + +typedef struct { + void *private_data; + int (*info)(void *private_data, simple_instrument_info_t *info); + int (*put_sample)(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); + int (*get_sample)(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); + int (*remove_sample)(void *private_data, simple_instrument_t *instr, + int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what); + snd_seq_kinstr_ops_t kops; +} snd_simple_ops_t; + +int snd_seq_simple_init(snd_simple_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next); + +#endif + +#endif /* __SOUND_AINSTR_SIMPLE_H */ diff -Nru linux/include/sound/ak4531_codec.h linux-2.4.19-pre5-mjc/include/sound/ak4531_codec.h --- linux/include/sound/ak4531_codec.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ak4531_codec.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,81 @@ +#ifndef __SOUND_AK4531_CODEC_H +#define __SOUND_AK4531_CODEC_H + +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "info.h" +#include "control.h" + +/* + * ASAHI KASEI - AK4531 codec + * - not really AC'97 codec, but it uses very similar interface as AC'97 + */ + +/* + * AK4531 codec registers + */ + +#define AK4531_LMASTER 0x00 /* master volume left */ +#define AK4531_RMASTER 0x01 /* master volume right */ +#define AK4531_LVOICE 0x02 /* channel volume left */ +#define AK4531_RVOICE 0x03 /* channel volume right */ +#define AK4531_LFM 0x04 /* FM volume left */ +#define AK4531_RFM 0x05 /* FM volume right */ +#define AK4531_LCD 0x06 /* CD volume left */ +#define AK4531_RCD 0x07 /* CD volume right */ +#define AK4531_LLINE 0x08 /* LINE volume left */ +#define AK4531_RLINE 0x09 /* LINE volume right */ +#define AK4531_LAUXA 0x0a /* AUXA volume left */ +#define AK4531_RAUXA 0x0b /* AUXA volume right */ +#define AK4531_MONO1 0x0c /* MONO1 volume left */ +#define AK4531_MONO2 0x0d /* MONO1 volume right */ +#define AK4531_MIC 0x0e /* MIC volume */ +#define AK4531_MONO_OUT 0x0f /* Mono-out volume */ +#define AK4531_OUT_SW1 0x10 /* Output mixer switch 1 */ +#define AK4531_OUT_SW2 0x11 /* Output mixer switch 2 */ +#define AK4531_LIN_SW1 0x12 /* Input left mixer switch 1 */ +#define AK4531_RIN_SW1 0x13 /* Input right mixer switch 1 */ +#define AK4531_LIN_SW2 0x14 /* Input left mixer switch 2 */ +#define AK4531_RIN_SW2 0x15 /* Input right mixer switch 2 */ +#define AK4531_RESET 0x16 /* Reset & power down */ +#define AK4531_CLOCK 0x17 /* Clock select */ +#define AK4531_AD_IN 0x18 /* AD input select */ +#define AK4531_MIC_GAIN 0x19 /* MIC amplified gain */ + +typedef struct _snd_ak4531 ak4531_t; + +struct _snd_ak4531 { + void (*write) (ak4531_t *ak4531, unsigned short reg, unsigned short val); + snd_info_entry_t *proc_entry; + void *private_data; + void (*private_free) (ak4531_t *ak4531); + /* --- */ + unsigned char regs[0x20]; + spinlock_t reg_lock; +}; + +int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531); + +#endif /* __SOUND_AK4531_CODEC_H */ diff -Nru linux/include/sound/asequencer.h linux-2.4.19-pre5-mjc/include/sound/asequencer.h --- linux/include/sound/asequencer.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/asequencer.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,918 @@ +/* + * Main header file for the ALSA sequencer + * Copyright (c) 1998-1999 by Frank van de Pol + * (c) 1998-1999 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SOUND_ASEQUENCER_H +#define __SOUND_ASEQUENCER_H + +#ifndef __KERNEL__ +#include +#include +#endif + +#include + +/** version of the sequencer */ +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 0) + +/** + * definition of sequencer event types + */ + +/** system messages + * event data type = #sndrv_seq_result_t + */ +#define SNDRV_SEQ_EVENT_SYSTEM 0 +#define SNDRV_SEQ_EVENT_RESULT 1 + +/** note messages (channel specific) + * event data type = #sndrv_seq_ev_note + */ +#define SNDRV_SEQ_EVENT_NOTE 5 +#define SNDRV_SEQ_EVENT_NOTEON 6 +#define SNDRV_SEQ_EVENT_NOTEOFF 7 +#define SNDRV_SEQ_EVENT_KEYPRESS 8 + +/** control messages (channel specific) + * event data type = #sndrv_seq_ev_ctrl + */ +#define SNDRV_SEQ_EVENT_CONTROLLER 10 +#define SNDRV_SEQ_EVENT_PGMCHANGE 11 +#define SNDRV_SEQ_EVENT_CHANPRESS 12 +#define SNDRV_SEQ_EVENT_PITCHBEND 13 /**< from -8192 to 8191 */ +#define SNDRV_SEQ_EVENT_CONTROL14 14 /**< 14 bit controller value */ +#define SNDRV_SEQ_EVENT_NONREGPARAM 15 /**< 14 bit NRPN */ +#define SNDRV_SEQ_EVENT_REGPARAM 16 /**< 14 bit RPN */ + +/** synchronisation messages + * event data type = #sndrv_seq_ev_ctrl + */ +#define SNDRV_SEQ_EVENT_SONGPOS 20 /* Song Position Pointer with LSB and MSB values */ +#define SNDRV_SEQ_EVENT_SONGSEL 21 /* Song Select with song ID number */ +#define SNDRV_SEQ_EVENT_QFRAME 22 /* midi time code quarter frame */ +#define SNDRV_SEQ_EVENT_TIMESIGN 23 /* SMF Time Signature event */ +#define SNDRV_SEQ_EVENT_KEYSIGN 24 /* SMF Key Signature event */ + +/** timer messages + * event data type = sndrv_seq_ev_queue_control_t + */ +#define SNDRV_SEQ_EVENT_START 30 /* midi Real Time Start message */ +#define SNDRV_SEQ_EVENT_CONTINUE 31 /* midi Real Time Continue message */ +#define SNDRV_SEQ_EVENT_STOP 32 /* midi Real Time Stop message */ +#define SNDRV_SEQ_EVENT_SETPOS_TICK 33 /* set tick queue position */ +#define SNDRV_SEQ_EVENT_SETPOS_TIME 34 /* set realtime queue position */ +#define SNDRV_SEQ_EVENT_TEMPO 35 /* (SMF) Tempo event */ +#define SNDRV_SEQ_EVENT_CLOCK 36 /* midi Real Time Clock message */ +#define SNDRV_SEQ_EVENT_TICK 37 /* midi Real Time Tick message */ +#define SNDRV_SEQ_EVENT_QUEUE_SKEW 38 /* skew queue tempo */ + +/** others + * event data type = none + */ +#define SNDRV_SEQ_EVENT_TUNE_REQUEST 40 /* tune request */ +#define SNDRV_SEQ_EVENT_RESET 41 /* reset to power-on state */ +#define SNDRV_SEQ_EVENT_SENSING 42 /* "active sensing" event */ + +/** echo back, kernel private messages + * event data type = any type + */ +#define SNDRV_SEQ_EVENT_ECHO 50 /* echo event */ +#define SNDRV_SEQ_EVENT_OSS 51 /* OSS raw event */ + +/** system status messages (broadcast for subscribers) + * event data type = sndrv_seq_addr_t + */ +#define SNDRV_SEQ_EVENT_CLIENT_START 60 /* new client has connected */ +#define SNDRV_SEQ_EVENT_CLIENT_EXIT 61 /* client has left the system */ +#define SNDRV_SEQ_EVENT_CLIENT_CHANGE 62 /* client status/info has changed */ +#define SNDRV_SEQ_EVENT_PORT_START 63 /* new port was created */ +#define SNDRV_SEQ_EVENT_PORT_EXIT 64 /* port was deleted from system */ +#define SNDRV_SEQ_EVENT_PORT_CHANGE 65 /* port status/info has changed */ + +/** port connection changes + * event data type = sndrv_seq_connect_t + */ +#define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED 66 /* ports connected */ +#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67 /* ports disconnected */ + +/** synthesizer events + * event data type = sndrv_seq_eve_sample_control_t + */ +#define SNDRV_SEQ_EVENT_SAMPLE 70 /* sample select */ +#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER 71 /* sample cluster select */ +#define SNDRV_SEQ_EVENT_SAMPLE_START 72 /* voice start */ +#define SNDRV_SEQ_EVENT_SAMPLE_STOP 73 /* voice stop */ +#define SNDRV_SEQ_EVENT_SAMPLE_FREQ 74 /* playback frequency */ +#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME 75 /* volume and balance */ +#define SNDRV_SEQ_EVENT_SAMPLE_LOOP 76 /* sample loop */ +#define SNDRV_SEQ_EVENT_SAMPLE_POSITION 77 /* sample position */ +#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1 78 /* private (hardware dependent) event */ + +/** user-defined events with fixed length + * event data type = any + */ +#define SNDRV_SEQ_EVENT_USR0 90 +#define SNDRV_SEQ_EVENT_USR1 91 +#define SNDRV_SEQ_EVENT_USR2 92 +#define SNDRV_SEQ_EVENT_USR3 93 +#define SNDRV_SEQ_EVENT_USR4 94 +#define SNDRV_SEQ_EVENT_USR5 95 +#define SNDRV_SEQ_EVENT_USR6 96 +#define SNDRV_SEQ_EVENT_USR7 97 +#define SNDRV_SEQ_EVENT_USR8 98 +#define SNDRV_SEQ_EVENT_USR9 99 + +/** instrument layer + * variable length data can be passed directly to the driver + */ +#define SNDRV_SEQ_EVENT_INSTR_BEGIN 100 /* begin of instrument management */ +#define SNDRV_SEQ_EVENT_INSTR_END 101 /* end of instrument management */ +#define SNDRV_SEQ_EVENT_INSTR_INFO 102 /* instrument interface info */ +#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_FINFO 104 /* get format info */ +#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105 /* get format info */ +#define SNDRV_SEQ_EVENT_INSTR_RESET 106 /* reset instrument memory */ +#define SNDRV_SEQ_EVENT_INSTR_STATUS 107 /* instrument interface status */ +#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_PUT 109 /* put instrument to port */ +#define SNDRV_SEQ_EVENT_INSTR_GET 110 /* get instrument from port */ +#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_FREE 112 /* free instrument(s) */ +#define SNDRV_SEQ_EVENT_INSTR_LIST 113 /* instrument list */ +#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER 115 /* cluster parameters */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116 /* get cluster parameters */ +#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */ +#define SNDRV_SEQ_EVENT_INSTR_CHANGE 118 /* instrument change */ +/* 119-129: reserved */ + +/* 130-139: variable length events + * event data type = sndrv_seq_ev_ext + * (SNDRV_SEQ_EVENT_LENGTH_VARIABLE must be set) + */ +#define SNDRV_SEQ_EVENT_SYSEX 130 /* system exclusive data (variable length) */ +#define SNDRV_SEQ_EVENT_BOUNCE 131 /* error event */ +/* 132-134: reserved */ +#define SNDRV_SEQ_EVENT_USR_VAR0 135 +#define SNDRV_SEQ_EVENT_USR_VAR1 136 +#define SNDRV_SEQ_EVENT_USR_VAR2 137 +#define SNDRV_SEQ_EVENT_USR_VAR3 138 +#define SNDRV_SEQ_EVENT_USR_VAR4 139 + +/* 140-149: IPC shared memory events (*NOT SUPPORTED YET*) + * event data type = sndrv_seq_ev_ipcshm + * (SNDRV_SEQ_EVENT_LENGTH_VARIPC must be set) + */ +#define SNDRV_SEQ_EVENT_IPCSHM 140 +/* 141-144: reserved */ +#define SNDRV_SEQ_EVENT_USR_VARIPC0 145 +#define SNDRV_SEQ_EVENT_USR_VARIPC1 146 +#define SNDRV_SEQ_EVENT_USR_VARIPC2 147 +#define SNDRV_SEQ_EVENT_USR_VARIPC3 148 +#define SNDRV_SEQ_EVENT_USR_VARIPC4 149 + +/* 150-151: kernel events with quote - DO NOT use in user clients */ +#define SNDRV_SEQ_EVENT_KERNEL_ERROR 150 +#define SNDRV_SEQ_EVENT_KERNEL_QUOTE 151 + +/* 152-191: reserved */ + +/* 192-254: hardware specific events */ + +/* 255: special event */ +#define SNDRV_SEQ_EVENT_NONE 255 + + +typedef unsigned char sndrv_seq_event_type_t; + +/** event address */ +struct sndrv_seq_addr { + unsigned char client; /**< Client number: 0..255, 255 = broadcast to all clients */ + unsigned char port; /**< Port within client: 0..255, 255 = broadcast to all ports */ +}; + +/** port connection */ +struct sndrv_seq_connect { + struct sndrv_seq_addr sender; + struct sndrv_seq_addr dest; +}; + + +#define SNDRV_SEQ_ADDRESS_UNKNOWN 253 /* unknown source */ +#define SNDRV_SEQ_ADDRESS_SUBSCRIBERS 254 /* send event to all subscribed ports */ +#define SNDRV_SEQ_ADDRESS_BROADCAST 255 /* send event to all queues/clients/ports/channels */ +#define SNDRV_SEQ_QUEUE_DIRECT 253 /* direct dispatch */ + + /* event mode flag - NOTE: only 8 bits available! */ +#define SNDRV_SEQ_TIME_STAMP_TICK (0<<0) /* timestamp in clock ticks */ +#define SNDRV_SEQ_TIME_STAMP_REAL (1<<0) /* timestamp in real time */ +#define SNDRV_SEQ_TIME_STAMP_MASK (1<<0) + +#define SNDRV_SEQ_TIME_MODE_ABS (0<<1) /* absolute timestamp */ +#define SNDRV_SEQ_TIME_MODE_REL (1<<1) /* relative to current time */ +#define SNDRV_SEQ_TIME_MODE_MASK (1<<1) + +#define SNDRV_SEQ_EVENT_LENGTH_FIXED (0<<2) /* fixed event size */ +#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /* variable event size */ +#define SNDRV_SEQ_EVENT_LENGTH_VARUSR (2<<2) /* variable event size - user memory space */ +#define SNDRV_SEQ_EVENT_LENGTH_VARIPC (3<<2) /* variable event size - IPC */ +#define SNDRV_SEQ_EVENT_LENGTH_MASK (3<<2) + +#define SNDRV_SEQ_PRIORITY_NORMAL (0<<4) /* normal priority */ +#define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */ +#define SNDRV_SEQ_PRIORITY_MASK (1<<4) + + + /* note event */ +struct sndrv_seq_ev_note { + unsigned char channel; + unsigned char note; + unsigned char velocity; + unsigned char off_velocity; /* only for SNDRV_SEQ_EVENT_NOTE */ + unsigned int duration; /* only for SNDRV_SEQ_EVENT_NOTE */ +}; + + /* controller event */ +struct sndrv_seq_ev_ctrl { + unsigned char channel; + unsigned char unused1, unused2, unused3; /* pad */ + unsigned int param; + signed int value; +}; + + /* generic set of bytes (12x8 bit) */ +struct sndrv_seq_ev_raw8 { + unsigned char d[12]; /* 8 bit value */ +}; + + /* generic set of integers (3x32 bit) */ +struct sndrv_seq_ev_raw32 { + unsigned int d[3]; /* 32 bit value */ +}; + + /* external stored data */ +struct sndrv_seq_ev_ext { + size_t len; /* length of data */ + void *ptr; /* pointer to data (note: maybe 64-bit) */ +}; + + /* external stored data - IPC shared memory */ +struct sndrv_seq_ev_ipcshm { + size_t len; /* length of data */ + key_t ipc; /* IPC key */ +}; + +/* Instrument cluster type */ +typedef unsigned int sndrv_seq_instr_cluster_t; + +/* Instrument type */ +struct sndrv_seq_instr { + sndrv_seq_instr_cluster_t cluster; + unsigned int std; /* the upper byte means a private instrument (owner - client #) */ + unsigned short bank; + unsigned short prg; +}; + + /* sample number */ +struct sndrv_seq_ev_sample { + unsigned int std; + unsigned short bank; + unsigned short prg; +}; + + /* sample cluster */ +struct sndrv_seq_ev_cluster { + sndrv_seq_instr_cluster_t cluster; +}; + + /* sample position */ +typedef unsigned int sndrv_seq_position_t; /* playback position (in samples) * 16 */ + + /* sample stop mode */ +enum sndrv_seq_stop_mode { + SAMPLE_STOP_IMMEDIATELY = 0, /* terminate playing immediately */ + SAMPLE_STOP_VENVELOPE = 1, /* finish volume envelope */ + SAMPLE_STOP_LOOP = 2 /* terminate loop and finish wave */ +}; + + /* sample frequency */ +typedef int sndrv_seq_frequency_t; /* playback frequency in HZ * 16 */ + + /* sample volume control; if any value is set to -1 == do not change */ +struct sndrv_seq_ev_volume { + signed short volume; /* range: 0-16383 */ + signed short lr; /* left-right balance; range: 0-16383 */ + signed short fr; /* front-rear balance; range: 0-16383 */ + signed short du; /* down-up balance; range: 0-16383 */ +}; + + /* simple loop redefinition */ +struct sndrv_seq_ev_loop { + unsigned int start; /* loop start (in samples) * 16 */ + unsigned int end; /* loop end (in samples) * 16 */ +}; + +struct sndrv_seq_ev_sample_control { + unsigned char channel; + unsigned char unused1, unused2, unused3; /* pad */ + union { + struct sndrv_seq_ev_sample sample; + struct sndrv_seq_ev_cluster cluster; + sndrv_seq_position_t position; + enum sndrv_seq_stop_mode stop_mode; + sndrv_seq_frequency_t frequency; + struct sndrv_seq_ev_volume volume; + struct sndrv_seq_ev_loop loop; + unsigned char raw8[8]; + } param; +}; + + + +/* INSTR_BEGIN event */ +struct sndrv_seq_ev_instr_begin { + int timeout; /* zero = forever, otherwise timeout in ms */ +}; + +struct sndrv_seq_result { + int event; /* processed event type */ + int result; +}; + + +struct sndrv_seq_real_time { + unsigned int tv_sec; /* seconds */ + unsigned int tv_nsec; /* nanoseconds */ +}; + +typedef unsigned int sndrv_seq_tick_time_t; /* midi ticks */ + +union sndrv_seq_timestamp { + sndrv_seq_tick_time_t tick; + struct sndrv_seq_real_time time; +}; + +struct sndrv_seq_queue_skew { + unsigned int value; + unsigned int base; +}; + + /* queue timer control */ +struct sndrv_seq_ev_queue_control { + unsigned char queue; /* affected queue */ + unsigned char pad[3]; /* reserved */ + union { + signed int value; /* affected value (e.g. tempo) */ + union sndrv_seq_timestamp time; /* time */ + unsigned int position; /* sync position */ + struct sndrv_seq_queue_skew skew; + unsigned int d32[2]; + unsigned char d8[8]; + } param; +}; + + /* quoted event - inside the kernel only */ +struct sndrv_seq_ev_quote { + struct sndrv_seq_addr origin; /* original sender */ + unsigned short value; /* optional data */ + struct sndrv_seq_event *event; /* quoted event */ +}; + + + /* sequencer event */ +struct sndrv_seq_event { + sndrv_seq_event_type_t type; /* event type */ + unsigned char flags; /* event flags */ + char tag; + + unsigned char queue; /* schedule queue */ + union sndrv_seq_timestamp time; /* schedule time */ + + + struct sndrv_seq_addr source; /* source address */ + struct sndrv_seq_addr dest; /* destination address */ + + union { /* event data... */ + struct sndrv_seq_ev_note note; + struct sndrv_seq_ev_ctrl control; + struct sndrv_seq_ev_raw8 raw8; + struct sndrv_seq_ev_raw32 raw32; + struct sndrv_seq_ev_ext ext; + struct sndrv_seq_ev_ipcshm ipcshm; + struct sndrv_seq_ev_queue_control queue; + union sndrv_seq_timestamp time; + struct sndrv_seq_addr addr; + struct sndrv_seq_connect connect; + struct sndrv_seq_result result; + struct sndrv_seq_ev_instr_begin instr_begin; + struct sndrv_seq_ev_sample_control sample; + struct sndrv_seq_ev_quote quote; + } data; +}; + + +/* + * bounce event - stored as variable size data + */ +struct sndrv_seq_event_bounce { + int err; + struct sndrv_seq_event event; + /* external data follows here. */ +}; + +#define sndrv_seq_event_bounce_ext_data(ev) ((void*)((char *)(ev)->data.ext.ptr + sizeof(sndrv_seq_event_bounce_t))) + +/* + * type check macros + */ +/* result events: 0-4 */ +#define sndrv_seq_ev_is_result_type(ev) ((ev)->type < 5) +/* channel specific events: 5-19 */ +#define sndrv_seq_ev_is_channel_type(ev) ((ev)->type >= 5 && (ev)->type < 20) +/* note events: 5-9 */ +#define sndrv_seq_ev_is_note_type(ev) ((ev)->type >= 5 && (ev)->type < 10) +/* control events: 10-19 */ +#define sndrv_seq_ev_is_control_type(ev) ((ev)->type >= 10 && (ev)->type < 20) +/* queue control events: 30-39 */ +#define sndrv_seq_ev_is_queue_type(ev) ((ev)->type >= 30 && (ev)->type < 40) +/* system status messages */ +#define sndrv_seq_ev_is_message_type(ev) ((ev)->type >= 60 && (ev)->type < 69) +/* sample messages */ +#define sndrv_seq_ev_is_sample_type(ev) ((ev)->type >= 70 && (ev)->type < 79) +/* user-defined messages */ +#define sndrv_seq_ev_is_user_type(ev) ((ev)->type >= 90 && (ev)->type < 99) +/* fixed length events: 0-99 */ +#define sndrv_seq_ev_is_fixed_type(ev) ((ev)->type < 100) +/* instrument layer events: 100-129 */ +#define sndrv_seq_ev_is_instr_type(ev) ((ev)->type >= 100 && (ev)->type < 130) +/* variable length events: 130-139 */ +#define sndrv_seq_ev_is_variable_type(ev) ((ev)->type >= 130 && (ev)->type < 140) +/* ipc shmem events: 140-149 */ +#define sndrv_seq_ev_is_varipc_type(ev) ((ev)->type >= 140 && (ev)->type < 150) +/* reserved for kernel */ +#define sndrv_seq_ev_is_reserved(ev) ((ev)->type >= 150) + +/* direct dispatched events */ +#define sndrv_seq_ev_is_direct(ev) ((ev)->queue == SNDRV_SEQ_QUEUE_DIRECT) + +/* + * macros to check event flags + */ +/* prior events */ +#define sndrv_seq_ev_is_prior(ev) (((ev)->flags & SNDRV_SEQ_PRIORITY_MASK) == SNDRV_SEQ_PRIORITY_HIGH) + +/* event length type */ +#define sndrv_seq_ev_length_type(ev) ((ev)->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) +#define sndrv_seq_ev_is_fixed(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_FIXED) +#define sndrv_seq_ev_is_variable(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) +#define sndrv_seq_ev_is_varusr(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARUSR) +#define sndrv_seq_ev_is_varipc(ev) (sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIPC) + +/* time-stamp type */ +#define sndrv_seq_ev_timestamp_type(ev) ((ev)->flags & SNDRV_SEQ_TIME_STAMP_MASK) +#define sndrv_seq_ev_is_tick(ev) (sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_TICK) +#define sndrv_seq_ev_is_real(ev) (sndrv_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_REAL) + +/* time-mode type */ +#define sndrv_seq_ev_timemode_type(ev) ((ev)->flags & SNDRV_SEQ_TIME_MODE_MASK) +#define sndrv_seq_ev_is_abstime(ev) (sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS) +#define sndrv_seq_ev_is_reltime(ev) (sndrv_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL) + +/* queue sync port */ +#define sndrv_seq_queue_sync_port(q) ((q) + 16) + + /* system information */ +struct sndrv_seq_system_info { + int queues; /* maximum queues count */ + int clients; /* maximum clients count */ + int ports; /* maximum ports per client */ + int channels; /* maximum channels per port */ + int cur_clients; /* current clients */ + int cur_queues; /* current queues */ + char reserved[24]; +}; + + + /* known client numbers */ +#define SNDRV_SEQ_CLIENT_SYSTEM 0 +#define SNDRV_SEQ_CLIENT_DUMMY 62 /* dummy ports */ +#define SNDRV_SEQ_CLIENT_OSS 63 /* oss sequencer emulator */ + + + /* client types */ +enum sndrv_seq_client_type { + NO_CLIENT = 0, + USER_CLIENT = 1, + KERNEL_CLIENT = 2 +}; + + /* event filter flags */ +#define SNDRV_SEQ_FILTER_BROADCAST (1<<0) /* accept broadcast messages */ +#define SNDRV_SEQ_FILTER_MULTICAST (1<<1) /* accept multicast messages */ +#define SNDRV_SEQ_FILTER_BOUNCE (1<<2) /* accept bounce event in error */ +#define SNDRV_SEQ_FILTER_USE_EVENT (1<<31) /* use event filter */ + +struct sndrv_seq_client_info { + int client; /* client number to inquire */ + enum sndrv_seq_client_type type; /* client type */ + char name[64]; /* client name */ + unsigned int filter; /* filter flags */ + unsigned char multicast_filter[8]; /* multicast filter bitmap */ + unsigned char event_filter[32]; /* event filter bitmap */ + int num_ports; /* RO: number of ports */ + int event_lost; /* number of lost events */ + char reserved[64]; /* for future use */ +}; + + +/* client pool size */ +struct sndrv_seq_client_pool { + int client; /* client number to inquire */ + int output_pool; /* outgoing (write) pool size */ + int input_pool; /* incoming (read) pool size */ + int output_room; /* minimum free pool size for select/blocking mode */ + int output_free; /* unused size */ + int input_free; /* unused size */ + char reserved[64]; +}; + + +/* Remove events by specified criteria */ + +#define SNDRV_SEQ_REMOVE_INPUT (1<<0) /* Flush input queues */ +#define SNDRV_SEQ_REMOVE_OUTPUT (1<<1) /* Flush output queues */ +#define SNDRV_SEQ_REMOVE_DEST (1<<2) /* Restrict by destination q:client:port */ +#define SNDRV_SEQ_REMOVE_DEST_CHANNEL (1<<3) /* Restrict by channel */ +#define SNDRV_SEQ_REMOVE_TIME_BEFORE (1<<4) /* Restrict to before time */ +#define SNDRV_SEQ_REMOVE_TIME_AFTER (1<<5) /* Restrict to time or after */ +#define SNDRV_SEQ_REMOVE_TIME_TICK (1<<6) /* Time is in ticks */ +#define SNDRV_SEQ_REMOVE_EVENT_TYPE (1<<7) /* Restrict to event type */ +#define SNDRV_SEQ_REMOVE_IGNORE_OFF (1<<8) /* Do not flush off events */ +#define SNDRV_SEQ_REMOVE_TAG_MATCH (1<<9) /* Restrict to events with given tag */ + +struct sndrv_seq_remove_events { + unsigned int remove_mode; /* Flags that determine what gets removed */ + + union sndrv_seq_timestamp time; + + unsigned char queue; /* Queue for REMOVE_DEST */ + struct sndrv_seq_addr dest; /* Address for REMOVE_DEST */ + unsigned char channel; /* Channel for REMOVE_DEST */ + + int type; /* For REMOVE_EVENT_TYPE */ + char tag; /* Tag for REMOVE_TAG */ + + int reserved[10]; /* To allow for future binary compatibility */ + +}; + + + /* known port numbers */ +#define SNDRV_SEQ_PORT_SYSTEM_TIMER 0 +#define SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE 1 + + /* port capabilities (32 bits) */ +#define SNDRV_SEQ_PORT_CAP_READ (1<<0) /* readable from this port */ +#define SNDRV_SEQ_PORT_CAP_WRITE (1<<1) /* writable to this port */ + +#define SNDRV_SEQ_PORT_CAP_SYNC_READ (1<<2) +#define SNDRV_SEQ_PORT_CAP_SYNC_WRITE (1<<3) + +#define SNDRV_SEQ_PORT_CAP_DUPLEX (1<<4) + +#define SNDRV_SEQ_PORT_CAP_SUBS_READ (1<<5) /* allow read subscription */ +#define SNDRV_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /* allow write subscription */ +#define SNDRV_SEQ_PORT_CAP_NO_EXPORT (1<<7) /* routing not allowed */ + + /* port type */ +#define SNDRV_SEQ_PORT_TYPE_SPECIFIC (1<<0) /* hardware specific */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC (1<<1) /* generic MIDI device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GM (1<<2) /* General MIDI compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GS (1<<3) /* GS compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */ + +/* other standards...*/ +#define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device */ +#define SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11) /* Sampling device (support sample download) */ +#define SNDRV_SEQ_PORT_TYPE_SAMPLE (1<<12) /* Sampling device (sample can be downloaded at any time) */ +/*...*/ +#define SNDRV_SEQ_PORT_TYPE_APPLICATION (1<<20) /* application (sequencer/editor) */ + +/* misc. conditioning flags */ +#define SNDRV_SEQ_PORT_FLG_GIVEN_PORT (1<<0) + +struct sndrv_seq_port_info { + struct sndrv_seq_addr addr; /* client/port numbers */ + char name[64]; /* port name */ + + unsigned int capability; /* port capability bits */ + unsigned int type; /* port type bits */ + int midi_channels; /* channels per MIDI port */ + int midi_voices; /* voices per MIDI port */ + int synth_voices; /* voices per SYNTH port */ + + int read_use; /* R/O: subscribers for output (from this port) */ + int write_use; /* R/O: subscribers for input (to this port) */ + + void *kernel; /* reserved for kernel use (must be NULL) */ + + unsigned int flags; /* misc. conditioning */ + char reserved[60]; /* for future use */ +}; + + +/* queue flags */ +#define SNDRV_SEQ_QUEUE_FLG_SYNC (1<<0) /* sync enabled */ + +/* queue information */ +struct sndrv_seq_queue_info { + int queue; /* queue id */ + + /* + * security settings, only owner of this queue can start/stop timer + * etc. if the queue is locked for other clients + */ + int owner; /* client id for owner of the queue */ + int locked:1; /* timing queue locked for other queues */ + char name[64]; /* name of this queue */ + unsigned int flags; /* flags */ + char reserved[60]; /* for future use */ + +}; + +/* queue info/status */ +struct sndrv_seq_queue_status { + int queue; /* queue id */ + int events; /* read-only - queue size */ + sndrv_seq_tick_time_t tick; /* current tick */ + struct sndrv_seq_real_time time; /* current time */ + int running; /* running state of queue */ + int flags; /* various flags */ + char reserved[64]; /* for the future */ +}; + + +/* queue tempo */ +struct sndrv_seq_queue_tempo { + int queue; /* sequencer queue */ + unsigned int tempo; /* current tempo, us/tick */ + int ppq; /* time resolution, ticks/quarter */ + unsigned int skew_value; /* queue skew */ + unsigned int skew_base; /* queue skew base */ + char reserved[24]; /* for the future */ +}; + + +/* sequencer timer sources */ +#define SNDRV_SEQ_TIMER_ALSA 0 /* ALSA timer */ +#define SNDRV_SEQ_TIMER_MIDI_CLOCK 1 /* Midi Clock (CLOCK event) */ +#define SNDRV_SEQ_TIMER_MIDI_TICK 2 /* Midi Timer Tick (TICK event) */ + +/* queue timer info */ +struct sndrv_seq_queue_timer { + int queue; /* sequencer queue */ + int type; /* source timer type */ + union { + struct { + struct sndrv_timer_id id; /* ALSA's timer ID */ + unsigned int resolution; /* resolution in Hz */ + } alsa; + } u; + char reserved[64]; /* for the future use */ +}; + + +struct sndrv_seq_queue_client { + int queue; /* sequencer queue */ + int client; /* sequencer client */ + int used; /* queue is used with this client + (must be set for accepting events) */ + /* per client watermarks */ + char reserved[64]; /* for future use */ +}; + + +#define SNDRV_SEQ_PORT_SUBS_EXCLUSIVE (1<<0) /* exclusive connection */ +#define SNDRV_SEQ_PORT_SUBS_TIMESTAMP (1<<1) +#define SNDRV_SEQ_PORT_SUBS_TIME_REAL (1<<2) + +struct sndrv_seq_port_subscribe { + struct sndrv_seq_addr sender; /* sender address */ + struct sndrv_seq_addr dest; /* destination address */ + unsigned int voices; /* number of voices to be allocated (0 = don't care) */ + unsigned int flags; /* modes */ + unsigned char queue; /* input time-stamp queue (optional) */ + unsigned char pad[3]; /* reserved */ + char reserved[64]; +}; + +/* type of query subscription */ +#define SNDRV_SEQ_QUERY_SUBS_READ 0 +#define SNDRV_SEQ_QUERY_SUBS_WRITE 1 + +struct sndrv_seq_query_subs { + struct sndrv_seq_addr root; /* client/port id to be searched */ + int type; /* READ or WRITE */ + int index; /* 0..N-1 */ + int num_subs; /* R/O: number of subscriptions on this port */ + struct sndrv_seq_addr addr; /* R/O: result */ + unsigned char queue; /* R/O: result */ + unsigned int flags; /* R/O: result */ + char reserved[64]; /* for future use */ +}; + + +/* + * Instrument abstraction layer + * - based on events + */ + +/* instrument types */ +#define SNDRV_SEQ_INSTR_ATYPE_DATA 0 /* instrument data */ +#define SNDRV_SEQ_INSTR_ATYPE_ALIAS 1 /* instrument alias */ + +/* instrument ASCII identifiers */ +#define SNDRV_SEQ_INSTR_ID_DLS1 "DLS1" +#define SNDRV_SEQ_INSTR_ID_DLS2 "DLS2" +#define SNDRV_SEQ_INSTR_ID_SIMPLE "Simple Wave" +#define SNDRV_SEQ_INSTR_ID_SOUNDFONT "SoundFont" +#define SNDRV_SEQ_INSTR_ID_GUS_PATCH "GUS Patch" +#define SNDRV_SEQ_INSTR_ID_INTERWAVE "InterWave FFFF" +#define SNDRV_SEQ_INSTR_ID_OPL2_3 "OPL2/3 FM" +#define SNDRV_SEQ_INSTR_ID_OPL4 "OPL4" + +/* instrument types */ +#define SNDRV_SEQ_INSTR_TYPE0_DLS1 (1<<0) /* MIDI DLS v1 */ +#define SNDRV_SEQ_INSTR_TYPE0_DLS2 (1<<1) /* MIDI DLS v2 */ +#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE (1<<0) /* Simple Wave */ +#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT (1<<1) /* EMU SoundFont */ +#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH (1<<2) /* Gravis UltraSound Patch */ +#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE (1<<3) /* InterWave FFFF */ +#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3 (1<<0) /* Yamaha OPL2/3 FM */ +#define SNDRV_SEQ_INSTR_TYPE2_OPL4 (1<<1) /* Yamaha OPL4 */ + +/* put commands */ +#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE 0 +#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE 1 +#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY 2 +#define SNDRV_SEQ_INSTR_PUT_CMD_ADD 3 +#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE 4 + +/* get commands */ +#define SNDRV_SEQ_INSTR_GET_CMD_FULL 0 +#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL 1 + +/* query flags */ +#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0) + +/* free commands */ +#define SNDRV_SEQ_INSTR_FREE_CMD_ALL 0 +#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE 1 +#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER 2 +#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE 3 + +/* size of ROM/RAM */ +typedef unsigned int sndrv_seq_instr_size_t; + +/* INSTR_INFO */ + +struct sndrv_seq_instr_info { + int result; /* operation result */ + unsigned int formats[8]; /* bitmap of supported formats */ + int ram_count; /* count of RAM banks */ + sndrv_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */ + int rom_count; /* count of ROM banks */ + sndrv_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */ + char reserved[128]; +}; + +/* INSTR_STATUS */ + +struct sndrv_seq_instr_status { + int result; /* operation result */ + sndrv_seq_instr_size_t free_ram[16]; /* free RAM in banks */ + int instrument_count; /* count of downloaded instruments */ + char reserved[128]; +}; + +/* INSTR_FORMAT_INFO */ + +struct sndrv_seq_instr_format_info { + char format[16]; /* format identifier - SNDRV_SEQ_INSTR_ID_* */ + unsigned int len; /* max data length (without this structure) */ +}; + +struct sndrv_seq_instr_format_info_result { + int result; /* operation result */ + char format[16]; /* format identifier */ + unsigned int len; /* filled data length (without this structure) */ +}; + +/* instrument data */ +struct sndrv_seq_instr_data { + char name[32]; /* instrument name */ + char reserved[16]; /* for the future use */ + int type; /* instrument type */ + union { + char format[16]; /* format identifier */ + struct sndrv_seq_instr alias; + } data; +}; + +/* INSTR_PUT/GET, data are stored in one block (extended or IPC), header + data */ + +struct sndrv_seq_instr_header { + union { + struct sndrv_seq_instr instr; + sndrv_seq_instr_cluster_t cluster; + } id; /* instrument identifier */ + unsigned int cmd; /* get/put/free command */ + unsigned int flags; /* query flags (only for get) */ + unsigned int len; /* real instrument data length (without header) */ + int result; /* operation result */ + char reserved[16]; /* for the future */ + struct sndrv_seq_instr_data data; /* instrument data (for put/get result) */ +}; + +/* INSTR_CLUSTER_SET */ + +struct sndrv_seq_instr_cluster_set { + sndrv_seq_instr_cluster_t cluster; /* cluster identifier */ + char name[32]; /* cluster name */ + int priority; /* cluster priority */ + char reserved[64]; /* for the future use */ +}; + +/* INSTR_CLUSTER_GET */ + +struct sndrv_seq_instr_cluster_get { + sndrv_seq_instr_cluster_t cluster; /* cluster identifier */ + char name[32]; /* cluster name */ + int priority; /* cluster priority */ + char reserved[64]; /* for the future use */ +}; + +/* + * IOCTL commands + */ + +#define SNDRV_SEQ_IOCTL_PVERSION _IOR ('S', 0x00, int) +#define SNDRV_SEQ_IOCTL_CLIENT_ID _IOR ('S', 0x01, int) +#define SNDRV_SEQ_IOCTL_SYSTEM_INFO _IOWR('S', 0x02, struct sndrv_seq_system_info) + +#define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO _IOWR('S', 0x10, struct sndrv_seq_client_info) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO _IOW ('S', 0x11, struct sndrv_seq_client_info) + +#define SNDRV_SEQ_IOCTL_CREATE_PORT _IOWR('S', 0x20, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_DELETE_PORT _IOW ('S', 0x21, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_GET_PORT_INFO _IOWR('S', 0x22, struct sndrv_seq_port_info) +#define SNDRV_SEQ_IOCTL_SET_PORT_INFO _IOW ('S', 0x23, struct sndrv_seq_port_info) + +#define SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT _IOW ('S', 0x30, struct sndrv_seq_port_subscribe) +#define SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT _IOW ('S', 0x31, struct sndrv_seq_port_subscribe) + +#define SNDRV_SEQ_IOCTL_CREATE_QUEUE _IOWR('S', 0x32, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_DELETE_QUEUE _IOW ('S', 0x33, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_INFO _IOWR('S', 0x34, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_INFO _IOWR('S', 0x35, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE _IOWR('S', 0x36, struct sndrv_seq_queue_info) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct sndrv_seq_queue_status) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO _IOWR('S', 0x41, struct sndrv_seq_queue_tempo) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO _IOW ('S', 0x42, struct sndrv_seq_queue_tempo) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER _IOWR('S', 0x43, struct sndrv_seq_queue_owner) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER _IOW ('S', 0x44, struct sndrv_seq_queue_owner) +#define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER _IOWR('S', 0x45, struct sndrv_seq_queue_timer) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER _IOW ('S', 0x46, struct sndrv_seq_queue_timer) +/* XXX +#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct sndrv_seq_queue_sync) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct sndrv_seq_queue_sync) +*/ +#define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT _IOWR('S', 0x49, struct sndrv_seq_queue_client) +#define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT _IOW ('S', 0x4a, struct sndrv_seq_queue_client) +#define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL _IOWR('S', 0x4b, struct sndrv_seq_client_pool) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_POOL _IOW ('S', 0x4c, struct sndrv_seq_client_pool) +#define SNDRV_SEQ_IOCTL_REMOVE_EVENTS _IOW ('S', 0x4e, struct sndrv_seq_remove_events) +#define SNDRV_SEQ_IOCTL_QUERY_SUBS _IOWR('S', 0x4f, struct sndrv_seq_query_subs) +#define SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION _IOWR('S', 0x50, struct sndrv_seq_port_subscribe) +#define SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT _IOWR('S', 0x51, struct sndrv_seq_client_info) +#define SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT _IOWR('S', 0x52, struct sndrv_seq_port_info) + +#endif /* __SOUND_ASEQUENCER_H */ diff -Nru linux/include/sound/asound.h linux-2.4.19-pre5-mjc/include/sound/asound.h --- linux/include/sound/asound.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/asound.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,775 @@ +/* + * Advanced Linux Sound Architecture - ALSA - Driver + * Copyright (c) 1994-2000 by Jaroslav Kysela , + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_ASOUND_H +#define __SOUND_ASOUND_H + +#if defined(LINUX) || defined(__LINUX__) || defined(__linux__) + +#include + +#ifdef __KERNEL__ + +#include +#include + +#if __LITTLE_ENDIAN == 1234 +#define SNDRV_LITTLE_ENDIAN +#elif __BIG_ENDIAN == 4321 +#define SNDRV_BIG_ENDIAN +#else +#error "Unsupported endian..." +#endif + +#else /* !__KERNEL__ */ + +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SNDRV_LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +#define SNDRV_BIG_ENDIAN +#else +#error "Unsupported endian..." +#endif + +#endif /* __KERNEL **/ + +#endif /* LINUX */ + +#ifndef __KERNEL__ +#include +#include +#endif + +/* + * protocol version + */ + +#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor)) +#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff) +#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff) +#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff) +#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) \ + (SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || \ + (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && \ + SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion))) + +/**************************************************************************** + * * + * Digital audio interface * + * * + ****************************************************************************/ + +struct sndrv_aes_iec958 { + unsigned char status[24]; /* AES/IEC958 channel status bits */ + unsigned char subcode[147]; /* AES/IEC958 subcode bits */ + unsigned char pad; /* nothing */ + unsigned char dig_subframe[4]; /* AES/IEC958 subframe bits */ +}; + +/**************************************************************************** + * * + * Section for driver hardware dependent interface - /dev/snd/hw? * + * * + ****************************************************************************/ + +#define SNDRV_HWDEP_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 0) + +enum sndrv_hwdep_iface { + SNDRV_HWDEP_IFACE_OPL2 = 0, + SNDRV_HWDEP_IFACE_OPL3, + SNDRV_HWDEP_IFACE_OPL4, + SNDRV_HWDEP_IFACE_SB16CSP, /* Creative Signal Processor */ + SNDRV_HWDEP_IFACE_EMU10K1, /* FX8010 processor in EMU10K1 chip */ + SNDRV_HWDEP_IFACE_YSS225, /* Yamaha FX processor */ + SNDRV_HWDEP_IFACE_ICS2115, /* Wavetable synth */ + + /* Don't forget to change the following: */ + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_ICS2115, +}; + +struct sndrv_hwdep_info { + unsigned int device; /* WR: device number */ + int card; /* R: card number */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* hwdep name */ + enum sndrv_hwdep_iface iface; /* hwdep interface */ + unsigned char reserved[64]; /* reserved for future */ +}; + +enum { + SNDRV_HWDEP_IOCTL_PVERSION = _IOR ('H', 0x00, int), + SNDRV_HWDEP_IOCTL_INFO = _IOR ('H', 0x01, struct sndrv_hwdep_info), +}; + +/***************************************************************************** + * * + * Digital Audio (PCM) interface - /dev/snd/pcm?? * + * * + *****************************************************************************/ + +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1) + +typedef unsigned long sndrv_pcm_uframes_t; +typedef long sndrv_pcm_sframes_t; + +enum sndrv_pcm_class { + SNDRV_PCM_CLASS_GENERIC = 0, /* standard mono or stereo device */ + SNDRV_PCM_CLASS_MULTI, /* multichannel device */ + SNDRV_PCM_CLASS_MODEM, /* software modem class */ + SNDRV_PCM_CLASS_DIGITIZER, /* digitizer class */ + /* Don't forget to change the following: */ + SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER, +}; + +enum sndrv_pcm_subclass { + SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0, /* mono or stereo subdevices are mixed together */ + SNDRV_PCM_SUBCLASS_MULTI_MIX, /* multichannel subdevices are mixed together */ + /* Don't forget to change the following: */ + SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX, +}; + +enum sndrv_pcm_stream { + SNDRV_PCM_STREAM_PLAYBACK = 0, + SNDRV_PCM_STREAM_CAPTURE, + SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE, +}; + +enum sndrv_pcm_access { + SNDRV_PCM_ACCESS_MMAP_INTERLEAVED = 0, /* interleaved mmap */ + SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED, /* noninterleaved mmap */ + SNDRV_PCM_ACCESS_MMAP_COMPLEX, /* complex mmap */ + SNDRV_PCM_ACCESS_RW_INTERLEAVED, /* readi/writei */ + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, /* readn/writen */ + SNDRV_PCM_ACCESS_LAST = SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, +}; + +enum sndrv_pcm_format { + SNDRV_PCM_FORMAT_S8 = 0, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_S24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT_LE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT_BE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_LE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_BE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, /* IEC-958 subframe, Little Endian */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, /* IEC-958 subframe, Big Endian */ + SNDRV_PCM_FORMAT_MU_LAW, + SNDRV_PCM_FORMAT_A_LAW, + SNDRV_PCM_FORMAT_IMA_ADPCM, + SNDRV_PCM_FORMAT_MPEG, + SNDRV_PCM_FORMAT_GSM, + SNDRV_PCM_FORMAT_SPECIAL = 31, + SNDRV_PCM_FORMAT_LAST = 31, + +#ifdef SNDRV_LITTLE_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_LE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_LE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, +#endif +#ifdef SNDRV_BIG_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_BE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_BE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, +#endif +}; + +enum sndrv_pcm_subformat { + SNDRV_PCM_SUBFORMAT_STD = 0, + SNDRV_PCM_SUBFORMAT_LAST = SNDRV_PCM_SUBFORMAT_STD, +}; + +#define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ +#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ +#define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ +#define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ +#define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ +#define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ +#define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ +#define SNDRV_PCM_INFO_BLOCK_TRANSFER 0x00010000 /* hardware transfer block of samples */ +#define SNDRV_PCM_INFO_OVERRANGE 0x00020000 /* hardware supports ADC (capture) overrange detection */ +#define SNDRV_PCM_INFO_RESUME 0x00040000 /* hardware supports stream resume after suspend */ +#define SNDRV_PCM_INFO_PAUSE 0x00080000 /* pause ioctl is supported */ +#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */ +#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ +#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ + +enum sndrv_pcm_state { + SNDRV_PCM_STATE_OPEN = 0, /* stream is open */ + SNDRV_PCM_STATE_SETUP, /* stream has a setup */ + SNDRV_PCM_STATE_PREPARED, /* stream is ready to start */ + SNDRV_PCM_STATE_RUNNING, /* stream is running */ + SNDRV_PCM_STATE_XRUN, /* stream reached an xrun */ + SNDRV_PCM_STATE_DRAINING, /* stream is draining */ + SNDRV_PCM_STATE_PAUSED, /* stream is paused */ + SNDRV_PCM_STATE_SUSPENDED, /* hardware is suspended */ + SNDRV_PCM_STATE_LAST = SNDRV_PCM_STATE_SUSPENDED, +}; + +enum { + SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000, + SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000, + SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000, +}; + +union sndrv_pcm_sync_id { + unsigned char id[16]; + unsigned short id16[8]; + unsigned int id32[4]; +}; + +struct sndrv_pcm_info { + unsigned int device; /* RO/WR (control): device number */ + unsigned int subdevice; /* RO/WR (control): subdevice number */ + enum sndrv_pcm_stream stream; /* RO/WR (control): stream number */ + int card; /* R: card number */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* name of this device */ + unsigned char subname[32]; /* subdevice name */ + enum sndrv_pcm_class dev_class; /* SNDRV_PCM_CLASS_* */ + enum sndrv_pcm_subclass dev_subclass; /* SNDRV_PCM_SUBCLASS_* */ + unsigned int subdevices_count; + unsigned int subdevices_avail; + union sndrv_pcm_sync_id sync; /* hardware synchronization ID */ + unsigned char reserved[64]; /* reserved for future... */ +}; + +enum sndrv_pcm_hw_param { + SNDRV_PCM_HW_PARAM_ACCESS = 0, /* Access type */ + SNDRV_PCM_HW_PARAM_FIRST_MASK = SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, /* Format */ + SNDRV_PCM_HW_PARAM_SUBFORMAT, /* Subformat */ + SNDRV_PCM_HW_PARAM_LAST_MASK = SNDRV_PCM_HW_PARAM_SUBFORMAT, + + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, /* Bits per sample */ + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL = SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + SNDRV_PCM_HW_PARAM_FRAME_BITS, /* Bits per frame */ + SNDRV_PCM_HW_PARAM_CHANNELS, /* Channels */ + SNDRV_PCM_HW_PARAM_RATE, /* Approx rate */ + SNDRV_PCM_HW_PARAM_PERIOD_TIME, /* Approx distance between interrupts + in us */ + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, /* Approx frames between interrupts */ + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, /* Approx bytes between interrupts */ + SNDRV_PCM_HW_PARAM_PERIODS, /* Approx interrupts per buffer */ + SNDRV_PCM_HW_PARAM_BUFFER_TIME, /* Approx duration of buffer in us */ + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, /* Size of buffer in frames */ + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, /* Size of buffer in bytes */ + SNDRV_PCM_HW_PARAM_TICK_TIME, /* Approx tick duration in us */ + SNDRV_PCM_HW_PARAM_LAST_INTERVAL = SNDRV_PCM_HW_PARAM_TICK_TIME, + SNDRV_PCM_HW_PARAM_LAST = SNDRV_PCM_HW_PARAM_LAST_INTERVAL, +}; + +#define SNDRV_PCM_HW_PARAMS_RUNTIME (1<<0) + +struct sndrv_interval { + unsigned int min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +struct sndrv_pcm_hw_params { + unsigned int flags; + unsigned int masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + unsigned int rmask; + unsigned int cmask; + unsigned int info; /* R: Info flags for returned setup */ + unsigned int msbits; /* R: used most significant bits */ + unsigned int rate_num; /* R: rate numerator */ + unsigned int rate_den; /* R: rate denominator */ + sndrv_pcm_uframes_t fifo_size; /* R: chip FIFO size in frames */ + unsigned char reserved[64]; +}; + +enum sndrv_pcm_tstamp { + SNDRV_PCM_TSTAMP_NONE = 0, + SNDRV_PCM_TSTAMP_MMAP, + SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_MMAP, +}; + +struct sndrv_pcm_sw_params { + enum sndrv_pcm_tstamp tstamp_mode; /* timestamp mode */ + unsigned int period_step; + unsigned int sleep_min; /* min ticks to sleep */ + sndrv_pcm_uframes_t avail_min; /* min avail frames for wakeup */ + sndrv_pcm_uframes_t xfer_align; /* xfer size need to be a multiple */ + sndrv_pcm_uframes_t start_threshold; /* min hw_avail frames for automatic start */ + sndrv_pcm_uframes_t stop_threshold; /* min avail frames for automatic stop */ + sndrv_pcm_uframes_t silence_threshold; /* min distance from noise for silence filling */ + sndrv_pcm_uframes_t silence_size; /* silence block size */ + sndrv_pcm_uframes_t boundary; /* pointers wrap point */ + unsigned char reserved[64]; +}; + +struct sndrv_pcm_channel_info { + unsigned int channel; + off_t offset; /* mmap offset */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ +}; + +struct sndrv_pcm_status { + enum sndrv_pcm_state state; /* stream state */ + struct timeval trigger_tstamp; /* time when stream was started/stopped/paused */ + struct timeval tstamp; /* reference timestamp */ + sndrv_pcm_uframes_t appl_ptr; /* appl ptr */ + sndrv_pcm_uframes_t hw_ptr; /* hw ptr */ + sndrv_pcm_sframes_t delay; /* current delay in frames */ + sndrv_pcm_uframes_t avail; /* number of frames available */ + sndrv_pcm_uframes_t avail_max; /* max frames available on hw since last status */ + sndrv_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ + enum sndrv_pcm_state suspended_state; /* suspended stream state */ + unsigned char reserved[60]; /* must be filled with zero */ +}; + +struct sndrv_pcm_mmap_status { + enum sndrv_pcm_state state; /* RO: state - SNDRV_PCM_STATE_XXXX */ + int pad1; /* Needed for 64 bit alignment */ + sndrv_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */ + struct timeval tstamp; /* Timestamp */ + enum sndrv_pcm_state suspended_state; /* RO: suspended stream state */ +}; + +struct sndrv_pcm_mmap_control { + sndrv_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */ + sndrv_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */ +}; + +struct sndrv_xferi { + sndrv_pcm_sframes_t result; + void *buf; + sndrv_pcm_uframes_t frames; +}; + +struct sndrv_xfern { + sndrv_pcm_sframes_t result; + void **bufs; + sndrv_pcm_uframes_t frames; +}; + +enum { + SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), + SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info), + SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct sndrv_pcm_hw_params), + SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct sndrv_pcm_hw_params), + SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), + SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct sndrv_pcm_sw_params), + SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct sndrv_pcm_status), + SNDRV_PCM_IOCTL_DELAY = _IOR('A', 0x21, sndrv_pcm_sframes_t), + SNDRV_PCM_IOCTL_CHANNEL_INFO = _IOR('A', 0x32, struct sndrv_pcm_channel_info), + SNDRV_PCM_IOCTL_PREPARE = _IO('A', 0x40), + SNDRV_PCM_IOCTL_RESET = _IO('A', 0x41), + SNDRV_PCM_IOCTL_START = _IO('A', 0x42), + SNDRV_PCM_IOCTL_DROP = _IO('A', 0x43), + SNDRV_PCM_IOCTL_DRAIN = _IO('A', 0x44), + SNDRV_PCM_IOCTL_PAUSE = _IOW('A', 0x45, int), + SNDRV_PCM_IOCTL_REWIND = _IOW('A', 0x46, sndrv_pcm_uframes_t), + SNDRV_PCM_IOCTL_RESUME = _IO('A', 0x47), + SNDRV_PCM_IOCTL_XRUN = _IO('A', 0x48), + SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct sndrv_xferi), + SNDRV_PCM_IOCTL_READI_FRAMES = _IOR('A', 0x51, struct sndrv_xferi), + SNDRV_PCM_IOCTL_WRITEN_FRAMES = _IOW('A', 0x52, struct sndrv_xfern), + SNDRV_PCM_IOCTL_READN_FRAMES = _IOR('A', 0x53, struct sndrv_xfern), + SNDRV_PCM_IOCTL_LINK = _IOW('A', 0x60, int), + SNDRV_PCM_IOCTL_UNLINK = _IO('A', 0x61), +}; + +/* Trick to make alsa-lib/acinclude.m4 happy */ +#define SNDRV_PCM_IOCTL_REWIND SNDRV_PCM_IOCTL_REWIND + +/***************************************************************************** + * * + * MIDI v1.0 interface * + * * + *****************************************************************************/ + +/* + * Raw MIDI section - /dev/snd/midi?? + */ + +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +enum sndrv_rawmidi_stream { + SNDRV_RAWMIDI_STREAM_OUTPUT = 0, + SNDRV_RAWMIDI_STREAM_INPUT, + SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT, +}; + +#define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 +#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 +#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 + +struct sndrv_rawmidi_info { + unsigned int device; /* RO/WR (control): device number */ + unsigned int subdevice; /* RO/WR (control): subdevice number */ + enum sndrv_rawmidi_stream stream; /* WR: stream */ + int card; /* R: card number */ + unsigned int flags; /* SNDRV_RAWMIDI_INFO_XXXX */ + unsigned char id[64]; /* ID (user selectable) */ + unsigned char name[80]; /* name of device */ + unsigned char subname[32]; /* name of active or selected subdevice */ + unsigned int subdevices_count; + unsigned int subdevices_avail; + unsigned char reserved[64]; /* reserved for future use */ +}; + +struct sndrv_rawmidi_params { + enum sndrv_rawmidi_stream stream; + size_t buffer_size; /* queue size in bytes */ + size_t avail_min; /* minimum avail bytes for wakeup */ + unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */ + unsigned char reserved[16]; /* reserved for future use */ +}; + +struct sndrv_rawmidi_status { + enum sndrv_rawmidi_stream stream; + struct timeval tstamp; /* Timestamp */ + size_t avail; /* available bytes */ + size_t xruns; /* count of overruns since last status (in bytes) */ + unsigned char reserved[16]; /* reserved for future use */ +}; + +enum { + SNDRV_RAWMIDI_IOCTL_PVERSION = _IOR('W', 0x00, int), + SNDRV_RAWMIDI_IOCTL_INFO = _IOR('W', 0x01, struct sndrv_rawmidi_info), + SNDRV_RAWMIDI_IOCTL_PARAMS = _IOWR('W', 0x10, struct sndrv_rawmidi_params), + SNDRV_RAWMIDI_IOCTL_STATUS = _IOWR('W', 0x20, struct sndrv_rawmidi_status), + SNDRV_RAWMIDI_IOCTL_DROP = _IOW('W', 0x30, int), + SNDRV_RAWMIDI_IOCTL_DRAIN = _IOW('W', 0x31, int), +}; + +/* + * Timer section - /dev/snd/timer + */ + +#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +enum sndrv_timer_class { + SNDRV_TIMER_CLASS_NONE = -1, + SNDRV_TIMER_CLASS_SLAVE = 0, + SNDRV_TIMER_CLASS_GLOBAL, + SNDRV_TIMER_CLASS_CARD, + SNDRV_TIMER_CLASS_PCM, + SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM, +}; + +/* slave timer classes */ +enum sndrv_timer_slave_class { + SNDRV_TIMER_SCLASS_NONE = 0, + SNDRV_TIMER_SCLASS_APPLICATION, + SNDRV_TIMER_SCLASS_SEQUENCER, /* alias */ + SNDRV_TIMER_SCLASS_OSS_SEQUENCER, /* alias */ + SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER, +}; + +/* global timers (device member) */ +#define SNDRV_TIMER_GLOBAL_SYSTEM 0 +#define SNDRV_TIMER_GLOBAL_RTC 1 + +struct sndrv_timer_id { + enum sndrv_timer_class dev_class; + enum sndrv_timer_slave_class dev_sclass; + int card; + int device; + int subdevice; +}; + +struct sndrv_timer_select { + struct sndrv_timer_id id; /* bind to timer ID */ + unsigned char reserved[32]; /* reserved */ +}; + +#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */ + +struct sndrv_timer_info { + unsigned int flags; /* timer flags - SNDRV_TIMER_FLG_* */ + int card; /* R: card number */ + unsigned char id[64]; /* timer identificator */ + unsigned char name[80]; /* timer name */ + unsigned long ticks; /* maximum ticks */ + unsigned long resolution; /* average resolution */ + unsigned char reserved[64]; /* reserved */ +}; + +#define SNDRV_TIMER_PSFLG_AUTO (1<<0) /* supports auto start */ + +struct sndrv_timer_params { + unsigned int flags; /* flags - SNDRV_MIXER_PSFLG_* */ + unsigned int ticks; /* requested resolution in ticks */ + unsigned int queue_size; /* total size of queue (32-1024) */ + unsigned int reserved0; /* reserved, was: failure locations */ + unsigned char reserved[64]; /* reserved */ +}; + +struct sndrv_timer_status { + struct timeval tstamp; /* Timestamp */ + unsigned int resolution; /* current resolution */ + unsigned int lost; /* counter of master tick lost */ + unsigned int overrun; /* count of read queue overruns */ + unsigned int queue; /* used queue size */ + unsigned char reserved[64]; /* reserved */ +}; + +enum { + SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int), + SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct sndrv_timer_id), + SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct sndrv_timer_select), + SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info), + SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params), + SNDRV_TIMER_IOCTL_STATUS = _IOW('T', 0x14, struct sndrv_timer_status), + SNDRV_TIMER_IOCTL_START = _IO('T', 0x20), + SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21), + SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22), +}; + +struct sndrv_timer_read { + unsigned int resolution; + unsigned int ticks; +}; + +/**************************************************************************** + * * + * Section for driver control interface - /dev/snd/control? * + * * + ****************************************************************************/ + +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +struct sndrv_ctl_card_info { + int card; /* card number */ + int pad; /* reserved for future (was type) */ + unsigned char id[16]; /* ID of card (user selectable) */ + unsigned char driver[16]; /* Driver name */ + unsigned char name[32]; /* Short name of soundcard */ + unsigned char longname[80]; /* name + info text about soundcard */ + unsigned char reserved_[16]; /* reserved for future (was ID of mixer) */ + unsigned char mixername[80]; /* visual mixer identification */ + unsigned char components[80]; /* card components / fine identification, delimited with one space (AC97 etc..) */ + unsigned char reserved[48]; /* reserved for future */ +}; + +enum sndrv_ctl_elem_type { + SNDRV_CTL_ELEM_TYPE_NONE = 0, /* invalid */ + SNDRV_CTL_ELEM_TYPE_BOOLEAN, /* boolean type */ + SNDRV_CTL_ELEM_TYPE_INTEGER, /* integer type */ + SNDRV_CTL_ELEM_TYPE_ENUMERATED, /* enumerated type */ + SNDRV_CTL_ELEM_TYPE_BYTES, /* byte array */ + SNDRV_CTL_ELEM_TYPE_IEC958, /* IEC958 (S/PDIF) setup */ + SNDRV_CTL_ELEM_TYPE_LAST = SNDRV_CTL_ELEM_TYPE_IEC958, +}; + +enum sndrv_ctl_elem_iface { + SNDRV_CTL_ELEM_IFACE_CARD = 0, /* global control */ + SNDRV_CTL_ELEM_IFACE_HWDEP, /* hardware dependent device */ + SNDRV_CTL_ELEM_IFACE_MIXER, /* virtual mixer device */ + SNDRV_CTL_ELEM_IFACE_PCM, /* PCM device */ + SNDRV_CTL_ELEM_IFACE_RAWMIDI, /* RawMidi device */ + SNDRV_CTL_ELEM_IFACE_TIMER, /* timer device */ + SNDRV_CTL_ELEM_IFACE_SEQUENCER, /* sequencer client */ + SNDRV_CTL_ELEM_IFACE_LAST = SNDRV_CTL_ELEM_IFACE_SEQUENCER, +}; + +#define SNDRV_CTL_ELEM_ACCESS_READ (1<<0) +#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) +#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) +#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ +#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ +#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ +#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ +#define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access */ + +/* for further details see the ACPI and PCI power management specification */ +#define SNDRV_CTL_POWER_D0 0x0000 /* full On */ +#define SNDRV_CTL_POWER_D1 0x0100 /* partial On */ +#define SNDRV_CTL_POWER_D2 0x0200 /* partial On */ +#define SNDRV_CTL_POWER_D3 0x0300 /* Off */ +#define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000) /* Off, with power */ +#define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001) /* Off, without power */ + +struct sndrv_ctl_elem_id { + unsigned int numid; /* numeric identifier, zero = invalid */ + enum sndrv_ctl_elem_iface iface; /* interface identifier */ + unsigned int device; /* device/client number */ + unsigned int subdevice; /* subdevice (substream) number */ + unsigned char name[44]; /* ASCII name of item */ + unsigned int index; /* index of item */ +}; + +struct sndrv_ctl_elem_list { + unsigned int offset; /* W: first element ID to get */ + unsigned int space; /* W: count of element IDs to get */ + unsigned int used; /* R: count of element IDs set */ + unsigned int count; /* R: count of all elements */ + struct sndrv_ctl_elem_id *pids; /* R: IDs */ + unsigned char reserved[50]; +}; + +struct sndrv_ctl_elem_info { + struct sndrv_ctl_elem_id id; /* W: element ID */ + enum sndrv_ctl_elem_type type; /* R: value type - SNDRV_CTL_ELEM_TYPE_* */ + unsigned int access; /* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */ + unsigned int count; /* count of values */ + pid_t owner; /* owner's PID of this control */ + union { + struct { + long min; /* R: minimum value */ + long max; /* R: maximum value */ + long step; /* R: step (0 variable) */ + } integer; + struct { + unsigned int items; /* R: number of items */ + unsigned int item; /* W: item number */ + char name[64]; /* R: value name */ + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +}; + +struct sndrv_ctl_elem_value { + struct sndrv_ctl_elem_id id; /* W: element ID */ + unsigned int indirect: 1; /* W: use indirect pointer (xxx_ptr member) */ + union { + union { + long value[128]; + long *value_ptr; + } integer; + union { + unsigned int item[128]; + unsigned int *item_ptr; + } enumerated; + union { + unsigned char data[512]; + unsigned char *data_ptr; + } bytes; + struct sndrv_aes_iec958 iec958; + } value; /* RO */ + unsigned char reserved[128]; +}; + +enum { + SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), + SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info), + SNDRV_CTL_IOCTL_ELEM_LIST = _IOWR('U', 0x10, struct sndrv_ctl_elem_list), + SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct sndrv_ctl_elem_info), + SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct sndrv_ctl_elem_value), + SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct sndrv_ctl_elem_value), + SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct sndrv_ctl_elem_id), + SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct sndrv_ctl_elem_id), + SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS = _IOWR('U', 0x16, int), + SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), + SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info), + SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), + SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct sndrv_pcm_info), + SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int), + SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE = _IOWR('U', 0x40, int), + SNDRV_CTL_IOCTL_RAWMIDI_INFO = _IOWR('U', 0x41, struct sndrv_rawmidi_info), + SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE = _IOW('U', 0x42, int), + SNDRV_CTL_IOCTL_POWER = _IOWR('U', 0xd0, int), + SNDRV_CTL_IOCTL_POWER_STATE = _IOR('U', 0xd1, int), +}; + +/* + * Read interface. + */ + +enum sndrv_ctl_event_type { + SNDRV_CTL_EVENT_ELEM = 0, + SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM, +}; + +#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) /* element value was changed */ +#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) /* element info was changed */ +#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) /* element was added */ +#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) /* element was removed */ + +struct sndrv_ctl_event { + enum sndrv_ctl_event_type type; /* event type - SNDRV_CTL_EVENT_* */ + union { + struct { + unsigned int mask; + struct sndrv_ctl_elem_id id; + } elem; + unsigned char data8[60]; + } data; +}; + +/* + * Control names + */ + +#define SNDRV_CTL_NAME_NONE "" +#define SNDRV_CTL_NAME_PLAYBACK "Playback " +#define SNDRV_CTL_NAME_CAPTURE "Capture " + +#define SNDRV_CTL_NAME_IEC958_NONE "" +#define SNDRV_CTL_NAME_IEC958_SWITCH "Switch" +#define SNDRV_CTL_NAME_IEC958_VOLUME "Volume" +#define SNDRV_CTL_NAME_IEC958_DEFAULT "Default" +#define SNDRV_CTL_NAME_IEC958_MASK "Mask" +#define SNDRV_CTL_NAME_IEC958_CON_MASK "Con Mask" +#define SNDRV_CTL_NAME_IEC958_PRO_MASK "Pro Mask" +#define SNDRV_CTL_NAME_IEC958_PCM_STREAM "PCM Stream" +#define SNDRV_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what + +/* + * + */ + +struct sndrv_xferv { + const struct iovec *vector; + unsigned long count; +}; + +enum { + SNDRV_IOCTL_READV = _IOW('K', 0x00, struct sndrv_xferv), + SNDRV_IOCTL_WRITEV = _IOW('K', 0x01, struct sndrv_xferv), +}; + +#endif /* __SOUND_ASOUND_H */ diff -Nru linux/include/sound/asound_fm.h linux-2.4.19-pre5-mjc/include/sound/asound_fm.h --- linux/include/sound/asound_fm.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/asound_fm.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,119 @@ +#ifndef __SOUND_ASOUND_FM_H +#define __SOUND_ASOUND_FM_H + +/* + * Advanced Linux Sound Architecture - ALSA + * + * Interface file between ALSA driver & user space + * Copyright (c) 1994-98 by Jaroslav Kysela , + * 4Front Technologies + * + * Direct FM control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_DM_FM_MODE_OPL2 0x00 +#define SNDRV_DM_FM_MODE_OPL3 0x01 + +typedef struct snd_dm_fm_info { + unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ + unsigned char rhythm; /* percussion mode flag */ +} snd_dm_fm_info_t; + +/* + * Data structure composing an FM "note" or sound event. + */ + +typedef struct snd_dm_fm_voice { + unsigned char op; /* operator cell (0 or 1) */ + unsigned char voice; /* FM voice (0 to 17) */ + + unsigned char am; /* amplitude modulation */ + unsigned char vibrato; /* vibrato effect */ + unsigned char do_sustain; /* sustain phase */ + unsigned char kbd_scale; /* keyboard scaling */ + unsigned char harmonic; /* 4 bits: harmonic and multiplier */ + unsigned char scale_level; /* 2 bits: decrease output freq rises */ + unsigned char volume; /* 6 bits: volume */ + + unsigned char attack; /* 4 bits: attack rate */ + unsigned char decay; /* 4 bits: decay rate */ + unsigned char sustain; /* 4 bits: sustain level */ + unsigned char release; /* 4 bits: release rate */ + + unsigned char feedback; /* 3 bits: feedback for op0 */ + unsigned char connection; /* 0 for serial, 1 for parallel */ + unsigned char left; /* stereo left */ + unsigned char right; /* stereo right */ + unsigned char waveform; /* 3 bits: waveform shape */ +} snd_dm_fm_voice_t; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +typedef struct snd_dm_fm_note { + unsigned char voice; /* 0-17 voice channel */ + unsigned char octave; /* 3 bits: what octave to play */ + unsigned int fnum; /* 10 bits: frequency number */ + unsigned char key_on; /* set for active, clear for silent */ +} snd_dm_fm_note_t; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +typedef struct snd_dm_fm_params { + unsigned char am_depth; /* amplitude modulation depth (1=hi) */ + unsigned char vib_depth; /* vibrato depth (1=hi) */ + unsigned char kbd_split; /* keyboard split */ + unsigned char rhythm; /* percussion mode select */ + + /* This block is the percussion instrument data */ + unsigned char bass; + unsigned char snare; + unsigned char tomtom; + unsigned char cymbal; + unsigned char hihat; +} snd_dm_fm_params_t; + +/* + * FM mode ioctl settings + */ + +#define SNDRV_DM_FM_IOCTL_INFO _IOR('H', 0x20, snd_dm_fm_info_t) +#define SNDRV_DM_FM_IOCTL_RESET _IO ('H', 0x21) +#define SNDRV_DM_FM_IOCTL_PLAY_NOTE _IOW('H', 0x22, snd_dm_fm_note_t) +#define SNDRV_DM_FM_IOCTL_SET_VOICE _IOW('H', 0x23, snd_dm_fm_voice_t) +#define SNDRV_DM_FM_IOCTL_SET_PARAMS _IOW('H', 0x24, snd_dm_fm_params_t) +#define SNDRV_DM_FM_IOCTL_SET_MODE _IOW('H', 0x25, int) +/* for OPL3 only */ +#define SNDRV_DM_FM_IOCTL_SET_CONNECTION _IOW('H', 0x26, int) + +#ifdef __SND_OSS_COMPAT__ + +#define SNDRV_DM_FM_OSS_IOCTL_RESET 0x20 +#define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE 0x21 +#define SNDRV_DM_FM_OSS_IOCTL_SET_VOICE 0x22 +#define SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS 0x23 +#define SNDRV_DM_FM_OSS_IOCTL_SET_MODE 0x24 +#define SNDRV_DM_FM_OSS_IOCTL_SET_OPL 0x25 + +#endif + +#endif /* __SOUND_ASOUND_FM_H */ diff -Nru linux/include/sound/asoundef.h linux-2.4.19-pre5-mjc/include/sound/asoundef.h --- linux/include/sound/asoundef.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/asoundef.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,227 @@ +#ifndef __SOUND_ASOUNDEF_H +#define __SOUND_ASOUNDEF_H + +/* + * Advanced Linux Sound Architecture - ALSA - Driver + * Copyright (c) 1994-2000 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/**************************************************************************** + * * + * Digital audio interface * + * * + ****************************************************************************/ + +/* AES/IEC958 channel status bits */ +#define IEC958_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ +#define IEC958_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ +#define IEC958_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ +#define IEC958_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ +#define IEC958_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ +#define IEC958_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ +#define IEC958_AES0_PRO_FS (3<<6) /* mask - sample frequency */ +#define IEC958_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ +#define IEC958_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ +#define IEC958_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ +#define IEC958_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ +#define IEC958_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ +#define IEC958_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ +#define IEC958_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ +#define IEC958_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ +#define IEC958_AES0_CON_MODE (3<<6) /* mask - mode */ +#define IEC958_AES1_PRO_MODE (15<<0) /* mask - channel mode */ +#define IEC958_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ +#define IEC958_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ +#define IEC958_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ +#define IEC958_AES1_PRO_MODE_TWO (8<<0) /* two channels */ +#define IEC958_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ +#define IEC958_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ +#define IEC958_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ +#define IEC958_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ +#define IEC958_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ +#define IEC958_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ +#define IEC958_AES1_CON_CATEGORY 0x7f +#define IEC958_AES1_CON_GENERAL 0x00 +#define IEC958_AES1_CON_EXPERIMENTAL 0x40 +#define IEC958_AES1_CON_SOLIDMEM_MASK 0x0f +#define IEC958_AES1_CON_SOLIDMEM_ID 0x08 +#define IEC958_AES1_CON_BROADCAST1_MASK 0x07 +#define IEC958_AES1_CON_BROADCAST1_ID 0x04 +#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07 +#define IEC958_AES1_CON_DIGDIGCONV_ID 0x02 +#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f +#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x06 +#define IEC958_AES1_CON_ADC_MASK 0x1f +#define IEC958_AES1_CON_ADC_ID 0x16 +#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f +#define IEC958_AES1_CON_BROADCAST2_ID 0x0e +#define IEC958_AES1_CON_LASEROPT_MASK 0x07 +#define IEC958_AES1_CON_LASEROPT_ID 0x01 +#define IEC958_AES1_CON_MUSICAL_MASK 0x07 +#define IEC958_AES1_CON_MUSICAL_ID 0x05 +#define IEC958_AES1_CON_MAGNETIC_MASK 0x07 +#define IEC958_AES1_CON_MAGNETIC_ID 0x03 +#define IEC958_AES1_CON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x00) +#define IEC958_AES1_CON_NON_IEC908_CD (IEC958_AES1_CON_LASEROPT_ID|0x08) +#define IEC958_AES1_CON_PCM_CODER (IEC958_AES1_CON_DIGDIGCONV_ID|0x00) +#define IEC958_AES1_CON_SAMPLER (IEC958_AES1_CON_DIGDIGCONV_ID|0x20) +#define IEC958_AES1_CON_MIXER (IEC958_AES1_CON_DIGDIGCONV_ID|0x10) +#define IEC958_AES1_CON_RATE_CONVERTER (IEC958_AES1_CON_DIGDIGCONV_ID|0x18) +#define IEC958_AES1_CON_SYNTHESIZER (IEC958_AES1_CON_MUSICAL_ID|0x00) +#define IEC958_AES1_CON_MICROPHONE (IEC958_AES1_CON_MUSICAL_ID|0x08) +#define IEC958_AES1_CON_DAT (IEC958_AES1_CON_MAGNETIC_ID|0x00) +#define IEC958_AES1_CON_VCR (IEC958_AES1_CON_MAGNETIC_ID|0x08) +#define IEC958_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ +#define IEC958_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ +#define IEC958_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ +#define IEC958_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ +#define IEC958_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ +#define IEC958_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ +#define IEC958_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ +#define IEC958_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ +#define IEC958_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ +#define IEC958_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ +#define IEC958_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ +#define IEC958_AES2_CON_SOURCE (15<<0) /* mask - source number */ +#define IEC958_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ +#define IEC958_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ +#define IEC958_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ +#define IEC958_AES3_CON_FS (15<<0) /* mask - sample frequency */ +#define IEC958_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ +#define IEC958_AES3_CON_FS_48000 (2<<0) /* 48kHz */ +#define IEC958_AES3_CON_FS_32000 (3<<0) /* 32kHz */ +#define IEC958_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ +#define IEC958_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ +#define IEC958_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ +#define IEC958_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ + +/***************************************************************************** + * * + * MIDI v1.0 interface * + * * + *****************************************************************************/ + +#define MIDI_CHANNELS 16 +#define MIDI_GM_DRUM_CHANNEL (10-1) + +/* + * MIDI commands + */ + +#define MIDI_CMD_NOTE_OFF 0x80 +#define MIDI_CMD_NOTE_ON 0x90 +#define MIDI_CMD_NOTE_PRESSURE 0xa0 +#define MIDI_CMD_CONTROL 0xb0 +#define MIDI_CMD_PGM_CHANGE 0xc0 +#define MIDI_CMD_CHANNEL_PRESSURE 0xd0 +#define MIDI_CMD_BENDER 0xe0 + +#define MIDI_CMD_COMMON_SYSEX 0xf0 +#define MIDI_CMD_COMMON_MTC_QUARTER 0xf1 +#define MIDI_CMD_COMMON_SONG_POS 0xf2 +#define MIDI_CMD_COMMON_SONG_SELECT 0xf3 +#define MIDI_CMD_COMMON_TUNE_REQUEST 0xf6 +#define MIDI_CMD_COMMON_SYSEX_END 0xf7 +#define MIDI_CMD_COMMON_CLOCK 0xf8 +#define MIDI_CMD_COMMON_START 0xfa +#define MIDI_CMD_COMMON_CONTINUE 0xfb +#define MIDI_CMD_COMMON_STOP 0xfc +#define MIDI_CMD_COMMON_SENSING 0xfe +#define MIDI_CMD_COMMON_RESET 0xff + +/* + * MIDI controllers + */ + +#define MIDI_CTL_MSB_BANK 0x00 +#define MIDI_CTL_MSB_MODWHEEL 0x01 +#define MIDI_CTL_MSB_BREATH 0x02 +#define MIDI_CTL_MSB_FOOT 0x04 +#define MIDI_CTL_MSB_PORTNAMENTO_TIME 0x05 +#define MIDI_CTL_MSB_DATA_ENTRY 0x06 +#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 +#define MIDI_CTL_MSB_BALANCE 0x08 +#define MIDI_CTL_MSB_PAN 0x0a +#define MIDI_CTL_MSB_EXPRESSION 0x0b +#define MIDI_CTL_MSB_EFFECT1 0x0c +#define MIDI_CTL_MSB_EFFECT2 0x0d +#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 +#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 +#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 +#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 +#define MIDI_CTL_LSB_BANK 0x20 +#define MIDI_CTL_LSB_MODWHEEL 0x21 +#define MIDI_CTL_LSB_BREATH 0x22 +#define MIDI_CTL_LSB_FOOT 0x24 +#define MIDI_CTL_LSB_PORTNAMENTO_TIME 0x25 +#define MIDI_CTL_LSB_DATA_ENTRY 0x26 +#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 +#define MIDI_CTL_LSB_BALANCE 0x28 +#define MIDI_CTL_LSB_PAN 0x2a +#define MIDI_CTL_LSB_EXPRESSION 0x2b +#define MIDI_CTL_LSB_EFFECT1 0x2c +#define MIDI_CTL_LSB_EFFECT2 0x2d +#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 +#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 +#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 +#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 +#define MIDI_CTL_SUSTAIN 0x40 +#define MIDI_CTL_PORTAMENTO 0x41 +#define MIDI_CTL_SUSTENUTO 0x42 +#define MIDI_CTL_SOFT_PEDAL 0x43 +#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 +#define MIDI_CTL_HOLD2 0x45 +#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 +#define MIDI_CTL_SC2_TIMBRE 0x47 +#define MIDI_CTL_SC3_RELEASE_TIME 0x48 +#define MIDI_CTL_SC4_ATTACK_TIME 0x49 +#define MIDI_CTL_SC5_BRIGHTNESS 0x4a +#define MIDI_CTL_SC6 0x4b +#define MIDI_CTL_SC7 0x4c +#define MIDI_CTL_SC8 0x4d +#define MIDI_CTL_SC9 0x4e +#define MIDI_CTL_SC10 0x4f +#define MIDI_CTL_GENERAL_PURPOSE5 0x50 +#define MIDI_CTL_GENERAL_PURPOSE6 0x51 +#define MIDI_CTL_GENERAL_PURPOSE7 0x52 +#define MIDI_CTL_GENERAL_PURPOSE8 0x53 +#define MIDI_CTL_PORNAMENTO_CONTROL 0x54 +#define MIDI_CTL_E1_REVERB_DEPTH 0x5b +#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5c +#define MIDI_CTL_E3_CHORUS_DEPTH 0x5d +#define MIDI_CTL_E4_DETUNE_DEPTH 0x5e +#define MIDI_CTL_E5_PHASER_DEPTH 0x5f +#define MIDI_CTL_DATA_INCREMENT 0x60 +#define MIDI_CTL_DATA_DECREMENT 0x61 +#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 +#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 +#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 +#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 +#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 +#define MIDI_CTL_RESET_CONTROLLERS 0x79 +#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7a +#define MIDI_CTL_ALL_NOTES_OFF 0x7b +#define MIDI_CTL_OMNI_OFF 0x7c +#define MIDI_CTL_OMNI_ON 0x7d +#define MIDI_CTL_MONO1 0x7e +#define MIDI_CTL_MONO2 0x7f + +#endif /* __SOUND_ASOUNDEF_H */ diff -Nru linux/include/sound/control.h linux-2.4.19-pre5-mjc/include/sound/control.h --- linux/include/sound/control.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/control.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,118 @@ +#ifndef __SOUND_CONTROL_H +#define __SOUND_CONTROL_H + +/* + * Header file for control interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +typedef struct sndrv_aes_iec958 snd_aes_iec958_t; +typedef struct sndrv_ctl_card_info snd_ctl_card_info_t; +typedef enum sndrv_ctl_elem_type snd_ctl_elem_type_t; +typedef enum sndrv_ctl_elem_iface snd_ctl_elem_iface_t; +typedef struct sndrv_ctl_elem_id snd_ctl_elem_id_t; +typedef struct sndrv_ctl_elem_list snd_ctl_elem_list_t; +typedef struct sndrv_ctl_elem_info snd_ctl_elem_info_t; +typedef struct sndrv_ctl_elem_value snd_ctl_elem_value_t; +typedef enum sndrv_ctl_event_type snd_ctl_event_type_t; +typedef struct sndrv_ctl_event snd_ctl_event_t; + +#define _snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data) +#define snd_kcontrol_chip(kcontrol) snd_magic_cast1(chip_t, _snd_kcontrol_chip(kcontrol), return -ENXIO) + +typedef int (snd_kcontrol_info_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo); +typedef int (snd_kcontrol_get_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +typedef int (snd_kcontrol_put_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +typedef struct _snd_kcontrol_new { + snd_ctl_elem_iface_t iface; /* interface identifier */ + unsigned int device; /* device/client number */ + unsigned int subdevice; /* subdevice (substream) number */ + unsigned char *name; /* ASCII name of item */ + unsigned int index; /* index of item */ + unsigned int access; /* access rights */ + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + unsigned long private_value; +} snd_kcontrol_new_t; + +struct _snd_kcontrol { + struct list_head list; /* list of controls */ + snd_ctl_elem_id_t id; + snd_ctl_file_t *owner; /* locked */ + pid_t owner_pid; + unsigned int access; /* access rights */ + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + unsigned long private_value; + void *private_data; + void (*private_free)(snd_kcontrol_t *kcontrol); +}; + +#define snd_kcontrol(n) list_entry(n, snd_kcontrol_t, list) + +typedef struct _snd_kctl_event { + struct list_head list; /* list of events */ + snd_ctl_elem_id_t id; + unsigned int mask; +} snd_kctl_event_t; + +#define snd_kctl_event(n) list_entry(n, snd_kctl_event_t, list) + +struct _snd_ctl_file { + struct list_head list; /* list of all control files */ + snd_card_t *card; + pid_t pid; + int prefer_pcm_subdevice; + int prefer_rawmidi_subdevice; + wait_queue_head_t change_sleep; + spinlock_t read_lock; + struct fasync_struct *fasync; + int subscribed; /* read interface is activated */ + struct list_head events; /* waiting events for read */ +}; + +#define snd_ctl_file(n) list_entry(n, snd_ctl_file_t, list) + +typedef int (*snd_kctl_ioctl_func_t) (snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg); + +void snd_ctl_notify(snd_card_t * card, unsigned int mask, snd_ctl_elem_id_t * id); + +snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * kcontrol); +snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * kcontrolnew, void * private_data); +void snd_ctl_free_one(snd_kcontrol_t * kcontrol); +int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol); +int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol); +int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id); +int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id); +snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid); +snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id); + +int snd_ctl_register(snd_card_t *card); +int snd_ctl_unregister(snd_card_t *card); +int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); +int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn); + +#endif /* __SOUND_CONTROL_H */ diff -Nru linux/include/sound/core.h linux-2.4.19-pre5-mjc/include/sound/core.h --- linux/include/sound/core.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/core.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,356 @@ +#ifndef __SOUND_CORE_H +#define __SOUND_CORE_H + +/* + * Main header file for the ALSA driver + * Copyright (c) 1994-2001 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef CONFIG_PM +#include /* wake_up() and struct semaphore */ +#endif + +/* Typedef's */ +typedef struct timeval snd_timestamp_t; +typedef struct sndrv_interval snd_interval_t; +typedef enum sndrv_card_type snd_card_type; +typedef struct sndrv_xferi snd_xferi_t; +typedef struct sndrv_xfern snd_xfern_t; +typedef struct sndrv_xferv snd_xferv_t; + +/* device allocation stuff */ + +#define SNDRV_DEV_TYPE_RANGE_SIZE 0x1000 + +typedef enum { + SNDRV_DEV_TOPLEVEL = (0*SNDRV_DEV_TYPE_RANGE_SIZE), + SNDRV_DEV_LOWLEVEL_PRE, + SNDRV_DEV_LOWLEVEL_NORMAL = (1*SNDRV_DEV_TYPE_RANGE_SIZE), + SNDRV_DEV_PCM, + SNDRV_DEV_RAWMIDI, + SNDRV_DEV_TIMER, + SNDRV_DEV_SEQUENCER, + SNDRV_DEV_HWDEP, + SNDRV_DEV_LOWLEVEL = (2*SNDRV_DEV_TYPE_RANGE_SIZE) +} snd_device_type_t; + +typedef enum { + SNDRV_DEV_BUILD = 0, + SNDRV_DEV_REGISTERED = 1 +} snd_device_state_t; + +typedef enum { + SNDRV_DEV_CMD_PRE = 0, + SNDRV_DEV_CMD_NORMAL = 1, + SNDRV_DEV_CMD_POST = 2 +} snd_device_cmd_t; + +typedef struct _snd_card snd_card_t; +typedef struct _snd_device snd_device_t; + +typedef int (snd_dev_free_t)(snd_device_t *device); +typedef int (snd_dev_register_t)(snd_device_t *device); +typedef int (snd_dev_unregister_t)(snd_device_t *device); + +typedef struct { + snd_dev_free_t *dev_free; + snd_dev_register_t *dev_register; + snd_dev_unregister_t *dev_unregister; +} snd_device_ops_t; + +struct _snd_device { + struct list_head list; /* list of registered devices */ + snd_card_t *card; /* card which holds this device */ + snd_device_state_t state; /* state of the device */ + snd_device_type_t type; /* device type */ + void *device_data; /* device structure */ + snd_device_ops_t *ops; /* operations */ +}; + +#define snd_device(n) list_entry(n, snd_device_t, list) + +/* various typedefs */ + +typedef struct snd_info_entry snd_info_entry_t; +typedef struct _snd_pcm snd_pcm_t; +typedef struct _snd_pcm_str snd_pcm_str_t; +typedef struct _snd_pcm_substream snd_pcm_substream_t; +typedef struct _snd_mixer snd_kmixer_t; +typedef struct _snd_rawmidi snd_rawmidi_t; +typedef struct _snd_ctl_file snd_ctl_file_t; +typedef struct _snd_kcontrol snd_kcontrol_t; +typedef struct _snd_timer snd_timer_t; +typedef struct _snd_timer_instance snd_timer_instance_t; +typedef struct _snd_hwdep snd_hwdep_t; +#ifdef CONFIG_SND_OSSEMUL +typedef struct _snd_oss_mixer snd_mixer_oss_t; +#endif + +/* main structure for soundcard */ + +struct _snd_card { + int number; /* number of soundcard (index to snd_cards) */ + + char id[16]; /* id string of this card */ + char driver[16]; /* driver name */ + char shortname[32]; /* short name of this soundcard */ + char longname[80]; /* name of this soundcard */ + char mixername[80]; /* mixer name */ + char components[80]; /* card components delimited with space */ + + struct module *module; /* top-level module */ + + void *private_data; /* private data for soundcard */ + void (*private_free) (snd_card_t *card); /* callback for freeing of private data */ + + struct list_head devices; /* devices */ + + unsigned int last_numid; /* last used numeric ID */ + rwlock_t control_rwlock; /* control list lock */ + rwlock_t control_owner_lock; /* control list lock */ + int controls_count; /* count of all controls */ + struct list_head controls; /* all controls for this card */ + struct list_head ctl_files; /* active control files */ + + snd_info_entry_t *proc_root; /* root for soundcard specific files */ + snd_info_entry_t *proc_id; /* the card id */ + struct proc_dir_entry *proc_root_link; /* number link to real id */ + +#ifdef CONFIG_PM + int (*set_power_state) (snd_card_t *card, unsigned int state); + void *power_state_private_data; + unsigned int power_state; /* power state */ + struct semaphore power_lock; /* power lock */ + wait_queue_head_t power_sleep; +#endif + +#ifdef CONFIG_SND_OSSEMUL + snd_mixer_oss_t *mixer_oss; + int mixer_oss_change_count; +#endif +}; + +#ifdef CONFIG_PM +static inline void snd_power_lock(snd_card_t *card) +{ + down(&card->power_lock); +} + +static inline void snd_power_unlock(snd_card_t *card) +{ + up(&card->power_lock); +} + +void snd_power_wait(snd_card_t *card); + +static inline unsigned int snd_power_get_state(snd_card_t *card) +{ + return card->power_state; +} + +static inline void snd_power_change_state(snd_card_t *card, unsigned int state) +{ + card->power_state = state; + wake_up(&card->power_sleep); +} +#else +#define snd_power_lock(card) do { ; } while (0) +#define snd_power_unlock(card) do { ; } while (0) +#define snd_power_wait(card) do { ; } while (0) +#define snd_power_get_state(card) SNDRV_CTL_POWER_D0 +#define snd_power_change_state(card, state) do { ; } while (0) +#endif + +/* device.c */ + +struct _snd_minor { + struct list_head list; /* list of all minors per card */ + int number; /* minor number */ + int device; /* device number */ + const char *comment; /* for /proc/asound/devices */ + snd_info_entry_t *dev; /* for /proc/asound/dev */ + struct file_operations *f_ops; /* file operations */ +}; + +typedef struct _snd_minor snd_minor_t; + +/* sound.c */ + +extern int snd_ecards_limit; +extern int snd_device_mode; +extern int snd_device_gid; +extern int snd_device_uid; + +void snd_request_card(int card); + +int snd_register_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name); +int snd_unregister_device(int type, snd_card_t *card, int dev); + +#ifdef CONFIG_SND_OSSEMUL +int snd_register_oss_device(int type, snd_card_t *card, int dev, snd_minor_t *reg, const char *name); +int snd_unregister_oss_device(int type, snd_card_t *card, int dev); +#endif + +int snd_minor_info_init(void); +int snd_minor_info_done(void); + +/* sound_oss.c */ + +#ifdef CONFIG_SND_OSSEMUL + +int snd_minor_info_oss_init(void); +int snd_minor_info_oss_done(void); + +int snd_oss_init_module(void); +void snd_oss_cleanup_module(void); + +#endif + +/* memory.c */ + +#ifdef CONFIG_SND_DEBUG_MEMORY +void snd_memory_init(void); +void snd_memory_done(void); +int snd_memory_info_init(void); +int snd_memory_info_done(void); +void *snd_hidden_kmalloc(size_t size, int flags); +void snd_hidden_kfree(const void *obj); +void *snd_hidden_vmalloc(unsigned long size); +void snd_hidden_vfree(void *obj); +#define kmalloc(size, flags) snd_hidden_kmalloc(size, flags) +#define kfree(obj) snd_hidden_kfree(obj) +#define kfree_nocheck(obj) snd_wrapper_kfree(obj) +#define vmalloc(size) snd_hidden_vmalloc(size) +#define vfree(obj) snd_hidden_vfree(obj) +#else +#define kfree_nocheck(obj) kfree(obj) +#endif +void *snd_kcalloc(size_t size, int flags); +char *snd_kmalloc_strdup(const char *string, int flags); +void *snd_malloc_pages(unsigned long size, unsigned int dma_flags); +void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size); +void snd_free_pages(void *ptr, unsigned long size); +#ifdef CONFIG_ISA +void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr); +void *snd_malloc_isa_pages_fallback(unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size); +#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size) +#endif +#ifdef CONFIG_PCI +void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr); +void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size); +void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr); +#endif +int copy_to_user_fromio(void *dst, unsigned long src, size_t count); +int copy_from_user_toio(unsigned long dst, const void *src, size_t count); + +/* init.c */ + +extern int snd_cards_count; +extern snd_card_t *snd_cards[SNDRV_CARDS]; +extern rwlock_t snd_card_rwlock; +#ifdef CONFIG_SND_OSSEMUL +extern int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); +#endif + +snd_card_t *snd_card_new(int idx, const char *id, + struct module *module, int extra_size); +int snd_card_free(snd_card_t *card); +int snd_card_register(snd_card_t *card); +int snd_card_info_init(void); +int snd_card_info_done(void); +int snd_component_add(snd_card_t *card, const char *component); + +/* device.c */ + +int snd_device_new(snd_card_t *card, snd_device_type_t type, + void *device_data, snd_device_ops_t *ops); +int snd_device_register(snd_card_t *card, void *device_data); +int snd_device_register_all(snd_card_t *card); +int snd_device_free(snd_card_t *card, void *device_data); +int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd); + +/* isadma.c */ + +#define DMA_MODE_NO_ENABLE 0x0100 + +void snd_dma_program(unsigned long dma, unsigned long addr, unsigned int size, unsigned short mode); +void snd_dma_disable(unsigned long dma); +unsigned int snd_dma_residue(unsigned long dma); + +/* misc.c */ + +int snd_task_name(struct task_struct *task, char *name, size_t size); +#ifdef CONFIG_SND_VERBOSE_PRINTK +int snd_verbose_printk(const char *file, int line, const char *format); +#endif + +/* --- */ + +#ifdef CONFIG_SND_VERBOSE_PRINTK +#define snd_printk(format, args...) do { \ + printk(snd_verbose_printk(__FILE__, __LINE__, format) ? format + 3 : format, ##args); \ +} while (0) +#else +#define snd_printk(format, args...) do { \ + printk(format, ##args); \ +} while (0) +#endif + +#ifdef CONFIG_SND_DEBUG + +#define __ASTRING__(x) #x + +#define snd_printd(format, args...) snd_printk(format, ##args) +#define snd_assert(expr, args...) do {\ + if (!(expr)) {\ + snd_printk("BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ + args;\ + }\ +} while (0) +#define snd_runtime_check(expr, args...) do {\ + if (!(expr)) {\ + snd_printk("ERROR (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ + args;\ + }\ +} while (0) + +#else /* !CONFIG_SND_DEBUG */ + +#define snd_printd(format, args...) /* nothing */ +#define snd_assert(expr, args...) /* nothing */ +#define snd_runtime_check(expr, args...) do { if (!(expr)) { args; } } while (0) + +#endif /* CONFIG_SND_DEBUG */ + +#ifdef CONFIG_SND_DEBUG_DETECT +#define snd_printdd(format, args...) snd_printk(format, ##args) +#else +#define snd_printdd(format, args...) /* nothing */ +#endif + +#define snd_BUG() snd_assert(0, ) + + +#define snd_timestamp_now(tstamp) do_gettimeofday(tstamp) +#define snd_timestamp_zero(tstamp) do { (tstamp)->tv_sec = 0; (tstamp)->tv_usec = 0; } while (0) +#define snd_timestamp_null(tstamp) ((tstamp)->tv_sec == 0 && (tstamp)->tv_usec ==0) + +#define SNDRV_OSS_VERSION ((3<<16)|(8<<8)|(1<<4)|(0)) /* 3.8.1a */ + +#endif /* __SOUND_CORE_H */ diff -Nru linux/include/sound/cs4231.h linux-2.4.19-pre5-mjc/include/sound/cs4231.h --- linux/include/sound/cs4231.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/cs4231.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,324 @@ +#ifndef __SOUND_CS4231_H +#define __SOUND_CS4231_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for CS4231 & InterWave chips & compatible chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" +#include "timer.h" + +/* IO ports */ + +#define CS4231P(chip, x) ((chip)->port + c_d_c_CS4231##x) + +#define c_d_c_CS4231REGSEL 0 +#define c_d_c_CS4231REG 1 +#define c_d_c_CS4231STATUS 2 +#define c_d_c_CS4231PIO 3 + +/* codec registers */ + +#define CS4231_LEFT_INPUT 0x00 /* left input control */ +#define CS4231_RIGHT_INPUT 0x01 /* right input control */ +#define CS4231_AUX1_LEFT_INPUT 0x02 /* left AUX1 input control */ +#define CS4231_AUX1_RIGHT_INPUT 0x03 /* right AUX1 input control */ +#define CS4231_AUX2_LEFT_INPUT 0x04 /* left AUX2 input control */ +#define CS4231_AUX2_RIGHT_INPUT 0x05 /* right AUX2 input control */ +#define CS4231_LEFT_OUTPUT 0x06 /* left output control register */ +#define CS4231_RIGHT_OUTPUT 0x07 /* right output control register */ +#define CS4231_PLAYBK_FORMAT 0x08 /* clock and data format - playback - bits 7-0 MCE */ +#define CS4231_IFACE_CTRL 0x09 /* interface control - bits 7-2 MCE */ +#define CS4231_PIN_CTRL 0x0a /* pin control */ +#define CS4231_TEST_INIT 0x0b /* test and initialization */ +#define CS4231_MISC_INFO 0x0c /* miscellaneaous information */ +#define CS4231_LOOPBACK 0x0d /* loopback control */ +#define CS4231_PLY_UPR_CNT 0x0e /* playback upper base count */ +#define CS4231_PLY_LWR_CNT 0x0f /* playback lower base count */ +#define CS4231_ALT_FEATURE_1 0x10 /* alternate #1 feature enable */ +#define CS4231_ALT_FEATURE_2 0x11 /* alternate #2 feature enable */ +#define CS4231_LEFT_LINE_IN 0x12 /* left line input control */ +#define CS4231_RIGHT_LINE_IN 0x13 /* right line input control */ +#define CS4231_TIMER_LOW 0x14 /* timer low byte */ +#define CS4231_TIMER_HIGH 0x15 /* timer high byte */ +#define CS4231_LEFT_MIC_INPUT 0x16 /* left MIC input control register (InterWave only) */ +#define CS4231_RIGHT_MIC_INPUT 0x17 /* right MIC input control register (InterWave only) */ +#define CS4236_EXT_REG 0x17 /* extended register access */ +#define CS4231_IRQ_STATUS 0x18 /* irq status register */ +#define CS4231_LINE_LEFT_OUTPUT 0x19 /* left line output control register (InterWave only) */ +#define CS4231_VERSION 0x19 /* CS4231(A) - version values */ +#define CS4231_MONO_CTRL 0x1a /* mono input/output control */ +#define CS4231_LINE_RIGHT_OUTPUT 0x1b /* right line output control register (InterWave only) */ +#define CS4235_LEFT_MASTER 0x1b /* left master output control */ +#define CS4231_REC_FORMAT 0x1c /* clock and data format - record - bits 7-0 MCE */ +#define CS4231_PLY_VAR_FREQ 0x1d /* playback variable frequency */ +#define CS4235_RIGHT_MASTER 0x1d /* right master output control */ +#define CS4231_REC_UPR_CNT 0x1e /* record upper count */ +#define CS4231_REC_LWR_CNT 0x1f /* record lower count */ + +/* definitions for codec register select port - CODECP( REGSEL ) */ + +#define CS4231_INIT 0x80 /* CODEC is initializing */ +#define CS4231_MCE 0x40 /* mode change enable */ +#define CS4231_TRD 0x20 /* transfer request disable */ + +/* definitions for codec status register - CODECP( STATUS ) */ + +#define CS4231_GLOBALIRQ 0x01 /* IRQ is active */ + +/* definitions for codec irq status */ + +#define CS4231_PLAYBACK_IRQ 0x10 +#define CS4231_RECORD_IRQ 0x20 +#define CS4231_TIMER_IRQ 0x40 +#define CS4231_ALL_IRQS 0x70 +#define CS4231_REC_UNDERRUN 0x08 +#define CS4231_REC_OVERRUN 0x04 +#define CS4231_PLY_OVERRUN 0x02 +#define CS4231_PLY_UNDERRUN 0x01 + +/* definitions for CS4231_LEFT_INPUT and CS4231_RIGHT_INPUT registers */ + +#define CS4231_ENABLE_MIC_GAIN 0x20 + +#define CS4231_MIXS_LINE 0x00 +#define CS4231_MIXS_AUX1 0x40 +#define CS4231_MIXS_MIC 0x80 +#define CS4231_MIXS_ALL 0xc0 + +/* definitions for clock and data format register - CS4231_PLAYBK_FORMAT */ + +#define CS4231_LINEAR_8 0x00 /* 8-bit unsigned data */ +#define CS4231_ALAW_8 0x60 /* 8-bit A-law companded */ +#define CS4231_ULAW_8 0x20 /* 8-bit U-law companded */ +#define CS4231_LINEAR_16 0x40 /* 16-bit twos complement data - little endian */ +#define CS4231_LINEAR_16_BIG 0xc0 /* 16-bit twos complement data - big endian */ +#define CS4231_ADPCM_16 0xa0 /* 16-bit ADPCM */ +#define CS4231_STEREO 0x10 /* stereo mode */ +/* bits 3-1 define frequency divisor */ +#define CS4231_XTAL1 0x00 /* 24.576 crystal */ +#define CS4231_XTAL2 0x01 /* 16.9344 crystal */ + +/* definitions for interface control register - CS4231_IFACE_CTRL */ + +#define CS4231_RECORD_PIO 0x80 /* record PIO enable */ +#define CS4231_PLAYBACK_PIO 0x40 /* playback PIO enable */ +#define CS4231_CALIB_MODE 0x18 /* calibration mode bits */ +#define CS4231_AUTOCALIB 0x08 /* auto calibrate */ +#define CS4231_SINGLE_DMA 0x04 /* use single DMA channel */ +#define CS4231_RECORD_ENABLE 0x02 /* record enable */ +#define CS4231_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* definitions for pin control register - CS4231_PIN_CTRL */ + +#define CS4231_IRQ_ENABLE 0x02 /* enable IRQ */ +#define CS4231_XCTL1 0x40 /* external control #1 */ +#define CS4231_XCTL0 0x80 /* external control #0 */ + +/* definitions for test and init register - CS4231_TEST_INIT */ + +#define CS4231_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ +#define CS4231_DMA_REQUEST 0x10 /* DMA request in progress */ + +/* definitions for misc control register - CS4231_MISC_INFO */ + +#define CS4231_MODE2 0x40 /* MODE 2 */ +#define CS4231_IW_MODE3 0x6c /* MODE 3 - InterWave enhanced mode */ +#define CS4231_4236_MODE3 0xe0 /* MODE 3 - CS4236+ enhanced mode */ + +/* definitions for alternate feature 1 register - CS4231_ALT_FEATURE_1 */ + +#define CS4231_DACZ 0x01 /* zero DAC when underrun */ +#define CS4231_TIMER_ENABLE 0x40 /* codec timer enable */ +#define CS4231_OLB 0x80 /* output level bit */ + +/* definitions for Extended Registers - CS4236+ */ + +#define CS4236_REG(i23val) (((i23val << 2) & 0x10) | ((i23val >> 4) & 0x0f)) +#define CS4236_I23VAL(reg) ((((reg)&0xf) << 4) | (((reg)&0x10) >> 2) | 0x8) + +#define CS4236_LEFT_LINE 0x08 /* left LINE alternate volume */ +#define CS4236_RIGHT_LINE 0x18 /* right LINE alternate volume */ +#define CS4236_LEFT_MIC 0x28 /* left MIC volume */ +#define CS4236_RIGHT_MIC 0x38 /* right MIC volume */ +#define CS4236_LEFT_MIX_CTRL 0x48 /* synthesis and left input mixer control */ +#define CS4236_RIGHT_MIX_CTRL 0x58 /* right input mixer control */ +#define CS4236_LEFT_FM 0x68 /* left FM volume */ +#define CS4236_RIGHT_FM 0x78 /* right FM volume */ +#define CS4236_LEFT_DSP 0x88 /* left DSP serial port volume */ +#define CS4236_RIGHT_DSP 0x98 /* right DSP serial port volume */ +#define CS4236_RIGHT_LOOPBACK 0xa8 /* right loopback monitor volume */ +#define CS4236_DAC_MUTE 0xb8 /* DAC mute and IFSE enable */ +#define CS4236_ADC_RATE 0xc8 /* indenpendent ADC sample frequency */ +#define CS4236_DAC_RATE 0xd8 /* indenpendent DAC sample frequency */ +#define CS4236_LEFT_MASTER 0xe8 /* left master digital audio volume */ +#define CS4236_RIGHT_MASTER 0xf8 /* right master digital audio volume */ +#define CS4236_LEFT_WAVE 0x0c /* left wavetable serial port volume */ +#define CS4236_RIGHT_WAVE 0x1c /* right wavetable serial port volume */ +#define CS4236_VERSION 0x9c /* chip version and ID */ + +/* defines for codec.mode */ + +#define CS4231_MODE_NONE 0x0000 +#define CS4231_MODE_PLAY 0x0001 +#define CS4231_MODE_RECORD 0x0002 +#define CS4231_MODE_TIMER 0x0004 +#define CS4231_MODE_OPEN (CS4231_MODE_PLAY|CS4231_MODE_RECORD|CS4231_MODE_TIMER) + +/* defines for codec.hardware */ + +#define CS4231_HW_DETECT 0x0000 /* let CS4231 driver detect chip */ +#define CS4231_HW_DETECT3 0x0001 /* allow mode 3 */ +#define CS4231_HW_TYPE_MASK 0xff00 /* type mask */ +#define CS4231_HW_CS4231_MASK 0x0100 /* CS4231 serie */ +#define CS4231_HW_CS4231 0x0100 /* CS4231 chip */ +#define CS4231_HW_CS4231A 0x0101 /* CS4231A chip */ +#define CS4231_HW_CS4232_MASK 0x0200 /* CS4232 serie (has control ports) */ +#define CS4231_HW_CS4232 0x0200 /* CS4232 */ +#define CS4231_HW_CS4232A 0x0201 /* CS4232A */ +#define CS4231_HW_CS4236 0x0202 /* CS4236 */ +#define CS4231_HW_CS4236B_MASK 0x0400 /* CS4236B serie (has extended control regs) */ +#define CS4231_HW_CS4235 0x0400 /* CS4235 - Crystal Clear (tm) stereo enhancement */ +#define CS4231_HW_CS4236B 0x0401 /* CS4236B */ +#define CS4231_HW_CS4237B 0x0402 /* CS4237B - SRS 3D */ +#define CS4231_HW_CS4238B 0x0403 /* CS4238B - QSOUND 3D */ +#define CS4231_HW_CS4239 0x0404 /* CS4239 - Crystal Clear (tm) stereo enhancement */ +/* compatible, but clones */ +#define CS4231_HW_INTERWAVE 0x1000 /* InterWave chip */ +#define CS4231_HW_OPL3SA2 0x1001 /* OPL3-SA2 chip */ + +/* defines for codec.hwshare */ +#define CS4231_HWSHARE_IRQ (1<<0) +#define CS4231_HWSHARE_DMA1 (1<<1) +#define CS4231_HWSHARE_DMA2 (1<<2) + +typedef struct _snd_cs4231 cs4231_t; + +struct _snd_cs4231 { + unsigned long port; /* base i/o port */ + struct resource *res_port; + unsigned long cport; /* control base i/o port (CS4236) */ + struct resource *res_cport; + int irq; /* IRQ line */ + int dma1; /* playback DMA */ + int dma2; /* record DMA */ + unsigned short version; /* version of CODEC chip */ + unsigned short mode; /* see to CS4231_MODE_XXXX */ + unsigned short hardware; /* see to CS4231_HW_XXXX */ + unsigned short hwshare; /* shared resources */ + unsigned short single_dma:1; /* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */ + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_timer_t *timer; + + unsigned char image[32]; /* registers image */ + unsigned char eimage[32]; /* extended registers image */ + unsigned char cimage[16]; /* control registers image */ + int mce_bit; + int calibrate_mute; + int sw_3d_bit; + unsigned int p_dma_size; + unsigned int c_dma_size; + + spinlock_t reg_lock; + struct semaphore mce_mutex; + struct semaphore open_mutex; + + int (*rate_constraint) (snd_pcm_runtime_t *runtime); + void (*set_playback_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char pdfr); + void (*set_capture_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char cdfr); +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + void (*suspend) (cs4231_t *chip); + void (*resume) (cs4231_t *chip); +#endif + void *dma_private_data; + int (*claim_dma) (cs4231_t *chip, void *dma_private_data, int dma); + int (*release_dma) (cs4231_t *chip, void *dma_private_data, int dma); +}; + +/* exported functions */ + +void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char val); +unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg); +void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, unsigned char mask, unsigned char val); +void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val); +unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg); +void snd_cs4231_mce_up(cs4231_t *chip); +void snd_cs4231_mce_down(cs4231_t *chip); + +void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +const char *snd_cs4231_chip_id(cs4231_t *chip); + +int snd_cs4231_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip); +int snd_cs4231_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm); +int snd_cs4231_timer(cs4231_t * chip, int device, snd_timer_t **rtimer); +int snd_cs4231_mixer(cs4231_t * chip); + +int snd_cs4236_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip); +int snd_cs4236_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm); +int snd_cs4236_mixer(cs4231_t * chip); + +/* + * mixer library + */ + +#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4231_info_single, \ + get: snd_cs4231_get_single, put: snd_cs4231_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4231_info_double, \ + get: snd_cs4231_get_double, put: snd_cs4231_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); +int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); + +#ifdef CONFIG_SND_DEBUG +void snd_cs4231_debug(cs4231_t *chip); +#endif + +#endif /* __SOUND_CS4231_H */ diff -Nru linux/include/sound/cs46xx.h linux-2.4.19-pre5-mjc/include/sound/cs46xx.h --- linux/include/sound/cs46xx.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/cs46xx.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1720 @@ +#ifndef __SOUND_CS46XX_H +#define __SOUND_CS46XX_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Cirrus Logic, Inc. + * Definitions for Cirrus Logic CS46xx chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "ac97_codec.h" + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4610 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4612 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4615 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 +#endif + +/* + * Direct registers + */ + +/* + * The following define the offsets of the registers accessed via base address + * register zero on the CS46xx part. + */ +#define BA0_HISR 0x00000000 +#define BA0_HSR0 0x00000004 +#define BA0_HICR 0x00000008 +#define BA0_DMSR 0x00000100 +#define BA0_HSAR 0x00000110 +#define BA0_HDAR 0x00000114 +#define BA0_HDMR 0x00000118 +#define BA0_HDCR 0x0000011C +#define BA0_PFMC 0x00000200 +#define BA0_PFCV1 0x00000204 +#define BA0_PFCV2 0x00000208 +#define BA0_PCICFG00 0x00000300 +#define BA0_PCICFG04 0x00000304 +#define BA0_PCICFG08 0x00000308 +#define BA0_PCICFG0C 0x0000030C +#define BA0_PCICFG10 0x00000310 +#define BA0_PCICFG14 0x00000314 +#define BA0_PCICFG18 0x00000318 +#define BA0_PCICFG1C 0x0000031C +#define BA0_PCICFG20 0x00000320 +#define BA0_PCICFG24 0x00000324 +#define BA0_PCICFG28 0x00000328 +#define BA0_PCICFG2C 0x0000032C +#define BA0_PCICFG30 0x00000330 +#define BA0_PCICFG34 0x00000334 +#define BA0_PCICFG38 0x00000338 +#define BA0_PCICFG3C 0x0000033C +#define BA0_CLKCR1 0x00000400 +#define BA0_CLKCR2 0x00000404 +#define BA0_PLLM 0x00000408 +#define BA0_PLLCC 0x0000040C +#define BA0_FRR 0x00000410 +#define BA0_CFL1 0x00000414 +#define BA0_CFL2 0x00000418 +#define BA0_SERMC1 0x00000420 +#define BA0_SERMC2 0x00000424 +#define BA0_SERC1 0x00000428 +#define BA0_SERC2 0x0000042C +#define BA0_SERC3 0x00000430 +#define BA0_SERC4 0x00000434 +#define BA0_SERC5 0x00000438 +#define BA0_SERBSP 0x0000043C +#define BA0_SERBST 0x00000440 +#define BA0_SERBCM 0x00000444 +#define BA0_SERBAD 0x00000448 +#define BA0_SERBCF 0x0000044C +#define BA0_SERBWP 0x00000450 +#define BA0_SERBRP 0x00000454 +#ifndef NO_CS4612 +#define BA0_ASER_FADDR 0x00000458 +#endif +#define BA0_ACCTL 0x00000460 +#define BA0_ACSTS 0x00000464 +#define BA0_ACOSV 0x00000468 +#define BA0_ACCAD 0x0000046C +#define BA0_ACCDA 0x00000470 +#define BA0_ACISV 0x00000474 +#define BA0_ACSAD 0x00000478 +#define BA0_ACSDA 0x0000047C +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_MIDCR 0x00000490 +#define BA0_MIDSR 0x00000494 +#define BA0_MIDWP 0x00000498 +#define BA0_MIDRP 0x0000049C +#define BA0_JSIO 0x000004A0 +#ifndef NO_CS4612 +#define BA0_ASER_MASTER 0x000004A4 +#endif +#define BA0_CFGI 0x000004B0 +#define BA0_SSVID 0x000004B4 +#define BA0_GPIOR 0x000004B8 +#ifndef NO_CS4612 +#define BA0_EGPIODR 0x000004BC +#define BA0_EGPIOPTR 0x000004C0 +#define BA0_EGPIOTR 0x000004C4 +#define BA0_EGPIOWR 0x000004C8 +#define BA0_EGPIOSR 0x000004CC +#define BA0_SERC6 0x000004D0 +#define BA0_SERC7 0x000004D4 +#define BA0_SERACC 0x000004D8 +#define BA0_ACCTL2 0x000004E0 +#define BA0_ACSTS2 0x000004E4 +#define BA0_ACOSV2 0x000004E8 +#define BA0_ACCAD2 0x000004EC +#define BA0_ACCDA2 0x000004F0 +#define BA0_ACISV2 0x000004F4 +#define BA0_ACSAD2 0x000004F8 +#define BA0_ACSDA2 0x000004FC +#define BA0_IOTAC0 0x00000500 +#define BA0_IOTAC1 0x00000504 +#define BA0_IOTAC2 0x00000508 +#define BA0_IOTAC3 0x0000050C +#define BA0_IOTAC4 0x00000510 +#define BA0_IOTAC5 0x00000514 +#define BA0_IOTAC6 0x00000518 +#define BA0_IOTAC7 0x0000051C +#define BA0_IOTAC8 0x00000520 +#define BA0_IOTAC9 0x00000524 +#define BA0_IOTAC10 0x00000528 +#define BA0_IOTAC11 0x0000052C +#define BA0_IOTFR0 0x00000540 +#define BA0_IOTFR1 0x00000544 +#define BA0_IOTFR2 0x00000548 +#define BA0_IOTFR3 0x0000054C +#define BA0_IOTFR4 0x00000550 +#define BA0_IOTFR5 0x00000554 +#define BA0_IOTFR6 0x00000558 +#define BA0_IOTFR7 0x0000055C +#define BA0_IOTFIFO 0x00000580 +#define BA0_IOTRRD 0x00000584 +#define BA0_IOTFP 0x00000588 +#define BA0_IOTCR 0x0000058C +#define BA0_DPCID 0x00000590 +#define BA0_DPCIA 0x00000594 +#define BA0_DPCIC 0x00000598 +#define BA0_PCPCIR 0x00000600 +#define BA0_PCPCIG 0x00000604 +#define BA0_PCPCIEN 0x00000608 +#define BA0_EPCIPMC 0x00000610 +#endif + +/* + * The following define the offsets of the registers and memories accessed via + * base address register one on the CS46xx part. + */ +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 +#define BA1_SPCR 0x00030000 +#define BA1_DREG 0x00030004 +#define BA1_DSRWP 0x00030008 +#define BA1_TWPR 0x0003000C +#define BA1_SPWR 0x00030010 +#define BA1_SPIR 0x00030014 +#define BA1_FGR1 0x00030020 +#define BA1_SPCS 0x00030028 +#define BA1_SDSR 0x0003002C +#define BA1_FRMT 0x00030030 +#define BA1_FRCC 0x00030034 +#define BA1_FRSC 0x00030038 +#define BA1_OMNI_MEM 0x000E0000 + +/* + * The following defines are for the flags in the host interrupt status + * register. + */ +#define HISR_VC_MASK 0x0000FFFF +#define HISR_VC0 0x00000001 +#define HISR_VC1 0x00000002 +#define HISR_VC2 0x00000004 +#define HISR_VC3 0x00000008 +#define HISR_VC4 0x00000010 +#define HISR_VC5 0x00000020 +#define HISR_VC6 0x00000040 +#define HISR_VC7 0x00000080 +#define HISR_VC8 0x00000100 +#define HISR_VC9 0x00000200 +#define HISR_VC10 0x00000400 +#define HISR_VC11 0x00000800 +#define HISR_VC12 0x00001000 +#define HISR_VC13 0x00002000 +#define HISR_VC14 0x00004000 +#define HISR_VC15 0x00008000 +#define HISR_INT0 0x00010000 +#define HISR_INT1 0x00020000 +#define HISR_DMAI 0x00040000 +#define HISR_FROVR 0x00080000 +#define HISR_MIDI 0x00100000 +#ifdef NO_CS4612 +#define HISR_RESERVED 0x0FE00000 +#else +#define HISR_SBINT 0x00200000 +#define HISR_RESERVED 0x0FC00000 +#endif +#define HISR_H0P 0x40000000 +#define HISR_INTENA 0x80000000 + +/* + * The following defines are for the flags in the host signal register 0. + */ +#define HSR0_VC_MASK 0xFFFFFFFF +#define HSR0_VC16 0x00000001 +#define HSR0_VC17 0x00000002 +#define HSR0_VC18 0x00000004 +#define HSR0_VC19 0x00000008 +#define HSR0_VC20 0x00000010 +#define HSR0_VC21 0x00000020 +#define HSR0_VC22 0x00000040 +#define HSR0_VC23 0x00000080 +#define HSR0_VC24 0x00000100 +#define HSR0_VC25 0x00000200 +#define HSR0_VC26 0x00000400 +#define HSR0_VC27 0x00000800 +#define HSR0_VC28 0x00001000 +#define HSR0_VC29 0x00002000 +#define HSR0_VC30 0x00004000 +#define HSR0_VC31 0x00008000 +#define HSR0_VC32 0x00010000 +#define HSR0_VC33 0x00020000 +#define HSR0_VC34 0x00040000 +#define HSR0_VC35 0x00080000 +#define HSR0_VC36 0x00100000 +#define HSR0_VC37 0x00200000 +#define HSR0_VC38 0x00400000 +#define HSR0_VC39 0x00800000 +#define HSR0_VC40 0x01000000 +#define HSR0_VC41 0x02000000 +#define HSR0_VC42 0x04000000 +#define HSR0_VC43 0x08000000 +#define HSR0_VC44 0x10000000 +#define HSR0_VC45 0x20000000 +#define HSR0_VC46 0x40000000 +#define HSR0_VC47 0x80000000 + +/* + * The following defines are for the flags in the host interrupt control + * register. + */ +#define HICR_IEV 0x00000001 +#define HICR_CHGM 0x00000002 + +/* + * The following defines are for the flags in the DMA status register. + */ +#define DMSR_HP 0x00000001 +#define DMSR_HR 0x00000002 +#define DMSR_SP 0x00000004 +#define DMSR_SR 0x00000008 + +/* + * The following defines are for the flags in the host DMA source address + * register. + */ +#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HSAR_DSP_ADDR_MASK 0x0000FFFF +#define HSAR_MEMID_MASK 0x000F0000 +#define HSAR_MEMID_SP_DMEM0 0x00000000 +#define HSAR_MEMID_SP_DMEM1 0x00010000 +#define HSAR_MEMID_SP_PMEM 0x00020000 +#define HSAR_MEMID_SP_DEBUG 0x00030000 +#define HSAR_MEMID_OMNI_MEM 0x000E0000 +#define HSAR_END 0x40000000 +#define HSAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA destination address + * register. + */ +#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HDAR_DSP_ADDR_MASK 0x0000FFFF +#define HDAR_MEMID_MASK 0x000F0000 +#define HDAR_MEMID_SP_DMEM0 0x00000000 +#define HDAR_MEMID_SP_DMEM1 0x00010000 +#define HDAR_MEMID_SP_PMEM 0x00020000 +#define HDAR_MEMID_SP_DEBUG 0x00030000 +#define HDAR_MEMID_OMNI_MEM 0x000E0000 +#define HDAR_END 0x40000000 +#define HDAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDMR_AC_MASK 0x0000F000 +#define HDMR_AC_8_16 0x00001000 +#define HDMR_AC_M_S 0x00002000 +#define HDMR_AC_B_L 0x00004000 +#define HDMR_AC_S_U 0x00008000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDCR_COUNT_MASK 0x000003FF +#define HDCR_DONE 0x00004000 +#define HDCR_OPT 0x00008000 +#define HDCR_WBD 0x00400000 +#define HDCR_WBS 0x00800000 +#define HDCR_DMS_MASK 0x07000000 +#define HDCR_DMS_LINEAR 0x00000000 +#define HDCR_DMS_16_DWORDS 0x01000000 +#define HDCR_DMS_32_DWORDS 0x02000000 +#define HDCR_DMS_64_DWORDS 0x03000000 +#define HDCR_DMS_128_DWORDS 0x04000000 +#define HDCR_DMS_256_DWORDS 0x05000000 +#define HDCR_DMS_512_DWORDS 0x06000000 +#define HDCR_DMS_1024_DWORDS 0x07000000 +#define HDCR_DH 0x08000000 +#define HDCR_SMS_MASK 0x70000000 +#define HDCR_SMS_LINEAR 0x00000000 +#define HDCR_SMS_16_DWORDS 0x10000000 +#define HDCR_SMS_32_DWORDS 0x20000000 +#define HDCR_SMS_64_DWORDS 0x30000000 +#define HDCR_SMS_128_DWORDS 0x40000000 +#define HDCR_SMS_256_DWORDS 0x50000000 +#define HDCR_SMS_512_DWORDS 0x60000000 +#define HDCR_SMS_1024_DWORDS 0x70000000 +#define HDCR_SH 0x80000000 +#define HDCR_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the performance monitor control + * register. + */ +#define PFMC_C1SS_MASK 0x0000001F +#define PFMC_C1EV 0x00000020 +#define PFMC_C1RS 0x00008000 +#define PFMC_C2SS_MASK 0x001F0000 +#define PFMC_C2EV 0x00200000 +#define PFMC_C2RS 0x80000000 +#define PFMC_C1SS_SHIFT 0 +#define PFMC_C2SS_SHIFT 16 +#define PFMC_BUS_GRANT 0 +#define PFMC_GRANT_AFTER_REQ 1 +#define PFMC_TRANSACTION 2 +#define PFMC_DWORD_TRANSFER 3 +#define PFMC_SLAVE_READ 4 +#define PFMC_SLAVE_WRITE 5 +#define PFMC_PREEMPTION 6 +#define PFMC_DISCONNECT_RETRY 7 +#define PFMC_INTERRUPT 8 +#define PFMC_BUS_OWNERSHIP 9 +#define PFMC_TRANSACTION_LAG 10 +#define PFMC_PCI_CLOCK 11 +#define PFMC_SERIAL_CLOCK 12 +#define PFMC_SP_CLOCK 13 + +/* + * The following defines are for the flags in the performance counter value 1 + * register. + */ +#define PFCV1_PC1V_MASK 0xFFFFFFFF +#define PFCV1_PC1V_SHIFT 0 + +/* + * The following defines are for the flags in the performance counter value 2 + * register. + */ +#define PFCV2_PC2V_MASK 0xFFFFFFFF +#define PFCV2_PC2V_SHIFT 0 + +/* + * The following defines are for the flags in the clock control register 1. + */ +#define CLKCR1_OSCS 0x00000001 +#define CLKCR1_OSCP 0x00000002 +#define CLKCR1_PLLSS_MASK 0x0000000C +#define CLKCR1_PLLSS_SERIAL 0x00000000 +#define CLKCR1_PLLSS_CRYSTAL 0x00000004 +#define CLKCR1_PLLSS_PCI 0x00000008 +#define CLKCR1_PLLSS_RESERVED 0x0000000C +#define CLKCR1_PLLP 0x00000010 +#define CLKCR1_SWCE 0x00000020 +#define CLKCR1_PLLOS 0x00000040 + +/* + * The following defines are for the flags in the clock control register 2. + */ +#define CLKCR2_PDIVS_MASK 0x0000000F +#define CLKCR2_PDIVS_1 0x00000001 +#define CLKCR2_PDIVS_2 0x00000002 +#define CLKCR2_PDIVS_4 0x00000004 +#define CLKCR2_PDIVS_7 0x00000007 +#define CLKCR2_PDIVS_8 0x00000008 +#define CLKCR2_PDIVS_16 0x00000000 + +/* + * The following defines are for the flags in the PLL multiplier register. + */ +#define PLLM_MASK 0x000000FF +#define PLLM_SHIFT 0 + +/* + * The following defines are for the flags in the PLL capacitor coefficient + * register. + */ +#define PLLCC_CDR_MASK 0x00000007 +#ifndef NO_CS4610 +#define PLLCC_CDR_240_350_MHZ 0x00000000 +#define PLLCC_CDR_184_265_MHZ 0x00000001 +#define PLLCC_CDR_144_205_MHZ 0x00000002 +#define PLLCC_CDR_111_160_MHZ 0x00000003 +#define PLLCC_CDR_87_123_MHZ 0x00000004 +#define PLLCC_CDR_67_96_MHZ 0x00000005 +#define PLLCC_CDR_52_74_MHZ 0x00000006 +#define PLLCC_CDR_45_58_MHZ 0x00000007 +#endif +#ifndef NO_CS4612 +#define PLLCC_CDR_271_398_MHZ 0x00000000 +#define PLLCC_CDR_227_330_MHZ 0x00000001 +#define PLLCC_CDR_167_239_MHZ 0x00000002 +#define PLLCC_CDR_150_215_MHZ 0x00000003 +#define PLLCC_CDR_107_154_MHZ 0x00000004 +#define PLLCC_CDR_98_140_MHZ 0x00000005 +#define PLLCC_CDR_73_104_MHZ 0x00000006 +#define PLLCC_CDR_63_90_MHZ 0x00000007 +#endif +#define PLLCC_LPF_MASK 0x000000F8 +#ifndef NO_CS4610 +#define PLLCC_LPF_23850_60000_KHZ 0x00000000 +#define PLLCC_LPF_7960_26290_KHZ 0x00000008 +#define PLLCC_LPF_4160_10980_KHZ 0x00000018 +#define PLLCC_LPF_1740_4580_KHZ 0x00000038 +#define PLLCC_LPF_724_1910_KHZ 0x00000078 +#define PLLCC_LPF_317_798_KHZ 0x000000F8 +#endif +#ifndef NO_CS4612 +#define PLLCC_LPF_25580_64530_KHZ 0x00000000 +#define PLLCC_LPF_14360_37270_KHZ 0x00000008 +#define PLLCC_LPF_6100_16020_KHZ 0x00000018 +#define PLLCC_LPF_2540_6690_KHZ 0x00000038 +#define PLLCC_LPF_1050_2780_KHZ 0x00000078 +#define PLLCC_LPF_450_1160_KHZ 0x000000F8 +#endif + +/* + * The following defines are for the flags in the feature reporting register. + */ +#define FRR_FAB_MASK 0x00000003 +#define FRR_MASK_MASK 0x0000001C +#ifdef NO_CS4612 +#define FRR_CFOP_MASK 0x000000E0 +#else +#define FRR_CFOP_MASK 0x00000FE0 +#endif +#define FRR_CFOP_NOT_DVD 0x00000020 +#define FRR_CFOP_A3D 0x00000040 +#define FRR_CFOP_128_PIN 0x00000080 +#ifndef NO_CS4612 +#define FRR_CFOP_CS4280 0x00000800 +#endif +#define FRR_FAB_SHIFT 0 +#define FRR_MASK_SHIFT 2 +#define FRR_CFOP_SHIFT 5 + +/* + * The following defines are for the flags in the configuration load 1 + * register. + */ +#define CFL1_CLOCK_SOURCE_MASK 0x00000003 +#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 +#define CFL1_CLOCK_SOURCE_AC97 0x00000001 +#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 +#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 +#define CFL1_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the configuration load 2 + * register. + */ +#define CFL2_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the serial port master control + * register 1. + */ +#define SERMC1_MSPE 0x00000001 +#define SERMC1_PTC_MASK 0x0000000E +#define SERMC1_PTC_CS423X 0x00000000 +#define SERMC1_PTC_AC97 0x00000002 +#define SERMC1_PTC_DAC 0x00000004 +#define SERMC1_PLB 0x00000010 +#define SERMC1_XLB 0x00000020 + +/* + * The following defines are for the flags in the serial port master control + * register 2. + */ +#define SERMC2_LROE 0x00000001 +#define SERMC2_MCOE 0x00000002 +#define SERMC2_MCDIV 0x00000004 + +/* + * The following defines are for the flags in the serial port 1 configuration + * register. + */ +#define SERC1_SO1EN 0x00000001 +#define SERC1_SO1F_MASK 0x0000000E +#define SERC1_SO1F_CS423X 0x00000000 +#define SERC1_SO1F_AC97 0x00000002 +#define SERC1_SO1F_DAC 0x00000004 +#define SERC1_SO1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 2 configuration + * register. + */ +#define SERC2_SI1EN 0x00000001 +#define SERC2_SI1F_MASK 0x0000000E +#define SERC2_SI1F_CS423X 0x00000000 +#define SERC2_SI1F_AC97 0x00000002 +#define SERC2_SI1F_ADC 0x00000004 +#define SERC2_SI1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 3 configuration + * register. + */ +#define SERC3_SO2EN 0x00000001 +#define SERC3_SO2F_MASK 0x00000006 +#define SERC3_SO2F_DAC 0x00000000 +#define SERC3_SO2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 4 configuration + * register. + */ +#define SERC4_SO3EN 0x00000001 +#define SERC4_SO3F_MASK 0x00000006 +#define SERC4_SO3F_DAC 0x00000000 +#define SERC4_SO3F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 5 configuration + * register. + */ +#define SERC5_SI2EN 0x00000001 +#define SERC5_SI2F_MASK 0x00000006 +#define SERC5_SI2F_ADC 0x00000000 +#define SERC5_SI2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor sample + * pointer register. + */ +#define SERBSP_FSP_MASK 0x0000000F +#define SERBSP_FSP_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor status + * register. + */ +#define SERBST_RRDY 0x00000001 +#define SERBST_WBSY 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor command + * register. + */ +#define SERBCM_RDC 0x00000001 +#define SERBCM_WRC 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor address + * register. + */ +#ifdef NO_CS4612 +#define SERBAD_FAD_MASK 0x000000FF +#else +#define SERBAD_FAD_MASK 0x000001FF +#endif +#define SERBAD_FAD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor + * configuration register. + */ +#define SERBCF_HBP 0x00000001 + +/* + * The following defines are for the flags in the serial port backdoor write + * port register. + */ +#define SERBWP_FWD_MASK 0x000FFFFF +#define SERBWP_FWD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor read + * port register. + */ +#define SERBRP_FRD_MASK 0x000FFFFF +#define SERBRP_FRD_SHIFT 0 + +/* + * The following defines are for the flags in the async FIFO address register. + */ +#ifndef NO_CS4612 +#define ASER_FADDR_A1_MASK 0x000001FF +#define ASER_FADDR_EN1 0x00008000 +#define ASER_FADDR_A2_MASK 0x01FF0000 +#define ASER_FADDR_EN2 0x80000000 +#define ASER_FADDR_A1_SHIFT 0 +#define ASER_FADDR_A2_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the AC97 control register. + */ +#define ACCTL_RSTN 0x00000001 +#define ACCTL_ESYN 0x00000002 +#define ACCTL_VFRM 0x00000004 +#define ACCTL_DCV 0x00000008 +#define ACCTL_CRW 0x00000010 +#define ACCTL_ASYN 0x00000020 +#ifndef NO_CS4612 +#define ACCTL_TC 0x00000040 +#endif + +/* + * The following defines are for the flags in the AC97 status register. + */ +#define ACSTS_CRDY 0x00000001 +#define ACSTS_VSTS 0x00000002 +#ifndef NO_CS4612 +#define ACSTS_WKUP 0x00000004 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register. + */ +#define ACOSV_SLV3 0x00000001 +#define ACOSV_SLV4 0x00000002 +#define ACOSV_SLV5 0x00000004 +#define ACOSV_SLV6 0x00000008 +#define ACOSV_SLV7 0x00000010 +#define ACOSV_SLV8 0x00000020 +#define ACOSV_SLV9 0x00000040 +#define ACOSV_SLV10 0x00000080 +#define ACOSV_SLV11 0x00000100 +#define ACOSV_SLV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 command address + * register. + */ +#define ACCAD_CI_MASK 0x0000007F +#define ACCAD_CI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 command data register. + */ +#define ACCDA_CD_MASK 0x0000FFFF +#define ACCDA_CD_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 input slot valid + * register. + */ +#define ACISV_ISV3 0x00000001 +#define ACISV_ISV4 0x00000002 +#define ACISV_ISV5 0x00000004 +#define ACISV_ISV6 0x00000008 +#define ACISV_ISV7 0x00000010 +#define ACISV_ISV8 0x00000020 +#define ACISV_ISV9 0x00000040 +#define ACISV_ISV10 0x00000080 +#define ACISV_ISV11 0x00000100 +#define ACISV_ISV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 status address + * register. + */ +#define ACSAD_SI_MASK 0x0000007F +#define ACSAD_SI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 status data register. + */ +#define ACSDA_SD_MASK 0x0000FFFF +#define ACSDA_SD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick poll/trigger + * register. + */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* + * The following defines are for the flags in the joystick control register. + */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* + * The following defines are for the flags in the joystick coordinate pair 1 + * readback register. + */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 + +/* + * The following defines are for the flags in the joystick coordinate pair 2 + * readback register. + */ +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* + * The following defines are for the flags in the MIDI control register. + */ +#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ +#define MIDCR_RXE 0x00000002 /* Enable receiving. */ +#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ +#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ +#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ +#define MIDCR_MRST 0x00000020 /* Reset interface. */ + +/* + * The following defines are for the flags in the MIDI status register. + */ +#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ +#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ + +/* + * The following defines are for the flags in the MIDI write port register. + */ +#define MIDWP_MWD_MASK 0x000000FF +#define MIDWP_MWD_SHIFT 0 + +/* + * The following defines are for the flags in the MIDI read port register. + */ +#define MIDRP_MRD_MASK 0x000000FF +#define MIDRP_MRD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick GPIO register. + */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * The following defines are for the flags in the master async/sync serial + * port enable register. + */ +#ifndef NO_CS4612 +#define ASER_MASTER_ME 0x00000001 +#endif + +/* + * The following defines are for the flags in the configuration interface + * register. + */ +#define CFGI_CLK 0x00000001 +#define CFGI_DOUT 0x00000002 +#define CFGI_DIN_EEN 0x00000004 +#define CFGI_EELD 0x00000008 + +/* + * The following defines are for the flags in the subsystem ID and vendor ID + * register. + */ +#define SSVID_VID_MASK 0x0000FFFF +#define SSVID_SID_MASK 0xFFFF0000 +#define SSVID_VID_SHIFT 0 +#define SSVID_SID_SHIFT 16 + +/* + * The following defines are for the flags in the GPIO pin interface register. + */ +#define GPIOR_VOLDN 0x00000001 +#define GPIOR_VOLUP 0x00000002 +#define GPIOR_SI2D 0x00000004 +#define GPIOR_SI2OE 0x00000008 + +/* + * The following defines are for the flags in the extended GPIO pin direction + * register. + */ +#ifndef NO_CS4612 +#define EGPIODR_GPOE0 0x00000001 +#define EGPIODR_GPOE1 0x00000002 +#define EGPIODR_GPOE2 0x00000004 +#define EGPIODR_GPOE3 0x00000008 +#define EGPIODR_GPOE4 0x00000010 +#define EGPIODR_GPOE5 0x00000020 +#define EGPIODR_GPOE6 0x00000040 +#define EGPIODR_GPOE7 0x00000080 +#define EGPIODR_GPOE8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin polarity/ + * type register. + */ +#ifndef NO_CS4612 +#define EGPIOPTR_GPPT0 0x00000001 +#define EGPIOPTR_GPPT1 0x00000002 +#define EGPIOPTR_GPPT2 0x00000004 +#define EGPIOPTR_GPPT3 0x00000008 +#define EGPIOPTR_GPPT4 0x00000010 +#define EGPIOPTR_GPPT5 0x00000020 +#define EGPIOPTR_GPPT6 0x00000040 +#define EGPIOPTR_GPPT7 0x00000080 +#define EGPIOPTR_GPPT8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin sticky + * register. + */ +#ifndef NO_CS4612 +#define EGPIOTR_GPS0 0x00000001 +#define EGPIOTR_GPS1 0x00000002 +#define EGPIOTR_GPS2 0x00000004 +#define EGPIOTR_GPS3 0x00000008 +#define EGPIOTR_GPS4 0x00000010 +#define EGPIOTR_GPS5 0x00000020 +#define EGPIOTR_GPS6 0x00000040 +#define EGPIOTR_GPS7 0x00000080 +#define EGPIOTR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO ping wakeup + * register. + */ +#ifndef NO_CS4612 +#define EGPIOWR_GPW0 0x00000001 +#define EGPIOWR_GPW1 0x00000002 +#define EGPIOWR_GPW2 0x00000004 +#define EGPIOWR_GPW3 0x00000008 +#define EGPIOWR_GPW4 0x00000010 +#define EGPIOWR_GPW5 0x00000020 +#define EGPIOWR_GPW6 0x00000040 +#define EGPIOWR_GPW7 0x00000080 +#define EGPIOWR_GPW8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin status + * register. + */ +#ifndef NO_CS4612 +#define EGPIOSR_GPS0 0x00000001 +#define EGPIOSR_GPS1 0x00000002 +#define EGPIOSR_GPS2 0x00000004 +#define EGPIOSR_GPS3 0x00000008 +#define EGPIOSR_GPS4 0x00000010 +#define EGPIOSR_GPS5 0x00000020 +#define EGPIOSR_GPS6 0x00000040 +#define EGPIOSR_GPS7 0x00000080 +#define EGPIOSR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the serial port 6 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC6_ASDO2EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the serial port 7 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC7_ASDI2EN 0x00000001 +#define SERC7_POSILB 0x00000002 +#define SERC7_SIPOLB 0x00000004 +#define SERC7_SOSILB 0x00000008 +#define SERC7_SISOLB 0x00000010 +#endif + +/* + * The following defines are for the flags in the serial port AC link + * configuration register. + */ +#ifndef NO_CS4612 +#define SERACC_CHIP_TYPE_MASK 0x00000001 +#define SERACC_CHIP_TYPE_1_03 0x00000000 +#define SERACC_CHIP_TYPE_2_0 0x00000001 +#define SERACC_TWO_CODECS 0x00000002 +#define SERACC_MDM 0x00000004 +#define SERACC_HSP 0x00000008 +#endif + +/* + * The following defines are for the flags in the AC97 control register 2. + */ +#ifndef NO_CS4612 +#define ACCTL2_RSTN 0x00000001 +#define ACCTL2_ESYN 0x00000002 +#define ACCTL2_VFRM 0x00000004 +#define ACCTL2_DCV 0x00000008 +#define ACCTL2_CRW 0x00000010 +#define ACCTL2_ASYN 0x00000020 +#endif + +/* + * The following defines are for the flags in the AC97 status register 2. + */ +#ifndef NO_CS4612 +#define ACSTS2_CRDY 0x00000001 +#define ACSTS2_VSTS 0x00000002 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACOSV2_SLV3 0x00000001 +#define ACOSV2_SLV4 0x00000002 +#define ACOSV2_SLV5 0x00000004 +#define ACOSV2_SLV6 0x00000008 +#define ACOSV2_SLV7 0x00000010 +#define ACOSV2_SLV8 0x00000020 +#define ACOSV2_SLV9 0x00000040 +#define ACOSV2_SLV10 0x00000080 +#define ACOSV2_SLV11 0x00000100 +#define ACOSV2_SLV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 command address + * register 2. + */ +#ifndef NO_CS4612 +#define ACCAD2_CI_MASK 0x0000007F +#define ACCAD2_CI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 command data register + * 2. + */ +#ifndef NO_CS4612 +#define ACCDA2_CD_MASK 0x0000FFFF +#define ACCDA2_CD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 input slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACISV2_ISV3 0x00000001 +#define ACISV2_ISV4 0x00000002 +#define ACISV2_ISV5 0x00000004 +#define ACISV2_ISV6 0x00000008 +#define ACISV2_ISV7 0x00000010 +#define ACISV2_ISV8 0x00000020 +#define ACISV2_ISV9 0x00000040 +#define ACISV2_ISV10 0x00000080 +#define ACISV2_ISV11 0x00000100 +#define ACISV2_ISV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 status address + * register 2. + */ +#ifndef NO_CS4612 +#define ACSAD2_SI_MASK 0x0000007F +#define ACSAD2_SI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 status data register 2. + */ +#ifndef NO_CS4612 +#define ACSDA2_SD_MASK 0x0000FFFF +#define ACSDA2_SD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap address and control + * registers (all 12). + */ +#ifndef NO_CS4612 +#define IOTAC_SA_MASK 0x0000FFFF +#define IOTAC_MSK_MASK 0x000F0000 +#define IOTAC_IODC_MASK 0x06000000 +#define IOTAC_IODC_16_BIT 0x00000000 +#define IOTAC_IODC_10_BIT 0x02000000 +#define IOTAC_IODC_12_BIT 0x04000000 +#define IOTAC_WSPI 0x08000000 +#define IOTAC_RSPI 0x10000000 +#define IOTAC_WSE 0x20000000 +#define IOTAC_WE 0x40000000 +#define IOTAC_RE 0x80000000 +#define IOTAC_SA_SHIFT 0 +#define IOTAC_MSK_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap fast read registers + * (all 8). + */ +#ifndef NO_CS4612 +#define IOTFR_D_MASK 0x0000FFFF +#define IOTFR_A_MASK 0x000F0000 +#define IOTFR_R_MASK 0x0F000000 +#define IOTFR_ALL 0x40000000 +#define IOTFR_VL 0x80000000 +#define IOTFR_D_SHIFT 0 +#define IOTFR_A_SHIFT 16 +#define IOTFR_R_SHIFT 24 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO register. + */ +#ifndef NO_CS4612 +#define IOTFIFO_BA_MASK 0x00003FFF +#define IOTFIFO_S_MASK 0x00FF0000 +#define IOTFIFO_OF 0x40000000 +#define IOTFIFO_SPIOF 0x80000000 +#define IOTFIFO_BA_SHIFT 0 +#define IOTFIFO_S_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap retry read data + * register. + */ +#ifndef NO_CS4612 +#define IOTRRD_D_MASK 0x0000FFFF +#define IOTRRD_RDV 0x80000000 +#define IOTRRD_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO pointer + * register. + */ +#ifndef NO_CS4612 +#define IOTFP_CA_MASK 0x00003FFF +#define IOTFP_PA_MASK 0x3FFF0000 +#define IOTFP_CA_SHIFT 0 +#define IOTFP_PA_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap control register. + */ +#ifndef NO_CS4612 +#define IOTCR_ITD 0x00000001 +#define IOTCR_HRV 0x00000002 +#define IOTCR_SRV 0x00000004 +#define IOTCR_DTI 0x00000008 +#define IOTCR_DFI 0x00000010 +#define IOTCR_DDP 0x00000020 +#define IOTCR_JTE 0x00000040 +#define IOTCR_PPE 0x00000080 +#endif + +/* + * The following defines are for the flags in the direct PCI data register. + */ +#ifndef NO_CS4612 +#define DPCID_D_MASK 0xFFFFFFFF +#define DPCID_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI address register. + */ +#ifndef NO_CS4612 +#define DPCIA_A_MASK 0xFFFFFFFF +#define DPCIA_A_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI command register. + */ +#ifndef NO_CS4612 +#define DPCIC_C_MASK 0x0000000F +#define DPCIC_C_IOREAD 0x00000002 +#define DPCIC_C_IOWRITE 0x00000003 +#define DPCIC_BE_MASK 0x000000F0 +#endif + +/* + * The following defines are for the flags in the PC/PCI request register. + */ +#ifndef NO_CS4612 +#define PCPCIR_RDC_MASK 0x00000007 +#define PCPCIR_C_MASK 0x00007000 +#define PCPCIR_REQ 0x00008000 +#define PCPCIR_RDC_SHIFT 0 +#define PCPCIR_C_SHIFT 12 +#endif + +/* + * The following defines are for the flags in the PC/PCI grant register. + */ +#ifndef NO_CS4612 +#define PCPCIG_GDC_MASK 0x00000007 +#define PCPCIG_VL 0x00008000 +#define PCPCIG_GDC_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the PC/PCI master enable + * register. + */ +#ifndef NO_CS4612 +#define PCPCIEN_EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the extended PCI power + * management control register. + */ +#ifndef NO_CS4612 +#define EPCIPMC_GWU 0x00000001 +#define EPCIPMC_FSPC 0x00000002 +#endif + +/* + * The following defines are for the flags in the SP control register. + */ +#define SPCR_RUN 0x00000001 +#define SPCR_STPFR 0x00000002 +#define SPCR_RUNFR 0x00000004 +#define SPCR_TICK 0x00000008 +#define SPCR_DRQEN 0x00000020 +#define SPCR_RSTSP 0x00000040 +#define SPCR_OREN 0x00000080 +#ifndef NO_CS4612 +#define SPCR_PCIINT 0x00000100 +#define SPCR_OINTD 0x00000200 +#define SPCR_CRE 0x00008000 +#endif + +/* + * The following defines are for the flags in the debug index register. + */ +#define DREG_REGID_MASK 0x0000007F +#define DREG_DEBUG 0x00000080 +#define DREG_RGBK_MASK 0x00000700 +#define DREG_TRAP 0x00000800 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000 +#endif +#endif +#define DREG_REGID_SHIFT 0 +#define DREG_RGBK_SHIFT 8 +#define DREG_RGBK_REGID_MASK 0x0000077F +#define DREG_REGID_R0 0x00000010 +#define DREG_REGID_R1 0x00000011 +#define DREG_REGID_R2 0x00000012 +#define DREG_REGID_R3 0x00000013 +#define DREG_REGID_R4 0x00000014 +#define DREG_REGID_R5 0x00000015 +#define DREG_REGID_R6 0x00000016 +#define DREG_REGID_R7 0x00000017 +#define DREG_REGID_R8 0x00000018 +#define DREG_REGID_R9 0x00000019 +#define DREG_REGID_RA 0x0000001A +#define DREG_REGID_RB 0x0000001B +#define DREG_REGID_RC 0x0000001C +#define DREG_REGID_RD 0x0000001D +#define DREG_REGID_RE 0x0000001E +#define DREG_REGID_RF 0x0000001F +#define DREG_REGID_RA_BUS_LOW 0x00000020 +#define DREG_REGID_RA_BUS_HIGH 0x00000038 +#define DREG_REGID_YBUS_LOW 0x00000050 +#define DREG_REGID_YBUS_HIGH 0x00000058 +#define DREG_REGID_TRAP_0 0x00000100 +#define DREG_REGID_TRAP_1 0x00000101 +#define DREG_REGID_TRAP_2 0x00000102 +#define DREG_REGID_TRAP_3 0x00000103 +#define DREG_REGID_TRAP_4 0x00000104 +#define DREG_REGID_TRAP_5 0x00000105 +#define DREG_REGID_TRAP_6 0x00000106 +#define DREG_REGID_TRAP_7 0x00000107 +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E +#define DREG_REGID_TOP_OF_STACK 0x0000010F +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110 +#define DREG_REGID_TRAP_9 0x00000111 +#define DREG_REGID_TRAP_10 0x00000112 +#define DREG_REGID_TRAP_11 0x00000113 +#define DREG_REGID_TRAP_12 0x00000114 +#define DREG_REGID_TRAP_13 0x00000115 +#define DREG_REGID_TRAP_14 0x00000116 +#define DREG_REGID_TRAP_15 0x00000117 +#define DREG_REGID_TRAP_16 0x00000118 +#define DREG_REGID_TRAP_17 0x00000119 +#define DREG_REGID_TRAP_18 0x0000011A +#define DREG_REGID_TRAP_19 0x0000011B +#define DREG_REGID_TRAP_20 0x0000011C +#define DREG_REGID_TRAP_21 0x0000011D +#define DREG_REGID_TRAP_22 0x0000011E +#define DREG_REGID_TRAP_23 0x0000011F +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200 +#define DREG_REGID_RSA0_HIGH 0x00000201 +#define DREG_REGID_RSA1_LOW 0x00000202 +#define DREG_REGID_RSA1_HIGH 0x00000203 +#define DREG_REGID_RSA2 0x00000204 +#define DREG_REGID_RSA3 0x00000205 +#define DREG_REGID_RSI0_LOW 0x00000206 +#define DREG_REGID_RSI0_HIGH 0x00000207 +#define DREG_REGID_RSI1 0x00000208 +#define DREG_REGID_RSI2 0x00000209 +#define DREG_REGID_SAGUSTATUS 0x0000020A +#define DREG_REGID_RSCONFIG01_LOW 0x0000020B +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C +#define DREG_REGID_RSCONFIG23_LOW 0x0000020D +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E +#define DREG_REGID_RSDMA01E 0x0000020F +#define DREG_REGID_RSDMA23E 0x00000210 +#define DREG_REGID_RSD0_LOW 0x00000211 +#define DREG_REGID_RSD0_HIGH 0x00000212 +#define DREG_REGID_RSD1_LOW 0x00000213 +#define DREG_REGID_RSD1_HIGH 0x00000214 +#define DREG_REGID_RSD2_LOW 0x00000215 +#define DREG_REGID_RSD2_HIGH 0x00000216 +#define DREG_REGID_RSD3_LOW 0x00000217 +#define DREG_REGID_RSD3_HIGH 0x00000218 +#define DREG_REGID_SRAR_HIGH 0x0000021A +#define DREG_REGID_SRAR_LOW 0x0000021B +#define DREG_REGID_DMA_STATE 0x0000021C +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E +#define DREG_REGID_CPU_STATUS 0x00000300 +#define DREG_REGID_MAC_MODE 0x00000301 +#define DREG_REGID_STACK_AND_REPEAT 0x00000302 +#define DREG_REGID_INDEX0 0x00000304 +#define DREG_REGID_INDEX1 0x00000305 +#define DREG_REGID_DMA_STATE_0_3 0x00000400 +#define DREG_REGID_DMA_STATE_4_7 0x00000404 +#define DREG_REGID_DMA_STATE_8_11 0x00000408 +#define DREG_REGID_DMA_STATE_12_15 0x0000040C +#define DREG_REGID_DMA_STATE_16_19 0x00000410 +#define DREG_REGID_DMA_STATE_20_23 0x00000414 +#define DREG_REGID_DMA_STATE_24_27 0x00000418 +#define DREG_REGID_DMA_STATE_28_31 0x0000041C +#define DREG_REGID_DMA_STATE_32_35 0x00000420 +#define DREG_REGID_DMA_STATE_36_39 0x00000424 +#define DREG_REGID_DMA_STATE_40_43 0x00000428 +#define DREG_REGID_DMA_STATE_44_47 0x0000042C +#define DREG_REGID_DMA_STATE_48_51 0x00000430 +#define DREG_REGID_DMA_STATE_52_55 0x00000434 +#define DREG_REGID_DMA_STATE_56_59 0x00000438 +#define DREG_REGID_DMA_STATE_60_63 0x0000043C +#define DREG_REGID_DMA_STATE_64_67 0x00000440 +#define DREG_REGID_DMA_STATE_68_71 0x00000444 +#define DREG_REGID_DMA_STATE_72_75 0x00000448 +#define DREG_REGID_DMA_STATE_76_79 0x0000044C +#define DREG_REGID_DMA_STATE_80_83 0x00000450 +#define DREG_REGID_DMA_STATE_84_87 0x00000454 +#define DREG_REGID_DMA_STATE_88_91 0x00000458 +#define DREG_REGID_DMA_STATE_92_95 0x0000045C +#define DREG_REGID_TRAP_SELECT 0x00000500 +#define DREG_REGID_TRAP_WRITE_0 0x00000500 +#define DREG_REGID_TRAP_WRITE_1 0x00000501 +#define DREG_REGID_TRAP_WRITE_2 0x00000502 +#define DREG_REGID_TRAP_WRITE_3 0x00000503 +#define DREG_REGID_TRAP_WRITE_4 0x00000504 +#define DREG_REGID_TRAP_WRITE_5 0x00000505 +#define DREG_REGID_TRAP_WRITE_6 0x00000506 +#define DREG_REGID_TRAP_WRITE_7 0x00000507 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510 +#define DREG_REGID_TRAP_WRITE_9 0x00000511 +#define DREG_REGID_TRAP_WRITE_10 0x00000512 +#define DREG_REGID_TRAP_WRITE_11 0x00000513 +#define DREG_REGID_TRAP_WRITE_12 0x00000514 +#define DREG_REGID_TRAP_WRITE_13 0x00000515 +#define DREG_REGID_TRAP_WRITE_14 0x00000516 +#define DREG_REGID_TRAP_WRITE_15 0x00000517 +#define DREG_REGID_TRAP_WRITE_16 0x00000518 +#define DREG_REGID_TRAP_WRITE_17 0x00000519 +#define DREG_REGID_TRAP_WRITE_18 0x0000051A +#define DREG_REGID_TRAP_WRITE_19 0x0000051B +#define DREG_REGID_TRAP_WRITE_20 0x0000051C +#define DREG_REGID_TRAP_WRITE_21 0x0000051D +#define DREG_REGID_TRAP_WRITE_22 0x0000051E +#define DREG_REGID_TRAP_WRITE_23 0x0000051F +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 +#define DREG_REGID_MAC0_ACC0_MID 0x00000608 +#define DREG_REGID_MAC0_ACC1_MID 0x00000609 +#define DREG_REGID_MAC0_ACC2_MID 0x0000060A +#define DREG_REGID_MAC0_ACC3_MID 0x0000060B +#define DREG_REGID_MAC1_ACC0_MID 0x0000060C +#define DREG_REGID_MAC1_ACC1_MID 0x0000060D +#define DREG_REGID_MAC1_ACC2_MID 0x0000060E +#define DREG_REGID_MAC1_ACC3_MID 0x0000060F +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 +#define DREG_REGID_RSHOUT_LOW 0x00000620 +#define DREG_REGID_RSHOUT_MID 0x00000628 +#define DREG_REGID_RSHOUT_HIGH 0x00000630 + +/* + * The following defines are for the flags in the DMA stream requestor write + */ +#define DSRWP_DSR_MASK 0x0000000F +#define DSRWP_DSR_BG_RQ 0x00000001 +#define DSRWP_DSR_PRIORITY_MASK 0x00000006 +#define DSRWP_DSR_PRIORITY_0 0x00000000 +#define DSRWP_DSR_PRIORITY_1 0x00000002 +#define DSRWP_DSR_PRIORITY_2 0x00000004 +#define DSRWP_DSR_PRIORITY_3 0x00000006 +#define DSRWP_DSR_RQ_PENDING 0x00000008 + +/* + * The following defines are for the flags in the trap write port register. + */ +#define TWPR_TW_MASK 0x0000FFFF +#define TWPR_TW_SHIFT 0 + +/* + * The following defines are for the flags in the stack pointer write + * register. + */ +#define SPWR_STKP_MASK 0x0000000F +#define SPWR_STKP_SHIFT 0 + +/* + * The following defines are for the flags in the SP interrupt register. + */ +#define SPIR_FRI 0x00000001 +#define SPIR_DOI 0x00000002 +#define SPIR_GPI2 0x00000004 +#define SPIR_GPI3 0x00000008 +#define SPIR_IP0 0x00000010 +#define SPIR_IP1 0x00000020 +#define SPIR_IP2 0x00000040 +#define SPIR_IP3 0x00000080 + +/* + * The following defines are for the flags in the functional group 1 register. + */ +#define FGR1_F1S_MASK 0x0000FFFF +#define FGR1_F1S_SHIFT 0 + +/* + * The following defines are for the flags in the SP clock status register. + */ +#define SPCS_FRI 0x00000001 +#define SPCS_DOI 0x00000002 +#define SPCS_GPI2 0x00000004 +#define SPCS_GPI3 0x00000008 +#define SPCS_IP0 0x00000010 +#define SPCS_IP1 0x00000020 +#define SPCS_IP2 0x00000040 +#define SPCS_IP3 0x00000080 +#define SPCS_SPRUN 0x00000100 +#define SPCS_SLEEP 0x00000200 +#define SPCS_FG 0x00000400 +#define SPCS_ORUN 0x00000800 +#define SPCS_IRQ 0x00001000 +#define SPCS_FGN_MASK 0x0000E000 +#define SPCS_FGN_SHIFT 13 + +/* + * The following defines are for the flags in the SP DMA requestor status + * register. + */ +#define SDSR_DCS_MASK 0x000000FF +#define SDSR_DCS_SHIFT 0 +#define SDSR_DCS_NONE 0x00000007 + +/* + * The following defines are for the flags in the frame timer register. + */ +#define FRMT_FTV_MASK 0x0000FFFF +#define FRMT_FTV_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer current count + * register. + */ +#define FRCC_FCC_MASK 0x0000FFFF +#define FRCC_FCC_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer save count + * register. + */ +#define FRSC_FCS_MASK 0x0000FFFF +#define FRSC_FCS_SHIFT 0 + +/* + * The following define the various flags stored in the scatter/gather + * descriptors. + */ +#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 +#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 +#define DMA_SG_SAMPLE_END_FLAG 0x10000000 +#define DMA_SG_LOOP_END_FLAG 0x20000000 +#define DMA_SG_SIGNAL_END_FLAG 0x40000000 +#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 +#define DMA_SG_NEXT_ENTRY_SHIFT 3 +#define DMA_SG_SAMPLE_END_SHIFT 16 + +/* + * The following define the offsets of the fields within the on-chip generic + * DMA requestor. + */ +#define DMA_RQ_CONTROL1 0x00000000 +#define DMA_RQ_CONTROL2 0x00000004 +#define DMA_RQ_SOURCE_ADDR 0x00000008 +#define DMA_RQ_DESTINATION_ADDR 0x0000000C +#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 +#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 +#define DMA_RQ_LOOP_START_ADDR 0x00000018 +#define DMA_RQ_POST_LOOP_ADDR 0x0000001C +#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 + +/* + * The following defines are for the flags in the first control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C1_COUNT_MASK 0x000003FF +#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 +#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 +#define DMA_RQ_C1_DONE_FLAG 0x00004000 +#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 +#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 +#define DMA_RQ_C1_FULL_PAGE 0x00000000 +#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 +#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 +#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 +#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 +#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 +#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 +#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 +#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 +#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 +#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 +#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 +#define DMA_RQ_C1_PM_RESERVED 0x00200000 +#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 +#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 +#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 +#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 +#define DMA_RQ_C1_DEST_LINEAR 0x00000000 +#define DMA_RQ_C1_DEST_MOD16 0x01000000 +#define DMA_RQ_C1_DEST_MOD32 0x02000000 +#define DMA_RQ_C1_DEST_MOD64 0x03000000 +#define DMA_RQ_C1_DEST_MOD128 0x04000000 +#define DMA_RQ_C1_DEST_MOD256 0x05000000 +#define DMA_RQ_C1_DEST_MOD512 0x06000000 +#define DMA_RQ_C1_DEST_MOD1024 0x07000000 +#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 +#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 +#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 +#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 +#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 +#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 +#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 +#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 +#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 +#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 +#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 +#define DMA_RQ_C1_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the second control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F +#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 +#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 +#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 +#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 +#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 +#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 +#define DMA_RQ_C2_AC_NONE 0x00000000 +#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 +#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 +#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 +#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 +#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 +#define DMA_RQ_C2_LOOP_MASK 0x30000000 +#define DMA_RQ_C2_NO_LOOP 0x00000000 +#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 +#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 +#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 +#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 +#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 +#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 +#define DMA_RQ_C2_LOOP_END_SHIFT 16 + +/* + * The following defines are for the flags in the source and destination words + * of the on-chip generic DMA requestor. + */ +#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF +#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 +#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 +#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 +#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 +#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 +#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 +#define DMA_RQ_SD_END_FLAG 0x40000000 +#define DMA_RQ_SD_ERROR_FLAG 0x80000000 +#define DMA_RQ_SD_ADDRESS_SHIFT 0 + +/* + * The following defines are for the flags in the page map address word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 +#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 +#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 + +#define BA1_VARIDEC_BUF_1 0x000 + +#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ +#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ +#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ +#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ +#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ +#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ + +#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ +#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ +#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ +#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ +#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ +#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ +#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ + +#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ +#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ +#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ +#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ + +/* + * + */ + +#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */ +#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */ + +/* + * + */ + +#define SAVE_REG_MAX 0x10 +#define POWER_DOWN_ALL 0x7f0f + +/* + * + */ + +typedef struct _snd_cs46xx cs46xx_t; + +typedef struct { + char name[24]; + unsigned long base; + unsigned long remap_addr; + unsigned long size; + struct resource *resource; + void *proc_entry; +} snd_cs46xx_region_t; + +struct _snd_cs46xx { + int irq; + unsigned long ba0_addr; + unsigned long ba1_addr; + union { + struct { + snd_cs46xx_region_t ba0; + snd_cs46xx_region_t data0; + snd_cs46xx_region_t data1; + snd_cs46xx_region_t pmem; + snd_cs46xx_region_t reg; + } name; + snd_cs46xx_region_t idx[5]; + } region; + + unsigned int mode; + + struct { + unsigned char *hw_area; + dma_addr_t hw_addr; /* PCI bus address, not accessible */ + unsigned long hw_size; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + unsigned int sw_bufsize; + unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ + unsigned int sw_io; + int sw_ready; /* Bytes ready to be transferred to/from hw */ + unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ + unsigned int hw_io; /* Ring buffer hw pointer */ + int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ + size_t appl_ptr; /* Last seen appl_ptr */ + snd_pcm_substream_t *substream; + } play, capt; + + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + snd_info_entry_t *proc_entry; + + int amplifier; + void (*amplifier_ctrl)(cs46xx_t *, int); + void (*active_ctrl)(cs46xx_t *, int); + struct pci_dev *acpi_dev; + int acpi_port; + snd_kcontrol_t *eapd_switch; /* for amplifier hack */ + +#ifdef CONFIG_PM + struct pm_dev *pm_dev; +#endif +}; + +int snd_cs46xx_create(snd_card_t *card, + struct pci_dev *pci, + int external_amp, int thinkpad, + cs46xx_t **rcodec); + +int snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t **rpcm); +int snd_cs46xx_mixer(cs46xx_t *chip); +int snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rmidi); + +#ifdef CONFIG_PM +void snd_cs46xx_suspend(cs46xx_t *chip); +void snd_cs46xx_resume(cs46xx_t *chip); +#endif + +#endif /* __SOUND_CS46XX_H */ diff -Nru linux/include/sound/cs8403.h linux-2.4.19-pre5-mjc/include/sound/cs8403.h --- linux/include/sound/cs8403.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/cs8403.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,257 @@ +#ifndef __SOUND_CS8403_H +#define __SOUND_CS8403_H + +/* + * Routines for Cirrus Logic CS8403/CS8404A IEC958 (S/PDIF) Transmitter + * Copyright (c) by Jaroslav Kysela , + * Takashi Iwai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef SND_CS8403 + +#ifndef SND_CS8403_DECL +#define SND_CS8403_DECL static +#endif +#ifndef SND_CS8403_DECODE +#define SND_CS8403_DECODE snd_cs8403_decode_spdif_bits +#endif +#ifndef SND_CS8403_ENCODE +#define SND_CS8403_ENCODE snd_cs8403_encode_spdif_bits +#endif + + +SND_CS8403_DECL void SND_CS8403_DECODE(snd_aes_iec958_t *diga, unsigned char bits) +{ + if (bits & 0x01) { /* consumer */ + if (!(bits & 0x02)) + diga->status[0] |= IEC958_AES0_NONAUDIO; + if (!(bits & 0x08)) + diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + switch (bits & 0x10) { + case 0x10: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; break; + case 0x00: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; break; + } + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_CON_ORIGINAL; + switch (bits & 0x60) { + case 0x00: diga->status[1] |= IEC958_AES1_CON_MAGNETIC_ID; break; + case 0x20: diga->status[1] |= IEC958_AES1_CON_DIGDIGCONV_ID; break; + case 0x40: diga->status[1] |= IEC958_AES1_CON_LASEROPT_ID; break; + case 0x60: diga->status[1] |= IEC958_AES1_CON_GENERAL; break; + } + switch (bits & 0x06) { + case 0x00: diga->status[3] |= IEC958_AES3_CON_FS_44100; break; + case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_48000; break; + case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_32000; break; + } + } else { + diga->status[0] = IEC958_AES0_PROFESSIONAL; + switch (bits & 0x18) { + case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 0x10: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 0x08: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break; + case 0x18: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + switch (bits & 0x60) { + case 0x20: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break; + case 0x40: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break; + case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break; + case 0x60: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break; + } + if (bits & 0x80) + diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC; + } +} + +SND_CS8403_DECL unsigned char SND_CS8403_ENCODE(snd_aes_iec958_t *diga) +{ + unsigned char bits; + + if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) { + bits = 0x01; /* consumer mode */ + if (diga->status[0] & IEC958_AES0_NONAUDIO) + bits &= ~0x02; + else + bits |= 0x02; + if (diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) + bits &= ~0x08; + else + bits |= 0x08; + switch (diga->status[0] & IEC958_AES0_CON_EMPHASIS) { + default: + case IEC958_AES0_CON_EMPHASIS_NONE: bits |= 0x10; break; + case IEC958_AES0_CON_EMPHASIS_5015: bits |= 0x00; break; + } + if (diga->status[1] & IEC958_AES1_CON_ORIGINAL) + bits &= ~0x80; + else + bits |= 0x80; + if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL) + bits |= 0x60; + else { + switch(diga->status[1] & IEC958_AES1_CON_MAGNETIC_MASK) { + case IEC958_AES1_CON_MAGNETIC_ID: + bits |= 0x00; break; + case IEC958_AES1_CON_DIGDIGCONV_ID: + bits |= 0x20; break; + default: + case IEC958_AES1_CON_LASEROPT_ID: + bits |= 0x40; break; + } + } + switch (diga->status[3] & IEC958_AES3_CON_FS) { + default: + case IEC958_AES3_CON_FS_44100: bits |= 0x00; break; + case IEC958_AES3_CON_FS_48000: bits |= 0x02; break; + case IEC958_AES3_CON_FS_32000: bits |= 0x04; break; + } + } else { + bits = 0x00; /* professional mode */ + if (diga->status[0] & IEC958_AES0_NONAUDIO) + bits &= ~0x02; + else + bits |= 0x02; + /* CHECKME: I'm not sure about the bit order in val here */ + switch (diga->status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_32000: bits |= 0x00; break; + case IEC958_AES0_PRO_FS_44100: bits |= 0x10; break; /* 44.1kHz */ + case IEC958_AES0_PRO_FS_48000: bits |= 0x08; break; /* 48kHz */ + default: + case IEC958_AES0_PRO_FS_NOTID: bits |= 0x18; break; + } + switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) { + case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x20; break; + case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x40; break; + case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break; + default: + case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x60; break; + } + switch (diga->status[1] & IEC958_AES1_PRO_MODE) { + case IEC958_AES1_PRO_MODE_TWO: + case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break; + default: bits |= 0x80; break; + } + } + return bits; +} + +#endif /* SND_CS8403 */ + +#ifdef SND_CS8404 + +#ifndef SND_CS8404_DECL +#define SND_CS8404_DECL static +#endif +#ifndef SND_CS8404_DECODE +#define SND_CS8404_DECODE snd_cs8404_decode_spdif_bits +#endif +#ifndef SND_CS8404_ENCODE +#define SND_CS8404_ENCODE snd_cs8404_encode_spdif_bits +#endif + + +SND_CS8404_DECL void SND_CS8404_DECODE(snd_aes_iec958_t *diga, unsigned char bits) +{ + if (bits & 0x10) { /* consumer */ + if (!(bits & 0x20)) + diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + if (!(bits & 0x40)) + diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_CON_ORIGINAL; + switch (bits & 0x03) { + case 0x00: diga->status[1] |= IEC958_AES1_CON_DAT; break; + case 0x03: diga->status[1] |= IEC958_AES1_CON_GENERAL; break; + } + switch (bits & 0x06) { + case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_32000; break; + case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_48000; break; + case 0x06: diga->status[3] |= IEC958_AES3_CON_FS_44100; break; + } + } else { + diga->status[0] = IEC958_AES0_PROFESSIONAL; + if (!(bits & 0x04)) + diga->status[0] |= IEC958_AES0_NONAUDIO; + switch (bits & 0x60) { + case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 0x40: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 0x20: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break; + case 0x60: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + switch (bits & 0x03) { + case 0x02: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break; + case 0x01: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break; + case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break; + case 0x03: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break; + } + if (!(bits & 0x80)) + diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC; + } +} + +SND_CS8404_DECL unsigned char SND_CS8404_ENCODE(snd_aes_iec958_t *diga) +{ + unsigned char bits; + + if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) { + bits = 0x10; /* consumer mode */ + if (!(diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT)) + bits |= 0x20; + if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_NONE) + bits |= 0x40; + if (!(diga->status[1] & IEC958_AES1_CON_ORIGINAL)) + bits |= 0x80; + if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL) + bits |= 0x03; + switch (diga->status[3] & IEC958_AES3_CON_FS) { + default: + case IEC958_AES3_CON_FS_44100: bits |= 0x06; break; + case IEC958_AES3_CON_FS_48000: bits |= 0x04; break; + case IEC958_AES3_CON_FS_32000: bits |= 0x02; break; + } + } else { + bits = 0x00; /* professional mode */ + if (!(diga->status[0] & IEC958_AES0_NONAUDIO)) + bits |= 0x04; + switch (diga->status[0] & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_32000: bits |= 0x00; break; + case IEC958_AES0_PRO_FS_44100: bits |= 0x40; break; /* 44.1kHz */ + case IEC958_AES0_PRO_FS_48000: bits |= 0x20; break; /* 48kHz */ + default: + case IEC958_AES0_PRO_FS_NOTID: bits |= 0x00; break; + } + switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) { + case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x02; break; + case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x01; break; + case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break; + default: + case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x03; break; + } + switch (diga->status[1] & IEC958_AES1_PRO_MODE) { + case IEC958_AES1_PRO_MODE_TWO: + case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break; + default: bits |= 0x80; break; + } + } + return bits; +} + +#endif /* SND_CS8404 */ + +#endif /* __SOUND_CS8403_H */ diff -Nru linux/include/sound/cs8427.h linux-2.4.19-pre5-mjc/include/sound/cs8427.h --- linux/include/sound/cs8427.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/cs8427.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,195 @@ +#ifndef __SOUND_CS8427_H +#define __SOUND_CS8427_H + +/* + * Routines for Cirrus Logic CS8427 + * Copyright (c) by Jaroslav Kysela , + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#define CS8427_BASE_ADDR 0x10 /* base I2C address */ + +#define CS8427_REG_AUTOINC 0x80 /* flag - autoincrement */ +#define CS8427_REG_CONTROL1 0x01 +#define CS8427_REG_CONTROL2 0x02 +#define CS8427_REG_DATAFLOW 0x03 +#define CS8427_REG_CLOCKSOURCE 0x04 +#define CS8427_REG_SERIALINPUT 0x05 +#define CS8427_REG_SERIALOUTPUT 0x06 +#define CS8427_REG_INT1STATUS 0x07 +#define CS8427_REG_INT2STATUS 0x08 +#define CS8427_REG_INT1MASK 0x09 +#define CS8427_REG_INT1MODEMSB 0x0a +#define CS8427_REG_INT1MODELSB 0x0b +#define CS8427_REG_INT2MASK 0x0c +#define CS8427_REG_INT2MODEMSB 0x0d +#define CS8427_REG_INT2MODELSB 0x0e +#define CS8427_REG_RECVCSDATA 0x0f +#define CS8427_REG_RECVERRORS 0x10 +#define CS8427_REG_RECVERRMASK 0x11 +#define CS8427_REG_CSDATABUF 0x12 +#define CS8427_REG_UDATABUF 0x13 +#define CS8427_REG_QSUBCODE 0x14 /* 0x14-0x1d (10 bytes) */ +#define CS8427_REG_OMCKRMCKRATIO 0x1e +#define CS8427_REG_CORU_DATABUF 0x20 +#define CS8427_REG_ID_AND_VER 0x7f + +/* CS8427_REG_CONTROL1 bits */ +#define CS8427_SWCLK (1<<7) /* 0 = RMCK default, 1 = OMCK output on RMCK pin */ +#define CS8427_VSET (1<<6) /* 0 = valid PCM data, 1 = invalid PCM data */ +#define CS8427_MUTESAO (1<<5) /* mute control for the serial audio output port, 0 = disabled, 1 = enabled */ +#define CS8427_MUTEAES (1<<4) /* mute control for the AES transmitter output, 0 = disabled, 1 = enabled */ +#define CS8427_INTMASK (3<<1) /* interrupt output pin setup mask */ +#define CS8427_INTACTHIGH (0<<1) /* active high */ +#define CS8427_INTACTLOW (1<<1) /* active low */ +#define CS8427_INTOPENDRAIN (2<<1) /* open drain, active low */ +#define CS8427_TCBLDIR (1<<0) /* 0 = TCBL is an input, 1 = TCBL is an output */ + +/* CS8427_REQ_CONTROL2 bits */ +#define CS8427_HOLDMASK (3<<5) /* action when a receiver error occurs */ +#define CS8427_HOLDLASTSAMPLE (0<<5) /* hold the last valid sample */ +#define CS8427_HOLDZERO (1<<5) /* replace the current audio sample with zero (mute) */ +#define CS8427_HOLDNOCHANGE (2<<5) /* do not change the received audio sample */ +#define CS8427_RMCKF (1<<4) /* 0 = 256*Fsi, 1 = 128*Fsi */ +#define CS8427_MMR (1<<3) /* AES3 receiver operation, 0 = stereo, 1 = mono */ +#define CS8427_MMT (1<<2) /* AES3 transmitter operation, 0 = stereo, 1 = mono */ +#define CS8427_MMTCS (1<<1) /* 0 = use A + B CS data, 1 = use MMTLR CS data */ +#define CS8427_MMTLR (1<<0) /* 0 = use A CS data, 1 = use B CS data */ + +/* CS8427_REG_DATAFLOW */ +#define CS8427_TXOFF (1<<6) /* AES3 transmitter Output, 0 = normal operation, 1 = off (0V) */ +#define CS8427_AESBP (1<<5) /* AES3 hardware bypass mode, 0 = normal, 1 = bypass (RX->TX) */ +#define CS8427_TXDMASK (3<<3) /* AES3 Transmitter Data Source Mask */ +#define CS8427_TXDSERIAL (1<<3) /* TXD - serial audio input port */ +#define CS8427_TXAES3DRECEIVER (2<<3) /* TXD - AES3 receiver */ +#define CS8427_SPDMASK (3<<1) /* Serial Audio Output Port Data Source Mask */ +#define CS8427_SPDSERIAL (1<<1) /* SPD - serial audio input port */ +#define CS8427_SPDAES3RECEIVER (2<<1) /* SPD - AES3 receiver */ + +/* CS8427_REG_CLOCKSOURCE */ +#define CS8427_RUN (1<<6) /* 0 = clock off, 1 = clock on */ +#define CS8427_CLKMASK (3<<4) /* OMCK frequency mask */ +#define CS8427_CLK256 (0<<4) /* 256*Fso */ +#define CS8427_CLK384 (1<<4) /* 384*Fso */ +#define CS8427_CLK512 (2<<4) /* 512*Fso */ +#define CS8427_OUTC (1<<3) /* Output Time Base, 0 = OMCK, 1 = recovered input clock */ +#define CS8427_INC (1<<2) /* Input Time Base Clock Source, 0 = recoverd input clock, 1 = OMCK input pin */ +#define CS8427_RXDMASK (3<<0) /* Recovered Input Clock Source Mask */ +#define CS8427_RXDILRCK (0<<0) /* 256*Fsi from ILRCK pin */ +#define CS8427_RXDAES3INPUT (1<<0) /* 256*Fsi from AES3 input */ +#define CS8427_EXTCLOCKRESET (2<<0) /* bypass PLL, 256*Fsi clock, synchronous reset */ +#define CS8427_EXTCLOCK (3<<0) /* bypass PLL, 256*Fsi clock */ + +/* CS8427_REG_SERIALINPUT */ +#define CS8427_SIMS (1<<7) /* 0 = slave, 1 = master mode */ +#define CS8427_SISF (1<<6) /* ISCLK freq, 0 = 64*Fsi, 1 = 128*Fsi */ +#define CS8427_SIRESMASK (3<<4) /* Resolution of the input data for right justified formats */ +#define CS8427_SIRES24 (0<<4) /* SIRES 24-bit */ +#define CS8427_SIRES20 (1<<4) /* SIRES 20-bit */ +#define CS8427_SIRES16 (2<<4) /* SIRES 16-bit */ +#define CS8427_SIJUST (1<<3) /* Justification of SDIN data relative to ILRCK, 0 = left-justified, 1 = right-justified */ +#define CS8427_SIDEL (1<<2) /* Delay of SDIN data relative to ILRCK for left-justified data formats, 0 = first ISCLK period, 1 = second ISCLK period */ +#define CS8427_SISPOL (1<<1) /* ICLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */ +#define CS8427_SILRPOL (1<<0) /* ILRCK clock polarity, 0 = SDIN data left channel when ILRCK is high, 1 = SDIN right when ILRCK is high */ + +/* CS8427_REG_SERIALOUTPUT */ +#define CS8427_SOMS (1<<7) /* 0 = slave, 1 = master mode */ +#define CS8427_SOSF (1<<6) /* OSCLK freq, 0 = 64*Fso, 1 = 128*Fso */ +#define CS8427_SORESMASK (3<<4) /* Resolution of the output data on SDOUT and AES3 output */ +#define CS8427_SORES24 (0<<4) /* SIRES 24-bit */ +#define CS8427_SORES20 (1<<4) /* SIRES 20-bit */ +#define CS8427_SORES16 (2<<4) /* SIRES 16-bit */ +#define CS8427_SORESDIRECT (2<<4) /* SIRES direct copy from AES3 receiver */ +#define CS8427_SOJUST (1<<3) /* Justification of SDOUT data relative to OLRCK, 0 = left-justified, 1 = right-justified */ +#define CS8427_SODEL (1<<2) /* Delay of SDOUT data relative to OLRCK for left-justified data formats, 0 = first OSCLK period, 1 = second OSCLK period */ +#define CS8427_SOSPOL (1<<1) /* OSCLK clock polarity, 0 = rising edge of ISCLK, 1 = falling edge of ISCLK */ +#define CS8427_SOLRPOL (1<<0) /* OLRCK clock polarity, 0 = SDOUT data left channel when OLRCK is high, 1 = SDOUT right when OLRCK is high */ + +/* CS8427_REG_INT1STATUS */ +#define CS8427_TSLIP (1<<7) /* AES3 transmitter source data slip interrupt */ +#define CS8427_OSLIP (1<<6) /* Serial audio output port data slip interrupt */ +#define CS8427_DETC (1<<2) /* D to E C-buffer transfer interrupt */ +#define CS8427_EFTC (1<<1) /* E to F C-buffer transfer interrupt */ +#define CS8427_RERR (1<<0) /* A receiver error has occurred */ + +/* CS8427_REG_INT2STATUS */ +#define CS8427_DETU (1<<3) /* D to E U-buffer transfer interrupt */ +#define CS8427_EFTU (1<<2) /* E to F U-buffer transfer interrupt */ +#define CS8427_QCH (1<<1) /* A new block of Q-subcode data is available for reading */ + +/* CS8427_REG_INT1MODEMSB && CS8427_REG_INT1MODELSB */ +/* bits are defined in CS8427_REG_INT1STATUS */ +/* CS8427_REG_INT2MODEMSB && CS8427_REG_INT2MODELSB */ +/* bits are defined in CS8427_REG_INT2STATUS */ +#define CS8427_INTMODERISINGMSB 0 +#define CS8427_INTMODERESINGLSB 0 +#define CS8427_INTMODEFALLINGMSB 0 +#define CS8427_INTMODEFALLINGLSB 1 +#define CS8427_INTMODELEVELMSB 1 +#define CS8427_INTMODELEVELLSB 0 + +/* CS8427_REG_RECVCSDATA */ +#define CS8427_AUXMASK (15<<4) /* auxiliary data field width */ +#define CS8427_AUXSHIFT 4 +#define CS8427_PRO (1<<3) /* Channel status block format indicator */ +#define CS8427_AUDIO (1<<2) /* Audio indicator (0 = audio, 1 = nonaudio */ +#define CS8427_COPY (1<<1) /* 0 = copyright asserted, 1 = copyright not asserted */ +#define CS8427_ORIG (1<<0) /* SCMS generation indicator, 0 = 1st generation or highter, 1 = original */ + +/* CS8427_REG_RECVERRORS */ +/* CS8427_REG_RECVERRMASK for CS8427_RERR */ +#define CS8427_QCRC (1<<6) /* Q-subcode data CRC error indicator */ +#define CS8427_CCRC (1<<5) /* Chancnel Status Block Cyclick Redundancy Check Bit */ +#define CS8427_UNLOCK (1<<4) /* PLL lock status bit */ +#define CS8427_V (1<<3) /* 0 = valid data */ +#define CS8427_CONF (1<<2) /* Confidence bit */ +#define CS8427_BIP (1<<1) /* Bi-phase error bit */ +#define CS8427_PAR (1<<0) /* Parity error */ + +/* CS8427_REG_CSDATABUF */ +#define CS8427_BSEL (1<<5) /* 0 = CS data, 1 = U data */ +#define CS8427_CBMR (1<<4) /* 0 = overwrite first 5 bytes for CS D to E buffer, 1 = prevent */ +#define CS8427_DETCI (1<<3) /* D to E CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_EFTCI (1<<2) /* E to F CS data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_CAM (1<<1) /* CS data buffer control port access mode bit, 0 = one byte, 1 = two byte */ +#define CS8427_CHS (1<<0) /* Channel select bit, 0 = Channel A, 1 = Channel B */ + +/* CS8427_REG_UDATABUF */ +#define CS8427_UD (1<<4) /* User data pin (U) direction, 0 = input, 1 = output */ +#define CS8427_UBMMASK (3<<2) /* Operating mode of the AES3 U bit manager */ +#define CS8427_UBMZEROS (0<<2) /* transmit all zeros mode */ +#define CS8427_UBMBLOCK (1<<2) /* block mode */ +#define CS8427_DETUI (1<<1) /* D to E U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ +#define CS8427_EFTUI (1<<1) /* E to F U-data buffer transfer inhibit bit, 0 = allow, 1 = inhibit */ + +/* CS8427_REG_ID_AND_VER */ +#define CS8427_IDMASK (15<<4) +#define CS8427_IDSHIFT 4 +#define CS8427_VERMASK (15<<0) +#define CS8427_VERSHIFT 0 +#define CS8427_VER8427A 0x71 + +int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr); +int snd_cs8427_create(snd_i2c_bus_t *bus, unsigned char addr, snd_i2c_device_t **r_cs8427); +int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427, snd_pcm_substream_t *playback_substream, snd_pcm_substream_t *capture_substream); +int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active); +int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate); + +#endif /* __SOUND_CS8427_H */ diff -Nru linux/include/sound/driver.h linux-2.4.19-pre5-mjc/include/sound/driver.h --- linux/include/sound/driver.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/driver.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,81 @@ +#ifndef __SOUND_DRIVER_H +#define __SOUND_DRIVER_H + +/* + * Main header file for the ALSA driver + * Copyright (c) 1994-2000 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef ALSA_BUILD +#include "config.h" +#endif + +#include +#include + +#define SNDRV_CARDS 8 /* number of supported soundcards - don't change - minor numbers */ + +#ifndef CONFIG_SND_MAJOR /* standard configuration */ +#define CONFIG_SND_MAJOR 116 +#endif + +#ifndef CONFIG_SND_DEBUG +#undef CONFIG_SND_DEBUG_MEMORY +#endif + +#ifdef ALSA_BUILD +#include "adriver.h" +#endif + +#include + +/* + * ========================================================================== + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) +#if defined(__i386__) || defined(__ppc__) +/* + * Here a dirty hack for 2.4 kernels.. See kernel/memory.c. + */ +#define HACK_PCI_ALLOC_CONSISTENT +#include +void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle); +#undef pci_alloc_consistent +#define pci_alloc_consistent snd_pci_hack_alloc_consistent +#endif /* i386 or ppc */ +#endif /* 2.4.0 */ + +#ifdef CONFIG_SND_DEBUG_MEMORY +#include +#include +void *snd_wrapper_kmalloc(size_t, int); +#undef kmalloc +void snd_wrapper_kfree(const void *); +#undef kfree +void *snd_wrapper_vmalloc(size_t); +#undef vmalloc +void snd_wrapper_vfree(void *); +#undef vfree +#endif + +#include "sndmagic.h" + +#endif /* __SOUND_DRIVER_H */ diff -Nru linux/include/sound/emu10k1.h linux-2.4.19-pre5-mjc/include/sound/emu10k1.h --- linux/include/sound/emu10k1.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/emu10k1.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1341 @@ +#ifndef __SOUND_EMU10K1_H +#define __SOUND_EMU10K1_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Creative Labs, Inc. + * Definitions for EMU10K1 (SB Live!) chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef __KERNEL__ + +#include "pcm.h" +#include "rawmidi.h" +#include "hwdep.h" +#include "ac97_codec.h" +#include "util_mem.h" +#include + +#ifndef PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#endif +#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1 +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 +#endif + +/* ------------------- DEFINES -------------------- */ + +#define EMUPAGESIZE 4096 +#define MAXREQVOICES 8 +#define MAXPAGES 8192 +#define RESERVED 0 +#define NUM_MIDI 16 +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 + + +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 + +#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) + +// Audigy specify registers are prefixed with 'A_' + +/************************************************************************************************/ +/* PCI function 0 registers, address = + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ + /* NOTE: The CHANNELNUM and ADDRESS words can */ + /* be modified independently of each other. */ +#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */ + /* channel number of the register to be */ + /* accessed. For non per-channel registers the */ + /* value should be set to zero. */ +#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ +#define A_PTR_ADDRESS_MASK 0x0fff0000 + +#define DATA 0x04 /* Indexed register set data register */ + +#define IPR 0x08 /* Global interrupt pending register */ + /* Clear pending interrupts by writing a 1 to */ + /* the relevant bits and zero to the other bits */ + +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +#define IPR_A_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ +#define IPR_A_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */ + +#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ +#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ +#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */ +#define IPR_PCIERROR 0x00200000 /* PCI bus error */ +#define IPR_VOLINCR 0x00100000 /* Volume increment button pressed */ +#define IPR_VOLDECR 0x00080000 /* Volume decrement button pressed */ +#define IPR_MUTE 0x00040000 /* Mute button pressed */ +#define IPR_MICBUFFULL 0x00020000 /* Microphone buffer full */ +#define IPR_MICBUFHALFFULL 0x00010000 /* Microphone buffer half full */ +#define IPR_ADCBUFFULL 0x00008000 /* ADC buffer full */ +#define IPR_ADCBUFHALFFULL 0x00004000 /* ADC buffer half full */ +#define IPR_EFXBUFFULL 0x00002000 /* Effects buffer full */ +#define IPR_EFXBUFHALFFULL 0x00001000 /* Effects buffer half full */ +#define IPR_GPSPDIFSTATUSCHANGE 0x00000800 /* GPSPDIF channel status change */ +#define IPR_CDROMSTATUSCHANGE 0x00000400 /* CD-ROM channel status change */ +#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */ +#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */ +#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */ +#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */ +#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */ + /* Highest set channel in CLIPL or CLIPH. When */ + /* IP is written with CL set, the bit in CLIPL */ + /* or CLIPH corresponding to the CIN value */ + /* written will be cleared. */ + +#define INTE 0x0c /* Interrupt enable register */ +#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ +#define INTE_VIRTUALSB_220 0x00000000 /* Capture at I/O base address 0x220-0x22f */ +#define INTE_VIRTUALSB_240 0x40000000 /* Capture at I/O base address 0x240 */ +#define INTE_VIRTUALSB_260 0x80000000 /* Capture at I/O base address 0x260 */ +#define INTE_VIRTUALSB_280 0xc0000000 /* Capture at I/O base address 0x280 */ +#define INTE_VIRTUALMPU_MASK 0x30000000 /* Virtual MPU I/O port capture */ +#define INTE_VIRTUALMPU_300 0x00000000 /* Capture at I/O base address 0x300-0x301 */ +#define INTE_VIRTUALMPU_310 0x10000000 /* Capture at I/O base address 0x310 */ +#define INTE_VIRTUALMPU_320 0x20000000 /* Capture at I/O base address 0x320 */ +#define INTE_VIRTUALMPU_330 0x30000000 /* Capture at I/O base address 0x330 */ +#define INTE_MASTERDMAENABLE 0x08000000 /* Master DMA emulation at 0x000-0x00f */ +#define INTE_SLAVEDMAENABLE 0x04000000 /* Slave DMA emulation at 0x0c0-0x0df */ +#define INTE_MASTERPICENABLE 0x02000000 /* Master PIC emulation at 0x020-0x021 */ +#define INTE_SLAVEPICENABLE 0x01000000 /* Slave PIC emulation at 0x0a0-0x0a1 */ +#define INTE_VSBENABLE 0x00800000 /* Enable virtual Soundblaster */ +#define INTE_ADLIBENABLE 0x00400000 /* Enable AdLib emulation at 0x388-0x38b */ +#define INTE_MPUENABLE 0x00200000 /* Enable virtual MPU */ +#define INTE_FORCEINT 0x00100000 /* Continuously assert INTAN */ + +#define INTE_MRHANDENABLE 0x00080000 /* Enable the "Mr. Hand" logic */ + /* NOTE: There is no reason to use this under */ + /* Linux, and it will cause odd hardware */ + /* behavior and possibly random segfaults and */ + /* lockups if enabled. */ + +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +#define INTE_A_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_A_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */ + + +#define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */ + /* NOTE: This bit must always be enabled */ +#define INTE_FXDSPENABLE 0x00001000 /* Enable FX DSP interrupts */ +#define INTE_PCIERRORENABLE 0x00000800 /* Enable PCI bus error interrupts */ +#define INTE_VOLINCRENABLE 0x00000400 /* Enable volume increment button interrupts */ +#define INTE_VOLDECRENABLE 0x00000200 /* Enable volume decrement button interrupts */ +#define INTE_MUTEENABLE 0x00000100 /* Enable mute button interrupts */ +#define INTE_MICBUFENABLE 0x00000080 /* Enable microphone buffer interrupts */ +#define INTE_ADCBUFENABLE 0x00000040 /* Enable ADC buffer interrupts */ +#define INTE_EFXBUFENABLE 0x00000020 /* Enable Effects buffer interrupts */ +#define INTE_GPSPDIFENABLE 0x00000010 /* Enable GPSPDIF status interrupts */ +#define INTE_CDSPDIFENABLE 0x00000008 /* Enable CDSPDIF status interrupts */ +#define INTE_INTERVALTIMERENB 0x00000004 /* Enable interval timer interrupts */ +#define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ + +#define WC 0x10 /* Wall Clock register */ +#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ +#define WC_SAMPLECOUNTER 0x14060010 +#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */ + /* NOTE: Each channel takes 1/64th of a sample */ + /* period to be serviced. */ + +#define HCFG 0x14 /* Hardware config register */ + /* NOTE: There is no reason to use the legacy */ + /* SoundBlaster emulation stuff described below */ + /* under Linux, and all kinds of weird hardware */ + /* behavior can result if you try. Don't. */ +#define HCFG_LEGACYFUNC_MASK 0xe0000000 /* Legacy function number */ +#define HCFG_LEGACYFUNC_MPU 0x00000000 /* Legacy MPU */ +#define HCFG_LEGACYFUNC_SB 0x40000000 /* Legacy SB */ +#define HCFG_LEGACYFUNC_AD 0x60000000 /* Legacy AD */ +#define HCFG_LEGACYFUNC_MPIC 0x80000000 /* Legacy MPIC */ +#define HCFG_LEGACYFUNC_MDMA 0xa0000000 /* Legacy MDMA */ +#define HCFG_LEGACYFUNC_SPCI 0xc0000000 /* Legacy SPCI */ +#define HCFG_LEGACYFUNC_SDMA 0xe0000000 /* Legacy SDMA */ +#define HCFG_IOCAPTUREADDR 0x1f000000 /* The 4 LSBs of the captured I/O address. */ +#define HCFG_LEGACYWRITE 0x00800000 /* 1 = write, 0 = read */ +#define HCFG_LEGACYWORD 0x00400000 /* 1 = word, 0 = byte */ +#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */ + /* NOTE: The rest of the bits in this register */ + /* _are_ relevant under Linux. */ +#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */ +#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ +#define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ +#define HCFG_GPINPUT0 0x00004000 /* External pin112 */ +#define HCFG_GPINPUT1 0x00002000 /* External pin110 */ +#define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */ +#define HCFG_GPOUT0 0x00001000 /* External pin? (spdif enable on 5.1) */ +#define HCFG_GPOUT1 0x00000800 /* External pin? (IR) */ +#define HCFG_GPOUT2 0x00000400 /* External pin? (IR) */ +#define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */ +#define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ + /* 1 = Force all 3 async digital inputs to use */ + /* the same async sample rate tracker (ZVIDEO) */ +#define HCFG_AC3ENABLE_MASK 0x000000e0 /* AC3 async input control - Not implemented */ +#define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ +#define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ +#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ +#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ + /* will automatically mute their output when */ + /* they are not rate-locked to the external */ + /* async audio source */ +#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE 0x01020014 +#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ + /* NOTE: This is a 'cheap' way to implement a */ + /* master mute function on the mute button, and */ + /* in general should not be used unless a more */ + /* sophisticated master mute function has not */ + /* been written. */ +#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ + /* Should be set to 1 when the EMU10K1 is */ + /* completely initialized. */ + +//For Audigy, MPU port move to 0x70-0x74 ptr register + +#define MUDATA 0x18 /* MPU401 data register (8 bits) */ + +#define MUCMD 0x19 /* MPU401 command register (8 bits) */ +#define MUCMD_RESET 0xff /* RESET command */ +#define MUCMD_ENTERUARTMODE 0x3f /* Enter_UART_mode command */ + /* NOTE: All other commands are ignored */ + +#define MUSTAT MUCMD /* MPU401 status register (8 bits) */ +#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ +#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ + +#define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ +#define A_GPINPUT_MASK 0xff00 +#define A_GPOUTPUT_MASK 0x00ff + +#define TIMER 0x1a /* Timer terminal count register */ + /* NOTE: After the rate is changed, a maximum */ + /* of 1024 sample periods should be allowed */ + /* before the new rate is guaranteed accurate. */ +#define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ + /* 0 == 1024 periods, [1..4] are not useful */ +#define TIMER_RATE 0x0a00001a + +#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ + +#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ +#define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */ +#define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */ + +/************************************************************************************************/ +/* PCI function 1 registers, address = + PCIBASE1 */ +/************************************************************************************************/ + +#define JOYSTICK1 0x00 /* Analog joystick port register */ +#define JOYSTICK2 0x01 /* Analog joystick port register */ +#define JOYSTICK3 0x02 /* Analog joystick port register */ +#define JOYSTICK4 0x03 /* Analog joystick port register */ +#define JOYSTICK5 0x04 /* Analog joystick port register */ +#define JOYSTICK6 0x05 /* Analog joystick port register */ +#define JOYSTICK7 0x06 /* Analog joystick port register */ +#define JOYSTICK8 0x07 /* Analog joystick port register */ + +/* When writing, any write causes JOYSTICK_COMPARATOR output enable to be pulsed on write. */ +/* When reading, use these bitfields: */ +#define JOYSTICK_BUTTONS 0x0f /* Joystick button data */ +#define JOYSTICK_COMPARATOR 0xf0 /* Joystick comparator data */ + + +/********************************************************************************************************/ +/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ +/********************************************************************************************************/ + +#define CPF 0x00 /* Current pitch and fraction register */ +#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */ +#define CPF_CURRENTPITCH 0x10100000 +#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */ +#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */ +#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */ + +#define PTRX 0x01 /* Pitch target and send A/B amounts register */ +#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */ +#define PTRX_PITCHTARGET 0x10100001 +#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */ +#define PTRX_FXSENDAMOUNT_A 0x08080001 +#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */ +#define PTRX_FXSENDAMOUNT_B 0x08000001 + +#define CVCF 0x02 /* Current volume and filter cutoff register */ +#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */ +#define CVCF_CURRENTVOL 0x10100002 +#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */ +#define CVCF_CURRENTFILTER 0x10000002 + +#define VTFT 0x03 /* Volume target and filter cutoff target register */ +#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */ +#define VTFT_VOLUMETARGET 0x10100003 +#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ +#define VTFT_FILTERTARGET 0x10000003 + +#define Z1 0x05 /* Filter delay memory 1 register */ + +#define Z2 0x04 /* Filter delay memory 2 register */ + +#define PSST 0x06 /* Send C amount and loop start address register */ +#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */ + +#define PSST_FXSENDAMOUNT_C 0x08180006 + +#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */ +#define PSST_LOOPSTARTADDR 0x18000006 + +#define DSL 0x07 /* Send D amount and loop start address register */ +#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */ + +#define DSL_FXSENDAMOUNT_D 0x08180007 + +#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */ +#define DSL_LOOPENDADDR 0x18000007 + +#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ +#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */ +#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */ + /* 1 == full band, 7 == lowpass */ + /* ROM 0 is used when pitch shifting downward or less */ + /* then 3 semitones upward. Increasingly higher ROM */ + /* numbers are used, typically in steps of 3 semitones, */ + /* as upward pitch shifting is performed. */ +#define CCCA_INTERPROM_0 0x00000000 /* Select interpolation ROM 0 */ +#define CCCA_INTERPROM_1 0x02000000 /* Select interpolation ROM 1 */ +#define CCCA_INTERPROM_2 0x04000000 /* Select interpolation ROM 2 */ +#define CCCA_INTERPROM_3 0x06000000 /* Select interpolation ROM 3 */ +#define CCCA_INTERPROM_4 0x08000000 /* Select interpolation ROM 4 */ +#define CCCA_INTERPROM_5 0x0a000000 /* Select interpolation ROM 5 */ +#define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */ +#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */ +#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */ +#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */ +#define CCCA_CURRADDR 0x18000008 + +#define CCR 0x09 /* Cache control register */ +#define CCR_CACHEINVALIDSIZE 0x07190009 +#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */ +#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ +#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ +#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ +#define CCR_READADDRESS 0x06100009 +#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ +#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ + /* NOTE: This is valid only if CACHELOOPFLAG is set */ +#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ +#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ + +#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ + /* NOTE: This register is normally not used */ +#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */ + +#define FXRT 0x0b /* Effects send routing register */ + /* NOTE: It is illegal to assign the same routing to */ + /* two effects sends. */ +#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */ +#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */ +#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ +#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ + +#define MAPA 0x0c /* Cache map A */ + +#define MAPB 0x0d /* Cache map B */ + +#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ +#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ + +#define ENVVOL 0x10 /* Volume envelope register */ +#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDV 0x11 /* Volume envelope hold and attack register */ +#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ +#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ + +#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ +#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */ + /* this channel and from writing to pitch, filter and */ + /* volume targets. */ +#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL1 0x13 /* Modulation LFO value */ +#define LFOVAL_MASK 0x0000ffff /* Current value of modulation LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ENVVAL 0x14 /* Modulation envelope register */ +#define ENVVAL_MASK 0x0000ffff /* Current value of modulation envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ +#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ +#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ + +#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ +#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL2 0x17 /* Vibrato LFO register */ +#define LFOVAL2_MASK 0x0000ffff /* Current value of vibrato LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define IP 0x18 /* Initial pitch register */ +#define IP_MASK 0x0000ffff /* Exponential initial pitch shift */ + /* 4 bits of octave, 12 bits of fractional octave */ +#define IP_UNITY 0x0000e000 /* Unity pitch shift */ + +#define IFATN 0x19 /* Initial filter cutoff and attenuation register */ +#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */ + /* 6 most significant bits are semitones */ + /* 2 least significant bits are fractions */ +#define IFATN_FILTERCUTOFF 0x08080019 +#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ +#define IFATN_ATTENUATION 0x08000019 + + +#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ +#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ + /* Signed 2's complement, +/- one octave peak extremes */ +#define PEFE_PITCHAMOUNT 0x0808001a +#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ + /* Signed 2's complement, +/- six octaves peak extremes */ +#define PEFE_FILTERAMOUNT 0x0800001a +#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ +#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ + /* Signed 2's complement, +/- three octave extremes */ + + +#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ +#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ + /* Signed 2's complement, with +/- 12dB extremes */ + +#define TREMFRQ_FREQUENCY 0x000000ff /* Tremolo LFO frequency */ + /* ??Hz steps, maximum of ?? Hz. */ +#define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ +#define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FM2FRQ2_FREQUENCY 0x000000ff /* Vibrato LFO frequency */ + /* 0.039Hz steps, maximum of 9.85 Hz. */ + +#define TEMPENV 0x1e /* Tempory envelope register */ +#define TEMPENV_MASK 0x0000ffff /* 16-bit value */ + /* NOTE: All channels contain internal variables; do */ + /* not write to these locations. */ + +#define CD0 0x20 /* Cache data 0 register */ +#define CD1 0x21 /* Cache data 1 register */ +#define CD2 0x22 /* Cache data 2 register */ +#define CD3 0x23 /* Cache data 3 register */ +#define CD4 0x24 /* Cache data 4 register */ +#define CD5 0x25 /* Cache data 5 register */ +#define CD6 0x26 /* Cache data 6 register */ +#define CD7 0x27 /* Cache data 7 register */ +#define CD8 0x28 /* Cache data 8 register */ +#define CD9 0x29 /* Cache data 9 register */ +#define CDA 0x2a /* Cache data A register */ +#define CDB 0x2b /* Cache data B register */ +#define CDC 0x2c /* Cache data C register */ +#define CDD 0x2d /* Cache data D register */ +#define CDE 0x2e /* Cache data E register */ +#define CDF 0x2f /* Cache data F register */ + +#define PTB 0x40 /* Page table base register */ +#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */ + +#define TCB 0x41 /* Tank cache base register */ +#define TCB_MASK 0xfffff000 /* Physical address of the bottom of host based TRAM */ + +#define ADCCR 0x42 /* ADC sample rate/stereo control register */ +#define ADCCR_RCHANENABLE 0x00000010 /* Enables right channel for writing to the host */ +#define ADCCR_LCHANENABLE 0x00000008 /* Enables left channel for writing to the host */ + /* NOTE: To guarantee phase coherency, both channels */ + /* must be disabled prior to enabling both channels. */ +#define A_ADCCR_RCHANENABLE 0x00000020 +#define A_ADCCR_LCHANENABLE 0x00000010 + +#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ +#define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */ +#define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */ +#define ADCCR_SAMPLERATE_24 0x00000003 /* 24kHz sample rate */ +#define ADCCR_SAMPLERATE_22 0x00000004 /* 22.05kHz sample rate */ +#define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */ +#define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */ +#define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */ +#define A_ADCCR_SAMPLERATE_12 0x00000006 /* 12kHz sample rate */ +#define A_ADCCR_SAMPLERATE_11 0x00000007 /* 11.025kHz sample rate */ +#define A_ADCCR_SAMPLERATE_8 0x00000008 /* 8kHz sample rate */ + +#define FXWC 0x43 /* FX output write channels register */ + /* When set, each bit enables the writing of the */ + /* corresponding FX output channel into host memory */ +#define FXWC_DEFAULTROUTE_C (1<<0) /* left emu out? */ +#define FXWC_DEFAULTROUTE_B (1<<1) /* right emu out? */ +#define FXWC_DEFAULTROUTE_A (1<<12) +#define FXWC_DEFAULTROUTE_D (1<<13) +#define FXWC_ADCLEFT (1<<18) +#define FXWC_CDROMSPDIFLEFT (1<<18) +#define FXWC_ADCRIGHT (1<<19) +#define FXWC_CDROMSPDIFRIGHT (1<<19) +#define FXWC_MIC (1<<20) +#define FXWC_ZOOMLEFT (1<<20) +#define FXWC_ZOOMRIGHT (1<<21) +#define FXWC_SPDIFLEFT (1<<22) /* 0x00400000 */ +#define FXWC_SPDIFRIGHT (1<<23) /* 0x00800000 */ + +#define TCBS 0x44 /* Tank cache buffer size register */ +#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ +#define TCBS_BUFFSIZE_16K 0x00000000 +#define TCBS_BUFFSIZE_32K 0x00000001 +#define TCBS_BUFFSIZE_64K 0x00000002 +#define TCBS_BUFFSIZE_128K 0x00000003 +#define TCBS_BUFFSIZE_256K 0x00000004 +#define TCBS_BUFFSIZE_512K 0x00000005 +#define TCBS_BUFFSIZE_1024K 0x00000006 +#define TCBS_BUFFSIZE_2048K 0x00000007 + +#define MICBA 0x45 /* AC97 microphone buffer address register */ +#define MICBA_MASK 0xfffff000 /* 20 bit base address */ + +#define ADCBA 0x46 /* ADC buffer address register */ +#define ADCBA_MASK 0xfffff000 /* 20 bit base address */ + +#define FXBA 0x47 /* FX Buffer Address */ +#define FXBA_MASK 0xfffff000 /* 20 bit base address */ + +#define MICBS 0x49 /* Microphone buffer size register */ + +#define ADCBS 0x4a /* ADC buffer size register */ + +#define FXBS 0x4b /* FX buffer size register */ + +/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */ +#define ADCBS_BUFSIZE_NONE 0x00000000 +#define ADCBS_BUFSIZE_384 0x00000001 +#define ADCBS_BUFSIZE_448 0x00000002 +#define ADCBS_BUFSIZE_512 0x00000003 +#define ADCBS_BUFSIZE_640 0x00000004 +#define ADCBS_BUFSIZE_768 0x00000005 +#define ADCBS_BUFSIZE_896 0x00000006 +#define ADCBS_BUFSIZE_1024 0x00000007 +#define ADCBS_BUFSIZE_1280 0x00000008 +#define ADCBS_BUFSIZE_1536 0x00000009 +#define ADCBS_BUFSIZE_1792 0x0000000a +#define ADCBS_BUFSIZE_2048 0x0000000b +#define ADCBS_BUFSIZE_2560 0x0000000c +#define ADCBS_BUFSIZE_3072 0x0000000d +#define ADCBS_BUFSIZE_3584 0x0000000e +#define ADCBS_BUFSIZE_4096 0x0000000f +#define ADCBS_BUFSIZE_5120 0x00000010 +#define ADCBS_BUFSIZE_6144 0x00000011 +#define ADCBS_BUFSIZE_7168 0x00000012 +#define ADCBS_BUFSIZE_8192 0x00000013 +#define ADCBS_BUFSIZE_10240 0x00000014 +#define ADCBS_BUFSIZE_12288 0x00000015 +#define ADCBS_BUFSIZE_14366 0x00000016 +#define ADCBS_BUFSIZE_16384 0x00000017 +#define ADCBS_BUFSIZE_20480 0x00000018 +#define ADCBS_BUFSIZE_24576 0x00000019 +#define ADCBS_BUFSIZE_28672 0x0000001a +#define ADCBS_BUFSIZE_32768 0x0000001b +#define ADCBS_BUFSIZE_40960 0x0000001c +#define ADCBS_BUFSIZE_49152 0x0000001d +#define ADCBS_BUFSIZE_57344 0x0000001e +#define ADCBS_BUFSIZE_65536 0x0000001f + + +#define CDCS 0x50 /* CD-ROM digital channel status register */ + +#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/ + +#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define A_DBG 0x53 +#define A_DBG_SINGLE_STEP 0x00020000 /* Set to zero to start dsp */ +#define A_DBG_ZC 0x40000000 /* zero tram counter */ +#define A_DBG_STEP_ADDR 0x000003ff +#define A_DBG_SATURATION_OCCURED 0x20000000 +#define A_DBG_SATURATION_ADDR 0x0ffc0000 + +#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ + +#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */ + +#define SPCS2 0x56 /* SPDIF output Channel Status 2 register */ + +#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ +#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ +#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ +#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ +#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ +#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ +#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ +#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ +#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ +#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ +#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ +#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ +#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ +#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ +#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ +#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ +#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ +#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ +#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ +#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ +#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ +#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ +#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ + +/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */ +#define CLIEL 0x58 /* Channel loop interrupt enable low register */ + +#define CLIEH 0x59 /* Channel loop interrupt enable high register */ + +#define CLIPL 0x5a /* Channel loop interrupt pending low register */ + +#define CLIPH 0x5b /* Channel loop interrupt pending high register */ + +#define SOLEL 0x5c /* Stop on loop enable low register */ + +#define SOLEH 0x5d /* Stop on loop enable high register */ + +#define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ +#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ + +#define AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_CNTR 0x10 /* Center enable */ +#define AC97SLOT_LFE 0x20 /* LFE enable */ + +#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ + +#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */ + +#define ZVSRCS 0x62 /* ZVideo sample rate converter status */ + /* NOTE: This one has no SPDIFLOCKED field */ + /* Assumes sample lock */ + +/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */ +#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */ +#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ +#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ + +/* Note that these values can vary +/- by a small amount */ +#define SRCS_SPDIFRATE_44 0x0003acd9 +#define SRCS_SPDIFRATE_48 0x00040000 +#define SRCS_SPDIFRATE_96 0x00080000 + +#define MICIDX 0x63 /* Microphone recording buffer index register */ +#define MICIDX_MASK 0x0000ffff /* 16-bit value */ +#define MICIDX_IDX 0x10000063 + +#define ADCIDX 0x64 /* ADC recording buffer index register */ +#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */ +#define ADCIDX_IDX 0x10000064 + +#define A_ADCIDX 0x63 +#define A_ADCIDX_IDX 0x10000063 + +#define FXIDX 0x65 /* FX recording buffer index register */ +#define FXIDX_MASK 0x0000ffff /* 16-bit value */ +#define FXIDX_IDX 0x10000065 + +/* This is the MPU port on the card (via the game port) */ +#define A_MUDATA1 0x70 +#define A_MUCMD1 0x71 +#define A_MUSTAT1 A_MUCMD1 + +/* This is the MPU port on the Audigy Drive */ +#define A_MUDATA2 0x72 +#define A_MUCMD2 0x73 +#define A_MUSTAT2 A_MUCMD2 + +#define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ +#define A_SPDIF_48000 0x00000080 +#define A_SPDIF_44100 0x00000000 +#define A_SPDIF_96000 0x00000040 + +#define A_FXRT2 0x7c +#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */ +#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send F */ +#define A_FXRT_CHANNELG 0x003f0000 /* Effects send bus number for channel's effects send G */ +#define A_FXRT_CHANNELH 0x3f000000 /* Effects send bus number for channel's effects send H */ + +#define A_SENDAMOUNTS 0x7d +#define A_FXSENDAMOUNT_E_MASK 0xFF000000 +#define A_FXSENDAMOUNT_F_MASK 0x00FF0000 +#define A_FXSENDAMOUNT_G_MASK 0x0000FF00 +#define A_FXSENDAMOUNT_H_MASK 0x000000FF + +/* The send amounts for this one are the same as used with the emu10k1 */ +#define A_FXRT1 0x7e +#define A_FXRT_CHANNELA 0x0000003f +#define A_FXRT_CHANNELB 0x00003f00 +#define A_FXRT_CHANNELC 0x003f0000 +#define A_FXRT_CHANNELD 0x3f000000 + + +/* Each FX general purpose register is 32 bits in length, all bits are used */ +#define FXGPREGBASE 0x100 /* FX general purpose registers base */ +#define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */ + +/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ +/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ +/* locations are for external TRAM. */ +#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ +#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ + +/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ +#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ + +#define MICROCODEBASE 0x400 /* Microcode data base address */ + +/* Each DSP microcode instruction is mapped into 2 doublewords */ +/* NOTE: When writing, always write the LO doubleword first. Reads can be in either order. */ +#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ +#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ +#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ +#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ +#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ + + +/* Audigy Soundcard have a different instruction format */ +#define A_MICROCODEBASE 0x600 +#define A_LOWORD_OPY_MASK 0x000007ff +#define A_LOWORD_OPX_MASK 0x007ff000 +#define A_HIWORD_OPCODE_MASK 0x0f000000 +#define A_HIWORD_RESULT_MASK 0x007ff000 +#define A_HIWORD_OPA_MASK 0x000007ff + + +/* ------------------- STRUCTURES -------------------- */ + +typedef struct _snd_emu10k1 emu10k1_t; +typedef struct _snd_emu10k1_voice emu10k1_voice_t; +typedef struct _snd_emu10k1_pcm emu10k1_pcm_t; + +typedef enum { + EMU10K1_PCM, + EMU10K1_SYNTH, + EMU10K1_MIDI +} emu10k1_voice_type_t; + +struct _snd_emu10k1_voice { + emu10k1_t *emu; + int number; + int use: 1, + pcm: 1, + synth: 1, + midi: 1; + void (*interrupt)(emu10k1_t *emu, emu10k1_voice_t *pvoice); + + emu10k1_pcm_t *epcm; +}; + +typedef enum { + PLAYBACK_EMUVOICE, + CAPTURE_AC97ADC, + CAPTURE_AC97MIC, + CAPTURE_EFX +} snd_emu10k1_pcm_type_t; + +struct _snd_emu10k1_pcm { + emu10k1_t *emu; + snd_emu10k1_pcm_type_t type; + snd_pcm_substream_t *substream; + emu10k1_voice_t *voices[2]; + emu10k1_voice_t *extra; + int running; + snd_util_memblk_t *memblk; + unsigned int start_addr; + unsigned int ccca_start_addr; + unsigned int capture_ipr; /* interrupt acknowledge mask */ + unsigned int capture_inte; /* interrupt enable mask */ + unsigned int capture_ba_reg; /* buffer address register */ + unsigned int capture_bs_reg; /* buffer size register */ + unsigned int capture_idx_reg; /* buffer index register */ + unsigned int capture_cr_val; /* control value */ + unsigned int capture_bs_val; /* buffer size value */ + unsigned int capture_bufsize; /* buffer size in bytes */ +}; + +typedef struct { + unsigned char send_routing[3][8]; + unsigned char send_volume[3][8]; + unsigned short attn[3]; + snd_kcontrol_t *ctl_send_routing; + snd_kcontrol_t *ctl_send_volume; + snd_kcontrol_t *ctl_attn; + emu10k1_pcm_t *epcm; +} emu10k1_pcm_mixer_t; + +#define snd_emu10k1_compose_send_routing(route) \ +((route[0] | (route[1] << 4) | (route[2] << 8) | (route[3] << 12)) << 16) + +#define snd_emu10k1_compose_audigy_fxrt1(route) \ +(((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 12)) << 24) + +#define snd_emu10k1_compose_audigy_fxrt2(route) \ +(((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 12)) << 24) + +typedef struct snd_emu10k1_memblk { + snd_util_memblk_t mem; + /* private part */ + short first_page, last_page, pages, mapped_page; + unsigned int map_locked; + struct list_head mapped_link; + struct list_head mapped_order_link; +} emu10k1_memblk_t; + +#define snd_emu10k1_memblk_offset(blk) (((blk)->mapped_page << PAGE_SHIFT) | ((blk)->mem.offset & (PAGE_SIZE - 1))) + +#define EMU10K1_MAX_TRAM_BLOCKS_PER_CODE 16 + +typedef struct { + struct list_head list; /* list link container */ + unsigned int vcount; + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GRP_TRANSLATION*) */ + snd_kcontrol_t *kcontrol; +} snd_emu10k1_fx8010_ctl_t; + +typedef void (snd_fx8010_irq_handler_t)(emu10k1_t *emu, void *private_data); + +typedef struct _snd_emu10k1_fx8010_irq { + struct _snd_emu10k1_fx8010_irq *next; + snd_fx8010_irq_handler_t *handler; + unsigned char gpr_running; + void *private_data; +} snd_emu10k1_fx8010_irq_t; + +typedef struct { + unsigned int valid: 1, + opened: 1, + active: 1; + unsigned int channels; /* 16-bit channels count */ + unsigned int tram_start; /* initial ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ring buffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char etram[32]; /* external TRAM address & data */ + unsigned int sw_data, hw_data; + unsigned int sw_io, hw_io; + unsigned int sw_ready, hw_ready; + unsigned int appl_ptr; + unsigned int tram_pos; + unsigned int tram_shift; + snd_emu10k1_fx8010_irq_t *irq; +} snd_emu10k1_fx8010_pcm_t; + +typedef struct { + unsigned short fxbus_mask; /* used FX buses (bitmask) */ + unsigned short extin_mask; /* used external inputs (bitmask) */ + unsigned short extout_mask; /* used external outputs (bitmask) */ + unsigned short pad1; + unsigned int itram_size; /* internal TRAM size in samples */ + unsigned int etram_size; /* external TRAM size in samples */ + void *etram_pages; /* allocated pages for external TRAM */ + dma_addr_t etram_pages_dmaaddr; + unsigned int dbg; /* FX debugger register */ + unsigned char name[128]; + int gpr_size; /* size of allocated GPR controls */ + int gpr_count; /* count of used kcontrols */ + struct list_head gpr_ctl; /* GPR controls */ + struct semaphore lock; + snd_emu10k1_fx8010_pcm_t pcm[8]; + spinlock_t irq_lock; + snd_emu10k1_fx8010_irq_t *irq_handlers; +} snd_emu10k1_fx8010_t; + +#define emu10k1_gpr_ctl(n) list_entry(n, snd_emu10k1_fx8010_ctl_t, list) + +typedef struct { + struct _snd_emu10k1 *emu; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream_input; + snd_rawmidi_substream_t *substream_output; + unsigned int midi_mode; + spinlock_t input_lock; + spinlock_t output_lock; + spinlock_t open_lock; + int tx_enable, rx_enable; + int port; + int ipr_tx, ipr_rx; + void (*interrupt)(emu10k1_t *emu, unsigned int status); +} emu10k1_midi_t; + +struct _snd_emu10k1 { + int irq; + + unsigned long port; /* I/O port number */ + struct resource *res_port; + int APS: 1, /* APS flag */ + tos_link: 1; /* tos link detected */ + unsigned int audigy; /* is Audigy? */ + unsigned int revision; /* chip revision */ + unsigned int serial; /* serial number */ + unsigned short model; /* subsystem id */ + unsigned int card_type; /* EMU10K1_CARD_* */ + unsigned int ecard_ctrl; /* ecard control bits */ + int max_cache_pages; /* max memory size / PAGE_SIZE */ + void *silent_page; /* silent page */ + dma_addr_t silent_page_dmaaddr; + volatile unsigned int *ptb_pages; /* page table pages */ + dma_addr_t ptb_pages_dmaaddr; + snd_util_memhdr_t *memhdr; /* page allocation list */ + emu10k1_memblk_t *reserved_page; /* reserved page */ + + struct list_head mapped_link_head; + struct list_head mapped_order_link_head; + void **page_ptr_table; + unsigned long *page_addr_table; + spinlock_t memblk_lock; + + unsigned int spdif_bits[3]; /* s/pdif out setup */ + + snd_emu10k1_fx8010_t fx8010; /* FX8010 info */ + int gpr_base; + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm_mic; + snd_pcm_t *pcm_efx; + snd_pcm_t *pcm_fx8010; + + spinlock_t synth_lock; + void *synth; + int (*get_synth_voice)(emu10k1_t *emu); + + spinlock_t reg_lock; + spinlock_t emu_lock; + spinlock_t voice_lock; + struct semaphore ptb_lock; + + emu10k1_voice_t voices[64]; + emu10k1_pcm_mixer_t pcm_mixer[32]; + + void (*hwvol_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_mic_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_efx_interrupt)(emu10k1_t *emu, unsigned int status); + void (*timer_interrupt)(emu10k1_t *emu); + void (*spdif_interrupt)(emu10k1_t *emu, unsigned int status); + void (*dsp_interrupt)(emu10k1_t *emu); + + snd_pcm_substream_t *pcm_capture_substream; + snd_pcm_substream_t *pcm_capture_mic_substream; + snd_pcm_substream_t *pcm_capture_efx_substream; + + emu10k1_midi_t midi; + emu10k1_midi_t midi2; /* for audigy */ + + unsigned int efx_voices_mask; + + snd_info_entry_t *proc_entry; + snd_info_entry_t *proc_entry_fx8010_gpr; + snd_info_entry_t *proc_entry_fx8010_tram_data; + snd_info_entry_t *proc_entry_fx8010_tram_addr; + snd_info_entry_t *proc_entry_fx8010_code; + snd_info_entry_t *proc_entry_fx8010_iblocks; +}; + +int snd_emu10k1_create(snd_card_t * card, + struct pci_dev *pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + emu10k1_t ** remu); + +int snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_mixer(emu10k1_t * emu); +int snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep); + +void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +/* initialization */ +void snd_emu10k1_voice_init(emu10k1_t * emu, int voice); +int snd_emu10k1_init_efx(emu10k1_t *emu); +void snd_emu10k1_free_efx(emu10k1_t *emu); +int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size); + +/* I/O functions */ +unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn); +void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data); +void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data); +unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc); +void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait); +static inline unsigned int snd_emu10k1_wc(emu10k1_t *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; } +unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg); +void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data); +unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate); +unsigned char snd_emu10k1_sum_vol_attn(unsigned int value); + +/* memory allocation */ +snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size); +int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk); +snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size); +int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk); +int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size); +int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size); +int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk); + +/* voice allocation */ +int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice); +int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice); + +/* MIDI uart */ +int snd_emu10k1_midi(emu10k1_t * emu); +int snd_emu10k1_audigy_midi(emu10k1_t * emu); + +/* proc interface */ +int snd_emu10k1_proc_init(emu10k1_t * emu); +int snd_emu10k1_proc_done(emu10k1_t * emu); + +#endif /* __KERNEL__ */ + +/* + * ---- FX8010 ---- + */ + +#define EMU10K1_CARD_CREATIVE 0x00000000 +#define EMU10K1_CARD_EMUAPS 0x00000001 + +#define EMU10K1_FX8010_PCM_COUNT 8 + +/* instruction set */ +#define iMAC0 0x00 /* R = A + (X * Y >> 31) ; saturation */ +#define iMAC1 0x01 /* R = A + (-X * Y >> 31) ; saturation */ +#define iMAC2 0x02 /* R = A + (X * Y >> 31) ; wraparound */ +#define iMAC3 0x03 /* R = A + (-X * Y >> 31) ; wraparound */ +#define iMACINT0 0x04 /* R = A + X * Y ; saturation */ +#define iMACINT1 0x05 /* R = A + X * Y ; wraparound (31-bit) */ +#define iACC3 0x06 /* R = A + X + Y ; saturation */ +#define iMACMV 0x07 /* R = A, acc += X * Y >> 31 */ +#define iANDXOR 0x08 /* R = (A & X) ^ Y */ +#define iTSTNEG 0x09 /* R = (A >= Y) ? X : ~X */ +#define iLIMITGE 0x0a /* R = (A >= Y) ? X : Y */ +#define iLIMITLT 0x0b /* R = (A < Y) ? X : Y */ +#define iLOG 0x0c /* R = linear_data, A (log_data), X (max_exp), Y (format_word) */ +#define iEXP 0x0d /* R = log_data, A (linear_data), X (max_exp), Y (format_word) */ +#define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ +#define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ + +/* GPRs */ +#define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ +#define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ +#define EXTOUT(x) (0x20 + (x)) /* x = 0x00 - 0x0f */ +#define C_00000000 0x40 +#define C_00000001 0x41 +#define C_00000002 0x42 +#define C_00000003 0x43 +#define C_00000004 0x44 +#define C_00000008 0x45 +#define C_00000010 0x46 +#define C_00000020 0x47 +#define C_00000100 0x48 +#define C_00010000 0x49 +#define C_00080000 0x4a +#define C_10000000 0x4b +#define C_20000000 0x4c +#define C_40000000 0x4d +#define C_80000000 0x4e +#define C_7fffffff 0x4f +#define C_ffffffff 0x50 +#define C_fffffffe 0x51 +#define C_c0000000 0x52 +#define C_4f1bbcdc 0x53 +#define C_5a7ef9db 0x54 +#define C_00100000 0x55 /* ?? */ +#define GPR_ACCU 0x56 /* ACCUM, accumulator */ +#define GPR_COND 0x57 /* CCR, condition register */ +#define GPR_NOISE0 0x58 /* noise source */ +#define GPR_NOISE1 0x59 /* noise source */ +#define GPR_IRQ 0x5a /* IRQ register */ +#define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ +#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ +#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ +#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ + +#define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f? */ +#define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x1f? */ +#define A_EXTOUT(x) (0x60 + (x)) /* x = 0x00 - 0x1f? */ +#define A_GPR(x) (A_FXGPREGBASE + (x)) + +/* cc_reg constants */ +#define CC_REG_NORMALIZED C_00000001 +#define CC_REG_BORROW C_00000002 +#define CC_REG_MINUS C_00000004 +#define CC_REG_ZERO C_00000008 +#define CC_REG_SATURATE C_00000010 +#define CC_REG_NONZERO C_00000100 + +/* FX buses */ +#define FXBUS_PCM_LEFT 0x00 +#define FXBUS_PCM_RIGHT 0x01 +#define FXBUS_PCM_LEFT_REAR 0x02 +#define FXBUS_PCM_RIGHT_REAR 0x03 +#define FXBUS_MIDI_LEFT 0x04 +#define FXBUS_MIDI_RIGHT 0x05 +#define FXBUS_PCM_CENTER 0x06 +#define FXBUS_PCM_LFE 0x07 +#define FXBUS_MIDI_REVERB 0x0c +#define FXBUS_MIDI_CHORUS 0x0d + +/* Inputs */ +#define EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define EXTIN_SPDIF_CD_L 0x02 /* internal S/PDIF CD - onboard - left */ +#define EXTIN_SPDIF_CD_R 0x03 /* internal S/PDIF CD - onboard - right */ +#define EXTIN_ZOOM_L 0x04 /* Zoom Video I2S - left */ +#define EXTIN_ZOOM_R 0x05 /* Zoom Video I2S - right */ +#define EXTIN_TOSLINK_L 0x06 /* LiveDrive - TOSLink Optical - left */ +#define EXTIN_TOSLINK_R 0x07 /* LiveDrive - TOSLink Optical - right */ +#define EXTIN_LINE1_L 0x08 /* LiveDrive - Line/Mic 1 - left */ +#define EXTIN_LINE1_R 0x09 /* LiveDrive - Line/Mic 1 - right */ +#define EXTIN_COAX_SPDIF_L 0x0a /* LiveDrive - Coaxial S/PDIF - left */ +#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */ +#define EXTIN_LINE2_L 0x0c /* LiveDrive - Line/Mic 2 - left */ +#define EXTIN_LINE2_R 0x0d /* LiveDrive - Line/Mic 2 - right */ + +/* Outputs */ +#define EXTOUT_AC97_L 0x00 /* AC'97 playback channel - left */ +#define EXTOUT_AC97_R 0x01 /* AC'97 playback channel - right */ +#define EXTOUT_TOSLINK_L 0x02 /* LiveDrive - TOSLink Optical - left */ +#define EXTOUT_TOSLINK_R 0x03 /* LiveDrive - TOSLink Optical - right */ +#define EXTOUT_CENTER 0x04 /* SB Live 5.1 - center */ +#define EXTOUT_LFE 0x05 /* SB Live 5.1 - LFE */ +#define EXTOUT_HEADPHONE_L 0x06 /* LiveDrive - Headphone - left */ +#define EXTOUT_HEADPHONE_R 0x07 /* LiveDrive - Headphone - right */ +#define EXTOUT_REAR_L 0x08 /* Rear channel - left */ +#define EXTOUT_REAR_R 0x09 /* Rear channel - right */ +#define EXTOUT_ADC_CAP_L 0x0a /* ADC Capture buffer - left */ +#define EXTOUT_ADC_CAP_R 0x0b /* ADC Capture buffer - right */ +#define EXTOUT_MIC_CAP 0x0c /* MIC Capture buffer */ +#define EXTOUT_ACENTER 0x11 /* Analog Center */ +#define EXTOUT_ALFE 0x12 /* Analog LFE */ + +/* Audigy Inputs */ +#define A_EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ +#define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ +#define A_EXTIN_LINE2_L 0x08 /* audigy drive line2/mic2 - left */ +#define A_EXTIN_LINE2_R 0x09 /* right */ +#define A_EXTIN_AUX2_L 0x0c /* audigy drive aux2 - left */ +#define A_EXTIN_AUX2_R 0x0d /* - right */ + +/* Audigiy Outputs */ +#define A_EXTOUT_FRONT_L 0x00 /* digital front left */ +#define A_EXTOUT_FRONT_R 0x01 /* right */ +#define A_EXTOUT_CENTER 0x02 /* digital front center */ +#define A_EXTOUT_LFE 0x03 /* digital front lfe */ +#define A_EXTOUT_HEADPHONE_L 0x04 /* headphone audigy drive left */ +#define A_EXTOUT_HEADPHONE_R 0x05 /* right */ +#define A_EXTOUT_REAR_L 0x06 /* digital rear left */ +#define A_EXTOUT_REAR_R 0x07 /* right */ +#define A_EXTOUT_AFRONT_L 0x08 /* analog front left */ +#define A_EXTOUT_AFRONT_R 0x09 /* right */ +#define A_EXTOUT_ACENTER 0x0a /* analog center */ +#define A_EXTOUT_ALFE 0x0b /* analog LFE */ +/* 0x0c ?? */ +/* 0x0d ?? */ +#define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ +#define A_EXTOUT_AREAR_R 0x0f /* right */ +#define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ +#define A_EXTOUT_AC97_R 0x11 /* right */ +#define A_EXTOUT_ADC_CAP_L 0x12 /* ADC capture buffer left */ +#define A_EXTOUT_ADC_CAP_R 0x13 /* right */ + +/* Audigy constants */ +#define A_C_00000000 0xc0 +#define A_C_00000001 0xc1 +#define A_C_00000002 0xc2 +#define A_C_00000003 0xc3 +#define A_C_00000004 0xc4 +#define A_C_00000008 0xc5 +#define A_C_00000010 0xc6 +#define A_C_00000020 0xc7 +#define A_C_00000100 0xc8 +#define A_C_00010000 0xc9 +#define A_C_00000800 0xca +#define A_C_10000000 0xcb +#define A_C_20000000 0xcc +#define A_C_40000000 0xcd +#define A_C_80000000 0xce +#define A_C_7fffffff 0xcf +#define A_C_ffffffff 0xd0 +#define A_C_fffffffe 0xd1 +#define A_C_c0000000 0xd2 +#define A_C_4f1bbcdc 0xd3 +#define A_C_5a7ef9db 0xd4 +#define A_C_00100000 0xd5 +/* 0xd6 = 0x7fffffff (?) ACCUM? */ +/* 0xd7 = 0x0000000 CCR */ +/* 0xd8 = noise1 */ +/* 0xd9 = noise2 */ + +/* definitions for debug register */ +#define EMU10K1_DBG_ZC 0x80000000 /* zero tram counter */ +#define EMU10K1_DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ +#define EMU10K1_DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ +#define EMU10K1_DBG_SINGLE_STEP 0x00008000 /* single step mode */ +#define EMU10K1_DBG_STEP 0x00004000 /* start single step */ +#define EMU10K1_DBG_CONDITION_CODE 0x00003e00 /* condition code */ +#define EMU10K1_DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ + +/* tank memory address line */ +#ifndef __KERNEL__ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ +#endif + +typedef struct { + unsigned int card; /* card type */ + unsigned int internal_tram_size; /* in samples */ + unsigned int external_tram_size; /* in samples */ + char fxbus_names[16][32]; /* names of FXBUSes */ + char extin_names[16][32]; /* names of external inputs */ + char extout_names[32][32]; /* names of external outputs */ + unsigned int gpr_controls; /* count of GPR controls */ +} emu10k1_fx8010_info_t; + +#define EMU10K1_GPR_TRANSLATION_NONE 0 +#define EMU10K1_GPR_TRANSLATION_TABLE100 1 +#define EMU10K1_GRP_TRANSLATION_BASS 2 +#define EMU10K1_GRP_TRANSLATION_TREBLE 3 +#define EMU10K1_GPR_TRANSLATION_ONOFF 4 + +typedef struct { + snd_ctl_elem_id_t id; /* full control ID definition */ + unsigned int vcount; /* visible count */ + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; /* initial values */ + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GRP_TRANSLATION*) */ +} emu10k1_fx8010_control_gpr_t; + +typedef struct { + char name[128]; + unsigned int gpr_valid[0x100/32]; /* bitmask of valid initializers */ + unsigned int gpr_map[0x100]; /* initializers */ + unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */ + emu10k1_fx8010_control_gpr_t *gpr_add_controls; /* GPR controls to add/replace */ + unsigned int gpr_del_control_count; /* count of GPR controls to remove */ + snd_ctl_elem_id_t *gpr_del_controls; /* IDs of GPR controls to remove */ + unsigned int tram_valid[0xa0/32]; /* bitmask of valid initializers */ + unsigned int tram_data_map[0xa0]; /* data initializers */ + unsigned int tram_addr_map[0xa0]; /* map initializers */ + unsigned int code_valid[512/32]; /* bitmask of valid instructions */ + unsigned int code[512][2]; /* one instruction - 64 bits */ +} emu10k1_fx8010_code_t; + +typedef struct { + unsigned int address; /* 31.bit == 1 -> external TRAM */ + unsigned int size; /* size in samples (4 bytes) */ + unsigned int *samples; /* pointer to samples (20-bit) */ + /* NULL->clear memory */ +} emu10k1_fx8010_tram_t; + +typedef struct { + unsigned int substream; /* substream number */ + unsigned int res1; /* reserved */ + unsigned int channels; /* 16-bit channels count, zero = remove this substream */ + unsigned int tram_start; /* ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ringbuffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char pad; /* reserved */ + unsigned char etram[32]; /* external TRAM address & data (one per channel) */ + unsigned int res2; /* reserved */ +} emu10k1_fx8010_pcm_t; + +#define SNDRV_EMU10K1_IOCTL_INFO _IOR ('H', 0x10, emu10k1_fx8010_info_t) +#define SNDRV_EMU10K1_IOCTL_CODE_POKE _IOW ('H', 0x11, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_CODE_PEEK _IOW ('H', 0x12, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_SETUP _IOW ('H', 0x20, int) +#define SNDRV_EMU10K1_IOCTL_TRAM_POKE _IOW ('H', 0x21, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK _IOR ('H', 0x22, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_PCM_POKE _IOW ('H', 0x30, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_PCM_PEEK _IOWR('H', 0x31, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_STOP _IO ('H', 0x80) +#define SNDRV_EMU10K1_IOCTL_CONTINUE _IO ('H', 0x81) +#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82) +#define SNDRV_EMU10K1_IOCTL_SINGLE_STEP _IOW ('H', 0x83, int) +#define SNDRV_EMU10K1_IOCTL_DBG_READ _IOR ('H', 0x84, int) + +#endif /* __SOUND_EMU10K1_H */ diff -Nru linux/include/sound/emu10k1_synth.h linux-2.4.19-pre5-mjc/include/sound/emu10k1_synth.h --- linux/include/sound/emu10k1_synth.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/emu10k1_synth.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,39 @@ +#ifndef __EMU10K1_SYNTH_H +#define __EMU10K1_SYNTH_H +/* + * Defines for the Emu10k1 WaveTable synth + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu10k1.h" +#include "emux_synth.h" + +/* sequencer device id */ +#define SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH "emu10k1-synth" + +/* argument for snd_seq_device_new */ +typedef struct snd_emu10k1_synth_arg { + emu10k1_t *hwptr; /* chip */ + int index; /* sequencer client index */ + int seq_ports; /* number of sequencer ports to be created */ + int max_voices; /* maximum number of voices for wavetable */ +} snd_emu10k1_synth_arg_t; + +#define EMU10K1_MAX_MEMSIZE (32 * 1024 * 1024) /* 32MB */ + +#endif diff -Nru linux/include/sound/emu8000.h linux-2.4.19-pre5-mjc/include/sound/emu8000.h --- linux/include/sound/emu8000.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/emu8000.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,116 @@ +#ifndef __SOUND_EMU8000_H +#define __SOUND_EMU8000_H +/* + * Defines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emux_synth.h" +#include "seq_kernel.h" + +/* + * Hardware parameters. + */ +#define EMU8000_MAX_DRAM (28 * 1024 * 1024) /* Max on-board mem is 28Mb ???*/ +#define EMU8000_DRAM_OFFSET 0x200000 /* Beginning of on board ram */ +#define EMU8000_CHANNELS 32 /* Number of hardware channels */ +#define EMU8000_DRAM_VOICES 30 /* number of normal voices */ + +/* Flags to set a dma channel to read or write */ +#define EMU8000_RAM_READ 0 +#define EMU8000_RAM_WRITE 1 +#define EMU8000_RAM_CLOSE 2 + +enum { + EMU8000_CONTROL_BASS = 0, + EMU8000_CONTROL_TREBLE, + EMU8000_CONTROL_CHORUS_MODE, + EMU8000_CONTROL_REVERB_MODE, + EMU8000_CONTROL_FM_CHORUS_DEPTH, + EMU8000_CONTROL_FM_REVERB_DEPTH, + EMU8000_NUM_CONTROLS, +}; + +/* + * Structure to hold all state information for the emu8000 driver. + * + * Note 1: The chip supports 32 channels in hardware this is max_channels + * some of the channels may be used for other things so max_channels is + * the number in use for wave voices. + */ +typedef struct snd_emu8000 { + + snd_emux_t *emu; + + int index; /* sequencer client index */ + int seq_ports; /* number of sequencer ports */ + int fm_chorus_depth; /* FM OPL3 chorus depth */ + int fm_reverb_depth; /* FM OPL3 reverb depth */ + + int mem_size; /* memory size */ + unsigned long port1; /* Port usually base+0 */ + unsigned long port2; /* Port usually at base+0x400 */ + unsigned long port3; /* Port usually at base+0x800 */ + struct resource *res_port1; + struct resource *res_port2; + struct resource *res_port3; + unsigned short last_reg;/* Last register command */ + spinlock_t reg_lock; + + int dram_checked; + + snd_card_t *card; /* The card that this belongs to */ + + int chorus_mode; + int reverb_mode; + int bass_level; + int treble_level; + + snd_util_memhdr_t *memhdr; + + spinlock_t control_lock; + snd_kcontrol_t *controls[EMU8000_NUM_CONTROLS]; + +} emu8000_t; + +/* sequencer device id */ +#define SNDRV_SEQ_DEV_ID_EMU8000 "emu8000-synth" + + +/* exported functions */ +int snd_emu8000_new(snd_card_t *card, int device, long port, int seq_ports, snd_seq_device_t **ret); +void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, + unsigned int val); +unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, + unsigned int reg); +void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, + unsigned int val); +unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, + unsigned int reg); +void snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode); + +void snd_emu8000_init_fm(emu8000_t *emu); + +void snd_emu8000_update_chorus_mode(emu8000_t *emu); +void snd_emu8000_update_reverb_mode(emu8000_t *emu); +void snd_emu8000_update_equalizer(emu8000_t *emu); +int snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len); +int snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len); + +#endif /* __SOUND_EMU8000_H */ diff -Nru linux/include/sound/emu8000_reg.h linux-2.4.19-pre5-mjc/include/sound/emu8000_reg.h --- linux/include/sound/emu8000_reg.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/emu8000_reg.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,207 @@ +#ifndef __SOUND_EMU8000_REG_H +#define __SOUND_EMU8000_REG_H +/* + * Register operations for the EMU8000 + * + * Copyright (C) 1999 Steve Ratcliffe + * + * Based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Data port addresses relative to the EMU base. + */ +#define EMU8000_DATA0(e) ((e)->port1) +#define EMU8000_DATA1(e) ((e)->port2) +#define EMU8000_DATA2(e) ((e)->port2+2) +#define EMU8000_DATA3(e) ((e)->port3) +#define EMU8000_PTR(e) ((e)->port3+2) + +/* + * Make a command from a register and channel. + */ +#define EMU8000_CMD(reg, chan) ((reg)<<5 | (chan)) + +/* + * Commands to read and write the EMU8000 registers. + * These macros should be used for all register accesses. + */ +#define EMU8000_CPF_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_PTRX_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan))) +#define EMU8000_CVCF_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_VTFT_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_PSST_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_CSL_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_CCCA_READ(emu, chan) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_HWCF4_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9)) +#define EMU8000_HWCF5_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10)) +#define EMU8000_HWCF6_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13)) +#define EMU8000_SMALR_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20)) +#define EMU8000_SMARR_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21)) +#define EMU8000_SMALW_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22)) +#define EMU8000_SMARW_READ(emu) \ + snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23)) +#define EMU8000_SMLD_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26)) +#define EMU8000_SMRD_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26)) +#define EMU8000_WC_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27)) +#define EMU8000_HWCF1_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29)) +#define EMU8000_HWCF2_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30)) +#define EMU8000_HWCF3_READ(emu) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31)) +#define EMU8000_INIT1_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_INIT2_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_INIT3_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_INIT4_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_ENVVOL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_DCYSUSV_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan))) +#define EMU8000_ENVVAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_DCYSUS_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_ATKHLDV_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_LFO1VAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan))) +#define EMU8000_ATKHLD_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan))) +#define EMU8000_LFO2VAL_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan))) +#define EMU8000_IP_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan))) +#define EMU8000_IFATN_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan))) +#define EMU8000_PEFE_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan))) +#define EMU8000_FMMOD_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan))) +#define EMU8000_TREMFRQ_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan))) +#define EMU8000_FM2FRQ2_READ(emu, chan) \ + snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan))) + + +#define EMU8000_CPF_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_PTRX_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan)), (val)) +#define EMU8000_CVCF_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_VTFT_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_PSST_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_CSL_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_CCCA_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_HWCF4_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9), (val)) +#define EMU8000_HWCF5_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10), (val)) +#define EMU8000_HWCF6_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13), (val)) +/* this register is not documented */ +#define EMU8000_HWCF7_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 14), (val)) +#define EMU8000_SMALR_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20), (val)) +#define EMU8000_SMARR_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21), (val)) +#define EMU8000_SMALW_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22), (val)) +#define EMU8000_SMARW_WRITE(emu, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23), (val)) +#define EMU8000_SMLD_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26), (val)) +#define EMU8000_SMRD_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26), (val)) +#define EMU8000_WC_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27), (val)) +#define EMU8000_HWCF1_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29), (val)) +#define EMU8000_HWCF2_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30), (val)) +#define EMU8000_HWCF3_WRITE(emu, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31), (val)) +#define EMU8000_INIT1_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_INIT2_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_INIT3_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_INIT4_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_ENVVOL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_DCYSUSV_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan)), (val)) +#define EMU8000_ENVVAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_DCYSUS_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_ATKHLDV_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_LFO1VAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan)), (val)) +#define EMU8000_ATKHLD_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan)), (val)) +#define EMU8000_LFO2VAL_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan)), (val)) +#define EMU8000_IP_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan)), (val)) +#define EMU8000_IFATN_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan)), (val)) +#define EMU8000_PEFE_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan)), (val)) +#define EMU8000_FMMOD_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan)), (val)) +#define EMU8000_TREMFRQ_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_FM2FRQ2_WRITE(emu, chan, val) \ + snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan)), (val)) + +#define EMU8000_0080_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(4, (chan)), (val)) +#define EMU8000_00A0_WRITE(emu, chan, val) \ + snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(5, (chan)), (val)) + +#endif /* __SOUND_EMU8000_REG_H */ diff -Nru linux/include/sound/emux_legacy.h linux-2.4.19-pre5-mjc/include/sound/emux_legacy.h --- linux/include/sound/emux_legacy.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/emux_legacy.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,146 @@ +#ifndef __SOUND_EMUX_LEGACY_H +#define __SOUND_EMUX_LEGACY_H + +/* + * Copyright (c) 1999-2000 Takashi Iwai + * + * Definitions of OSS compatible headers for Emu8000 device informations + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_oss_legacy.h" + +/* + * awe hardware controls + */ + +#define _EMUX_OSS_DEBUG_MODE 0x00 +#define _EMUX_OSS_REVERB_MODE 0x01 +#define _EMUX_OSS_CHORUS_MODE 0x02 +#define _EMUX_OSS_REMOVE_LAST_SAMPLES 0x03 +#define _EMUX_OSS_INITIALIZE_CHIP 0x04 +#define _EMUX_OSS_SEND_EFFECT 0x05 +#define _EMUX_OSS_TERMINATE_CHANNEL 0x06 +#define _EMUX_OSS_TERMINATE_ALL 0x07 +#define _EMUX_OSS_INITIAL_VOLUME 0x08 +#define _EMUX_OSS_INITIAL_ATTEN _EMUX_OSS_INITIAL_VOLUME +#define _EMUX_OSS_RESET_CHANNEL 0x09 +#define _EMUX_OSS_CHANNEL_MODE 0x0a +#define _EMUX_OSS_DRUM_CHANNELS 0x0b +#define _EMUX_OSS_MISC_MODE 0x0c +#define _EMUX_OSS_RELEASE_ALL 0x0d +#define _EMUX_OSS_NOTEOFF_ALL 0x0e +#define _EMUX_OSS_CHN_PRESSURE 0x0f +#define _EMUX_OSS_EQUALIZER 0x11 + +#define _EMUX_OSS_MODE_FLAG 0x80 +#define _EMUX_OSS_COOKED_FLAG 0x40 /* not supported */ +#define _EMUX_OSS_MODE_VALUE_MASK 0x3F + + +/* + * mode type definitions + */ +enum { +/* 0*/ EMUX_MD_EXCLUSIVE_OFF, /* obsolete */ +/* 1*/ EMUX_MD_EXCLUSIVE_ON, /* obsolete */ +/* 2*/ EMUX_MD_VERSION, /* read only */ +/* 3*/ EMUX_MD_EXCLUSIVE_SOUND, /* 0/1: exclusive note on (default=1) */ +/* 4*/ EMUX_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */ +/* 5*/ EMUX_MD_GUS_BANK, /* bank number for GUS patches (default=0) */ +/* 6*/ EMUX_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */ +/* 7*/ EMUX_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */ +/* 8*/ EMUX_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */ +/* 9*/ EMUX_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */ +/*10*/ EMUX_MD_DEF_PRESET, /* integer: default preset number (def=0) */ +/*11*/ EMUX_MD_DEF_BANK, /* integer: default bank number (def=0) */ +/*12*/ EMUX_MD_DEF_DRUM, /* integer: default drumset number (def=0) */ +/*13*/ EMUX_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */ +/*14*/ EMUX_MD_NEW_VOLUME_CALC, /* 0/1: volume calculation mode (def=1) */ +/*15*/ EMUX_MD_CHORUS_MODE, /* integer: chorus mode (def=2) */ +/*16*/ EMUX_MD_REVERB_MODE, /* integer: chorus mode (def=4) */ +/*17*/ EMUX_MD_BASS_LEVEL, /* integer: bass level (def=5) */ +/*18*/ EMUX_MD_TREBLE_LEVEL, /* integer: treble level (def=9) */ +/*19*/ EMUX_MD_DEBUG_MODE, /* integer: debug level (def=0) */ +/*20*/ EMUX_MD_PAN_EXCHANGE, /* 0/1: exchange panning direction (def=0) */ + EMUX_MD_END, +}; + + +/* + * effect parameters + */ +enum { + +/* modulation envelope parameters */ +/* 0*/ EMUX_FX_ENV1_DELAY, /* WORD: ENVVAL */ +/* 1*/ EMUX_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */ +/* 2*/ EMUX_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */ +/* 3*/ EMUX_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */ +/* 4*/ EMUX_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */ +/* 5*/ EMUX_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */ +/* 6*/ EMUX_FX_ENV1_PITCH, /* BYTE: up PEFE */ +/* 7*/ EMUX_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */ + +/* volume envelope parameters */ +/* 8*/ EMUX_FX_ENV2_DELAY, /* WORD: ENVVOL */ +/* 9*/ EMUX_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */ +/*10*/ EMUX_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */ +/*11*/ EMUX_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */ +/*12*/ EMUX_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */ +/*13*/ EMUX_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */ + +/* LFO1 (tremolo & vibrato) parameters */ +/*14*/ EMUX_FX_LFO1_DELAY, /* WORD: LFO1VAL */ +/*15*/ EMUX_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */ +/*16*/ EMUX_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */ +/*17*/ EMUX_FX_LFO1_PITCH, /* BYTE: up FMMOD */ +/*18*/ EMUX_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */ + +/* LFO2 (vibrato) parameters */ +/*19*/ EMUX_FX_LFO2_DELAY, /* WORD: LFO2VAL */ +/*20*/ EMUX_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */ +/*21*/ EMUX_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */ + +/* Other overall effect parameters */ +/*22*/ EMUX_FX_INIT_PITCH, /* SHORT: pitch offset */ +/*23*/ EMUX_FX_CHORUS, /* BYTE: chorus effects send (0-255) */ +/*24*/ EMUX_FX_REVERB, /* BYTE: reverb effects send (0-255) */ +/*25*/ EMUX_FX_CUTOFF, /* BYTE: up IFATN */ +/*26*/ EMUX_FX_FILTERQ, /* BYTE: up CCCA */ + +/* Sample / loop offset changes */ +/*27*/ EMUX_FX_SAMPLE_START, /* SHORT: offset */ +/*28*/ EMUX_FX_LOOP_START, /* SHORT: offset */ +/*29*/ EMUX_FX_LOOP_END, /* SHORT: offset */ +/*30*/ EMUX_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */ +/*31*/ EMUX_FX_COARSE_LOOP_START, /* SHORT: upper word offset */ +/*32*/ EMUX_FX_COARSE_LOOP_END, /* SHORT: upper word offset */ +/*33*/ EMUX_FX_ATTEN, /* BYTE: lo IFATN */ + + EMUX_FX_END, +}; +/* number of effects */ +#define EMUX_NUM_EFFECTS EMUX_FX_END + +/* effect flag values */ +#define EMUX_FX_FLAG_OFF 0 +#define EMUX_FX_FLAG_SET 1 +#define EMUX_FX_FLAG_ADD 2 + + +#endif /* __SOUND_EMUX_LEGACY_H */ diff -Nru linux/include/sound/emux_synth.h linux-2.4.19-pre5-mjc/include/sound/emux_synth.h --- linux/include/sound/emux_synth.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/emux_synth.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,239 @@ +#ifndef __SOUND_EMUX_SYNTH_H +#define __SOUND_EMUX_SYNTH_H + +/* + * Defines for the Emu-series WaveTable chip + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "seq_kernel.h" +#include "seq_device.h" +#include "soundfont.h" +#include "seq_midi_emul.h" +#ifdef CONFIG_SND_OSSEMUL +#include "seq_oss.h" +#endif +#include "emux_legacy.h" +#include "seq_virmidi.h" + +/* + * compile flags + */ +#define SNDRV_EMUX_USE_RAW_EFFECT + + +/* + * typedefs + */ +typedef struct snd_emux_effect_table snd_emux_effect_table_t; +typedef struct snd_emux_port snd_emux_port_t; +typedef struct snd_emux_voice snd_emux_voice_t; +typedef struct snd_emux snd_emux_t; + + +/* + * operators + */ +typedef struct snd_emux_operators { + struct module *owner; + snd_emux_voice_t *(*get_voice)(snd_emux_t *emu, snd_emux_port_t *port); + int (*prepare)(snd_emux_voice_t *vp); + void (*trigger)(snd_emux_voice_t *vp); + void (*release)(snd_emux_voice_t *vp); + void (*update)(snd_emux_voice_t *vp, int update); + void (*terminate)(snd_emux_voice_t *vp); + void (*free_voice)(snd_emux_voice_t *vp); + void (*reset)(snd_emux_t *emu, int ch); + /* the first parameters are snd_emux_t */ + int (*sample_new)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count); + int (*sample_free)(snd_emux_t *emu, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); + void (*sample_reset)(snd_emux_t *emu); + int (*load_fx)(snd_emux_t *emu, int type, int arg, const void *data, long count); + void (*sysex)(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +#ifdef CONFIG_SND_OSSEMUL + int (*oss_ioctl)(snd_emux_t *emu, int cmd, int p1, int p2); +#endif +} snd_emux_operators_t; + + +/* + * constant values + */ +#define SNDRV_EMUX_MAX_PORTS 32 /* max # of sequencer ports */ +#define SNDRV_EMUX_MAX_VOICES 64 /* max # of voices */ +#define SNDRV_EMUX_MAX_MULTI_VOICES 16 /* max # of playable voices + * simultineously + */ + +/* + * flags + */ +#define SNDRV_EMUX_ACCEPT_ROM (1<<0) + +/* + * emuX wavetable + */ +struct snd_emux { + + snd_card_t *card; /* assigned card */ + + /* following should be initialized before registration */ + int max_voices; /* Number of voices */ + int mem_size; /* memory size (in byte) */ + int num_ports; /* number of ports to be created */ + int pitch_shift; /* pitch shift value (for Emu10k1) */ + snd_emux_operators_t ops; /* operators */ + void *hw; /* hardware */ + unsigned long flags; /* other conditions */ + int midi_ports; /* number of virtual midi devices */ + int midi_devidx; /* device offset of virtual midi */ + + /* private */ + int num_voices; /* current number of voices */ + snd_sf_list_t *sflist; /* root of SoundFont list */ + snd_emux_voice_t *voices; /* Voices (EMU 'channel') */ + int use_time; /* allocation counter */ + spinlock_t voice_lock; /* Lock for voice access */ + struct semaphore register_mutex; + int client; /* For the sequencer client */ + int ports[SNDRV_EMUX_MAX_PORTS]; /* The ports for this device */ + int used; /* use counter */ + char *name; /* name of the device (internal) */ + snd_rawmidi_t **vmidi; + struct timer_list tlist; /* for pending note-offs */ + int timer_active; + + snd_util_memhdr_t *memhdr; /* memory chunk information */ + +#ifdef CONFIG_PROC_FS + snd_info_entry_t *proc; +#endif + +#ifdef CONFIG_SND_OSSEMUL + snd_seq_device_t *oss_synth; +#endif +}; + + +/* + * sequencer port information + */ +struct snd_emux_port { + + snd_midi_channel_set_t chset; + snd_emux_t *emu; + + char port_mode; /* operation mode */ + int volume_atten; /* emuX raw attenuation */ + unsigned long drum_flags; /* drum bitmaps */ + int ctrls[EMUX_MD_END]; /* control parameters */ +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_effect_table_t *effect; +#endif +#ifdef CONFIG_SND_OSSEMUL + snd_seq_oss_arg_t *oss_arg; +#endif +}; + +/* port_mode */ +#define SNDRV_EMUX_PORT_MODE_MIDI 0 /* normal MIDI port */ +#define SNDRV_EMUX_PORT_MODE_OSS_SYNTH 1 /* OSS synth port */ +#define SNDRV_EMUX_PORT_MODE_OSS_MIDI 2 /* OSS multi channel synth port */ + +/* + * A structure to keep track of each hardware voice + */ +struct snd_emux_voice { + int ch; /* Hardware channel number */ + + int state; /* status */ +#define SNDRV_EMUX_ST_OFF 0x00 /* Not playing, and inactive */ +#define SNDRV_EMUX_ST_ON 0x01 /* Note on */ +#define SNDRV_EMUX_ST_RELEASED (0x02|SNDRV_EMUX_ST_ON) /* Note released */ +#define SNDRV_EMUX_ST_SUSTAINED (0x04|SNDRV_EMUX_ST_ON) /* Note sustained */ +#define SNDRV_EMUX_ST_STANDBY (0x08|SNDRV_EMUX_ST_ON) /* Waiting to be triggered */ +#define SNDRV_EMUX_ST_PENDING (0x10|SNDRV_EMUX_ST_ON) /* Note will be released */ +#define SNDRV_EMUX_ST_LOCKED 0x100 /* Not accessible */ + + unsigned int time; /* An allocation time */ + unsigned char note; /* Note currently assigned to this voice */ + unsigned char key; + unsigned char velocity; /* Velocity of current note */ + + snd_sf_zone_t *zone; /* Zone assigned to this note */ + void *block; /* sample block pointer (optional) */ + snd_midi_channel_t *chan; /* Midi channel for this note */ + snd_emux_port_t *port; /* associated port */ + snd_emux_t *emu; /* assigned root info */ + void *hw; /* hardware pointer (emu8000_t or emu10k1_t) */ + unsigned long ontime; /* jiffies at note triggered */ + + /* Emu8k/Emu10k1 registers */ + soundfont_voice_info_t reg; + + /* additional registers */ + int avol; /* volume attenuation */ + int acutoff; /* cutoff target */ + int apitch; /* pitch offset */ + int apan; /* pan/aux pair */ + int aaux; + int ptarget; /* pitch target */ + int vtarget; /* volume target */ + int ftarget; /* filter target */ + +}; + +/* + * update flags (can be combined) + */ +#define SNDRV_EMUX_UPDATE_VOLUME (1<<0) +#define SNDRV_EMUX_UPDATE_PITCH (1<<1) +#define SNDRV_EMUX_UPDATE_PAN (1<<2) +#define SNDRV_EMUX_UPDATE_FMMOD (1<<3) +#define SNDRV_EMUX_UPDATE_TREMFREQ (1<<4) +#define SNDRV_EMUX_UPDATE_FM2FRQ2 (1<<5) +#define SNDRV_EMUX_UPDATE_Q (1<<6) + + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +/* + * effect table + */ +struct snd_emux_effect_table { + /* Emu8000 specific effects */ + short val[EMUX_NUM_EFFECTS]; + unsigned char flag[EMUX_NUM_EFFECTS]; +}; +#endif /* SNDRV_EMUX_USE_RAW_EFFECT */ + + +/* + * prototypes - interface to Emu10k1 and Emu8k routines + */ +int snd_emux_new(snd_emux_t **remu); +int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name); +int snd_emux_free(snd_emux_t *emu); + +/* + * exported functions + */ +void snd_emux_terminate_all(snd_emux_t *emu); +void snd_emux_lock_voice(snd_emux_t *emu, int voice); +void snd_emux_unlock_voice(snd_emux_t *emu, int voice); + +#endif /* __SOUND_EMUX_SYNTH_H */ diff -Nru linux/include/sound/es1688.h linux-2.4.19-pre5-mjc/include/sound/es1688.h --- linux/include/sound/es1688.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/es1688.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,125 @@ +#ifndef __SOUND_ES1688_H +#define __SOUND_ES1688_H + +/* + * Header file for ES488/ES1688 + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "control.h" +#include "pcm.h" + +#define ES1688_HW_AUTO 0x0000 +#define ES1688_HW_688 0x0001 +#define ES1688_HW_1688 0x0002 + +struct _snd_es1688 { + unsigned long port; /* port of ESS chip */ + struct resource *res_port; + unsigned long mpu_port; /* MPU-401 port of ESS chip */ + int irq; /* IRQ number of ESS chip */ + int mpu_irq; /* MPU IRQ */ + int dma8; /* 8-bit DMA */ + unsigned short version; /* version of ESS chip */ + unsigned short hardware; /* see to ES1688_HW_XXXX */ + + unsigned short trigger_value; + unsigned char pad; + unsigned int dma_size; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + spinlock_t reg_lock; + spinlock_t mixer_lock; +}; + +typedef struct _snd_es1688 es1688_t; + +/* I/O ports */ + +#define ES1688P(codec, x) ((codec)->port + e_s_s_ESS1688##x) + +#define e_s_s_ESS1688RESET 0x6 +#define e_s_s_ESS1688READ 0xa +#define e_s_s_ESS1688WRITE 0xc +#define e_s_s_ESS1688COMMAND 0xc +#define e_s_s_ESS1688STATUS 0xc +#define e_s_s_ESS1688DATA_AVAIL 0xe +#define e_s_s_ESS1688DATA_AVAIL_16 0xf +#define e_s_s_ESS1688MIXER_ADDR 0x4 +#define e_s_s_ESS1688MIXER_DATA 0x5 +#define e_s_s_ESS1688OPL3_LEFT 0x0 +#define e_s_s_ESS1688OPL3_RIGHT 0x2 +#define e_s_s_ESS1688OPL3_BOTH 0x8 +#define e_s_s_ESS1688ENABLE0 0x0 +#define e_s_s_ESS1688ENABLE1 0x9 +#define e_s_s_ESS1688ENABLE2 0xb +#define e_s_s_ESS1688INIT1 0x7 + +#define ES1688_DSP_CMD_DMAOFF 0xd0 +#define ES1688_DSP_CMD_SPKON 0xd1 +#define ES1688_DSP_CMD_SPKOFF 0xd3 +#define ES1688_DSP_CMD_DMAON 0xd4 + +#define ES1688_PCM_DEV 0x14 +#define ES1688_MIC_DEV 0x1a +#define ES1688_REC_DEV 0x1c +#define ES1688_MASTER_DEV 0x32 +#define ES1688_FM_DEV 0x36 +#define ES1688_CD_DEV 0x38 +#define ES1688_AUX_DEV 0x3a +#define ES1688_SPEAKER_DEV 0x3c +#define ES1688_LINE_DEV 0x3e +#define ES1688_RECLEV_DEV 0xb4 + +#define ES1688_MIXS_MASK 0x17 +#define ES1688_MIXS_MIC 0x00 +#define ES1688_MIXS_MIC_MASTER 0x01 +#define ES1688_MIXS_CD 0x02 +#define ES1688_MIXS_AOUT 0x03 +#define ES1688_MIXS_MIC1 0x04 +#define ES1688_MIXS_REC_MIX 0x05 +#define ES1688_MIXS_LINE 0x06 +#define ES1688_MIXS_MASTER 0x07 +#define ES1688_MIXS_MUTE 0x10 + +/* + + */ + +void snd_es1688_mixer_write(es1688_t *chip, unsigned char reg, unsigned char data); +unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg); + +void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +int snd_es1688_create(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + int irq, + int mpu_irq, + int dma8, + unsigned short hardware, + es1688_t ** rchip); +int snd_es1688_pcm(es1688_t *chip, int device, snd_pcm_t ** rpcm); +int snd_es1688_mixer(es1688_t *chip); + +#endif /* __SOUND_ES1688_H */ diff -Nru linux/include/sound/gus.h linux-2.4.19-pre5-mjc/include/sound/gus.h --- linux/include/sound/gus.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/gus.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,730 @@ +#ifndef __SOUND_GUS_H +#define __SOUND_GUS_H + +/* + * Global structures used for GUS part of ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "timer.h" +#include "seq_midi_emul.h" +#include "seq_device.h" +#include "ainstr_iw.h" +#include "ainstr_gf1.h" +#include "ainstr_simple.h" +#include + +#define SNDRV_SEQ_DEV_ID_GUS "gus-synth" + +/* IO ports */ + +#define GUSP(gus, x) ((gus)->gf1.port + SNDRV_g_u_s_##x) + +#define SNDRV_g_u_s_MIDICTRL (0x320-0x220) +#define SNDRV_g_u_s_MIDISTAT (0x320-0x220) +#define SNDRV_g_u_s_MIDIDATA (0x321-0x220) + +#define SNDRV_g_u_s_GF1PAGE (0x322-0x220) +#define SNDRV_g_u_s_GF1REGSEL (0x323-0x220) +#define SNDRV_g_u_s_GF1DATALOW (0x324-0x220) +#define SNDRV_g_u_s_GF1DATAHIGH (0x325-0x220) +#define SNDRV_g_u_s_IRQSTAT (0x226-0x220) +#define SNDRV_g_u_s_TIMERCNTRL (0x228-0x220) +#define SNDRV_g_u_s_TIMERDATA (0x229-0x220) +#define SNDRV_g_u_s_DRAM (0x327-0x220) +#define SNDRV_g_u_s_MIXCNTRLREG (0x220-0x220) +#define SNDRV_g_u_s_IRQDMACNTRLREG (0x22b-0x220) +#define SNDRV_g_u_s_REGCNTRLS (0x22f-0x220) +#define SNDRV_g_u_s_BOARDVERSION (0x726-0x220) +#define SNDRV_g_u_s_MIXCNTRLPORT (0x726-0x220) +#define SNDRV_g_u_s_IVER (0x325-0x220) +#define SNDRV_g_u_s_MIXDATAPORT (0x326-0x220) +#define SNDRV_g_u_s_MAXCNTRLPORT (0x326-0x220) + +/* GF1 registers */ + +/* global registers */ +#define SNDRV_GF1_GB_ACTIVE_VOICES 0x0e +#define SNDRV_GF1_GB_VOICES_IRQ 0x0f +#define SNDRV_GF1_GB_GLOBAL_MODE 0x19 +#define SNDRV_GF1_GW_LFO_BASE 0x1a +#define SNDRV_GF1_GB_VOICES_IRQ_READ 0x1f +#define SNDRV_GF1_GB_DRAM_DMA_CONTROL 0x41 +#define SNDRV_GF1_GW_DRAM_DMA_LOW 0x42 +#define SNDRV_GF1_GW_DRAM_IO_LOW 0x43 +#define SNDRV_GF1_GB_DRAM_IO_HIGH 0x44 +#define SNDRV_GF1_GB_SOUND_BLASTER_CONTROL 0x45 +#define SNDRV_GF1_GB_ADLIB_TIMER_1 0x46 +#define SNDRV_GF1_GB_ADLIB_TIMER_2 0x47 +#define SNDRV_GF1_GB_RECORD_RATE 0x48 +#define SNDRV_GF1_GB_REC_DMA_CONTROL 0x49 +#define SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL 0x4b +#define SNDRV_GF1_GB_RESET 0x4c +#define SNDRV_GF1_GB_DRAM_DMA_HIGH 0x50 +#define SNDRV_GF1_GW_DRAM_IO16 0x51 +#define SNDRV_GF1_GW_MEMORY_CONFIG 0x52 +#define SNDRV_GF1_GB_MEMORY_CONTROL 0x53 +#define SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR 0x54 +#define SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR 0x55 +#define SNDRV_GF1_GW_FIFO_SIZE 0x56 +#define SNDRV_GF1_GW_INTERLEAVE 0x57 +#define SNDRV_GF1_GB_COMPATIBILITY 0x59 +#define SNDRV_GF1_GB_DECODE_CONTROL 0x5a +#define SNDRV_GF1_GB_VERSION_NUMBER 0x5b +#define SNDRV_GF1_GB_MPU401_CONTROL_A 0x5c +#define SNDRV_GF1_GB_MPU401_CONTROL_B 0x5d +#define SNDRV_GF1_GB_EMULATION_IRQ 0x60 +/* voice specific registers */ +#define SNDRV_GF1_VB_ADDRESS_CONTROL 0x00 +#define SNDRV_GF1_VW_FREQUENCY 0x01 +#define SNDRV_GF1_VW_START_HIGH 0x02 +#define SNDRV_GF1_VW_START_LOW 0x03 +#define SNDRV_GF1_VA_START SNDRV_GF1_VW_START_HIGH +#define SNDRV_GF1_VW_END_HIGH 0x04 +#define SNDRV_GF1_VW_END_LOW 0x05 +#define SNDRV_GF1_VA_END SNDRV_GF1_VW_END_HIGH +#define SNDRV_GF1_VB_VOLUME_RATE 0x06 +#define SNDRV_GF1_VB_VOLUME_START 0x07 +#define SNDRV_GF1_VB_VOLUME_END 0x08 +#define SNDRV_GF1_VW_VOLUME 0x09 +#define SNDRV_GF1_VW_CURRENT_HIGH 0x0a +#define SNDRV_GF1_VW_CURRENT_LOW 0x0b +#define SNDRV_GF1_VA_CURRENT SNDRV_GF1_VW_CURRENT_HIGH +#define SNDRV_GF1_VB_PAN 0x0c +#define SNDRV_GF1_VW_OFFSET_RIGHT 0x0c +#define SNDRV_GF1_VB_VOLUME_CONTROL 0x0d +#define SNDRV_GF1_VB_UPPER_ADDRESS 0x10 +#define SNDRV_GF1_VW_EFFECT_HIGH 0x11 +#define SNDRV_GF1_VW_EFFECT_LOW 0x12 +#define SNDRV_GF1_VA_EFFECT SNDRV_GF1_VW_EFFECT_HIGH +#define SNDRV_GF1_VW_OFFSET_LEFT 0x13 +#define SNDRV_GF1_VB_ACCUMULATOR 0x14 +#define SNDRV_GF1_VB_MODE 0x15 +#define SNDRV_GF1_VW_EFFECT_VOLUME 0x16 +#define SNDRV_GF1_VB_FREQUENCY_LFO 0x17 +#define SNDRV_GF1_VB_VOLUME_LFO 0x18 +#define SNDRV_GF1_VW_OFFSET_RIGHT_FINAL 0x1b +#define SNDRV_GF1_VW_OFFSET_LEFT_FINAL 0x1c +#define SNDRV_GF1_VW_EFFECT_VOLUME_FINAL 0x1d + +/* ICS registers */ + +#define SNDRV_ICS_MIC_DEV 0 +#define SNDRV_ICS_LINE_DEV 1 +#define SNDRV_ICS_CD_DEV 2 +#define SNDRV_ICS_GF1_DEV 3 +#define SNDRV_ICS_NONE_DEV 4 +#define SNDRV_ICS_MASTER_DEV 5 + +/* LFO */ + +#define SNDRV_LFO_TREMOLO 0 +#define SNDRV_LFO_VIBRATO 1 + +/* misc */ + +#define SNDRV_GF1_DMA_UNSIGNED 0x80 +#define SNDRV_GF1_DMA_16BIT 0x40 +#define SNDRV_GF1_DMA_IRQ 0x20 +#define SNDRV_GF1_DMA_WIDTH16 0x04 +#define SNDRV_GF1_DMA_READ 0x02 /* read from GUS's DRAM */ +#define SNDRV_GF1_DMA_ENABLE 0x01 + +/* ramp ranges */ + +#define SNDRV_GF1_ATTEN(x) (snd_gf1_atten_table[x]) +#define SNDRV_GF1_MIN_VOLUME 1800 +#define SNDRV_GF1_MAX_VOLUME 4095 +#define SNDRV_GF1_MIN_OFFSET (SNDRV_GF1_MIN_VOLUME>>4) +#define SNDRV_GF1_MAX_OFFSET 255 +#define SNDRV_GF1_MAX_TDEPTH 90 + +/* defines for memory manager */ + +#define SNDRV_GF1_MEM_BLOCK_16BIT 0x0001 + +#define SNDRV_GF1_MEM_OWNER_DRIVER 0x0001 +#define SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE 0x0002 +#define SNDRV_GF1_MEM_OWNER_WAVE_GF1 0x0003 +#define SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF 0x0004 + +/* constants for interrupt handlers */ + +#define SNDRV_GF1_HANDLER_MIDI_OUT 0x00010000 +#define SNDRV_GF1_HANDLER_MIDI_IN 0x00020000 +#define SNDRV_GF1_HANDLER_TIMER1 0x00040000 +#define SNDRV_GF1_HANDLER_TIMER2 0x00080000 +#define SNDRV_GF1_HANDLER_VOICE 0x00100000 +#define SNDRV_GF1_HANDLER_DMA_WRITE 0x00200000 +#define SNDRV_GF1_HANDLER_DMA_READ 0x00400000 +#define SNDRV_GF1_HANDLER_ALL (0xffff0000&~SNDRV_GF1_HANDLER_VOICE) + +/* constants for DMA flags */ + +#define SNDRV_GF1_DMA_TRIGGER 1 + +/* --- */ + +struct _snd_gus_card; +typedef struct _snd_gus_card snd_gus_card_t; + +/* GF1 specific structure */ + +typedef struct _snd_gf1_bank_info { + unsigned int address; + unsigned int size; +} snd_gf1_bank_info_t; + +typedef struct _snd_gf1_mem_block { + unsigned short flags; /* flags - SNDRV_GF1_MEM_BLOCK_XXXX */ + unsigned short owner; /* owner - SNDRV_GF1_MEM_OWNER_XXXX */ + unsigned int share; /* share count */ + unsigned int share_id[4]; /* share ID */ + unsigned int ptr; + unsigned int size; + char *name; + struct _snd_gf1_mem_block *next; + struct _snd_gf1_mem_block *prev; +} snd_gf1_mem_block_t; + +typedef struct _snd_gf1_mem { + snd_gf1_bank_info_t banks_8[4]; + snd_gf1_bank_info_t banks_16[4]; + snd_gf1_mem_block_t *first; + snd_gf1_mem_block_t *last; + snd_info_entry_t *info_entry; + struct semaphore memory_mutex; +} snd_gf1_mem_t; + +typedef struct snd_gf1_dma_block { + void *buffer; /* buffer in computer's RAM */ + unsigned long buf_addr; /* buffer address */ + unsigned int addr; /* address in onboard memory */ + unsigned int count; /* count in bytes */ + unsigned int cmd; /* DMA command (format) */ + void (*ack)(snd_gus_card_t * gus, void *private_data); + void *private_data; + struct snd_gf1_dma_block *next; +} snd_gf1_dma_block_t; + +typedef struct { + snd_midi_channel_set_t * chset; + snd_gus_card_t * gus; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + int midi_has_voices: 1; +} snd_gus_port_t; + +typedef struct _snd_gus_voice snd_gus_voice_t; + +typedef struct { + void (*sample_start)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); + void (*sample_stop)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); + void (*sample_freq)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); + void (*sample_volume)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); + void (*sample_loop)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); + void (*sample_pos)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); + void (*sample_private1)(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); +} snd_gus_sample_ops_t; + +#define SNDRV_GF1_VOICE_TYPE_PCM 0 +#define SNDRV_GF1_VOICE_TYPE_SYNTH 1 +#define SNDRV_GF1_VOICE_TYPE_MIDI 2 + +#define SNDRV_GF1_VFLG_RUNNING (1<<0) +#define SNDRV_GF1_VFLG_EFFECT_TIMER1 (1<<1) +#define SNDRV_GF1_VFLG_PAN (1<<2) + +typedef enum { + VENV_BEFORE, + VENV_ATTACK, + VENV_SUSTAIN, + VENV_RELEASE, + VENV_DONE, + VENV_VOLUME +} snd_gus_volume_state_t; + +struct _snd_gus_voice { + int number; + int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + unsigned char pad; + +#ifdef CONFIG_SND_DEBUG + unsigned int interrupt_stat_wave; + unsigned int interrupt_stat_volume; +#endif + void (*handler_wave) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*handler_volume) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*handler_effect) (snd_gus_card_t * gus, snd_gus_voice_t * voice); + void (*volume_change) (snd_gus_card_t * gus); + + snd_gus_sample_ops_t *sample_ops; + + snd_seq_instr_t instr; + + /* running status / registers */ + + snd_seq_ev_volume_t sample_volume; + + unsigned short fc_register; + unsigned short fc_lfo; + unsigned short gf1_volume; + unsigned char control; + unsigned char mode; + unsigned char gf1_pan; + unsigned char effect_accumulator; + unsigned char volume_control; + unsigned char venv_value_next; + snd_gus_volume_state_t venv_state; + snd_gus_volume_state_t venv_state_prev; + unsigned short vlo; + unsigned short vro; + unsigned short gf1_effect_volume; + + /* --- */ + + void *private_data; + void (*private_free)(snd_gus_voice_t *voice); +}; + +struct _snd_gf1 { + + unsigned int enh_mode:1, /* enhanced mode (GFA1) */ + hw_lfo:1, /* use hardware LFO */ + sw_lfo:1, /* use software LFO */ + effect:1; /* use effect voices */ + + unsigned long port; /* port of GF1 chip */ + struct resource *res_port1; + struct resource *res_port2; + int irq; /* IRQ number */ + int dma1; /* DMA1 number */ + int dma2; /* DMA2 number */ + unsigned int memory; /* GUS's DRAM size in bytes */ + unsigned int rom_memory; /* GUS's ROM size in bytes */ + unsigned int rom_present; /* bitmask */ + unsigned int rom_banks; /* GUS's ROM banks */ + + snd_gf1_mem_t mem_alloc; + snd_info_entry_t *ram_entries[4]; + snd_info_entry_t *rom_entries[4]; + + /* registers */ + unsigned short reg_page; + unsigned short reg_regsel; + unsigned short reg_data8; + unsigned short reg_data16; + unsigned short reg_irqstat; + unsigned short reg_dram; + unsigned short reg_timerctrl; + unsigned short reg_timerdata; + unsigned char ics_regs[6][2]; + /* --------- */ + + unsigned char active_voices; /* active voices */ + unsigned char active_voice; /* selected voice (GF1PAGE register) */ + + snd_gus_voice_t voices[32]; /* GF1 voices */ + + unsigned int default_voice_address; + + unsigned short playback_freq; /* GF1 playback (mixing) frequency */ + unsigned short mode; /* see to SNDRV_GF1_MODE_XXXX */ + unsigned char volume_ramp; + unsigned char smooth_pan; + unsigned char full_range_pan; + unsigned char pad0; + + unsigned char *lfos; + + /* interrupt handlers */ + + void (*interrupt_handler_midi_out) (snd_gus_card_t * gus); + void (*interrupt_handler_midi_in) (snd_gus_card_t * gus); + void (*interrupt_handler_timer1) (snd_gus_card_t * gus); + void (*interrupt_handler_timer2) (snd_gus_card_t * gus); + void (*interrupt_handler_dma_write) (snd_gus_card_t * gus); + void (*interrupt_handler_dma_read) (snd_gus_card_t * gus); + +#ifdef CONFIG_SND_DEBUG + unsigned int interrupt_stat_midi_out; + unsigned int interrupt_stat_midi_in; + unsigned int interrupt_stat_timer1; + unsigned int interrupt_stat_timer2; + unsigned int interrupt_stat_dma_write; + unsigned int interrupt_stat_dma_read; + unsigned int interrupt_stat_voice_lost; +#endif + + /* synthesizer */ + + int seq_client; + snd_gus_port_t seq_ports[4]; + snd_seq_kinstr_list_t *ilist; + snd_iwffff_ops_t iwffff_ops; + snd_gf1_ops_t gf1_ops; + snd_simple_ops_t simple_ops; + + /* timer */ + + unsigned short timer_enabled; + snd_timer_t *timer1; + snd_timer_t *timer2; + + /* midi */ + + unsigned short uart_cmd; + unsigned int uart_framing; + unsigned int uart_overrun; + + /* dma operations */ + + unsigned int dma_flags; + unsigned int dma_shared; + snd_gf1_dma_block_t *dma_data_pcm; + snd_gf1_dma_block_t *dma_data_pcm_last; + snd_gf1_dma_block_t *dma_data_synth; + snd_gf1_dma_block_t *dma_data_synth_last; + void (*dma_ack)(snd_gus_card_t * gus, void *private_data); + void *dma_private_data; + + /* pcm */ + int pcm_channels; + int pcm_alloc_voices; + unsigned short pcm_volume_level_left; + unsigned short pcm_volume_level_right; + unsigned short pcm_volume_level_left1; + unsigned short pcm_volume_level_right1; + + unsigned char pcm_rcntrl_reg; + unsigned char pad_end; +}; + +/* main structure for GUS card */ + +struct _snd_gus_card { + snd_card_t *card; + + unsigned int + initialized: 1, /* resources were initialized */ + equal_irq:1, /* GF1 and CODEC shares IRQ (GUS MAX only) */ + equal_dma:1, /* if dma channels are equal (not valid for daughter board) */ + ics_flag:1, /* have we ICS mixer chip */ + ics_flipped:1, /* ICS mixer have flipped some channels? */ + codec_flag:1, /* have we CODEC chip? */ + max_flag:1, /* have we GUS MAX card? */ + max_ctrl_flag:1, /* have we original GUS MAX card? */ + daughter_flag:1, /* have we daughter board? */ + interwave:1, /* hey - we have InterWave card */ + ess_flag:1, /* ESS chip found... GUS Extreme */ + ace_flag:1, /* GUS ACE detected */ + uart_enable:1; /* enable MIDI UART */ + unsigned short revision; /* revision of chip */ + unsigned short max_cntrl_val; /* GUS MAX control value */ + unsigned short mix_cntrl_reg; /* mixer control register */ + unsigned short joystick_dac; /* joystick DAC level */ + int timer_dev; /* timer device */ + + struct _snd_gf1 gf1; /* gf1 specific variables */ +#ifdef CONFIG_SND_DEBUG + snd_info_entry_t *irq_entry; +#endif + snd_pcm_t *pcm; + snd_pcm_substream_t *pcm_cap_substream; + unsigned int c_dma_size; + unsigned int c_period_size; + unsigned int c_pos; + + snd_rawmidi_t *midi_uart; + snd_rawmidi_substream_t *midi_substream_output; + snd_rawmidi_substream_t *midi_substream_input; + + snd_seq_device_t *seq_dev; + + spinlock_t reg_lock; + spinlock_t voice_alloc; + spinlock_t active_voice_lock; + spinlock_t event_lock; + spinlock_t dma_lock; + spinlock_t pcm_volume_level_lock; + spinlock_t uart_cmd_lock; + struct semaphore dma_mutex; + struct semaphore register_mutex; +}; + +/* I/O functions for GF1/InterWave chip - gus_io.c */ + +static inline void snd_gf1_select_voice(snd_gus_card_t * gus, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->active_voice_lock, flags); + if (voice != gus->gf1.active_voice) { + gus->gf1.active_voice = voice; + outb(voice, GUSP(gus, GF1PAGE)); + } + spin_unlock_irqrestore(&gus->active_voice_lock, flags); +} + +static inline void snd_gf1_uart_cmd(snd_gus_card_t * gus, unsigned char b) +{ + outb(gus->gf1.uart_cmd = b, GUSP(gus, MIDICTRL)); +} + +static inline unsigned char snd_gf1_uart_stat(snd_gus_card_t * gus) +{ + return inb(GUSP(gus, MIDISTAT)); +} + +static inline void snd_gf1_uart_put(snd_gus_card_t * gus, unsigned char b) +{ + outb(b, GUSP(gus, MIDIDATA)); +} + +static inline unsigned char snd_gf1_uart_get(snd_gus_card_t * gus) +{ + return inb(GUSP(gus, MIDIDATA)); +} + +extern void snd_gf1_delay(snd_gus_card_t * gus); + +extern void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg); + +extern void snd_gf1_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned char snd_gf1_read8(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_look8(gus, reg | 0x80); +} +extern void snd_gf1_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data); +extern unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned short snd_gf1_read16(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_look16(gus, reg | 0x80); +} +extern void snd_gf1_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data); +extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data); +extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr); +extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count); +extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit); +extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit); +extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg); +extern void snd_gf1_i_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg); +extern void snd_gf1_i_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data); +extern inline unsigned char snd_gf1_i_read8(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_i_look8(gus, reg | 0x80); +} +extern unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg); +extern inline unsigned short snd_gf1_i_read16(snd_gus_card_t * gus, unsigned char reg) +{ + return snd_gf1_i_look16(gus, reg | 0x80); +} +extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data); +extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit); +extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit); + +extern void snd_gf1_select_active_voices(snd_gus_card_t * gus); + +/* gus_lfo.c */ + +struct _SND_IW_LFO_PROGRAM { + unsigned short freq_and_control; + unsigned char depth_final; + unsigned char depth_inc; + unsigned short twave; + unsigned short depth; +}; + +#if 0 +extern void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice); +#endif +extern void snd_gf1_lfo_init(snd_gus_card_t * gus); +extern void snd_gf1_lfo_done(snd_gus_card_t * gus); +extern void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, struct _SND_IW_LFO_PROGRAM *program); +extern void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type); +extern void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type); +extern void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, int lfo_type, int freq); +extern void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, int lfo_type, int depth); +extern void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, int freq, int current_depth, int depth, int sweep, int shape); +extern void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type); +#if 0 +extern void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *command); +#endif + +/* gus_mem.c */ + +void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup); +int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block); +snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, + unsigned int address); +snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, + unsigned int *share_id); +snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, + char *name, int size, int w_16, + int align, unsigned int *share_id); +int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address); +int snd_gf1_mem_free_owner(snd_gf1_mem_t * alloc, int owner); +int snd_gf1_mem_init(snd_gus_card_t * gus); +int snd_gf1_mem_done(snd_gus_card_t * gus); + +/* gus_mem_proc.c */ + +int snd_gf1_mem_proc_init(snd_gus_card_t * gus); +int snd_gf1_mem_proc_done(snd_gus_card_t * gus); + +/* gus_dma.c */ + +void snd_gf1_dma_program(snd_gus_card_t * gus, unsigned int addr, + unsigned long buf_addr, unsigned int count, + unsigned int cmd); +void snd_gf1_dma_ack(snd_gus_card_t * gus); +int snd_gf1_dma_init(snd_gus_card_t * gus); +int snd_gf1_dma_done(snd_gus_card_t * gus); +int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, + snd_gf1_dma_block_t * block, + int atomic, + int synth); + +/* gus_volume.c */ + +unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol); +unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol); +unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, + unsigned short start, + unsigned short end, + unsigned int us); +unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2); +unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens); +unsigned short snd_gf1_compute_freq(unsigned int freq, + unsigned int rate, + unsigned short mix_rate); + +/* gus_reset.c */ + +void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what); +void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice); +void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice); +void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max); +void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max); +snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port); +void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice); +int snd_gf1_start(snd_gus_card_t * gus); +int snd_gf1_stop(snd_gus_card_t * gus); + +/* gus_mixer.c */ + +int snd_gf1_new_mixer(snd_gus_card_t * gus); + +/* gus_pcm.c */ + +int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm); + +#ifdef CONFIG_SND_DEBUG +extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus); +extern void snd_gf1_print_global_registers(snd_gus_card_t * gus); +extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus); +extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit); +#endif + +/* gus.c */ + +int snd_gus_use_inc(snd_gus_card_t * gus); +void snd_gus_use_dec(snd_gus_card_t * gus); +int snd_gus_create(snd_card_t * card, + unsigned long port, + int irq, int dma1, int dma2, + int timer_dev, + int voices, + int pcm_channels, + int effect, + snd_gus_card_t ** rgus); +int snd_gus_initialize(snd_gus_card_t * gus); + +/* gus_irq.c */ + +void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs); +#ifdef CONFIG_SND_DEBUG +void snd_gus_irq_profile_init(snd_gus_card_t *gus); +void snd_gus_irq_profile_done(snd_gus_card_t *gus); +#endif + +/* gus_uart.c */ + +int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t **rrawmidi); + +#if 0 +extern void snd_engine_instrument_register(unsigned short mode, + struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds, + struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds, + struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds); +extern int snd_engine_instrument_register_ask(unsigned short mode); +#endif + +/* gus_dram.c */ +int snd_gus_dram_write(snd_gus_card_t *gus, char *ptr, + unsigned int addr, unsigned int size); +int snd_gus_dram_read(snd_gus_card_t *gus, char *ptr, + unsigned int addr, unsigned int size, int rom); + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + +/* gus_sample.c */ +void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p); + +/* gus_simple.c */ +void snd_gf1_simple_init(snd_gus_voice_t *voice); + +/* gus_instr.c */ +int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, + int atomic); +int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic); +int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, + int atomic); +int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); +int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic); +int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, + int atomic); + +#endif /* CONFIG_SND_SEQUENCER */ + +#endif /* __SOUND_GUS_H */ diff -Nru linux/include/sound/hwdep.h linux-2.4.19-pre5-mjc/include/sound/hwdep.h --- linux/include/sound/hwdep.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/hwdep.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,63 @@ +#ifndef __SOUND_HWDEP_H +#define __SOUND_HWDEP_H + +/* + * Hardware dependent layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +typedef enum sndrv_hwdep_iface snd_hwdep_iface_t; +typedef struct sndrv_hwdep_info snd_hwdep_info_t; + +typedef struct _snd_hwdep_ops { + long long (*llseek) (snd_hwdep_t *hw, struct file * file, long long offset, int orig); + long (*read) (snd_hwdep_t * hw, char *buf, long count, loff_t *offset); + long (*write) (snd_hwdep_t * hw, const char *buf, long count, loff_t *offset); + int (*open) (snd_hwdep_t * hw, struct file * file); + int (*release) (snd_hwdep_t * hw, struct file * file); + unsigned int (*poll) (snd_hwdep_t * hw, struct file * file, poll_table * wait); + int (*ioctl) (snd_hwdep_t * hw, struct file * file, unsigned int cmd, unsigned long arg); + int (*mmap) (snd_hwdep_t * hw, struct file * file, struct vm_area_struct * vma); +} snd_hwdep_ops_t; + +struct _snd_hwdep { + snd_card_t *card; + int device; + char id[32]; + char name[80]; + int iface; + +#ifdef CONFIG_SND_OSSEMUL + char oss_dev[32]; + int oss_type; + int ossreg; +#endif + + snd_hwdep_ops_t ops; + wait_queue_head_t open_wait; + void *private_data; + void (*private_free) (snd_hwdep_t *hwdep); +}; + +extern int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep); + +#endif /* __SOUND_HWDEP_H */ diff -Nru linux/include/sound/i2c.h linux-2.4.19-pre5-mjc/include/sound/i2c.h --- linux/include/sound/i2c.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/i2c.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,92 @@ +#ifndef __SOUND_I2C_H +#define __SOUND_I2C_H + +/* + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +typedef struct _snd_i2c_device snd_i2c_device_t; +typedef struct _snd_i2c_bus snd_i2c_bus_t; + +#define SND_I2C_DEVICE_ADDRTEN (1<<0) /* 10-bit I2C address */ + +struct _snd_i2c_device { + struct list_head list; + snd_i2c_bus_t *bus; /* I2C bus */ + char name[32]; /* some useful device name */ + unsigned short flags; /* device flags */ + unsigned short addr; /* device address (might be 10-bit) */ + unsigned long private_value; + void *private_data; + void (*private_free)(snd_i2c_device_t *device); +}; + +#define snd_i2c_device(n) list_entry(n, snd_i2c_device_t, list) + +typedef struct _snd_i2c_bit_ops { + void (*start)(snd_i2c_bus_t *bus); /* transfer start */ + void (*stop)(snd_i2c_bus_t *bus); /* transfer stop */ + void (*direction)(snd_i2c_bus_t *bus, int clock, int data); /* set line direction (0 = write, 1 = read) */ + void (*setlines)(snd_i2c_bus_t *bus, int clock, int data); + int (*getclock)(snd_i2c_bus_t *bus); + int (*getdata)(snd_i2c_bus_t *bus, int ack); +} snd_i2c_bit_ops_t; + +typedef struct _snd_i2c_ops { + int (*sendbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count); + int (*readbytes)(snd_i2c_device_t *device, unsigned char *bytes, int count); + int (*probeaddr)(snd_i2c_bus_t *bus, unsigned short addr); +} snd_i2c_ops_t; + +struct _snd_i2c_bus { + snd_card_t *card; /* card which I2C belongs to */ + char name[32]; /* some useful label */ + + spinlock_t lock; + + snd_i2c_bus_t *master; /* master bus when SCK/SCL is shared */ + struct list_head buses; /* master: slave buses sharing SCK/SCL, slave: link list */ + + struct list_head devices; /* attached devices to this bus */ + + union { + snd_i2c_bit_ops_t *bit; + void *ops; + } hw_ops; /* lowlevel operations */ + snd_i2c_ops_t *ops; /* midlevel operations */ + + unsigned long private_value; + void *private_data; + void (*private_free)(snd_i2c_bus_t *bus); +}; + +#define snd_i2c_slave_bus(n) list_entry(n, snd_i2c_bus_t, buses) + +int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c); +int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice); +int snd_i2c_device_free(snd_i2c_device_t *device); + +static inline void snd_i2c_lock(snd_i2c_bus_t *bus) { spin_lock(&(bus->master ? bus->master->lock : bus->lock)); } +static inline void snd_i2c_unlock(snd_i2c_bus_t *bus) { spin_unlock(&(bus->master ? bus->master->lock : bus->lock)); } + +int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr); + +#endif /* __SOUND_I2C_H */ diff -Nru linux/include/sound/info.h linux-2.4.19-pre5-mjc/include/sound/info.h --- linux/include/sound/info.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/info.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,192 @@ +#ifndef __SOUND_INFO_H +#define __SOUND_INFO_H + +/* + * Header file for info interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +/* buffer for information */ +struct snd_info_buffer { + char *buffer; /* pointer to begin of buffer */ + char *curr; /* current position in buffer */ + unsigned long size; /* current size */ + unsigned long len; /* total length of buffer */ + int stop; /* stop flag */ + int error; /* error code */ +}; + +typedef struct snd_info_buffer snd_info_buffer_t; + +#define SNDRV_INFO_CONTENT_TEXT 0 +#define SNDRV_INFO_CONTENT_DATA 1 +#define SNDRV_INFO_CONTENT_DEVICE 2 + +struct snd_info_entry; + +struct snd_info_entry_text { + unsigned long read_size; + unsigned long write_size; + void (*read) (snd_info_entry_t *entry, snd_info_buffer_t * buffer); + void (*write) (snd_info_entry_t *entry, snd_info_buffer_t * buffer); +}; + +struct snd_info_entry_ops { + int (*open) (snd_info_entry_t *entry, + unsigned short mode, void **file_private_data); + int (*release) (snd_info_entry_t * entry, + unsigned short mode, void *file_private_data); + long (*read) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, char *buf, long count); + long (*write) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, const char *buf, long count); + long long (*llseek) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, long long offset, int orig); + unsigned int (*poll) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, poll_table * wait); + int (*ioctl) (snd_info_entry_t *entry, void *file_private_data, + struct file * file, unsigned int cmd, unsigned long arg); + int (*mmap) (snd_info_entry_t *entry, void *file_private_data, + struct inode * inode, struct file * file, + struct vm_area_struct * vma); +}; + +struct snd_info_entry_device { + unsigned short major; + unsigned short minor; +}; + +struct snd_info_entry { + const char *name; + mode_t mode; + long size; + unsigned short content; + union { + struct snd_info_entry_text text; + struct snd_info_entry_ops *ops; + struct snd_info_entry_device device; + } c; + snd_info_entry_t *parent; + snd_card_t *card; + struct module *module; + void *private_data; + void (*private_free)(snd_info_entry_t *entry); + struct proc_dir_entry *p; + struct semaphore access; +}; + +extern int snd_info_check_reserved_words(const char *str); + +#ifdef CONFIG_SND_OSSEMUL +extern int snd_info_minor_register(void); +extern int snd_info_minor_unregister(void); +#endif + + +#ifdef CONFIG_PROC_FS + +extern snd_info_entry_t *snd_seq_root; +#ifdef CONFIG_SND_OSSEMUL +extern snd_info_entry_t *snd_oss_root; +#else +#define snd_oss_root NULL +#endif + +int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3))); +int snd_info_init(void); +int snd_info_done(void); + +int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len); +char *snd_info_get_str(char *dest, char *src, int len); +snd_info_entry_t *snd_info_create_module_entry(struct module * module, + const char *name, + snd_info_entry_t * parent); +snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, + const char *name, + snd_info_entry_t * parent); +void snd_info_free_entry(snd_info_entry_t * entry); +snd_info_entry_t *snd_info_create_device(const char *name, + unsigned int number, + unsigned int mode); +void snd_info_free_device(snd_info_entry_t * entry); +int snd_info_store_text(snd_info_entry_t * entry); +int snd_info_restore_text(snd_info_entry_t * entry); + +int snd_info_card_register(snd_card_t * card); +int snd_info_card_unregister(snd_card_t * card); +int snd_info_register(snd_info_entry_t * entry); +int snd_info_unregister(snd_info_entry_t * entry); + +struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent); +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de); + +#else + +#define snd_seq_root NULL +#define snd_oss_root NULL + +static inline int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) { return 0; } +static inline int snd_info_init(void) { return 0; } +static inline int snd_info_done(void) { return 0; } + +static inline int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) { return 0; } +static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; } +static inline snd_info_entry_t *snd_info_create_module_entry(struct module * module, const char *name) { return NULL; } +static inline snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, const char *name) { return NULL; } +static inline void snd_info_free_entry(snd_info_entry_t * entry) { ; } +static inline snd_info_entry_t *snd_info_create_device(const char *name, + unsigned int number, + unsigned int mode) { return NULL; } +static inline void snd_info_free_device(snd_info_entry_t * entry) { ; } + +static inline int snd_info_card_register(snd_card_t * card) { return 0; } +static inline int snd_info_card_unregister(snd_card_t * card) { return 0; } +static inline int snd_info_register(snd_info_entry_t * entry) { return 0; } +static inline int snd_info_unregister(snd_info_entry_t * entry) { return 0; } + +static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { return 0; } +static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) { ; } + +#endif + +/* + * OSS info part + */ + +#ifdef CONFIG_SND_OSSEMUL + +#define SNDRV_OSS_INFO_DEV_AUDIO 0 +#define SNDRV_OSS_INFO_DEV_SYNTH 1 +#define SNDRV_OSS_INFO_DEV_MIDI 2 +#define SNDRV_OSS_INFO_DEV_TIMERS 4 +#define SNDRV_OSS_INFO_DEV_MIXERS 5 + +#define SNDRV_OSS_INFO_DEV_COUNT 6 + +extern int snd_oss_info_register(int dev, int num, char *string); +#define snd_oss_info_unregister(dev, num) snd_oss_info_register(dev, num, NULL) + +#endif /* CONFIG_SND_OSSEMUL */ + +#endif /* __SOUND_INFO_H */ diff -Nru linux/include/sound/initval.h linux-2.4.19-pre5-mjc/include/sound/initval.h --- linux/include/sound/initval.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/initval.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,171 @@ +#ifndef __SOUND_INITVAL_H +#define __SOUND_INITVAL_H + +/* + * Init values for soundcard modules + * Copyright (c) by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef MODULE_GENERIC_STRING +#ifdef MODULE +#define MODULE_GENERIC_STRING(name, string) \ +static const char __module_generic_string_##name [] \ + __attribute__ ((section(".modstring"))) = #name "=" string; +#else +#define MODULE_GENERIC_STRING(name, string) +#endif +#endif + +#define MODULE_CLASSES(val) MODULE_GENERIC_STRING(info_classes, val) +#define MODULE_DEVICES(val) MODULE_GENERIC_STRING(info_devices, val) +#define MODULE_PARM_SYNTAX(id, val) MODULE_GENERIC_STRING(info_parm_##id, val) + +#define SNDRV_AUTO_PORT 0xffff +#define SNDRV_AUTO_IRQ 0xffff +#define SNDRV_AUTO_DMA 0xffff +#define SNDRV_AUTO_DMA_SIZE (0x7fffffff) + +#define SNDRV_DEFAULT_IDX1 (-1) +#define SNDRV_DEFAULT_STR1 NULL +#define SNDRV_DEFAULT_ENABLE1 1 +#define SNDRV_DEFAULT_PORT1 SNDRV_AUTO_PORT +#define SNDRV_DEFAULT_IRQ1 SNDRV_AUTO_IRQ +#define SNDRV_DEFAULT_DMA1 SNDRV_AUTO_DMA +#define SNDRV_DEFAULT_DMA_SIZE1 SNDRV_AUTO_DMA_SIZE +#define SNDRV_DEFAULT_PTR1 SNDRV_DEFAULT_STR1 + +#define SNDRV_DEFAULT_IDX { [0 ... (SNDRV_CARDS-1)] = -1 } +#define SNDRV_DEFAULT_STR { [0 ... (SNDRV_CARDS-1)] = NULL } +#define SNDRV_DEFAULT_ENABLE { 1, [1 ... (SNDRV_CARDS-1)] = 0 } +#define SNDRV_DEFAULT_ENABLE_PNP { [0 ... (SNDRV_CARDS-1)] = 1 } +#ifdef __ISAPNP__ +#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE_PNP +#else +#define SNDRV_DEFAULT_ENABLE_ISAPNP SNDRV_DEFAULT_ENABLE +#endif +#define SNDRV_DEFAULT_PORT { SNDRV_AUTO_PORT, [1 ... (SNDRV_CARDS-1)] = -1 } +#define SNDRV_DEFAULT_IRQ { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_IRQ } +#define SNDRV_DEFAULT_DMA { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA } +#define SNDRV_DEFAULT_DMA_SIZE { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA_SIZE } +#define SNDRV_DEFAULT_PTR SNDRV_DEFAULT_STR + +#define SNDRV_BOOLEAN_TRUE_DESC "allows:{{0,Disabled},{1,Enabled}},default:1,dialog:check" +#define SNDRV_BOOLEAN_FALSE_DESC "allows:{{0,Disabled},{1,Enabled}},default:0,dialog:check" + +#define SNDRV_ENABLED "enable:(snd_enable)" + +#define SNDRV_INDEX_DESC SNDRV_ENABLED ",allows:{{0,7}},unique,skill:required,dialog:list" +#define SNDRV_ID_DESC SNDRV_ENABLED ",unique" +#define SNDRV_ENABLE_DESC SNDRV_BOOLEAN_FALSE_DESC +#define SNDRV_ISAPNP_DESC SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC +#define SNDRV_DMA8_DESC SNDRV_ENABLED ",allows:{{0,1},{3}},dialog:list" +#define SNDRV_DMA16_DESC SNDRV_ENABLED ",allows:{{5,7}},dialog:list" +#define SNDRV_DMA_DESC SNDRV_ENABLED ",allows:{{0,1},{3},{5,7}},dialog:list" +#define SNDRV_IRQ_DESC SNDRV_ENABLED ",allows:{{5},{7},{9},{10,12},{14,15}},dialog:list" +#define SNDRV_DMA_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" +#define SNDRV_DMA8_SIZE_DESC SNDRV_ENABLED ",allows:{{4, 64}},default:64,skill:advanced" +#define SNDRV_DMA16_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" +#define SNDRV_PORT12_DESC SNDRV_ENABLED ",allows:{{0,0x3fff}},base:16" +#define SNDRV_PORT_DESC SNDRV_ENABLED ",allows:{{0,0xffff}},base:16" + +#ifdef SNDRV_LEGACY_AUTO_PROBE +static int snd_legacy_auto_probe(unsigned long *ports, int (*probe)(unsigned long port)) +{ + int result = 0; /* number of detected cards */ + + while ((signed long)*ports != -1) { + if (probe(*ports) >= 0) + result++; + ports++; + } + return result; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_IOPORT +static long snd_legacy_find_free_ioport(long *port_table, long size) +{ + while (*port_table != -1) { + if (!check_region(*port_table, size)) + return *port_table; + port_table++; + } + return -1; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_IRQ +static void snd_legacy_empty_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int snd_legacy_find_free_irq(int *irq_table) +{ + while (*irq_table != -1) { + if (!request_irq(*irq_table, snd_legacy_empty_irq_handler, + SA_INTERRUPT, "ALSA Test IRQ", (void *) irq_table)) { + free_irq(*irq_table, (void *) irq_table); + return *irq_table; + } + irq_table++; + } + return -1; +} +#endif + +#ifdef SNDRV_LEGACY_FIND_FREE_DMA +static int snd_legacy_find_free_dma(int *dma_table) +{ + while (*dma_table != -1) { + if (!request_dma(*dma_table, "ALSA Test DMA")) { + free_dma(*dma_table); + return *dma_table; + } + dma_table++; + } + return -1; +} +#endif + +#if defined(SNDRV_GET_ID) && !defined(MODULE) +#include +static int __init get_id(char **str, char **dst) +{ + char *s, *d; + + if (!(*str) || !(**str)) + return 0; + for (s = *str; isalpha(*s) || isdigit(*s) || *s == '_'; s++); + if (s != *str) { + *dst = (char *)kmalloc(s - *str, GFP_KERNEL); + if ((d = *dst) != NULL) { + s = *str; + while (isalpha(*s) || isdigit(*s) || *s == '_') + *d++ = *s++; + } + } + *str = s; + if (*s == ',') { + (*str)++; + return 2; + } + return 1; +} +#endif + +#endif /* __SOUND_INITVAL_H */ diff -Nru linux/include/sound/minors.h linux-2.4.19-pre5-mjc/include/sound/minors.h --- linux/include/sound/minors.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/minors.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,86 @@ +#ifndef __SOUND_MINORS_H +#define __SOUND_MINORS_H + +/* + * MINOR numbers + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MINOR_DEVICES 32 +#define SNDRV_MINOR_CARD(minor) ((minor) >> 5) +#define SNDRV_MINOR_DEVICE(minor) ((minor) & 0x001f) +#define SNDRV_MINOR(card, dev) (((card) << 5) | (dev)) + +#define SNDRV_MINOR_CONTROL 0 /* 0 - 0 */ +#define SNDRV_MINOR_SEQUENCER 1 +#define SNDRV_MINOR_TIMER (1+32) +#define SNDRV_MINOR_HWDEP 4 /* 4 - 7 */ +#define SNDRV_MINOR_HWDEPS 4 +#define SNDRV_MINOR_RAWMIDI 8 /* 8 - 11 */ +#define SNDRV_MINOR_RAWMIDIS 4 +#define SNDRV_MINOR_PCM_PLAYBACK 16 /* 16 - 23 */ +#define SNDRV_MINOR_PCM_CAPTURE 24 /* 24 - 31 */ +#define SNDRV_MINOR_PCMS 8 + +#define SNDRV_DEVICE_TYPE_CONTROL SNDRV_MINOR_CONTROL +#define SNDRV_DEVICE_TYPE_HWDEP SNDRV_MINOR_HWDEP +#define SNDRV_DEVICE_TYPE_MIXER SNDRV_MINOR_MIXER +#define SNDRV_DEVICE_TYPE_RAWMIDI SNDRV_MINOR_RAWMIDI +#define SNDRV_DEVICE_TYPE_PCM_PLAYBACK SNDRV_MINOR_PCM_PLAYBACK +#define SNDRV_DEVICE_TYPE_PCM_PLOOP SNDRV_MINOR_PCM_PLOOP +#define SNDRV_DEVICE_TYPE_PCM_CAPTURE SNDRV_MINOR_PCM_CAPTURE +#define SNDRV_DEVICE_TYPE_PCM_CLOOP SNDRV_MINOR_PCM_CLOOP +#define SNDRV_DEVICE_TYPE_SEQUENCER SNDRV_MINOR_SEQUENCER +#define SNDRV_DEVICE_TYPE_TIMER SNDRV_MINOR_TIMER + +#ifdef CONFIG_SND_OSSEMUL + +#define SNDRV_MINOR_OSS_DEVICES 16 +#define SNDRV_MINOR_OSS_CARD(minor) ((minor) >> 4) +#define SNDRV_MINOR_OSS_DEVICE(minor) ((minor) & 0x000f) +#define SNDRV_MINOR_OSS(card, dev) (((card) << 4) | (dev)) + +#define SNDRV_MINOR_OSS_MIXER 0 /* /dev/mixer - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_SEQUENCER 1 /* /dev/sequencer - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_MIDI 2 /* /dev/midi - native midi interface - OSS 3.XX compatible - UART */ +#define SNDRV_MINOR_OSS_PCM 3 /* alias */ +#define SNDRV_MINOR_OSS_PCM_8 3 /* /dev/dsp - 8bit PCM - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_AUDIO 4 /* /dev/audio - SunSparc compatible */ +#define SNDRV_MINOR_OSS_PCM_16 5 /* /dev/dsp16 - 16bit PCM - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_SNDSTAT 6 /* /dev/sndstat - for compatibility with OSS */ +#define SNDRV_MINOR_OSS_RESERVED7 7 /* reserved for future use */ +#define SNDRV_MINOR_OSS_MUSIC 8 /* /dev/music - OSS 3.XX compatible */ +#define SNDRV_MINOR_OSS_DMMIDI 9 /* /dev/dmmidi0 - this device can have another minor # with OSS */ +#define SNDRV_MINOR_OSS_DMFM 10 /* /dev/dmfm0 - this device can have another minor # with OSS */ +#define SNDRV_MINOR_OSS_MIXER1 11 /* alternate mixer */ +#define SNDRV_MINOR_OSS_PCM1 12 /* alternate PCM (GF-A-1) */ +#define SNDRV_MINOR_OSS_MIDI1 13 /* alternate midi - SYNTH */ +#define SNDRV_MINOR_OSS_DMMIDI1 14 /* alternate dmmidi - SYNTH */ +#define SNDRV_MINOR_OSS_RESERVED15 15 /* reserved for future use */ + +#define SNDRV_OSS_DEVICE_TYPE_MIXER 0 +#define SNDRV_OSS_DEVICE_TYPE_SEQUENCER 1 +#define SNDRV_OSS_DEVICE_TYPE_PCM 2 +#define SNDRV_OSS_DEVICE_TYPE_MIDI 3 +#define SNDRV_OSS_DEVICE_TYPE_DMFM 4 +#define SNDRV_OSS_DEVICE_TYPE_SNDSTAT 5 +#define SNDRV_OSS_DEVICE_TYPE_MUSIC 6 + +#endif + +#endif /* __SOUND_MINORS_H */ diff -Nru linux/include/sound/mixer_oss.h linux-2.4.19-pre5-mjc/include/sound/mixer_oss.h --- linux/include/sound/mixer_oss.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/mixer_oss.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,71 @@ +#ifndef __SOUND_MIXER_OSS_H +#define __SOUND_MIXER_OSS_H + +/* + * OSS MIXER API + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef CONFIG_SND_OSSEMUL + +typedef struct _snd_oss_mixer_slot snd_mixer_oss_slot_t; +typedef struct _snd_oss_file snd_mixer_oss_file_t; + +typedef int (*snd_mixer_oss_get_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *left, int *right); +typedef int (*snd_mixer_oss_put_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int left, int right); +typedef int (*snd_mixer_oss_get_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *active); +typedef int (*snd_mixer_oss_put_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int active); +typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index); +typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index); + +struct _snd_oss_mixer_slot { + int number; + int stereo: 1; + snd_mixer_oss_get_volume_t get_volume; + snd_mixer_oss_put_volume_t put_volume; + snd_mixer_oss_get_recsrc_t get_recsrc; + snd_mixer_oss_put_recsrc_t put_recsrc; + unsigned long private_value; + void *private_data; + void (*private_free)(snd_mixer_oss_slot_t *slot); +}; + +struct _snd_oss_mixer { + snd_card_t *card; + char id[16]; + char name[32]; + snd_mixer_oss_slot_t slots[32]; /* OSS mixer slots */ + unsigned int mask_recsrc; /* exclusive recsrc mask */ + snd_mixer_oss_get_recsrce_t get_recsrc; + snd_mixer_oss_put_recsrce_t put_recsrc; + void *private_data_recsrc; + void (*private_free_recsrc)(snd_mixer_oss_t *mixer); + /* --- */ + int oss_recsrc; +}; + +struct _snd_oss_file { + int volume[32][2]; + snd_card_t *card; + snd_mixer_oss_t *mixer; +}; + +#endif /* CONFIG_SND_OSSEMUL */ + +#endif /* __SOUND_MIXER_OSS_H */ diff -Nru linux/include/sound/mpu401.h linux-2.4.19-pre5-mjc/include/sound/mpu401.h --- linux/include/sound/mpu401.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/mpu401.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,109 @@ +#ifndef __SOUND_MPU401_H +#define __SOUND_MPU401_H + +/* + * Header file for MPU-401 and compatible cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "rawmidi.h" + +#define MPU401_HW_MPU401 1 /* native MPU401 */ +#define MPU401_HW_SB 2 /* SoundBlaster MPU-401 UART */ +#define MPU401_HW_ES1688 3 /* AudioDrive ES1688 MPU-401 UART */ +#define MPU401_HW_OPL3SA2 4 /* Yamaha OPL3-SA2 */ +#define MPU401_HW_SONICVIBES 5 /* S3 SonicVibes */ +#define MPU401_HW_CS4232 6 /* CS4232 */ +#define MPU401_HW_ES18XX 7 /* AudioDrive ES18XX MPU-401 UART */ +#define MPU401_HW_FM801 8 /* ForteMedia FM801 */ +#define MPU401_HW_TRID4DWAVE 9 /* Trident 4DWave */ +#define MPU401_HW_AZT2320 10 /* Aztech AZT2320 */ +#define MPU401_HW_ALS100 11 /* Avance Logic ALS100 */ +#define MPU401_HW_ICE1712 12 /* Envy24 */ +#define MPU401_HW_VIA686A 13 /* VIA 82C686A */ +#define MPU401_HW_YMFPCI 14 /* YMF DS-XG PCI */ +#define MPU401_HW_CMIPCI 15 /* CMIPCI MPU-401 UART */ +#define MPU401_HW_ALS4000 16 /* Avance Logic ALS4000 */ + +#define MPU401_MODE_BIT_INPUT 0 +#define MPU401_MODE_BIT_OUTPUT 1 +#define MPU401_MODE_BIT_INPUT_TRIGGER 2 +#define MPU401_MODE_BIT_OUTPUT_TRIGGER 3 +#define MPU401_MODE_BIT_RX_LOOP 4 +#define MPU401_MODE_BIT_TX_LOOP 5 + +#define MPU401_MODE_INPUT (1<port + 1) +#define MPU401D(mpu) ((mpu)->port + 0) + +/* + + */ + +void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +int snd_mpu401_uart_new(snd_card_t * card, + int device, + unsigned short hardware, + unsigned long port, + int integrated, + int irq, + int irq_flags, + snd_rawmidi_t ** rrawmidi); + +#endif /* __SOUND_MPU401_H */ diff -Nru linux/include/sound/opl3.h linux-2.4.19-pre5-mjc/include/sound/opl3.h --- linux/include/sound/opl3.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/opl3.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,332 @@ +#ifndef __SOUND_OPL3_H +#define __SOUND_OPL3_H + +/* + * Definitions of the OPL-3 registers. + * + * Copyright (c) by Jaroslav Kysela , + * Hannu Savolainen 1993-1996 + * + * + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exceptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "driver.h" +#include +#include "core.h" +#include "hwdep.h" +#include "timer.h" +#include "seq_midi_emul.h" +#ifdef CONFIG_SND_OSSEMUL +#include "seq_oss.h" +#include "seq_oss_legacy.h" +#endif +#include "seq_device.h" +#include "ainstr_fm.h" + +/* + * Register numbers for the global registers + */ + +#define OPL3_REG_TEST 0x01 +#define OPL3_ENABLE_WAVE_SELECT 0x20 + +#define OPL3_REG_TIMER1 0x02 +#define OPL3_REG_TIMER2 0x03 +#define OPL3_REG_TIMER_CONTROL 0x04 /* Left side */ +#define OPL3_IRQ_RESET 0x80 +#define OPL3_TIMER1_MASK 0x40 +#define OPL3_TIMER2_MASK 0x20 +#define OPL3_TIMER1_START 0x01 +#define OPL3_TIMER2_START 0x02 + +#define OPL3_REG_CONNECTION_SELECT 0x04 /* Right side */ +#define OPL3_LEFT_4OP_0 0x01 +#define OPL3_LEFT_4OP_1 0x02 +#define OPL3_LEFT_4OP_2 0x04 +#define OPL3_RIGHT_4OP_0 0x08 +#define OPL3_RIGHT_4OP_1 0x10 +#define OPL3_RIGHT_4OP_2 0x20 + +#define OPL3_REG_MODE 0x05 /* Right side */ +#define OPL3_OPL3_ENABLE 0x01 /* OPL3 mode */ +#define OPL3_OPL4_ENABLE 0x02 /* OPL4 mode */ + +#define OPL3_REG_KBD_SPLIT 0x08 /* Left side */ +#define OPL3_COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define OPL3_KEYBOARD_SPLIT 0x40 + +#define OPL3_REG_PERCUSSION 0xbd /* Left side only */ +#define OPL3_TREMOLO_DEPTH 0x80 +#define OPL3_VIBRATO_DEPTH 0x40 +#define OPL3_PERCUSSION_ENABLE 0x20 +#define OPL3_BASSDRUM_ON 0x10 +#define OPL3_SNAREDRUM_ON 0x08 +#define OPL3_TOMTOM_ON 0x04 +#define OPL3_CYMBAL_ON 0x02 +#define OPL3_HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ +#define OPL3_REG_AM_VIB 0x20 +#define OPL3_TREMOLO_ON 0x80 +#define OPL3_VIBRATO_ON 0x40 +#define OPL3_SUSTAIN_ON 0x20 +#define OPL3_KSR 0x10 /* Key scaling rate */ +#define OPL3_MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define OPL3_REG_KSL_LEVEL 0x40 +#define OPL3_KSL_MASK 0xc0 /* Envelope scaling bits */ +#define OPL3_TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define OPL3_REG_ATTACK_DECAY 0x60 +#define OPL3_ATTACK_MASK 0xf0 +#define OPL3_DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define OPL3_REG_SUSTAIN_RELEASE 0x80 +#define OPL3_SUSTAIN_MASK 0xf0 +#define OPL3_RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define OPL3_REG_WAVE_SELECT 0xe0 +#define OPL3_WAVE_SELECT_MASK 0x07 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define OPL3_REG_FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define OPL3_REG_KEYON_BLOCK 0xb0 +#define OPL3_KEYON_BIT 0x20 +#define OPL3_BLOCKNUM_MASK 0x1c +#define OPL3_FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halves (gives 4 ways to connect the operators). + */ +#define OPL3_REG_FEEDBACK_CONNECTION 0xc0 +#define OPL3_FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define OPL3_CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define OPL3_STEREO_BITS 0x30 /* OPL-3 only */ +#define OPL3_VOICE_TO_LEFT 0x10 +#define OPL3_VOICE_TO_RIGHT 0x20 + +/* + + */ + +#define OPL3_LEFT 0x0000 +#define OPL3_RIGHT 0x0100 + +#define OPL3_HW_AUTO 0x0000 +#define OPL3_HW_OPL2 0x0200 +#define OPL3_HW_OPL3 0x0300 +#define OPL3_HW_OPL3_SV 0x0301 /* S3 SonicVibes */ +#define OPL3_HW_OPL3_CS 0x0302 /* CS4232/CS4236+ */ +#define OPL3_HW_OPL3_FM801 0x0303 /* FM801 */ +#define OPL3_HW_OPL3_CS4281 0x0304 /* CS4281 */ +#define OPL3_HW_OPL4 0x0400 +#define OPL3_HW_MASK 0xff00 + +#define MAX_OPL2_VOICES 9 +#define MAX_OPL3_VOICES 18 + +typedef struct snd_opl3 opl3_t; + +/* + * A structure to keep track of each hardware voice + */ +typedef struct snd_opl3_voice { + int state; /* status */ +#define SNDRV_OPL3_ST_OFF 0 /* Not playing */ +#define SNDRV_OPL3_ST_ON_2OP 1 /* 2op voice is allocated */ +#define SNDRV_OPL3_ST_ON_4OP 2 /* 4op voice is allocated */ +#define SNDRV_OPL3_ST_NOT_AVAIL -1 /* voice is not available */ + + unsigned int time; /* An allocation time */ + unsigned char note; /* Note currently assigned to this voice */ + + unsigned long note_off; /* note-off time */ + int note_off_check; /* check note-off time */ + + unsigned char keyon_reg; /* KON register shadow */ + + snd_midi_channel_t *chan; /* Midi channel for this note */ +} snd_opl3_voice_t; + +struct snd_opl3 { + unsigned long l_port; + unsigned long r_port; + struct resource *res_l_port; + struct resource *res_r_port; + unsigned short hardware; + /* hardware access */ + void (*command) (opl3_t * opl3, unsigned short cmd, unsigned char val); + unsigned short timer_enable; + int seq_dev_num; /* sequencer device number */ + snd_timer_t *timer1; + snd_timer_t *timer2; + spinlock_t timer_lock; + + spinlock_t reg_lock; + snd_card_t *card; /* The card that this belongs to */ + int used; /* usage flag - exclusive */ + unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ + unsigned char rhythm; /* percussion mode flag */ + unsigned char max_voices; /* max number of voices */ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#define SNDRV_OPL3_MODE_SYNTH 0 /* OSS - voices allocated by application */ +#define SNDRV_OPL3_MODE_SEQ 1 /* ALSA - driver handles voice allocation */ + int synth_mode; /* synth mode */ + int seq_client; + + snd_seq_device_t *seq_dev; /* sequencer device */ + snd_midi_channel_set_t * chset; + +#ifdef CONFIG_SND_OSSEMUL + snd_seq_device_t *oss_seq_dev; /* OSS sequencer device, WIP */ + snd_midi_channel_set_t * oss_chset; +#endif + + snd_seq_kinstr_ops_t fm_ops; + snd_seq_kinstr_list_t *ilist; + + snd_opl3_voice_t voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */ + int use_time; /* allocation counter */ + + unsigned short connection_reg; /* connection reg shadow */ + unsigned char drum_reg; /* percussion reg shadow */ + + spinlock_t voice_lock; /* Lock for voice access */ + + struct timer_list tlist; /* timer for note-offs and effects */ + int sys_timer_status; /* system timer run status */ + spinlock_t sys_timer_lock; /* Lock for system timer access */ +#endif + struct semaphore access_mutex; /* locking */ +}; + +/* opl3.c */ +void snd_opl3_interrupt(snd_hwdep_t * hw); +int snd_opl3_create(snd_card_t * card, + unsigned long l_port, unsigned long r_port, + unsigned short hardware, + int integrated, + opl3_t ** opl3); +int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev); +int snd_opl3_hwdep_new(opl3_t * opl3, int device, int seq_device, + snd_hwdep_t ** rhwdep); + +/* opl3_synth */ +int snd_opl3_open(snd_hwdep_t * hw, struct file *file); +int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, + unsigned int cmd, unsigned long arg); +int snd_opl3_release(snd_hwdep_t * hw, struct file *file); + +void snd_opl3_reset(opl3_t * opl3); + +#endif /* __SOUND_OPL3_H */ diff -Nru linux/include/sound/pcm.h linux-2.4.19-pre5-mjc/include/sound/pcm.h --- linux/include/sound/pcm.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/pcm.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,864 @@ +#ifndef __SOUND_PCM_H +#define __SOUND_PCM_H + +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +typedef sndrv_pcm_uframes_t snd_pcm_uframes_t; +typedef sndrv_pcm_sframes_t snd_pcm_sframes_t; +typedef enum sndrv_pcm_class snd_pcm_class_t; +typedef enum sndrv_pcm_subclass snd_pcm_subclass_t; +typedef enum sndrv_pcm_stream snd_pcm_stream_t; +typedef enum sndrv_pcm_access snd_pcm_access_t; +typedef enum sndrv_pcm_format snd_pcm_format_t; +typedef enum sndrv_pcm_subformat snd_pcm_subformat_t; +typedef enum sndrv_pcm_state snd_pcm_state_t; +typedef union sndrv_pcm_sync_id snd_pcm_sync_id_t; +typedef struct sndrv_pcm_info snd_pcm_info_t; +typedef enum sndrv_pcm_hw_param snd_pcm_hw_param_t; +typedef struct sndrv_pcm_hw_params snd_pcm_hw_params_t; +typedef enum sndrv_pcm_start snd_pcm_start_t; +typedef enum sndrv_pcm_xrun snd_pcm_xrun_t; +typedef enum sndrv_pcm_tstamp snd_pcm_tstamp_t; +typedef struct sndrv_pcm_sw_params snd_pcm_sw_params_t; +typedef struct sndrv_pcm_channel_info snd_pcm_channel_info_t; +typedef struct sndrv_pcm_status snd_pcm_status_t; +typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t; +typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t; + +#define _snd_pcm_substream_chip(substream) ((substream)->pcm->private_data) +#define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO) +#define _snd_pcm_chip(pcm) ((pcm)->private_data) +#define snd_pcm_chip(pcm) snd_magic_cast1(chip_t, _snd_pcm_chip(pcm), return -ENXIO) + +typedef struct _snd_pcm_file snd_pcm_file_t; +typedef struct _snd_pcm_runtime snd_pcm_runtime_t; + +#ifdef CONFIG_SND_OSSEMUL +#include "pcm_oss.h" +#endif + +/* + * Hardware (lowlevel) section + */ + +typedef struct _snd_pcm_hardware { + unsigned int info; /* SNDRV_PCM_INFO_* */ + unsigned int formats; /* SNDRV_PCM_FMTBIT_* */ + unsigned int rates; /* SNDRV_PCM_RATE_* */ + unsigned int rate_min; /* min rate */ + unsigned int rate_max; /* max rate */ + unsigned int channels_min; /* min channels */ + unsigned int channels_max; /* max channels */ + size_t buffer_bytes_max; /* max buffer size */ + size_t period_bytes_min; /* min period size */ + size_t period_bytes_max; /* max period size */ + unsigned int periods_min; /* min # of periods */ + unsigned int periods_max; /* max # of periods */ + size_t fifo_size; /* fifo size in bytes */ +} snd_pcm_hardware_t; + +typedef struct _snd_pcm_ops { + int (*open)(snd_pcm_substream_t *substream); + int (*close)(snd_pcm_substream_t *substream); + int (*ioctl)(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg); + int (*hw_params)(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * params); + int (*hw_free)(snd_pcm_substream_t *substream); + int (*prepare)(snd_pcm_substream_t * substream); + int (*trigger)(snd_pcm_substream_t * substream, int cmd); + snd_pcm_uframes_t (*pointer)(snd_pcm_substream_t * substream); + int (*copy)(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, + void *buf, snd_pcm_uframes_t count); + int (*silence)(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count); +} snd_pcm_ops_t; + +/* + * + */ + +#define SNDRV_PCM_DEVICES 8 + +#define SNDRV_PCM_IOCTL1_FALSE ((void *)0) +#define SNDRV_PCM_IOCTL1_TRUE ((void *)1) + +#define SNDRV_PCM_IOCTL1_RESET 0 +#define SNDRV_PCM_IOCTL1_INFO 1 +#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 +#define SNDRV_PCM_IOCTL1_GSTATE 3 + +#define SNDRV_PCM_TRIGGER_STOP 0 +#define SNDRV_PCM_TRIGGER_START 1 +#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 3 +#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 4 +#define SNDRV_PCM_TRIGGER_SUSPEND 5 +#define SNDRV_PCM_TRIGGER_RESUME 6 + +#define SNDRV_PCM_DMA_TYPE_CONTINUOUS 0 /* continuous no-DMA memory */ +#define SNDRV_PCM_DMA_TYPE_ISA 1 /* ISA continuous */ +#define SNDRV_PCM_DMA_TYPE_PCI 2 /* PCI continuous */ + +/* If you change this don't forget to changed snd_pcm_rates table in pcm_lib.c */ +#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ +#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */ +#define SNDRV_PCM_RATE_11025 (1<<2) /* 11025Hz */ +#define SNDRV_PCM_RATE_16000 (1<<3) /* 16000Hz */ +#define SNDRV_PCM_RATE_22050 (1<<4) /* 22050Hz */ +#define SNDRV_PCM_RATE_32000 (1<<5) /* 32000Hz */ +#define SNDRV_PCM_RATE_44100 (1<<6) /* 44100Hz */ +#define SNDRV_PCM_RATE_48000 (1<<7) /* 48000Hz */ +#define SNDRV_PCM_RATE_64000 (1<<8) /* 64000Hz */ +#define SNDRV_PCM_RATE_88200 (1<<9) /* 88200Hz */ +#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ +#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ +#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ + +#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */ +#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ + +#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\ + SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\ + SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100) +#define SNDRV_PCM_RATE_8000_48000 (SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000) +#define SNDRV_PCM_RATE_8000_96000 (SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\ + SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) +#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ + SNDRV_PCM_RATE_192000) +#define SNDRV_PCM_FMTBIT_S8 (1 << SNDRV_PCM_FORMAT_S8) +#define SNDRV_PCM_FMTBIT_U8 (1 << SNDRV_PCM_FORMAT_U8) +#define SNDRV_PCM_FMTBIT_S16_LE (1 << SNDRV_PCM_FORMAT_S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE (1 << SNDRV_PCM_FORMAT_S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE (1 << SNDRV_PCM_FORMAT_U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE (1 << SNDRV_PCM_FORMAT_U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE (1 << SNDRV_PCM_FORMAT_S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE (1 << SNDRV_PCM_FORMAT_S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE (1 << SNDRV_PCM_FORMAT_U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE (1 << SNDRV_PCM_FORMAT_U24_BE) +#define SNDRV_PCM_FMTBIT_S32_LE (1 << SNDRV_PCM_FORMAT_S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE (1 << SNDRV_PCM_FORMAT_S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE (1 << SNDRV_PCM_FORMAT_U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE (1 << SNDRV_PCM_FORMAT_U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE (1 << SNDRV_PCM_FORMAT_FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE (1 << SNDRV_PCM_FORMAT_FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1 << SNDRV_PCM_FORMAT_FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1 << SNDRV_PCM_FORMAT_FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1 << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1 << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW (1 << SNDRV_PCM_FORMAT_MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW (1 << SNDRV_PCM_FORMAT_A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1 << SNDRV_PCM_FORMAT_IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG (1 << SNDRV_PCM_FORMAT_MPEG) +#define SNDRV_PCM_FMTBIT_GSM (1 << SNDRV_PCM_FORMAT_GSM) +#define SNDRV_PCM_FMTBIT_SPECIAL (1 << SNDRV_PCM_FORMAT_SPECIAL) + +#ifdef SNDRV_LITTLE_ENDIAN +#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE +#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_LE +#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_LE +#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_LE +#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_LE +#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_LE +#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_LE +#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_LE +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE +#endif +#ifdef SNDRV_BIG_ENDIAN +#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_BE +#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_BE +#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_BE +#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_BE +#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_BE +#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_BE +#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_BE +#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_BE +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE +#endif + +struct _snd_pcm_file { + snd_pcm_substream_t * substream; + struct _snd_pcm_file * next; +}; + +typedef struct _snd_pcm_hw_rule snd_pcm_hw_rule_t; + +typedef int (*snd_pcm_hw_rule_func_t)(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule); + +struct _snd_pcm_hw_rule { + unsigned int cond; + snd_pcm_hw_rule_func_t func; + int var; + int deps[4]; + void *private; +}; + +typedef struct _snd_pcm_hw_constraints { + unsigned int masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + snd_interval_t intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + unsigned int rules_num; + unsigned int rules_all; + snd_pcm_hw_rule_t *rules; +} snd_pcm_hw_constraints_t; + +static inline unsigned int *constrs_mask(snd_pcm_hw_constraints_t *constrs, + snd_pcm_hw_param_t var) +{ + return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; +} + +static inline snd_interval_t *constrs_interval(snd_pcm_hw_constraints_t *constrs, + snd_pcm_hw_param_t var) +{ + return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; +} + +typedef struct { + unsigned int num; + unsigned int den_min, den_max, den_step; +} ratnum_t; + +typedef struct { + unsigned int num_min, num_max, num_step; + unsigned int den; +} ratden_t; + +typedef struct { + int nrats; + ratnum_t *rats; +} snd_pcm_hw_constraint_ratnums_t; + +typedef struct { + int nrats; + ratden_t *rats; +} snd_pcm_hw_constraint_ratdens_t; + +typedef struct { + unsigned int count; + unsigned int *list; + unsigned int mask; +} snd_pcm_hw_constraint_list_t; + +struct _snd_pcm_runtime { + /* -- Status -- */ + snd_pcm_substream_t *trigger_master; + snd_timestamp_t trigger_tstamp; /* trigger timestamp */ + int overrange; + snd_pcm_uframes_t avail_max; + snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ + snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ + + /* -- HW params -- */ + snd_pcm_access_t access; /* access mode */ + snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ + snd_pcm_subformat_t subformat; /* subformat */ + unsigned int rate; /* rate in Hz */ + unsigned int channels; /* channels */ + snd_pcm_uframes_t period_size; /* period size */ + unsigned int periods; /* periods */ + snd_pcm_uframes_t buffer_size; /* buffer size */ + unsigned int tick_time; /* tick time */ + snd_pcm_uframes_t min_align; /* Min alignment for the format */ + size_t byte_align; + unsigned int frame_bits; + unsigned int sample_bits; + unsigned int info; + unsigned int rate_num; + unsigned int rate_den; + + /* -- SW params -- */ + snd_pcm_tstamp_t tstamp_mode; /* mmap timestamp is updated */ + unsigned int period_step; + unsigned int sleep_min; /* min ticks to sleep */ + snd_pcm_uframes_t xfer_align; /* xfer size need to be a multiple */ + snd_pcm_uframes_t start_threshold; + snd_pcm_uframes_t stop_threshold; + snd_pcm_uframes_t silence_threshold; /* Silence filling happens when + noise is nearest than this */ + snd_pcm_uframes_t silence_size; /* Silence filling size */ + snd_pcm_uframes_t boundary; /* pointers wrap point */ + + snd_pcm_uframes_t silenced_start; + snd_pcm_uframes_t silenced_size; + + snd_pcm_sync_id_t sync; /* hardware synchronization ID */ + + /* -- mmap -- */ + volatile snd_pcm_mmap_status_t *status; + volatile snd_pcm_mmap_control_t *control; + atomic_t mmap_count; + + /* -- locking / scheduling -- */ + spinlock_t lock; + wait_queue_head_t sleep; + struct timer_list tick_timer; + struct fasync_struct *fasync; + + /* -- private section -- */ + void *private_data; + void (*private_free)(snd_pcm_runtime_t *runtime); + + /* -- hardware description -- */ + snd_pcm_hardware_t hw; + snd_pcm_hw_constraints_t hw_constraints; + + /* -- interrupt callbacks -- */ + void (*transfer_ack_begin)(snd_pcm_substream_t *substream); + void (*transfer_ack_end)(snd_pcm_substream_t *substream); + + /* -- timer -- */ + unsigned int timer_resolution; /* timer resolution */ + + /* -- DMA -- */ + unsigned char *dma_area; /* DMA area */ + dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ + unsigned long dma_bytes; /* size of DMA area */ + +#ifdef CONFIG_SND_OSSEMUL + /* -- OSS things -- */ + snd_pcm_oss_runtime_t oss; +#endif +}; + +struct _snd_pcm_substream { + snd_pcm_t *pcm; + snd_pcm_str_t *pstr; + int number; + char name[32]; /* substream name */ + int stream; /* stream (direction) */ + size_t buffer_bytes_max; /* limit ring buffer size */ + int dma_type; + void *dma_area; + dma_addr_t dma_addr; + size_t dma_bytes; + size_t dma_max; + void *dma_private; + /* -- hardware operations -- */ + snd_pcm_ops_t *ops; + /* -- runtime information -- */ + snd_pcm_runtime_t *runtime; + /* -- timer section -- */ + snd_timer_t *timer; /* timer */ + int timer_running; /* time is running */ + spinlock_t timer_lock; + /* -- next substream -- */ + snd_pcm_substream_t *next; + /* -- linked substreams -- */ + snd_pcm_substream_t *link_next; + snd_pcm_substream_t *link_prev; + snd_pcm_file_t *file; + struct file *ffile; +#ifdef CONFIG_SND_OSSEMUL + /* -- OSS things -- */ + snd_pcm_oss_substream_t oss; +#endif + snd_info_entry_t *proc_root; + snd_info_entry_t *proc_info_entry; + snd_info_entry_t *proc_hw_params_entry; + snd_info_entry_t *proc_sw_params_entry; + snd_info_entry_t *proc_status_entry; + snd_info_entry_t *proc_prealloc_entry; +}; + +#ifdef CONFIG_SND_OSSEMUL +#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL || ((substream)->oss.file != NULL)) +#else +#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL) +#endif + + +struct _snd_pcm_str { + int stream; /* stream (direction) */ + snd_pcm_t *pcm; + /* -- substreams -- */ + unsigned int substream_count; + unsigned int substream_opened; + snd_pcm_substream_t *substream; +#ifdef CONFIG_SND_OSSEMUL + /* -- OSS things -- */ + snd_pcm_oss_stream_t oss; +#endif + snd_pcm_file_t *files; + snd_minor_t *reg; + snd_info_entry_t *proc_root; + snd_info_entry_t *proc_info_entry; +}; + +struct _snd_pcm { + snd_card_t *card; + unsigned int device; /* device number */ + unsigned int info_flags; + unsigned short dev_class; + unsigned short dev_subclass; + char id[64]; + char name[80]; + snd_pcm_str_t streams[2]; + struct semaphore open_mutex; + wait_queue_head_t open_wait; + void *private_data; + void (*private_free) (snd_pcm_t *pcm); +#ifdef CONFIG_SND_OSSEMUL + snd_pcm_oss_t oss; +#endif +}; + +typedef struct _snd_pcm_notify { + int (*n_register) (unsigned short minor, snd_pcm_t * pcm); + int (*n_unregister) (unsigned short minor, snd_pcm_t * pcm); + struct list_head list; +} snd_pcm_notify_t; + +/* + * Registering + */ + +extern snd_pcm_t *snd_pcm_devices[]; +extern snd_minor_t snd_pcm_reg[2]; + +void snd_pcm_lock(int unlock); + +int snd_pcm_new(snd_card_t * card, char *id, int device, + int playback_count, int capture_count, + snd_pcm_t **rpcm); + +int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree); + +/* + * Native I/O + */ + +int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info); +int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t *info); +int snd_pcm_status(snd_pcm_substream_t * substream, snd_pcm_status_t *status); +int snd_pcm_prepare(snd_pcm_substream_t *substream); +int snd_pcm_start(snd_pcm_substream_t *substream); +int snd_pcm_stop(snd_pcm_substream_t *substream, int status); +#ifdef CONFIG_PM +int snd_pcm_suspend(snd_pcm_substream_t *substream); +int snd_pcm_suspend_all(snd_pcm_t *pcm); +#endif +int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); +int snd_pcm_open(struct inode *inode, struct file *file); +int snd_pcm_release(struct inode *inode, struct file *file); +unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait); +unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait); +int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, snd_pcm_substream_t **rsubstream); +void snd_pcm_release_substream(snd_pcm_substream_t *substream); +void snd_pcm_vma_notify_data(void *client, void *data); +int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, struct vm_area_struct *area); + +#if BITS_PER_LONG >= 64 + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + *rem = *n % div; + *n /= div; +} + +#elif defined(i386) + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + u_int32_t low, high; + low = *n & 0xffffffff; + high = *n >> 32; + if (high) { + u_int32_t high1 = high % div; + high /= div; + asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1)); + *n = (u_int64_t)high << 32 | low; + } else { + *n = low / div; + *rem = low % div; + } +} +#else + +static inline void divl(u_int32_t high, u_int32_t low, + u_int32_t div, + u_int32_t *q, u_int32_t *r) +{ + u_int64_t n = (u_int64_t)high << 32 | low; + u_int64_t d = (u_int64_t)div << 31; + u_int32_t q1 = 0; + int c = 32; + while (n > 0xffffffffU) { + q1 <<= 1; + if (n > d) { + n -= d; + q1 |= 1; + } + d >>= 1; + c--; + } + q1 <<= c; + if (n) { + low = n; + *q = q1 | (low / div); + *r = low % div; + } else { + *r = 0; + *q = q1; + } + return; +} + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + u_int32_t low, high; + low = *n & 0xffffffff; + high = *n >> 32; + if (high) { + u_int32_t high1 = high % div; + u_int32_t low1 = low; + high /= div; + divl(high1, low1, div, &low, rem); + *n = (u_int64_t)high << 32 | low; + } else { + *n = low / div; + *rem = low % div; + } +} +#endif + +/* + * PCM library + */ + +static inline int snd_pcm_running(snd_pcm_substream_t *substream) +{ + return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING || + (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); +} + +static inline ssize_t bytes_to_samples(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * 8 / runtime->sample_bits; +} + +static inline snd_pcm_sframes_t bytes_to_frames(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * 8 / runtime->frame_bits; +} + +static inline ssize_t samples_to_bytes(snd_pcm_runtime_t *runtime, ssize_t size) +{ + return size * runtime->sample_bits / 8; +} + +static inline ssize_t frames_to_bytes(snd_pcm_runtime_t *runtime, snd_pcm_sframes_t size) +{ + return size * runtime->frame_bits / 8; +} + +static inline int frame_aligned(snd_pcm_runtime_t *runtime, ssize_t bytes) +{ + return bytes % runtime->byte_align == 0; +} + +static inline size_t snd_pcm_lib_buffer_bytes(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return frames_to_bytes(runtime, runtime->buffer_size); +} + +static inline size_t snd_pcm_lib_period_bytes(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return frames_to_bytes(runtime, runtime->period_size); +} + +/* + * result is: 0 ... (boundary - 1) + */ +static inline snd_pcm_uframes_t snd_pcm_playback_avail(snd_pcm_runtime_t *runtime) +{ + snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr; + if (avail < 0) + avail += runtime->boundary; + else if ((snd_pcm_uframes_t) avail >= runtime->boundary) + avail -= runtime->boundary; + return avail; +} + +/* + * result is: 0 ... (boundary - 1) + */ +static inline snd_pcm_uframes_t snd_pcm_capture_avail(snd_pcm_runtime_t *runtime) +{ + snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr; + if (avail < 0) + avail += runtime->boundary; + return avail; +} + +static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(snd_pcm_runtime_t *runtime) +{ + return runtime->buffer_size - snd_pcm_playback_avail(runtime); +} + +static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(snd_pcm_runtime_t *runtime) +{ + return runtime->buffer_size - snd_pcm_capture_avail(runtime); +} + +static inline void snd_pcm_trigger_done(snd_pcm_substream_t *substream, + snd_pcm_substream_t *master) +{ + substream->runtime->trigger_master = master; +} + +static inline int hw_is_mask(int var) +{ + return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK && + var <= SNDRV_PCM_HW_PARAM_LAST_MASK; +} + +static inline int hw_is_interval(int var) +{ + return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && + var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; +} + +typedef unsigned int snd_mask_t; +#define SND_MASK_MAX 32 + +static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (snd_mask_t*)¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; +} + +static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; +} + +static inline const snd_mask_t *hw_param_mask_c(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (const snd_mask_t *)hw_param_mask((snd_pcm_hw_params_t*) params, var); +} + +static inline const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var); +} + +#define params_access(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) - 1) +#define params_format(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) - 1) +#define params_subformat(p) (ffs(*hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) - 1) +#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min +#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min +#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min +#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8) +#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min +#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min +#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min +#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min + + +int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v); +void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c); +void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c); +void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, + unsigned int k, snd_interval_t *c); +void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, + const snd_interval_t *b, snd_interval_t *c); +int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask); +int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step); +int snd_interval_ratnum(snd_interval_t *i, + unsigned int rats_count, ratnum_t *rats, + unsigned int *nump, unsigned int *denp); +int snd_interval_ratden(snd_interval_t *i, + unsigned int rats_count, ratden_t *rats, + unsigned int *nump, unsigned int *denp); + +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params); +void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var); +int snd_pcm_hw_param_min(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_param_max(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_param_setinteger(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var); +int snd_pcm_hw_param_first(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_last(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_near(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, + unsigned int val, int *dir); +int snd_pcm_hw_params_choose(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); + +int snd_pcm_hw_refine(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params); + +int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream); +int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream); + +int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int mask); +int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max); +int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var); +int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_list_t *l); +int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratnums_t *r); +int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratdens_t *r); +int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits); +int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step); +int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, + unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...); + +int snd_pcm_format_signed(snd_pcm_format_t format); +int snd_pcm_format_unsigned(snd_pcm_format_t format); +int snd_pcm_format_linear(snd_pcm_format_t format); +int snd_pcm_format_little_endian(snd_pcm_format_t format); +int snd_pcm_format_big_endian(snd_pcm_format_t format); +int snd_pcm_format_width(snd_pcm_format_t format); /* in bits */ +int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ +u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format); +int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); +snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian); +ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); + +void snd_pcm_set_ops(snd_pcm_t * pcm, int direction, snd_pcm_ops_t *ops); +void snd_pcm_set_sync(snd_pcm_substream_t * substream); +int snd_pcm_lib_interleave_len(snd_pcm_substream_t *substream); +int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); +int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream); +int snd_pcm_playback_xrun_check(snd_pcm_substream_t *substream); +int snd_pcm_capture_xrun_check(snd_pcm_substream_t *substream); +int snd_pcm_playback_xrun_asap(snd_pcm_substream_t *substream); +int snd_pcm_capture_xrun_asap(snd_pcm_substream_t *substream); +void snd_pcm_playback_silence(snd_pcm_substream_t *substream); +int snd_pcm_playback_ready(snd_pcm_substream_t *substream); +int snd_pcm_capture_ready(snd_pcm_substream_t *substream); +long snd_pcm_playback_ready_jiffies(snd_pcm_substream_t *substream); +long snd_pcm_capture_ready_jiffies(snd_pcm_substream_t *substream); +int snd_pcm_playback_data(snd_pcm_substream_t *substream); +int snd_pcm_playback_empty(snd_pcm_substream_t *substream); +int snd_pcm_capture_empty(snd_pcm_substream_t *substream); +void snd_pcm_tick_prepare(snd_pcm_substream_t *substream); +void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks); +void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream); +void snd_pcm_period_elapsed(snd_pcm_substream_t *substream); +snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, + const void *buf, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, + void *buf, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, + void **bufs, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, + void **bufs, snd_pcm_uframes_t frames); + +/* + * Timer interface + */ + +void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream); +void snd_pcm_timer_init(snd_pcm_substream_t * substream); +void snd_pcm_timer_done(snd_pcm_substream_t * substream); + +/* + * Memory + */ + +int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream); +int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm); +int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, + size_t size, size_t max, + unsigned int flags); +int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max, + unsigned int flags); +int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size); +int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream); + +#ifdef CONFIG_ISA +int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max); +#endif +#ifdef CONFIG_PCI +int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max); +int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, + size_t max); +#endif + +static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) +{ + *max = dma < 4 ? 64 * 1024 : 128 * 1024; +} + +/* + * Misc + */ + +#define SNDRV_PCM_DEFAULT_CON_SPDIF (IEC958_AES0_CON_EMPHASIS_NONE|\ + (IEC958_AES1_CON_ORIGINAL<<8)|\ + (IEC958_AES1_CON_PCM_CODER<<8)|\ + (IEC958_AES3_CON_FS_48000<<24)) + +#endif /* __SOUND_PCM_H */ diff -Nru linux/include/sound/pcm_oss.h linux-2.4.19-pre5-mjc/include/sound/pcm_oss.h --- linux/include/sound/pcm_oss.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/pcm_oss.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,82 @@ +#ifndef __SOUND_PCM_OSS_H +#define __SOUND_PCM_OSS_H + +/* + * Digital Audio (PCM) - OSS compatibility abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +typedef struct _snd_pcm_plugin snd_pcm_plugin_t; +typedef struct _snd_pcm_oss_setup snd_pcm_oss_setup_t; + +struct _snd_pcm_oss_setup { + char *task_name; + unsigned int disable:1, + direct:1, + block:1, + nonblock:1; + unsigned int periods; + unsigned int period_size; + snd_pcm_oss_setup_t *next; +}; + +typedef struct _snd_pcm_oss_runtime { + int params: 1, /* format/parameter change */ + prepare: 1, /* need to prepare the operation */ + trigger: 1, /* trigger flag */ + sync_trigger: 1; /* sync trigger flag */ + int rate; /* requested rate */ + int format; /* requested OSS format */ + unsigned int channels; /* requested channels */ + unsigned int fragshift; + unsigned int maxfrags; + unsigned int subdivision; /* requested subdivision */ + size_t period_bytes; /* requested period size */ + unsigned int periods; + size_t buffer_bytes; /* requested period size */ + size_t bytes; /* total # bytes processed */ + size_t mmap_bytes; + char *buffer; /* vmallocated period */ + size_t buffer_used; /* used length from buffer */ + snd_pcm_plugin_t *plugin_first; + snd_pcm_plugin_t *plugin_last; + unsigned int prev_hw_ptr_interrupt; +} snd_pcm_oss_runtime_t; + +typedef struct _snd_pcm_oss_file { + snd_pcm_substream_t *streams[2]; +} snd_pcm_oss_file_t; + +typedef struct _snd_pcm_oss_substream { + int oss: 1; /* oss mode */ + snd_pcm_oss_setup_t *setup; /* active setup */ + snd_pcm_oss_file_t *file; +} snd_pcm_oss_substream_t; + +typedef struct _snd_pcm_oss_stream { + snd_pcm_oss_setup_t *setup_list; /* setup list */ + struct semaphore setup_mutex; + snd_info_entry_t *proc_entry; +} snd_pcm_oss_stream_t; + +typedef struct _snd_pcm_oss { + int reg; +} snd_pcm_oss_t; + +#endif /* __SOUND_PCM_OSS_H */ diff -Nru linux/include/sound/pcm_params.h linux-2.4.19-pre5-mjc/include/sound/pcm_params.h --- linux/include/sound/pcm_params.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/pcm_params.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,334 @@ +#ifndef __SOUND_PCM_PARAMS_H +#define __SOUND_PCM_PARAMS_H + +/* + * PCM params helpers + * Copyright (c) by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +extern int snd_pcm_hw_param_mask(snd_pcm_substream_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val); +extern unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +extern unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir); +extern int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir); +extern int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var); +extern int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir); + +/* To share the same code we have alsa-lib */ +#define snd_mask_bits(mask) (*(mask)) +#define INLINE static inline +#define assert(a) + +INLINE unsigned int ld2(u_int32_t v) +{ + unsigned r = 0; + + if (v >= 0x10000) { + v >>= 16; + r += 16; + } + if (v >= 0x100) { + v >>= 8; + r += 8; + } + if (v >= 0x10) { + v >>= 4; + r += 4; + } + if (v >= 4) { + v >>= 2; + r += 2; + } + if (v >= 2) + r++; + return r; +} + +INLINE size_t snd_mask_sizeof(void) +{ + return sizeof(snd_mask_t); +} + +INLINE void snd_mask_none(snd_mask_t *mask) +{ + snd_mask_bits(mask) = 0; +} + +INLINE void snd_mask_any(snd_mask_t *mask) +{ + snd_mask_bits(mask) = ~0U; +} + +INLINE void snd_mask_load(snd_mask_t *mask, unsigned int msk) +{ + snd_mask_bits(mask) = msk; +} + +INLINE int snd_mask_empty(const snd_mask_t *mask) +{ + return snd_mask_bits(mask) == 0; +} + +INLINE unsigned int snd_mask_min(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return ffs(snd_mask_bits(mask)) - 1; +} + +INLINE unsigned int snd_mask_max(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return ld2(snd_mask_bits(mask)); +} + +INLINE void snd_mask_set(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + snd_mask_bits(mask) |= (1U << val); +} + +INLINE void snd_mask_reset(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + snd_mask_bits(mask) &= ~(1U << val); +} + +INLINE void snd_mask_set_range(snd_mask_t *mask, unsigned int from, unsigned int to) +{ + assert(to <= SND_MASK_MAX && from <= to); + snd_mask_bits(mask) |= ((1U << (from - to + 1)) - 1) << from; +} + +INLINE void snd_mask_reset_range(snd_mask_t *mask, unsigned int from, unsigned int to) +{ + assert(to <= SND_MASK_MAX && from <= to); + snd_mask_bits(mask) &= ~(((1U << (from - to + 1)) - 1) << from); +} + +INLINE void snd_mask_leave(snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + snd_mask_bits(mask) &= 1U << val; +} + +INLINE void snd_mask_intersect(snd_mask_t *mask, const snd_mask_t *v) +{ + snd_mask_bits(mask) &= snd_mask_bits(v); +} + +INLINE int snd_mask_eq(const snd_mask_t *mask, const snd_mask_t *v) +{ + return snd_mask_bits(mask) == snd_mask_bits(v); +} + +INLINE void snd_mask_copy(snd_mask_t *mask, const snd_mask_t *v) +{ + snd_mask_bits(mask) = snd_mask_bits(v); +} + +INLINE int snd_mask_test(const snd_mask_t *mask, unsigned int val) +{ + assert(val <= SND_MASK_MAX); + return snd_mask_bits(mask) & (1U << val); +} + +INLINE int snd_mask_single(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return !(snd_mask_bits(mask) & (snd_mask_bits(mask) - 1)); +} + +INLINE int snd_mask_refine(snd_mask_t *mask, const snd_mask_t *v) +{ + snd_mask_t old; + assert(!snd_mask_empty(mask)); + snd_mask_copy(&old, mask); + snd_mask_intersect(mask, v); + if (snd_mask_empty(mask)) + return -EINVAL; + return !snd_mask_eq(mask, &old); +} + +INLINE int snd_mask_refine_first(snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_single(mask)) + return 0; + snd_mask_leave(mask, snd_mask_min(mask)); + return 1; +} + +INLINE int snd_mask_refine_last(snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_single(mask)) + return 0; + snd_mask_leave(mask, snd_mask_max(mask)); + return 1; +} + +INLINE int snd_mask_refine_min(snd_mask_t *mask, unsigned int val) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_min(mask) >= val) + return 0; + snd_mask_reset_range(mask, 0, val - 1); + if (snd_mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int snd_mask_refine_max(snd_mask_t *mask, unsigned int val) +{ + assert(!snd_mask_empty(mask)); + if (snd_mask_max(mask) <= val) + return 0; + snd_mask_reset_range(mask, val + 1, SND_MASK_MAX); + if (snd_mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int snd_mask_refine_set(snd_mask_t *mask, unsigned int val) +{ + int changed; + assert(!snd_mask_empty(mask)); + changed = !snd_mask_single(mask); + snd_mask_leave(mask, val); + if (snd_mask_empty(mask)) + return -EINVAL; + return changed; +} + +INLINE int snd_mask_value(const snd_mask_t *mask) +{ + assert(!snd_mask_empty(mask)); + return snd_mask_min(mask); +} + +INLINE void snd_interval_any(snd_interval_t *i) +{ + i->min = 0; + i->openmin = 0; + i->max = UINT_MAX; + i->openmax = 0; + i->integer = 0; + i->empty = 0; +} + +INLINE void snd_interval_none(snd_interval_t *i) +{ + i->empty = 1; +} + +INLINE int snd_interval_checkempty(const snd_interval_t *i) +{ + return (i->min > i->max || + (i->min == i->max && (i->openmin || i->openmax))); +} + +INLINE int snd_interval_empty(const snd_interval_t *i) +{ + return i->empty; +} + +INLINE int snd_interval_single(const snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + return (i->min == i->max || + (i->min + 1 == i->max && i->openmax)); +} + +INLINE int snd_interval_value(const snd_interval_t *i) +{ + assert(snd_interval_single(i)); + return i->min; +} + +INLINE int snd_interval_min(const snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + return i->min; +} + +INLINE int snd_interval_max(const snd_interval_t *i) +{ + unsigned int v; + assert(!snd_interval_empty(i)); + v = i->max; + if (i->openmax) + v--; + return v; +} + +INLINE int snd_interval_test(const snd_interval_t *i, unsigned int val) +{ + return !((i->min > val || (i->min == val && i->openmin) || + i->max < val || (i->max == val && i->openmax))); +} + +INLINE void snd_interval_copy(snd_interval_t *d, const snd_interval_t *s) +{ + *d = *s; +} + +INLINE int snd_interval_setinteger(snd_interval_t *i) +{ + if (i->integer) + return 0; + if (i->openmin && i->openmax && i->min == i->max) + return -EINVAL; + i->integer = 1; + return 1; +} + +INLINE int snd_interval_eq(const snd_interval_t *i1, const snd_interval_t *i2) +{ + if (i1->empty) + return i2->empty; + if (i2->empty) + return i1->empty; + return i1->min == i2->min && i1->openmin == i2->openmin && + i1->max == i2->max && i1->openmax == i2->openmax; +} + +static inline unsigned int add(unsigned int a, unsigned int b) +{ + if (a >= UINT_MAX - b) + return UINT_MAX; + return a + b; +} + +static inline unsigned int sub(unsigned int a, unsigned int b) +{ + if (a > b) + return a - b; + return 0; +} + +#undef INLINE +#undef assert + +#endif /* __SOUND_PCM_PARAMS_H */ + diff -Nru linux/include/sound/rawmidi.h linux-2.4.19-pre5-mjc/include/sound/rawmidi.h --- linux/include/sound/rawmidi.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/rawmidi.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,181 @@ +#ifndef __SOUND_RAWMIDI_H +#define __SOUND_RAWMIDI_H + +/* + * Abstract layer for MIDI v1.0 stream + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#include "seq_device.h" +#endif + +/* + * Raw MIDI interface + */ + +typedef enum sndrv_rawmidi_stream snd_rawmidi_stream_t; +typedef struct sndrv_rawmidi_info snd_rawmidi_info_t; +typedef struct sndrv_rawmidi_params snd_rawmidi_params_t; +typedef struct sndrv_rawmidi_status snd_rawmidi_status_t; + +#define SNDRV_RAWMIDI_DEVICES 4 + +#define SNDRV_RAWMIDI_LFLG_OUTPUT (1<<0) +#define SNDRV_RAWMIDI_LFLG_INPUT (1<<1) +#define SNDRV_RAWMIDI_LFLG_OPEN (3<<0) +#define SNDRV_RAWMIDI_LFLG_APPEND (1<<2) + +typedef struct _snd_rawmidi_runtime snd_rawmidi_runtime_t; +typedef struct _snd_rawmidi_substream snd_rawmidi_substream_t; +typedef struct _snd_rawmidi_str snd_rawmidi_str_t; + +typedef struct _snd_rawmidi_ops { + int (*open) (snd_rawmidi_substream_t * substream); + int (*close) (snd_rawmidi_substream_t * substream); + void (*trigger) (snd_rawmidi_substream_t * substream, int up); + void (*drain) (snd_rawmidi_substream_t * substream); +} snd_rawmidi_ops_t; + +typedef struct _snd_rawmidi_global_ops { + int (*dev_register) (snd_rawmidi_t * rmidi); + int (*dev_unregister) (snd_rawmidi_t * rmidi); +} snd_rawmidi_global_ops_t; + +struct _snd_rawmidi_runtime { + unsigned int trigger: 1, /* transfer is running */ + drain: 1, /* drain stage */ + oss: 1; /* OSS compatible mode */ + /* midi stream buffer */ + unsigned char *buffer; /* buffer for MIDI data */ + size_t buffer_size; /* size of buffer */ + size_t appl_ptr; /* application pointer */ + size_t hw_ptr; /* hardware pointer */ + size_t avail_min; /* min avail for wakeup */ + size_t avail; /* max used buffer for wakeup */ + size_t xruns; /* over/underruns counter */ + /* misc */ + spinlock_t lock; + wait_queue_head_t sleep; + /* event handler (room [output] or new bytes [input]) */ + void (*event)(snd_rawmidi_substream_t *substream); + /* private data */ + void *private_data; + void (*private_free)(snd_rawmidi_substream_t *substream); +}; + +struct _snd_rawmidi_substream { + struct list_head list; /* list of all substream for given stream */ + int stream; /* direction */ + int number; /* substream number */ + unsigned int opened: 1, /* open flag */ + append: 1, /* append flag (merge more streams) */ + active_sensing: 1; /* send active sensing when close */ + int use_count; /* use counter (for output) */ + size_t bytes; + snd_rawmidi_t *rmidi; + snd_rawmidi_str_t *pstr; + char name[32]; + snd_rawmidi_runtime_t *runtime; + /* hardware layer */ + snd_rawmidi_ops_t *ops; +}; + +typedef struct _snd_rawmidi_file { + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; +} snd_rawmidi_file_t; + +struct _snd_rawmidi_str { + unsigned int substream_count; + unsigned int substream_opened; + struct list_head substreams; +}; + +struct _snd_rawmidi { + snd_card_t *card; + + unsigned int device; /* device number */ + unsigned int info_flags; /* SNDRV_RAWMIDI_INFO_XXXX */ + char id[64]; + char name[80]; + +#ifdef CONFIG_SND_OSSEMUL + int ossreg; +#endif + + snd_rawmidi_global_ops_t *ops; + + snd_rawmidi_str_t streams[2]; + + void *private_data; + void (*private_free) (snd_rawmidi_t *rmidi); + + struct semaphore open_mutex; + wait_queue_head_t open_wait; + + snd_info_entry_t *dev; + snd_info_entry_t *proc_entry; + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + snd_seq_device_t *seq_dev; +#endif +}; + +/* main rawmidi functions */ + +int snd_rawmidi_new(snd_card_t * card, char *id, int device, + int output_count, int input_count, + snd_rawmidi_t ** rmidi); +void snd_rawmidi_set_ops(snd_rawmidi_t * rmidi, int stream, snd_rawmidi_ops_t * ops); + +/* control functions */ + +int snd_rawmidi_control_ioctl(snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, + unsigned long arg); + +/* callbacks */ + +void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream); +int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); +void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream); +int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream); +int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); +int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count); +int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count); + +/* main midi functions */ + +int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info); +int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, int mode, snd_rawmidi_file_t * rfile); +int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile); +int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params); +int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, snd_rawmidi_params_t * params); +int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream); +int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream); +int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream); +long snd_rawmidi_kernel_read(snd_rawmidi_substream_t * substream, unsigned char *buf, long count); +long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count); + +#endif /* __SOUND_RAWMIDI_H */ diff -Nru linux/include/sound/sb.h linux-2.4.19-pre5-mjc/include/sound/sb.h --- linux/include/sound/sb.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/sb.h Mon Apr 8 22:31:21 2002 @@ -0,0 +1,289 @@ +#ifndef __SOUND_SB_H +#define __SOUND_SB_H + +/* + * Header file for SoundBlaster cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include + +enum sb_hw_type { + SB_HW_AUTO, + SB_HW_10, + SB_HW_20, + SB_HW_201, + SB_HW_PRO, + SB_HW_16, + SB_HW_16CSP, /* SB16 with CSP chip */ + SB_HW_ALS100, /* Avance Logic ALS100 chip */ + SB_HW_ALS4000, /* Avance Logic ALS4000 chip */ +}; + +#define SB_OPEN_PCM 0x01 +#define SB_OPEN_MIDI_INPUT 0x02 +#define SB_OPEN_MIDI_OUTPUT 0x04 +#define SB_OPEN_MIDI_TRIGGER 0x08 + +#define SB_MODE_HALT 0x00 +#define SB_MODE_PLAYBACK_8 0x01 +#define SB_MODE_PLAYBACK_16 0x02 +#define SB_MODE_PLAYBACK (SB_MODE_PLAYBACK_8 | SB_MODE_PLAYBACK_16) +#define SB_MODE_CAPTURE_8 0x04 +#define SB_MODE_CAPTURE_16 0x08 +#define SB_MODE_CAPTURE (SB_MODE_CAPTURE_8 | SB_MODE_CAPTURE_16) + +#define SB_RATE_LOCK_PLAYBACK 0x10 +#define SB_RATE_LOCK_CAPTURE 0x20 +#define SB_RATE_LOCK (SB_RATE_LOCK_PLAYBACK | SB_RATE_LOCK_CAPTURE) + +#define SB_MPU_INPUT 1 + +struct _snd_sb { + unsigned long port; /* base port of DSP chip */ + struct resource *res_port; + unsigned long alt_port; /* alternate port (ALS4000) */ + struct resource *res_alt_port; + unsigned long mpu_port; /* MPU port for SB DSP 4.0+ */ + int irq; /* IRQ number of DSP chip */ + int dma8; /* 8-bit DMA */ + int dma16; /* 16-bit DMA */ + unsigned short version; /* version of DSP chip */ + enum sb_hw_type hardware; /* see to SB_HW_XXXX */ + + struct pci_dev *pci; /* ALS4000 */ + + unsigned int open; /* see to SB_OPEN_XXXX for sb8 */ + /* also SNDRV_SB_CSP_MODE_XXX for sb16_csp */ + unsigned int mode; /* current mode of stream */ + unsigned int force_mode16; /* force 16-bit mode of streams */ + unsigned int locked_rate; /* sb16 duplex */ + unsigned int playback_format; + unsigned int capture_format; + struct timer_list midi_timer; + unsigned int p_dma_size; + unsigned int p_period_size; + unsigned int c_dma_size; + unsigned int c_period_size; + + spinlock_t mixer_lock; + + char name[32]; + + void *csp; /* used only when CONFIG_SND_SB16_CSP is set */ + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_substream_input; + snd_rawmidi_substream_t *midi_substream_output; + + spinlock_t reg_lock; + spinlock_t open_lock; + spinlock_t midi_input_lock; + + snd_info_entry_t *proc_entry; +}; + +typedef struct _snd_sb sb_t; + +/* I/O ports */ + +#define SBP(chip, x) ((chip)->port + s_b_SB_##x) +#define SBP1(port, x) ((port) + s_b_SB_##x) + +#define s_b_SB_RESET 0x6 +#define s_b_SB_READ 0xa +#define s_b_SB_WRITE 0xc +#define s_b_SB_COMMAND 0xc +#define s_b_SB_STATUS 0xc +#define s_b_SB_DATA_AVAIL 0xe +#define s_b_SB_DATA_AVAIL_16 0xf +#define s_b_SB_MIXER_ADDR 0x4 +#define s_b_SB_MIXER_DATA 0x5 +#define s_b_SB_OPL3_LEFT 0x0 +#define s_b_SB_OPL3_RIGHT 0x2 +#define s_b_SB_OPL3_BOTH 0x8 + +#define SB_DSP_OUTPUT 0x14 +#define SB_DSP_INPUT 0x24 +#define SB_DSP_BLOCK_SIZE 0x48 +#define SB_DSP_HI_OUTPUT 0x91 +#define SB_DSP_HI_INPUT 0x99 +#define SB_DSP_LO_OUTPUT_AUTO 0x1c +#define SB_DSP_LO_INPUT_AUTO 0x2c +#define SB_DSP_HI_OUTPUT_AUTO 0x90 +#define SB_DSP_HI_INPUT_AUTO 0x98 +#define SB_DSP_IMMED_INT 0xf2 +#define SB_DSP_GET_VERSION 0xe1 +#define SB_DSP_SPEAKER_ON 0xd1 +#define SB_DSP_SPEAKER_OFF 0xd3 +#define SB_DSP_DMA8_OFF 0xd0 +#define SB_DSP_DMA8_ON 0xd4 +#define SB_DSP_DMA8_EXIT 0xda +#define SB_DSP_DMA16_OFF 0xd5 +#define SB_DSP_DMA16_ON 0xd6 +#define SB_DSP_DMA16_EXIT 0xd9 +#define SB_DSP_SAMPLE_RATE 0x40 +#define SB_DSP_SAMPLE_RATE_OUT 0x41 +#define SB_DSP_SAMPLE_RATE_IN 0x42 +#define SB_DSP_MONO_8BIT 0xa0 +#define SB_DSP_MONO_16BIT 0xa4 +#define SB_DSP_STEREO_8BIT 0xa8 +#define SB_DSP_STEREO_16BIT 0xac + +#define SB_DSP_MIDI_INPUT_IRQ 0x31 +#define SB_DSP_MIDI_OUTPUT 0x38 + +#define SB_DSP4_OUT8_AI 0xc6 +#define SB_DSP4_IN8_AI 0xce +#define SB_DSP4_OUT16_AI 0xb6 +#define SB_DSP4_IN16_AI 0xbe +#define SB_DSP4_MODE_UNS_MONO 0x00 +#define SB_DSP4_MODE_SIGN_MONO 0x10 +#define SB_DSP4_MODE_UNS_STEREO 0x20 +#define SB_DSP4_MODE_SIGN_STEREO 0x30 + +#define SB_DSP4_OUTPUT 0x3c +#define SB_DSP4_INPUT_LEFT 0x3d +#define SB_DSP4_INPUT_RIGHT 0x3e + +/* registers for SB 2.0 mixer */ +#define SB_DSP20_MASTER_DEV 0x02 +#define SB_DSP20_PCM_DEV 0x0A +#define SB_DSP20_CD_DEV 0x08 +#define SB_DSP20_FM_DEV 0x06 + +/* registers for SB PRO mixer */ +#define SB_DSP_MASTER_DEV 0x22 +#define SB_DSP_PCM_DEV 0x04 +#define SB_DSP_LINE_DEV 0x2e +#define SB_DSP_CD_DEV 0x28 +#define SB_DSP_FM_DEV 0x26 +#define SB_DSP_MIC_DEV 0x0a +#define SB_DSP_CAPTURE_SOURCE 0x0c +#define SB_DSP_CAPTURE_FILT 0x0c +#define SB_DSP_PLAYBACK_FILT 0x0e +#define SB_DSP_STEREO_SW 0x0e + +#define SB_DSP_MIXS_MIC0 0x00 /* same as MIC */ +#define SB_DSP_MIXS_CD 0x01 +#define SB_DSP_MIXS_MIC 0x02 +#define SB_DSP_MIXS_LINE 0x03 + +/* registers (only for left channel) for SB 16 mixer */ +#define SB_DSP4_MASTER_DEV 0x30 +#define SB_DSP4_BASS_DEV 0x46 +#define SB_DSP4_TREBLE_DEV 0x44 +#define SB_DSP4_SYNTH_DEV 0x34 +#define SB_DSP4_PCM_DEV 0x32 +#define SB_DSP4_SPEAKER_DEV 0x3b +#define SB_DSP4_LINE_DEV 0x38 +#define SB_DSP4_MIC_DEV 0x3a +#define SB_DSP4_OUTPUT_SW 0x3c +#define SB_DSP4_CD_DEV 0x36 +#define SB_DSP4_IGAIN_DEV 0x3f +#define SB_DSP4_OGAIN_DEV 0x41 +#define SB_DSP4_MIC_AGC 0x43 + +/* additional registers for SB 16 mixer */ +#define SB_DSP4_IRQSETUP 0x80 +#define SB_DSP4_DMASETUP 0x81 +#define SB_DSP4_IRQSTATUS 0x82 +#define SB_DSP4_MPUSETUP 0x84 + +#define SB_DSP4_3DSE 0x90 + +/* IRQ setting bitmap */ +#define SB_IRQSETUP_IRQ9 0x01 +#define SB_IRQSETUP_IRQ5 0x02 +#define SB_IRQSETUP_IRQ7 0x04 +#define SB_IRQSETUP_IRQ10 0x08 + +/* IRQ types */ +#define SB_IRQTYPE_8BIT 0x01 +#define SB_IRQTYPE_16BIT 0x02 +#define SB_IRQTYPE_MPUIN 0x04 + +/* DMA setting bitmap */ +#define SB_DMASETUP_DMA0 0x01 +#define SB_DMASETUP_DMA1 0x02 +#define SB_DMASETUP_DMA3 0x08 +#define SB_DMASETUP_DMA5 0x20 +#define SB_DMASETUP_DMA6 0x40 +#define SB_DMASETUP_DMA7 0x80 + +/* + * + */ + +static inline void snd_sb_ack_8bit(sb_t *chip) +{ + inb(SBP(chip, DATA_AVAIL)); +} + +static inline void snd_sb_ack_16bit(sb_t *chip) +{ + inb(SBP(chip, DATA_AVAIL_16)); +} + +/* sb_common.c */ +int snd_sbdsp_command(sb_t *chip, unsigned char val); +int snd_sbdsp_get_byte(sb_t *chip); +int snd_sbdsp_reset(sb_t *chip); +int snd_sbdsp_create(snd_card_t *card, + unsigned long port, + int irq, + void (*irq_handler)(int, void *, struct pt_regs *), + int dma8, int dma16, + unsigned short hardware, + sb_t **r_chip); +/* sb_mixer.c */ +void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data); +unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg); +int snd_sbmixer_new(sb_t *chip); + +/* sb8_init.c */ +int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +/* sb8.c */ +void snd_sb8dsp_interrupt(sb_t *chip); +int snd_sb8_playback_open(snd_pcm_substream_t *substream); +int snd_sb8_capture_open(snd_pcm_substream_t *substream); +int snd_sb8_playback_close(snd_pcm_substream_t *substream); +int snd_sb8_capture_close(snd_pcm_substream_t *substream); +/* midi8.c */ +void snd_sb8dsp_midi_interrupt(sb_t *chip); +int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi); + +/* sb16_init.c */ +int snd_sb16dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm); +int snd_sb16dsp_configure(sb_t *chip); +/* sb16.c */ +void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs); +int snd_sb16_playback_open(snd_pcm_substream_t *substream); +int snd_sb16_capture_open(snd_pcm_substream_t *substream); +int snd_sb16_playback_close(snd_pcm_substream_t *substream); +int snd_sb16_capture_close(snd_pcm_substream_t *substream); + +#endif /* __SOUND_SB_H */ diff -Nru linux/include/sound/sb16_csp.h linux-2.4.19-pre5-mjc/include/sound/sb16_csp.h --- linux/include/sound/sb16_csp.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/sb16_csp.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,168 @@ +#ifndef __SOUND_SB16_CSP_H +#define __SOUND_SB16_CSP_H + +/* + * Copyright (c) 1999 by Uros Bizjak + * Takashi Iwai + * + * SB16ASP/AWE32 CSP control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* CSP modes */ +#define SNDRV_SB_CSP_MODE_NONE 0x00 +#define SNDRV_SB_CSP_MODE_DSP_READ 0x01 /* Record from DSP */ +#define SNDRV_SB_CSP_MODE_DSP_WRITE 0x02 /* Play to DSP */ +#define SNDRV_SB_CSP_MODE_QSOUND 0x04 /* QSound */ + +/* CSP load flags */ +#define SNDRV_SB_CSP_LOAD_FROMUSER 0x01 +#define SNDRV_SB_CSP_LOAD_INITBLOCK 0x02 + +/* CSP sample width */ +#define SNDRV_SB_CSP_SAMPLE_8BIT 0x01 +#define SNDRV_SB_CSP_SAMPLE_16BIT 0x02 + +/* CSP channels */ +#define SNDRV_SB_CSP_MONO 0x01 +#define SNDRV_SB_CSP_STEREO 0x02 + +/* CSP rates */ +#define SNDRV_SB_CSP_RATE_8000 0x01 +#define SNDRV_SB_CSP_RATE_11025 0x02 +#define SNDRV_SB_CSP_RATE_22050 0x04 +#define SNDRV_SB_CSP_RATE_44100 0x08 +#define SNDRV_SB_CSP_RATE_ALL 0x0f + +/* CSP running state */ +#define SNDRV_SB_CSP_ST_IDLE 0x00 +#define SNDRV_SB_CSP_ST_LOADED 0x01 +#define SNDRV_SB_CSP_ST_RUNNING 0x02 +#define SNDRV_SB_CSP_ST_PAUSED 0x04 +#define SNDRV_SB_CSP_ST_AUTO 0x08 +#define SNDRV_SB_CSP_ST_QSOUND 0x10 + +/* maximum QSound value (180 degrees right) */ +#define SNDRV_SB_CSP_QSOUND_MAX_RIGHT 0x20 + +/* maximum microcode RIFF file size */ +#define SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE 0x3000 + +/* microcode header */ +typedef struct snd_sb_csp_mc_header { + char codec_name[16]; /* id name of codec */ + unsigned short func_req; /* requested function */ +} snd_sb_csp_mc_header_t; + +/* microcode to be loaded */ +typedef struct snd_sb_csp_microcode { + snd_sb_csp_mc_header_t info; + unsigned char data[SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE]; +} snd_sb_csp_microcode_t; + +/* start CSP with sample_width in mono/stereo */ +typedef struct snd_sb_csp_start { + int sample_width; /* sample width, look above */ + int channels; /* channels, look above */ +} snd_sb_csp_start_t; + +/* CSP information */ +typedef struct snd_sb_csp_info { + char codec_name[16]; /* id name of codec */ + unsigned short func_nr; /* function number */ + unsigned int acc_format; /* accepted PCM formats */ + unsigned short acc_channels; /* accepted channels */ + unsigned short acc_width; /* accepted sample width */ + unsigned short acc_rates; /* accepted sample rates */ + unsigned short csp_mode; /* CSP mode, see above */ + unsigned short run_channels; /* current channels */ + unsigned short run_width; /* current sample width */ + unsigned short version; /* version id: 0x10 - 0x1f */ + unsigned short state; /* state bits */ +} snd_sb_csp_info_t; + +/* HWDEP controls */ +/* get CSP information */ +#define SNDRV_SB_CSP_IOCTL_INFO _IOR('H', 0x10, snd_sb_csp_info_t) +/* load microcode to CSP */ +#define SNDRV_SB_CSP_IOCTL_LOAD_CODE _IOW('H', 0x11, snd_sb_csp_microcode_t) +/* unload microcode from CSP */ +#define SNDRV_SB_CSP_IOCTL_UNLOAD_CODE _IO('H', 0x12) +/* start CSP */ +#define SNDRV_SB_CSP_IOCTL_START _IOW('H', 0x13, snd_sb_csp_start_t) +/* stop CSP */ +#define SNDRV_SB_CSP_IOCTL_STOP _IO('H', 0x14) +/* pause CSP and DMA transfer */ +#define SNDRV_SB_CSP_IOCTL_PAUSE _IO('H', 0x15) +/* restart CSP and DMA transfer */ +#define SNDRV_SB_CSP_IOCTL_RESTART _IO('H', 0x16) + +#ifdef __KERNEL__ +#include "sb.h" +#include "hwdep.h" + +typedef struct snd_sb_csp snd_sb_csp_t; + +/* + * CSP operators + */ +typedef struct { + int (*csp_use) (snd_sb_csp_t * p); + int (*csp_unuse) (snd_sb_csp_t * p); + int (*csp_autoload) (snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode); + int (*csp_start) (snd_sb_csp_t * p, int sample_width, int channels); + int (*csp_stop) (snd_sb_csp_t * p); + int (*csp_qsound_transfer) (snd_sb_csp_t * p); +} snd_sb_csp_ops_t; + +/* + * CSP private data + */ +struct snd_sb_csp { + sb_t *chip; /* SB16 DSP */ + int used; /* usage flag - exclusive */ + char codec_name[16]; /* name of codec */ + unsigned short func_nr; /* function number */ + unsigned int acc_format; /* accepted PCM formats */ + int acc_channels; /* accepted channels */ + int acc_width; /* accepted sample width */ + int acc_rates; /* accepted sample rates */ + int mode; /* MODE */ + int run_channels; /* current CSP channels */ + int run_width; /* current sample width */ + int version; /* CSP version (0x10 - 0x1f) */ + int running; /* running state */ + + snd_sb_csp_ops_t ops; /* operators */ + + spinlock_t q_lock; /* locking */ + int q_enabled; /* enabled flag */ + int qpos_left; /* left position */ + int qpos_right; /* right position */ + int qpos_changed; /* position changed flag */ + + snd_kcontrol_t *qsound_switch; + snd_kcontrol_t *qsound_space; + + struct semaphore access_mutex; /* locking */ + snd_info_entry_t *proc; /* proc interface */ +}; + +int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep); +#endif + +#endif /* __SOUND_SB16_CSP */ diff -Nru linux/include/sound/seq_device.h linux-2.4.19-pre5-mjc/include/sound/seq_device.h --- linux/include/sound/seq_device.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_device.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,88 @@ +#ifndef __SOUND_SEQ_DEVICE_H +#define __SOUND_SEQ_DEVICE_H + +/* + * ALSA sequencer device management + * Copyright (c) 1999 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +typedef struct snd_seq_device snd_seq_device_t; +typedef struct snd_seq_dev_ops snd_seq_dev_ops_t; + +/* + * registered device information + */ + +#define ID_LEN 32 + +/* status flag */ +#define SNDRV_SEQ_DEVICE_FREE 0 +#define SNDRV_SEQ_DEVICE_REGISTERED 1 + +struct snd_seq_device { + /* device info */ + snd_card_t *card; /* sound card */ + int device; /* device number */ + char id[ID_LEN]; /* driver id */ + char name[80]; /* device name */ + int argsize; /* size of the argument */ + void *driver_data; /* private data for driver */ + int status; /* flag - read only */ + void *private_data; /* private data for the caller */ + void (*private_free)(snd_seq_device_t *device); + struct list_head list; /* link to next device */ +}; + + +/* driver operators + * init_device: + * Initialize the device with given parameters. + * Typically, + * 1. call snd_hwdep_new + * 2. allocate private data and initialize it + * 3. call snd_hwdep_register + * 4. store the instance to dev->driver_data pointer. + * + * free_device: + * Release the private data. + * Typically, call snd_device_free(dev->card, dev->driver_data) + */ +struct snd_seq_dev_ops { + int (*init_device)(snd_seq_device_t *dev); + int (*free_device)(snd_seq_device_t *dev); +}; + +/* + * prototypes + */ +void snd_seq_device_load_drivers(void); +int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, snd_seq_device_t **result); +int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize); +int snd_seq_device_unregister_driver(char *id); + +#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(snd_seq_device_t)) + + +/* + * id strings for generic devices + */ +#define SNDRV_SEQ_DEV_ID_MIDISYNTH "seq-midi" +#define SNDRV_SEQ_DEV_ID_OPL3 "opl3-synth" + + +#endif /* __SOUND_SEQ_DEVICE_H */ diff -Nru linux/include/sound/seq_instr.h linux-2.4.19-pre5-mjc/include/sound/seq_instr.h --- linux/include/sound/seq_instr.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_instr.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,112 @@ +#ifndef __SOUND_SEQ_INSTR_H +#define __SOUND_SEQ_INSTR_H + +/* + * Main kernel header file for the ALSA sequencer + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "seq_kernel.h" + +/* Instrument cluster */ +typedef struct _snd_seq_kcluster { + snd_seq_instr_cluster_t cluster; + char name[32]; + int priority; + struct _snd_seq_kcluster *next; +} snd_seq_kcluster_t; + +/* return pointer to private data */ +#define KINSTR_DATA(kinstr) (void *)(((char *)kinstr) + sizeof(snd_seq_kinstr_t)) + +typedef struct snd_seq_kinstr_ops snd_seq_kinstr_ops_t; + +/* Instrument structure */ +typedef struct _snd_seq_kinstr { + snd_seq_instr_t instr; + char name[32]; + int type; /* instrument type */ + int use; /* use count */ + int busy; /* not useable */ + int add_len; /* additional length */ + snd_seq_kinstr_ops_t *ops; /* operations */ + struct _snd_seq_kinstr *next; +} snd_seq_kinstr_t; + +#define SNDRV_SEQ_INSTR_HASH_SIZE 32 + +/* Instrument flags */ +#define SNDRV_SEQ_INSTR_FLG_DIRECT (1<<0) /* accept only direct events */ + +/* List of all instruments */ +typedef struct { + snd_seq_kinstr_t *hash[SNDRV_SEQ_INSTR_HASH_SIZE]; + int count; /* count of all instruments */ + + snd_seq_kcluster_t *chash[SNDRV_SEQ_INSTR_HASH_SIZE]; + int ccount; /* count of all clusters */ + + int owner; /* current owner of the instrument list */ + unsigned int flags; + + spinlock_t lock; + spinlock_t ops_lock; + struct semaphore ops_mutex; + unsigned long ops_flags; +} snd_seq_kinstr_list_t; + +#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE 0 +#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE 1 + +struct snd_seq_kinstr_ops { + void *private_data; + long add_len; /* additional length */ + char *instr_type; + int (*info)(void *private_data, char *info_data, long len); + int (*put)(void *private_data, snd_seq_kinstr_t *kinstr, + char *instr_data, long len, int atomic, int cmd); + int (*get)(void *private_data, snd_seq_kinstr_t *kinstr, + char *instr_data, long len, int atomic, int cmd); + int (*get_size)(void *private_data, snd_seq_kinstr_t *kinstr, long *size); + int (*remove)(void *private_data, snd_seq_kinstr_t *kinstr, int atomic); + void (*notify)(void *private_data, snd_seq_kinstr_t *kinstr, int what); + struct snd_seq_kinstr_ops *next; +}; + + +/* instrument operations */ +snd_seq_kinstr_list_t *snd_seq_instr_list_new(void); +void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list); +int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list, + snd_seq_instr_header_t *ifree, + int client, + int atomic); +snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list, + snd_seq_instr_t *instr, + int exact, + int follow_alias); +void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list, + snd_seq_kinstr_t *instr); +int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int client, + int atomic, + int hop); + +#endif /* __SOUND_SEQ_INSTR_H */ diff -Nru linux/include/sound/seq_kernel.h linux-2.4.19-pre5-mjc/include/sound/seq_kernel.h --- linux/include/sound/seq_kernel.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_kernel.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,182 @@ +#ifndef __SOUND_SEQ_KERNEL_H +#define __SOUND_SEQ_KERNEL_H + +/* + * Main kernel header file for the ALSA sequencer + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include "asequencer.h" + +typedef sndrv_seq_tick_time_t snd_seq_tick_time_t; +typedef sndrv_seq_position_t snd_seq_position_t; +typedef sndrv_seq_frequency_t snd_seq_frequency_t; +typedef sndrv_seq_instr_cluster_t snd_seq_instr_cluster_t; +typedef enum sndrv_seq_client_type snd_seq_client_type_t; +typedef enum sndrv_seq_stop_mode snd_seq_stop_mode_t; +typedef struct sndrv_seq_port_info snd_seq_port_info_t; +typedef struct sndrv_seq_port_subscribe snd_seq_port_subscribe_t; +typedef struct sndrv_seq_event snd_seq_event_t; +typedef struct sndrv_seq_addr snd_seq_addr_t; +typedef struct sndrv_seq_ev_volume snd_seq_ev_volume_t; +typedef struct sndrv_seq_ev_loop snd_seq_ev_loop_t; +typedef struct sndrv_seq_remove_events snd_seq_remove_events_t; +typedef struct sndrv_seq_query_subs snd_seq_query_subs_t; +typedef struct sndrv_seq_real_time snd_seq_real_time_t; +typedef struct sndrv_seq_system_info snd_seq_system_info_t; +typedef struct sndrv_seq_client_info snd_seq_client_info_t; +typedef struct sndrv_seq_queue_info snd_seq_queue_info_t; +typedef struct sndrv_seq_queue_status snd_seq_queue_status_t; +typedef struct sndrv_seq_queue_tempo snd_seq_queue_tempo_t; +typedef struct sndrv_seq_queue_owner snd_seq_queue_owner_t; +typedef struct sndrv_seq_queue_timer snd_seq_queue_timer_t; +typedef struct sndrv_seq_queue_client snd_seq_queue_client_t; +typedef struct sndrv_seq_client_pool snd_seq_client_pool_t; +typedef struct sndrv_seq_instr snd_seq_instr_t; +typedef struct sndrv_seq_instr_data snd_seq_instr_data_t; +typedef struct sndrv_seq_instr_header snd_seq_instr_header_t; +typedef union sndrv_seq_timestamp snd_seq_timestamp_t; + +#define snd_seq_event_bounce_ext_data sndrv_seq_event_bounce_ext_data +#define snd_seq_ev_is_result_type sndrv_seq_ev_is_result_type +#define snd_seq_ev_is_channel_type sndrv_seq_ev_is_channel_type +#define snd_seq_ev_is_note_type sndrv_seq_ev_is_note_type +#define snd_seq_ev_is_control_type sndrv_seq_ev_is_control_type +#define snd_seq_ev_is_queue_type sndrv_seq_ev_is_queue_type +#define snd_seq_ev_is_message_type sndrv_seq_ev_is_message_type +#define snd_seq_ev_is_sample_type sndrv_seq_ev_is_sample_type +#define snd_seq_ev_is_user_type sndrv_seq_ev_is_user_type +#define snd_seq_ev_is_fixed_type sndrv_seq_ev_is_fixed_type +#define snd_seq_ev_is_instr_type sndrv_seq_ev_is_instr_type +#define snd_seq_ev_is_variable_type sndrv_seq_ev_is_variable_type +#define snd_seq_ev_is_varipc_type sndrv_seq_ev_is_varipc_type +#define snd_seq_ev_is_reserved sndrv_seq_ev_is_reserved +#define snd_seq_ev_is_direct sndrv_seq_ev_is_direct +#define snd_seq_ev_is_prior sndrv_seq_ev_is_prior +#define snd_seq_ev_length_type sndrv_seq_ev_length_type +#define snd_seq_ev_is_fixed sndrv_seq_ev_is_fixed +#define snd_seq_ev_is_variable sndrv_seq_ev_is_variable +#define snd_seq_ev_is_varusr sndrv_seq_ev_is_varusr +#define snd_seq_ev_is_varipc sndrv_seq_ev_is_varipc +#define snd_seq_ev_timestamp_type sndrv_seq_ev_timestamp_type +#define snd_seq_ev_is_tick sndrv_seq_ev_is_tick +#define snd_seq_ev_is_real sndrv_seq_ev_is_real +#define snd_seq_ev_timemode_type sndrv_seq_ev_timemode_type +#define snd_seq_ev_is_abstime sndrv_seq_ev_is_abstime +#define snd_seq_ev_is_reltime sndrv_seq_ev_is_reltime +#define snd_seq_queue_sync_port sndrv_seq_queue_sync_port +#define snd_seq_queue_owner sndrv_seq_queue_owner + +/* maximum number of events dequeued per schedule interval */ +#define SNDRV_SEQ_MAX_DEQUEUE 50 + +/* maximum number of queues */ +#define SNDRV_SEQ_MAX_QUEUES 8 + +/* max number of concurrent clients */ +#define SNDRV_SEQ_MAX_CLIENTS 192 + +/* max number of concurrent ports */ +#define SNDRV_SEQ_MAX_PORTS 254 + +/* max number of events in memory pool */ +#define SNDRV_SEQ_MAX_EVENTS 2000 + +/* default number of events in memory chunk */ +#define SNDRV_SEQ_DEFAULT_CHUNK_EVENTS 64 + +/* default number of events in memory pool */ +#define SNDRV_SEQ_DEFAULT_EVENTS 500 + +/* max number of events in memory pool for one client (outqueue) */ +#define SNDRV_SEQ_MAX_CLIENT_EVENTS 2000 + +/* default number of events in memory pool for one client (outqueue) */ +#define SNDRV_SEQ_DEFAULT_CLIENT_EVENTS 200 + +/* max delivery path length */ +#define SNDRV_SEQ_MAX_HOPS 10 + +/* max size of event size */ +#define SNDRV_SEQ_MAX_EVENT_LEN 0x3fffffff + +/* typedefs */ +struct _snd_seq_user_client; +struct _snd_seq_kernel_client; +struct _snd_seq_client; +struct _snd_seq_queue; + +typedef struct _snd_seq_user_client user_client_t; +typedef struct _snd_seq_kernel_client kernel_client_t; +typedef struct _snd_seq_client client_t; +typedef struct _snd_seq_queue queue_t; + +/* call-backs for kernel client */ + +typedef struct { + void *private_data; + int allow_input: 1, + allow_output: 1; + /*...*/ +} snd_seq_client_callback_t; + +/* call-backs for kernel port */ +typedef int (snd_seq_kernel_port_open_t)(void *private_data, snd_seq_port_subscribe_t *info); +typedef int (snd_seq_kernel_port_close_t)(void *private_data, snd_seq_port_subscribe_t *info); +typedef int (snd_seq_kernel_port_input_t)(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop); +typedef void (snd_seq_kernel_port_private_free_t)(void *private_data); + +typedef struct { + struct module *owner; + void *private_data; + snd_seq_kernel_port_open_t *subscribe; + snd_seq_kernel_port_close_t *unsubscribe; + snd_seq_kernel_port_open_t *use; + snd_seq_kernel_port_close_t *unuse; + snd_seq_kernel_port_input_t *event_input; + snd_seq_kernel_port_private_free_t *private_free; + unsigned int callback_all; /* call subscribe callbacks at each connection/disconnection */ + /*...*/ +} snd_seq_port_callback_t; + +/* interface for kernel client */ +extern int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t *callback); +extern int snd_seq_delete_kernel_client(int client); +extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop); +extern int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t *ev, int atomic, int hop); +extern int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg); + +#define SNDRV_SEQ_EXT_MASK 0xc0000000 +#define SNDRV_SEQ_EXT_USRPTR 0x80000000 +#define SNDRV_SEQ_EXT_CHAINED 0x40000000 + +typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count); +int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned); +int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data); + +/* port callback routines */ +void snd_port_init_callback(snd_seq_port_callback_t *p); +snd_seq_port_callback_t *snd_port_alloc_callback(void); + +/* port attach/detach */ +int snd_seq_event_port_attach(int client, snd_seq_port_callback_t *pcbp, + int cap, int type, char *portname); +int snd_seq_event_port_detach(int client, int port); + +#endif /* __SOUND_SEQ_KERNEL_H */ diff -Nru linux/include/sound/seq_midi_emul.h linux-2.4.19-pre5-mjc/include/sound/seq_midi_emul.h --- linux/include/sound/seq_midi_emul.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_midi_emul.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,197 @@ +#ifndef __SOUND_SEQ_MIDI_EMUL_H +#define __SOUND_SEQ_MIDI_EMUL_H + +/* + * Midi channel definition for optional channel management. + * + * Copyright (C) 1999 Steve Ratcliffe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_kernel.h" + +/* + * This structure is used to keep track of the current state on each + * channel. All drivers for hardware that does not understand midi + * directly will probably need to use this structure. + */ +typedef struct snd_midi_channel { + void *private; /* A back pointer to driver data */ + int number; /* The channel number */ + int client; /* The client associated with this channel */ + int port; /* The port associated with this channel */ + + unsigned char midi_mode; /* GM, GS, XG etc */ + unsigned int + drum_channel:1, /* Drum channel */ + param_type:1 /* RPN/NRPN */ + ; + + unsigned char midi_aftertouch; /* Aftertouch (key pressure) */ + unsigned char midi_pressure; /* Channel pressure */ + unsigned char midi_program; /* Instrument number */ + short midi_pitchbend; /* Pitch bend amount */ + + unsigned char control[128]; /* Current value of all controls */ + unsigned char note[128]; /* Current status for all notes */ + + short gm_rpn_pitch_bend_range; /* Pitch bend range */ + short gm_rpn_fine_tuning; /* Master fine tuning */ + short gm_rpn_coarse_tuning; /* Master coarse tuning */ + +} snd_midi_channel_t; + +/* + * A structure that represets a set of channels bound to a port. There + * would usually be 16 channels per port. But fewer could be used for + * particular cases. + * The channel set consists of information describing the client and + * port for this midi synth and an array of snd_midi_channel_t structures. + * A driver that had no need for snd_midi_channel_t could still use the + * channel set type if it wished with the channel array null. + */ +typedef struct snd_midi_channel_set { + void *private_data; /* Driver data */ + int client; /* Client for this port */ + int port; /* The port number */ + + int max_channels; /* Size of the channels array */ + snd_midi_channel_t *channels; + + unsigned char midi_mode; /* MIDI operating mode */ + unsigned char gs_master_volume; /* SYSEX master volume: 0-127 */ + unsigned char gs_chorus_mode; + unsigned char gs_reverb_mode; + +} snd_midi_channel_set_t; + +typedef struct snd_seq_midi_op { + void (*note_on)(void *private_data, int note, int vel, snd_midi_channel_t *chan); + void (*note_off)(void *private_data,int note, int vel, snd_midi_channel_t *chan); /* release note */ + void (*key_press)(void *private_data, int note, int vel, snd_midi_channel_t *chan); + void (*note_terminate)(void *private_data, int note, snd_midi_channel_t *chan); /* terminate note immediately */ + void (*control)(void *private_data, int type, snd_midi_channel_t *chan); + void (*nrpn)(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); + void (*sysex)(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +} snd_midi_op_t; + +/* + * These defines are used so that pitchbend, aftertouch etc, can be + * distinguished from controller values. + */ +/* 0-127 controller values */ +#define MIDI_CTL_PITCHBEND 0x80 +#define MIDI_CTL_AFTERTOUCH 0x81 +#define MIDI_CTL_CHAN_PRESSURE 0x82 + +/* + * These names exist to allow symbolic access to the controls array. + * The usage is eg: chan->gm_bank_select. Another implementation would + * be really have these members in the struct, and not the array. + */ +#define gm_bank_select control[0] +#define gm_modulation control[1] +#define gm_breath control[2] +#define gm_foot_pedal control[4] +#define gm_portamento_time control[5] +#define gm_data_entry control[6] +#define gm_volume control[7] +#define gm_balance control[8] +#define gm_pan control[10] +#define gm_expression control[11] +#define gm_effect_control1 control[12] +#define gm_effect_control2 control[13] +#define gm_slider1 control[16] +#define gm_slider2 control[17] +#define gm_slider3 control[18] +#define gm_slider4 control[19] + +#define gm_bank_select_lsb control[32] +#define gm_modulation_wheel_lsb control[33] +#define gm_breath_lsb control[34] +#define gm_foot_pedal_lsb control[36] +#define gm_portamento_time_lsb control[37] +#define gm_data_entry_lsb control[38] +#define gm_volume_lsb control[39] +#define gm_balance_lsb control[40] +#define gm_pan_lsb control[42] +#define gm_expression_lsb control[43] +#define gm_effect_control1_lsb control[44] +#define gm_effect_control2_lsb control[45] + +#define gm_sustain control[MIDI_CTL_SUSTAIN] +#define gm_hold gm_sustain +#define gm_portamento control[MIDI_CTL_PORTAMENTO] +#define gm_sustenuto control[MIDI_CTL_SUSTENUTO] + +/* + * These macros give the complete value of the controls that consist + * of coarse and fine pairs. Of course the fine controls are seldom used + * but there is no harm in being complete. + */ +#define SNDRV_GM_BANK_SELECT(cp) (((cp)->control[0]<<7)|((cp)->control[32])) +#define SNDRV_GM_MODULATION_WHEEL(cp) (((cp)->control[1]<<7)|((cp)->control[33])) +#define SNDRV_GM_BREATH(cp) (((cp)->control[2]<<7)|((cp)->control[34])) +#define SNDRV_GM_FOOT_PEDAL(cp) (((cp)->control[4]<<7)|((cp)->control[36])) +#define SNDRV_GM_PORTAMENTO_TIME(cp) (((cp)->control[5]<<7)|((cp)->control[37])) +#define SNDRV_GM_DATA_ENTRY(cp) (((cp)->control[6]<<7)|((cp)->control[38])) +#define SNDRV_GM_VOLUME(cp) (((cp)->control[7]<<7)|((cp)->control[39])) +#define SNDRV_GM_BALANCE(cp) (((cp)->control[8]<<7)|((cp)->control[40])) +#define SNDRV_GM_PAN(cp) (((cp)->control[10]<<7)|((cp)->control[42])) +#define SNDRV_GM_EXPRESSION(cp) (((cp)->control[11]<<7)|((cp)->control[43])) + + +/* MIDI mode */ +#define SNDRV_MIDI_MODE_NONE 0 /* Generic midi */ +#define SNDRV_MIDI_MODE_GM 1 +#define SNDRV_MIDI_MODE_GS 2 +#define SNDRV_MIDI_MODE_XG 3 +#define SNDRV_MIDI_MODE_MT32 4 + +/* MIDI note state */ +#define SNDRV_MIDI_NOTE_OFF 0x00 +#define SNDRV_MIDI_NOTE_ON 0x01 +#define SNDRV_MIDI_NOTE_RELEASED 0x02 +#define SNDRV_MIDI_NOTE_SUSTENUTO 0x04 + +#define SNDRV_MIDI_PARAM_TYPE_REGISTERED 0 +#define SNDRV_MIDI_PARAM_TYPE_NONREGISTERED 1 + +/* SYSEX parse flag */ +enum { + SNDRV_MIDI_SYSEX_NOT_PARSED = 0, + SNDRV_MIDI_SYSEX_GM_ON, + SNDRV_MIDI_SYSEX_GS_ON, + SNDRV_MIDI_SYSEX_GS_RESET, + SNDRV_MIDI_SYSEX_GS_CHORUS_MODE, + SNDRV_MIDI_SYSEX_GS_REVERB_MODE, + SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME, + SNDRV_MIDI_SYSEX_GS_PROGRAM, + SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL, + SNDRV_MIDI_SYSEX_XG_ON, +}; + +/* Prototypes for midi_process.c */ +void snd_midi_process_event(snd_midi_op_t *ops, snd_seq_event_t *ev, + snd_midi_channel_set_t *chanset); +void snd_midi_channel_set_clear(snd_midi_channel_set_t *chset); +void snd_midi_channel_init(snd_midi_channel_t *p, int n); +snd_midi_channel_t *snd_midi_channel_init_set(int n); +snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n); +void snd_midi_channel_free_set(snd_midi_channel_set_t *chset); + +#endif /* __SOUND_SEQ_MIDI_EMUL_H */ diff -Nru linux/include/sound/seq_midi_event.h linux-2.4.19-pre5-mjc/include/sound/seq_midi_event.h --- linux/include/sound/seq_midi_event.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_midi_event.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,54 @@ +#ifndef __SOUND_SEQ_MIDI_EVENT_H +#define __SOUND_SEQ_MIDI_EVENT_H + +/* + * MIDI byte <-> sequencer event coder + * + * Copyright (C) 1998,99 Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "asequencer.h" + +#define MAX_MIDI_EVENT_BUF 256 + +typedef struct snd_midi_event_t snd_midi_event_t; + +/* midi status */ +struct snd_midi_event_t { + int qlen; /* queue length */ + int read; /* chars read */ + int type; /* current event type */ + unsigned char lastcmd; + int bufsize; + unsigned char *buf; /* input buffer */ + spinlock_t lock; +}; + +int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev); +int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize); +void snd_midi_event_free(snd_midi_event_t *dev); +void snd_midi_event_init(snd_midi_event_t *dev); +void snd_midi_event_reset_encode(snd_midi_event_t *dev); +void snd_midi_event_reset_decode(snd_midi_event_t *dev); +/* encode from byte stream - return number of written bytes if success */ +long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev); +int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev); +/* decode from event to bytes - return number of written bytes if success */ +long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev); + +#endif /* __SOUND_SEQ_MIDI_EVENT_H */ diff -Nru linux/include/sound/seq_oss.h linux-2.4.19-pre5-mjc/include/sound/seq_oss.h --- linux/include/sound/seq_oss.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_oss.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,102 @@ +#ifndef __SOUND_SEQ_OSS_H +#define __SOUND_SEQ_OSS_H + +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "asequencer.h" +#include "seq_kernel.h" + +/* + * type definitions + */ +typedef struct snd_seq_oss_arg_t snd_seq_oss_arg_t; +typedef struct snd_seq_oss_callback_t snd_seq_oss_callback_t; + +/* + * argument structure for synthesizer operations + */ +struct snd_seq_oss_arg_t { + /* given by OSS sequencer */ + int app_index; /* application unique index */ + int file_mode; /* file mode - see below */ + int seq_mode; /* sequencer mode - see below */ + + /* following must be initialized in open callback */ + snd_seq_addr_t addr; /* opened port address */ + void *private_data; /* private data for lowlevel drivers */ + + /* note-on event passing mode: initially given by OSS seq, + * but configurable by drivers - see below + */ + int event_passing; +}; + + +/* + * synthesizer operation callbacks + */ +struct snd_seq_oss_callback_t { + struct module *owner; + int (*open)(snd_seq_oss_arg_t *p, void *closure); + int (*close)(snd_seq_oss_arg_t *p); + int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg); + int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count); + int (*reset)(snd_seq_oss_arg_t *p); + int (*raw_event)(snd_seq_oss_arg_t *p, unsigned char *data); +}; + +/* flag: file_mode */ +#define SNDRV_SEQ_OSS_FILE_ACMODE 3 +#define SNDRV_SEQ_OSS_FILE_READ 1 +#define SNDRV_SEQ_OSS_FILE_WRITE 2 +#define SNDRV_SEQ_OSS_FILE_NONBLOCK 4 + +/* flag: seq_mode */ +#define SNDRV_SEQ_OSS_MODE_SYNTH 0 +#define SNDRV_SEQ_OSS_MODE_MUSIC 1 + +/* flag: event_passing */ +#define SNDRV_SEQ_OSS_PROCESS_EVENTS 0 /* key == 255 is processed as velocity change */ +#define SNDRV_SEQ_OSS_PASS_EVENTS 1 /* pass all events to callback */ +#define SNDRV_SEQ_OSS_PROCESS_KEYPRESS 2 /* key >= 128 will be processed as key-pressure */ + +/* default control rate: fixed */ +#define SNDRV_SEQ_OSS_CTRLRATE 100 + +/* default max queue length: configurable by module option */ +#define SNDRV_SEQ_OSS_MAX_QLEN 1024 + + +/* + * data pointer to snd_seq_register_device + */ +typedef struct snd_seq_oss_reg { + int type; + int subtype; + int nvoices; + snd_seq_oss_callback_t oper; + void *private_data; +} snd_seq_oss_reg_t; + +/* device id */ +#define SNDRV_SEQ_DEV_ID_OSS "seq-oss" + +#endif /* __SOUND_SEQ_OSS_H */ diff -Nru linux/include/sound/seq_oss_legacy.h linux-2.4.19-pre5-mjc/include/sound/seq_oss_legacy.h --- linux/include/sound/seq_oss_legacy.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_oss_legacy.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,31 @@ +#ifndef __SOUND_SEQ_OSS_LEGACY_H +#define __SOUND_SEQ_OSS_LEGACY_H + +/* + * OSS compatible macro definitions + * + * Copyright (C) 2000 Abramo Bagnara + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#ifndef SAMPLE_TYPE_AWE32 +#define SAMPLE_TYPE_AWE32 0x20 +#endif + +#endif /* __SOUND_SEQ_OSS_LEGACY_H */ + diff -Nru linux/include/sound/seq_virmidi.h linux-2.4.19-pre5-mjc/include/sound/seq_virmidi.h --- linux/include/sound/seq_virmidi.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/seq_virmidi.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,82 @@ +#ifndef __SOUND_SEQ_VIRMIDI_H +#define __SOUND_SEQ_VIRMIDI_H + +/* + * Virtual Raw MIDI client on Sequencer + * Copyright (c) 2000 by Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "rawmidi.h" +#include "seq_midi_event.h" + +typedef struct _snd_virmidi_dev snd_virmidi_dev_t; + +/* + * device file instance: + * This instance is created at each time the midi device file is + * opened. Each instance has its own input buffer and MIDI parser + * (buffer), and is associated with the device instance. + */ +typedef struct _snd_virmidi { + struct list_head list; + int seq_mode; + int client; + int port; + int trigger: 1; + snd_midi_event_t *parser; + snd_seq_event_t event; + snd_virmidi_dev_t *rdev; + snd_rawmidi_substream_t *substream; +} snd_virmidi_t; + +#define SNDRV_VIRMIDI_SUBSCRIBE (1<<0) +#define SNDRV_VIRMIDI_USE (1<<1) + +/* + * device record: + * Each virtual midi device has one device instance. It contains + * common information and the linked-list of opened files, + */ +struct _snd_virmidi_dev { + snd_card_t *card; /* associated card */ + snd_rawmidi_t *rmidi; /* rawmidi device */ + int seq_mode; /* SNDRV_VIRMIDI_XXX */ + int device; /* sequencer device */ + int client; /* created/attached client */ + int port; /* created/attached port */ + unsigned int flags; /* SNDRV_VIRMIDI_* */ + rwlock_t filelist_lock; + struct list_head filelist; +}; + +/* sequencer mode: + * ATTACH = input/output events from midi device are routed to the + * attached sequencer port. sequencer port is not created + * by virmidi itself. + * DISPATCH = input/output events are routed to subscribers. + * sequencer port is created in virmidi. + */ +#define SNDRV_VIRMIDI_SEQ_NONE 0 +#define SNDRV_VIRMIDI_SEQ_ATTACH 1 +#define SNDRV_VIRMIDI_SEQ_DISPATCH 2 + +int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi); + +#endif /* __SOUND_SEQ_VIRMIDI */ + diff -Nru linux/include/sound/sfnt_info.h linux-2.4.19-pre5-mjc/include/sound/sfnt_info.h --- linux/include/sound/sfnt_info.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/sfnt_info.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,184 @@ +#ifndef __SOUND_SFNT_INFO_H +#define __SOUND_SFNT_INFO_H + +/* + * Patch record compatible with AWE driver on OSS + * + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "seq_oss_legacy.h" + +/* + * patch information record + */ + +/* patch interface header: 16 bytes */ +typedef struct soundfont_patch_info_t { + unsigned short key; /* use the key below */ +#define SNDRV_OSS_SOUNDFONT_PATCH _PATCHKEY(0x07) + + short device_no; /* synthesizer number */ + unsigned short sf_id; /* file id (should be zero) */ + short optarg; /* optional argument */ + int len; /* data length (without this header) */ + + short type; /* patch operation type */ +#define SNDRV_SFNT_LOAD_INFO 0 /* awe_voice_rec */ +#define SNDRV_SFNT_LOAD_DATA 1 /* awe_sample_info */ +#define SNDRV_SFNT_OPEN_PATCH 2 /* awe_open_parm */ +#define SNDRV_SFNT_CLOSE_PATCH 3 /* none */ + /* 4 is obsolete */ +#define SNDRV_SFNT_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/ +#define SNDRV_SFNT_MAP_PRESET 6 /* awe_voice_map */ + /* 7 is not used */ +#define SNDRV_SFNT_PROBE_DATA 8 /* optarg=sample */ +#define SNDRV_SFNT_REMOVE_INFO 9 /* optarg=(bank<<8)|instr */ + + short reserved; /* word alignment data */ + + /* the actual patch data begins after this */ +} soundfont_patch_info_t; + + +/* + * open patch + */ + +#define SNDRV_SFNT_PATCH_NAME_LEN 32 + +typedef struct soundfont_open_parm_t { + unsigned short type; /* sample type */ +#define SNDRV_SFNT_PAT_TYPE_MISC 0 +#define SNDRV_SFNT_PAT_TYPE_GUS 6 +#define SNDRV_SFNT_PAT_TYPE_MAP 7 +#define SNDRV_SFNT_PAT_LOCKED 0x100 /* lock the samples */ +#define SNDRV_SFNT_PAT_SHARED 0x200 /* sample is shared */ + + short reserved; + char name[SNDRV_SFNT_PATCH_NAME_LEN]; +} soundfont_open_parm_t; + + +/* + * raw voice information record + */ + +/* wave table envelope & effect parameters to control EMU8000 */ +typedef struct soundfont_voice_parm_t { + unsigned short moddelay; /* modulation delay (0x8000) */ + unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */ + unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */ + unsigned short modrelease; /* modulation release time (0x807f) */ + short modkeyhold, modkeydecay; /* envelope change per key (not used) */ + unsigned short voldelay; /* volume delay (0x8000) */ + unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */ + unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */ + unsigned short volrelease; /* volume release time (0x807f) */ + short volkeyhold, volkeydecay; /* envelope change per key (not used) */ + unsigned short lfo1delay; /* LFO1 delay (0x8000) */ + unsigned short lfo2delay; /* LFO2 delay (0x8000) */ + unsigned short pefe; /* modulation pitch & cutoff (0x0000) */ + unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */ + unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */ + unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */ + unsigned char cutoff; /* initial cutoff (0xff) */ + unsigned char filterQ; /* initial filter Q [0-15] (0x0) */ + unsigned char chorus; /* chorus send (0x00) */ + unsigned char reverb; /* reverb send (0x00) */ + unsigned short reserved[4]; /* not used */ +} soundfont_voice_parm_t; + + +/* wave table parameters: 92 bytes */ +typedef struct soundfont_voice_info_t { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* sample offset correction */ + int loopstart, loopend; /* loop offset correction */ + short rate_offset; /* sample rate pitch offset */ + unsigned short mode; /* sample mode */ +#define SNDRV_SFNT_MODE_ROMSOUND 0x8000 +#define SNDRV_SFNT_MODE_STEREO 1 +#define SNDRV_SFNT_MODE_LOOPING 2 +#define SNDRV_SFNT_MODE_NORELEASE 4 /* obsolete */ +#define SNDRV_SFNT_MODE_INIT_PARM 8 + + short root; /* midi root key */ + short tune; /* pitch tuning (in cents) */ + unsigned char low, high; /* key note range */ + unsigned char vellow, velhigh; /* velocity range */ + signed char fixkey, fixvel; /* fixed key, velocity */ + signed char pan, fixpan; /* panning, fixed panning */ + short exclusiveClass; /* exclusive class (0 = none) */ + unsigned char amplitude; /* sample volume (127 max) */ + unsigned char attenuation; /* attenuation (0.375dB) */ + short scaleTuning; /* pitch scale tuning(%), normally 100 */ + soundfont_voice_parm_t parm; /* voice envelope parameters */ + unsigned short sample_mode; /* sample mode_flag (set by driver) */ +} soundfont_voice_info_t; + + +/* instrument info header: 4 bytes */ +typedef struct soundfont_voice_rec_hdr_t { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + char nvoices; /* number of voices */ + char write_mode; /* write mode; normally 0 */ +#define SNDRV_SFNT_WR_APPEND 0 /* append anyway */ +#define SNDRV_SFNT_WR_EXCLUSIVE 1 /* skip if already exists */ +#define SNDRV_SFNT_WR_REPLACE 2 /* replace if already exists */ +} soundfont_voice_rec_hdr_t; + + +/* + * sample wave information + */ + +/* wave table sample header: 32 bytes */ +typedef struct soundfont_sample_info_t { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* start & end offset */ + int loopstart, loopend; /* loop start & end offset */ + int size; /* size (0 = ROM) */ + short dummy; /* not used */ + unsigned short mode_flags; /* mode flags */ +#define SNDRV_SFNT_SAMPLE_8BITS 1 /* wave data is 8bits */ +#define SNDRV_SFNT_SAMPLE_UNSIGNED 2 /* wave data is unsigned */ +#define SNDRV_SFNT_SAMPLE_NO_BLANK 4 /* no blank loop is attached */ +#define SNDRV_SFNT_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */ +#define SNDRV_SFNT_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */ +#define SNDRV_SFNT_SAMPLE_STEREO_LEFT 32 /* stereo left sound */ +#define SNDRV_SFNT_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */ +#define SNDRV_SFNT_SAMPLE_REVERSE_LOOP 128 /* reverse looping */ + unsigned int truesize; /* used memory size (set by driver) */ +} soundfont_sample_info_t; + + +/* + * voice preset mapping (aliasing) + */ + +typedef struct soundfont_voice_map_t { + int map_bank, map_instr, map_key; /* key = -1 means all keys */ + int src_bank, src_instr, src_key; +} soundfont_voice_map_t; + + +#endif /* __SOUND_SFNT_INFO_H */ diff -Nru linux/include/sound/snd_wavefront.h linux-2.4.19-pre5-mjc/include/sound/snd_wavefront.h --- linux/include/sound/snd_wavefront.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/snd_wavefront.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,141 @@ +#ifndef __SOUND_SND_WAVEFRONT_H__ +#define __SOUND_SND_WAVEFRONT_H__ + +#include "cs4231.h" +#include "mpu401.h" +#include "hwdep.h" +#include "rawmidi.h" +#include "wavefront.h" /* generic OSS/ALSA/user-level wavefront header */ + +/* MIDI interface */ + +struct _snd_wavefront_midi; +struct _snd_wavefront_card; +struct _snd_wavefront; + +typedef struct _snd_wavefront_midi snd_wavefront_midi_t; +typedef struct _snd_wavefront_card snd_wavefront_card_t; +typedef struct _snd_wavefront snd_wavefront_t; + +typedef enum { internal_mpu = 0, external_mpu = 1 } snd_wavefront_mpu_id; + +struct _snd_wavefront_midi { + unsigned long base; /* I/O port address */ + char isvirtual; /* doing virtual MIDI stuff ? */ + char istimer; /* timer is used */ + snd_wavefront_mpu_id output_mpu; /* most-recently-used */ + snd_wavefront_mpu_id input_mpu; /* most-recently-used */ + unsigned int mode[2]; /* MPU401_MODE_XXX */ + snd_rawmidi_substream_t *substream_output[2]; + snd_rawmidi_substream_t *substream_input[2]; + struct timer_list timer; + spinlock_t open; + spinlock_t virtual; /* protects isvirtual */ +}; + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define UART_MODE_ON 0x3F + +extern snd_rawmidi_ops_t snd_wavefront_midi_output; +extern snd_rawmidi_ops_t snd_wavefront_midi_input; + +extern void snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *); +extern void snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *); +extern void snd_wavefront_midi_interrupt (snd_wavefront_card_t *); +extern int snd_wavefront_midi_start (snd_wavefront_card_t *); + +struct _snd_wavefront { + unsigned long irq; /* "you were one, one of the few ..." */ + unsigned long base; /* low i/o port address */ + struct resource *res_base; /* i/o port resource allocation */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + volatile int irq_cnt; /* ditto */ + char debug; /* debugging flags */ + int freemem; /* installed RAM, in bytes */ + + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char has_fx; /* has FX processor (Tropez+) */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_are_midi; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ + wait_queue_head_t interrupt_sleeper; + snd_wavefront_midi_t midi; /* ICS2115 MIDI interface */ +}; + +struct _snd_wavefront_card { + snd_wavefront_t wavefront; +#ifdef __ISAPNP__ + struct isapnp_dev *wss; + struct isapnp_dev *ctrl; + struct isapnp_dev *mpu; + struct isapnp_dev *synth; +#endif /* CONFIG_ISAPNP */ +}; + +extern void snd_wavefront_internal_interrupt (snd_wavefront_card_t *card); +extern int snd_wavefront_interrupt_bits (int irq); +extern int snd_wavefront_detect_irq (snd_wavefront_t *dev) ; +extern int snd_wavefront_check_irq (snd_wavefront_t *dev, int irq); +extern int snd_wavefront_restart (snd_wavefront_t *dev); +extern int snd_wavefront_start (snd_wavefront_t *dev); +extern int snd_wavefront_detect (snd_wavefront_card_t *card); +extern int snd_wavefront_config_midi (snd_wavefront_t *dev) ; +extern int snd_wavefront_cmd (snd_wavefront_t *, int, unsigned char *, + unsigned char *); + +extern int snd_wavefront_synth_ioctl (snd_hwdep_t *, + struct file *, + unsigned int cmd, + unsigned long arg); +extern int snd_wavefront_synth_open (snd_hwdep_t *, struct file *); +extern int snd_wavefront_synth_release (snd_hwdep_t *, struct file *); + +/* FX processor - see also yss225.[ch] */ + +extern int snd_wavefront_fx_start (snd_wavefront_t *); +extern int snd_wavefront_fx_detect (snd_wavefront_t *); +extern int snd_wavefront_fx_ioctl (snd_hwdep_t *, + struct file *, + unsigned int cmd, + unsigned long arg); +extern int snd_wavefront_fx_open (snd_hwdep_t *, struct file *); +extern int snd_wavefront_fx_release (snd_hwdep_t *, struct file *); + +/* prefix in all snd_printk() delivered messages */ + +#define LOGNAME "WaveFront: " + +#endif /* __SOUND_SND_WAVEFRONT_H__ */ diff -Nru linux/include/sound/sndmagic.h linux-2.4.19-pre5-mjc/include/sound/sndmagic.h --- linux/include/sound/sndmagic.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/sndmagic.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,145 @@ +#ifndef __SOUND_SNDMAGIC_H +#define __SOUND_SNDMAGIC_H + +/* + * Magic allocation, deallocation, check + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifdef CONFIG_SND_DEBUG_MEMORY + +void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags); +void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags); +void snd_magic_kfree(void *ptr); + +#define snd_magic_kcalloc(type, extra, flags) (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) +#define snd_magic_kmalloc(type, extra, flags) (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) + +static inline unsigned long _snd_magic_value(void *obj) +{ + return obj == NULL ? -1 : *(((unsigned long *)obj) - 1); +} + +static inline int _snd_magic_bad(void *obj, unsigned long magic) +{ + return _snd_magic_value(obj) != magic; +} + +#define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd) + +#define snd_magic_cast(type, ptr, action...) (type *) ({\ + void *__ptr = ptr;\ + unsigned long __magic = _snd_magic_value(__ptr);\ + if (__magic != type##_magic) {\ + snd_printk("bad MAGIC (0x%lx)\n", __magic);\ + action;\ + }\ + __ptr;\ +}) + +#define snd_device_t_magic 0xa15a00ff +#define snd_pcm_t_magic 0xa15a0101 +#define snd_pcm_file_t_magic 0xa15a0102 +#define snd_pcm_substream_t_magic 0xa15a0103 +#define snd_pcm_proc_private_t_magic 0xa15a0104 +#define snd_pcm_oss_file_t_magic 0xa15a0105 +#define snd_mixer_oss_t_magic 0xa15a0106 + +#define snd_info_private_data_t_magic 0xa15a0201 +#define snd_ctl_file_t_magic 0xa15a0301 +#define snd_kcontrol_t_magic 0xa15a0302 +#define snd_rawmidi_t_magic 0xa15a0401 +#define snd_rawmidi_file_t_magic 0xa15a0402 +#define snd_virmidi_t_magic 0xa15a0403 +#define snd_virmidi_dev_t_magic 0xa15a0404 +#define snd_timer_t_magic 0xa15a0501 +#define snd_timer_user_t_magic 0xa15a0502 +#define snd_hwdep_t_magic 0xa15a0601 +#define snd_seq_device_t_magic 0xa15a0701 + +#define es18xx_t_magic 0xa15a1101 +#define trident_t_magic 0xa15a1201 +#define es1938_t_magic 0xa15a1301 +#define cs46xx_t_magic 0xa15a1401 +#define ensoniq_t_magic 0xa15a1501 +#define sonicvibes_t_magic 0xa15a1601 +#define mpu401_t_magic 0xa15a1701 +#define fm801_t_magic 0xa15a1801 +#define ac97_t_magic 0xa15a1901 +#define ak4531_t_magic 0xa15a1a01 +#define snd_uart16550_t_magic 0xa15a1b01 +#define emu10k1_t_magic 0xa15a1c01 +#define emu10k1_pcm_t_magic 0xa15a1c02 +#define emu10k1_midi_t_magic 0xa15a1c03 +#define snd_gus_card_t_magic 0xa15a1d01 +#define gus_pcm_private_t_magic 0xa15a1d02 +#define gus_proc_private_t_magic 0xa15a1d03 +#define tea6330t_t_magic 0xa15a1e01 +#define ad1848_t_magic 0xa15a1f01 +#define cs4231_t_magic 0xa15a2001 +#define es1688_t_magic 0xa15a2101 +#define opti93x_t_magic 0xa15a2201 +#define emu8000_t_magic 0xa15a2301 +#define emu8000_proc_private_t_magic 0xa15a2302 +#define snd_emux_t_magic 0xa15a2303 +#define snd_emux_port_t_magic 0xa15a2304 +#define sb_t_magic 0xa15a2401 +#define snd_sb_csp_t_magic 0xa15a2402 +#define snd_card_dummy_t_magic 0xa15a2501 +#define snd_card_dummy_pcm_t_magic 0xa15a2502 +#define opl3_t_magic 0xa15a2601 +#define snd_seq_dummy_port_t_magic 0xa15a2701 +#define ice1712_t_magic 0xa15a2801 +#define ad1816a_t_magic 0xa15a2901 +#define intel8x0_t_magic 0xa15a2a01 +#define es1968_t_magic 0xa15a2b01 +#define esschan_t_magic 0xa15a2b02 +#define via686a_t_magic 0xa15a2c01 +#define pdplus_t_magic 0xa15a2d01 +#define cmipci_t_magic 0xa15a2e01 +#define ymfpci_t_magic 0xa15a2f01 +#define ymfpci_pcm_t_magic 0xa15a2f02 +#define cs4281_t_magic 0xa15a3001 +#define snd_i2c_bus_t_magic 0xa15a3101 +#define snd_i2c_device_t_magic 0xa15a3102 +#define cs8427_t_magic 0xa15a3111 +#define m3_t_magic 0xa15a3201 +#define m3_dma_t_magic 0xa15a3202 +#define nm256_t_magic 0xa15a3301 +#define nm256_dma_t_magic 0xa15a3302 +#define via8233_t_magic 0xa15a3401 +#define pmac_t_magic 0xa15a3501 +#define ali_t_magic 0xa15a3601 +#define mtpav_t_magic 0xa15a3701 +#define mtpav_port_t_magic 0xa15a3702 +#define korg1212_t_magic 0xa15a3800 +#define opl3sa2_t_magic 0xa15a3900 + +#else + +#define snd_magic_kcalloc(type, extra, flags) (type *) snd_kcalloc(sizeof(type) + extra, flags) +#define snd_magic_kmalloc(type, extra, flags) (type *) kmalloc(sizeof(type) + extra, flags) +#define snd_magic_cast(type, ptr, retval) (type *) ptr +#define snd_magic_cast1(type, ptr, retval) snd_magic_cast(type, ptr, retval) +#define snd_magic_kfree kfree + +#endif + +#endif /* __SOUND_SNDMAGIC_H */ diff -Nru linux/include/sound/soundfont.h linux-2.4.19-pre5-mjc/include/sound/soundfont.h --- linux/include/sound/soundfont.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/soundfont.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,130 @@ +#ifndef __SOUND_SOUNDFONT_H +#define __SOUND_SOUNDFONT_H + +/* + * Soundfont defines and definitions. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sfnt_info.h" +#include "util_mem.h" + +#define SF_MAX_INSTRUMENTS 128 /* maximum instrument number */ +#define SF_MAX_PRESETS 256 /* drums are mapped from 128 to 256 */ +#define SF_IS_DRUM_BANK(z) ((z) == 128) + +typedef struct snd_sf_zone { + struct snd_sf_zone *next; /* Link to next */ + unsigned char bank; /* Midi bank for this zone */ + unsigned char instr; /* Midi program for this zone */ + unsigned char mapped; /* True if mapped to something else */ + + soundfont_voice_info_t v; /* All the soundfont parameters */ + int counter; + struct snd_sf_sample *sample; /* Link to sample */ + + /* The following deals with preset numbers (programs) */ + struct snd_sf_zone *next_instr; /* Next zone of this instrument */ + struct snd_sf_zone *next_zone; /* Next zone in play list */ +} snd_sf_zone_t; + +typedef struct snd_sf_sample { + soundfont_sample_info_t v; + int counter; + snd_util_memblk_t *block; /* allocated data block */ + struct snd_sf_sample *next; +} snd_sf_sample_t; + +/* + * This represents all the information relating to a soundfont. + */ +typedef struct snd_soundfont { + struct snd_soundfont *next; /* Link to next */ + /*struct snd_soundfont *prev;*/ /* Link to previous */ + short id; /* file id */ + short type; /* font type */ + unsigned char name[SNDRV_SFNT_PATCH_NAME_LEN]; /* identifier */ + snd_sf_zone_t *zones; /* Font information */ + snd_sf_sample_t *samples; /* The sample headers */ +} snd_soundfont_t; + +/* + * Type of the sample access callback + */ +typedef int (*snd_sf_sample_new_t)(void *private_data, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *buf, long count); +typedef int (*snd_sf_sample_free_t)(void *private_data, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr); +typedef void (*snd_sf_sample_reset_t)(void *private); + +typedef struct snd_sf_callback { + void *private_data; + snd_sf_sample_new_t sample_new; + snd_sf_sample_free_t sample_free; + snd_sf_sample_reset_t sample_reset; +} snd_sf_callback_t; + +/* + * List of soundfonts. + */ +typedef struct snd_sf_list { + snd_soundfont_t *currsf; /* The currently open soundfont */ + int open_client; /* client pointer for lock */ + int mem_used; /* used memory size */ + snd_sf_zone_t *presets[SF_MAX_PRESETS]; + snd_soundfont_t *fonts; /* The list of soundfonts */ + int fonts_size; /* number of fonts allocated */ + int zone_counter; /* last allocated time for zone */ + int sample_counter; /* last allocated time for sample */ + int zone_locked; /* locked time for zone */ + int sample_locked; /* locked time for sample */ + snd_sf_callback_t callback; /* callback functions */ + char sf_locked; /* font lock flag */ + struct semaphore presets_mutex; + spinlock_t lock; + snd_util_memhdr_t *memhdr; +} snd_sf_list_t; + +/* Prototypes for soundfont.c */ +int snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client); +int snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data, + long count, int client); +int snd_soundfont_close_check(snd_sf_list_t *sflist, int client); + +snd_sf_list_t *snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr); +void snd_sf_free(snd_sf_list_t *sflist); + +int snd_soundfont_remove_samples(snd_sf_list_t *sflist); +int snd_soundfont_remove_unlocked(snd_sf_list_t *sflist); +int snd_soundfont_mem_used(snd_sf_list_t *sflist); + +int snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel, + int preset, int bank, + int def_preset, int def_bank, + snd_sf_zone_t **table, int max_layers); + +/* Parameter conversions */ +int snd_sf_calc_parm_hold(int msec); +int snd_sf_calc_parm_attack(int msec); +int snd_sf_calc_parm_decay(int msec); +#define snd_sf_calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725); +extern int snd_sf_vol_table[128]; + + +#endif /* __SOUND_SOUNDFONT_H */ diff -Nru linux/include/sound/soundmem.h linux-2.4.19-pre5-mjc/include/sound/soundmem.h --- linux/include/sound/soundmem.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/soundmem.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,8 @@ +/* + * Onboard memory management + */ + +struct SNDRV_STRU_BANK_INFO { + unsigned int address; + unsigned int size; +}; diff -Nru linux/include/sound/tea6330t.h linux-2.4.19-pre5-mjc/include/sound/tea6330t.h --- linux/include/sound/tea6330t.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/tea6330t.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,42 @@ +#ifndef __SOUND_TEA6330T_H +#define __SOUND_TEA6330T_H + +/* + * Routines for control of TEA6330T circuit. + * Sound fader control circuit for car radios. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +#include "control.h" +#include "i2c.h" /* generic i2c support */ + +typedef struct { + snd_i2c_device_t *device; + snd_i2c_bus_t *bus; + int equalizer; + int fader; + unsigned char regs[8]; + unsigned char mleft, mright; + unsigned char bass, treble; + unsigned char max_bass, max_treble; +} tea6330t_t; + +extern int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer); +extern int snd_tea6330t_update_mixer(snd_card_t * card, snd_i2c_bus_t * bus, int equalizer, int fader); + +#endif /* __SOUND_TEA6330T_H */ diff -Nru linux/include/sound/timer.h linux-2.4.19-pre5-mjc/include/sound/timer.h --- linux/include/sound/timer.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/timer.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,137 @@ +#ifndef __SOUND_TIMER_H +#define __SOUND_TIMER_H + +/* + * Timer abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +typedef enum sndrv_timer_class snd_timer_class_t; +typedef enum sndrv_timer_slave_class snd_timer_slave_class_t; +typedef enum sndrv_timer_global snd_timer_global_t; +typedef struct sndrv_timer_id snd_timer_id_t; +typedef struct sndrv_timer_select snd_timer_select_t; +typedef struct sndrv_timer_info snd_timer_info_t; +typedef struct sndrv_timer_params snd_timer_params_t; +typedef struct sndrv_timer_status snd_timer_status_t; +typedef struct sndrv_timer_read snd_timer_read_t; + +#define _snd_timer_chip(timer) ((timer)->private_data) +#define snd_timer_chip(timer) snd_magic_cast1(chip_t, _snd_timer_chip(timer), return -ENXIO) + +#define SNDRV_TIMER_DEVICES 16 + +#define SNDRV_TIMER_DEV_FLG_PCM 0x10000000 + +#define SNDRV_TIMER_HW_AUTO 0x00000001 /* auto trigger is supported */ +#define SNDRV_TIMER_HW_STOP 0x00000002 /* call stop before start */ +#define SNDRV_TIMER_HW_SLAVE 0x00000004 /* only slave timer (variable resolution) */ +#define SNDRV_TIMER_HW_FIRST 0x00000008 /* first tick can be incomplete */ + +#define SNDRV_TIMER_IFLG_SLAVE 0x00000001 +#define SNDRV_TIMER_IFLG_RUNNING 0x00000002 +#define SNDRV_TIMER_IFLG_START 0x00000004 +#define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */ + +#define SNDRV_TIMER_FLG_SYSTEM 0x00000001 /* system timer */ +#define SNDRV_TIMER_FLG_CHANGE 0x00000002 +#define SNDRV_TIMER_FLG_RESCHED 0x00000004 /* need reschedule */ + +typedef void (*snd_timer_callback_t) (snd_timer_instance_t * timeri, unsigned long ticks, unsigned long resolution, void *data); + +struct _snd_timer_hardware { + /* -- must be filled with low-level driver */ + unsigned int flags; /* various flags */ + unsigned long resolution; /* average timer resolution for one tick in nsec */ + unsigned long ticks; /* max timer ticks per interrupt */ + /* -- low-level functions -- */ + int (*open) (snd_timer_t * timer); + int (*close) (snd_timer_t * timer); + unsigned long (*c_resolution) (snd_timer_t * timer); + int (*start) (snd_timer_t * timer); + int (*stop) (snd_timer_t * timer); +}; + +struct _snd_timer { + snd_timer_class_t tmr_class; + snd_card_t *card; + int tmr_device; + int tmr_subdevice; + char id[64]; + char name[80]; + unsigned int flags; + int running; /* running instances */ + unsigned long sticks; /* schedule ticks */ + void *private_data; + void (*private_free) (snd_timer_t *timer); + struct _snd_timer_hardware hw; + spinlock_t lock; + struct list_head device_list; + struct list_head open_list_head; + struct list_head active_list_head; +}; + +struct _snd_timer_instance { + snd_timer_t * timer; + char *owner; + unsigned int flags; + void *private_data; + void (*private_free) (snd_timer_instance_t *ti); + snd_timer_callback_t callback; + void *callback_data; + unsigned long ticks; + unsigned long cticks; + unsigned long lost; /* lost ticks */ + snd_timer_slave_class_t slave_class; + unsigned int slave_id; + struct list_head open_list; + struct list_head active_list; + struct list_head slave_list_head; + struct list_head slave_active_head; + snd_timer_instance_t *master; + atomic_t in_use; /* don't free */ +}; + +/* + * Registering + */ + +extern int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer); +extern int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer); +extern int snd_timer_global_free(snd_timer_t *timer); +extern int snd_timer_global_register(snd_timer_t *timer); +extern int snd_timer_global_unregister(snd_timer_t *timer); + +extern snd_timer_instance_t *snd_timer_open(char *owner, snd_timer_id_t *tid, unsigned int slave_id); +extern int snd_timer_close(snd_timer_instance_t * timeri); +extern int snd_timer_set_owner(snd_timer_instance_t * timeri, pid_t pid, gid_t gid); +extern int snd_timer_reset_owner(snd_timer_instance_t * timeri); +extern int snd_timer_set_resolution(snd_timer_instance_t * timeri, unsigned long resolution); +extern unsigned long snd_timer_resolution(snd_timer_instance_t * timeri); +extern int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks); +extern int snd_timer_stop(snd_timer_instance_t * timeri); +extern int snd_timer_continue(snd_timer_instance_t * timeri); + +extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left); + +extern unsigned int snd_timer_system_resolution(void); + +#endif /* __SOUND_TIMER_H */ diff -Nru linux/include/sound/trident.h linux-2.4.19-pre5-mjc/include/sound/trident.h --- linux/include/sound/trident.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/trident.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,476 @@ +#ifndef __SOUND_TRIDENT_H +#define __SOUND_TRIDENT_H + +/* + * audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Definitions for Trident 4DWave DX/NX chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "mpu401.h" +#include "ac97_codec.h" +#include "seq_midi_emul.h" +#include "seq_device.h" +#include "util_mem.h" +//#include "ainstr_iw.h" +//#include "ainstr_gf1.h" +#include "ainstr_simple.h" + +#ifndef PCI_VENDOR_ID_TRIDENT +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#endif +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#endif +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#endif + +#ifndef PCI_VENDOR_ID_SI +#define PCI_VENDOR_ID_SI 0x1039 +#endif +#ifndef PCI_DEVICE_ID_SI_7018 +#define PCI_DEVICE_ID_SI_7018 0x7018 +#endif + +#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) +#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) +#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) + +/* Trident chipsets have 1GB memory limit */ +#ifdef __alpha__ +#define TRIDENT_DMA_TYPE SNDRV_DMA_TYPE_PCI_16MB +#define TRIDENT_GFP_FLAGS GFP_DMA +#else +#define TRIDENT_DMA_TYPE SNDRV_DMA_TYPE_PCI +#if defined(__i386__) && !defined(CONFIG_1GB) +#define TRIDENT_GFP_FLAGS GFP_DMA +#else +#define TRIDENT_GFP_FLAGS 0 +#endif +#endif + +#define SNDRV_SEQ_DEV_ID_TRIDENT "trident-synth" + +#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 +#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 +#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 + +#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0) + +/* TLB code constants */ +#define SNDRV_TRIDENT_PAGE_SIZE 4096 +#define SNDRV_TRIDENT_PAGE_SHIFT 12 +#define SNDRV_TRIDENT_PAGE_MASK ((1<port + (x)) + +#define ID_4DWAVE_DX 0x2000 +#define ID_4DWAVE_NX 0x2001 + +/* Bank definitions */ + +#define T4D_BANK_A 0 +#define T4D_BANK_B 1 +#define T4D_NUM_BANKS 2 + +/* Register definitions */ + +/* Global registers */ + +enum global_control_bits { + CHANNEL_IDX = 0x0000003f, OVERRUN_IE = 0x00000400, + UNDERRUN_IE = 0x00000800, ENDLP_IE = 0x00001000, + MIDLP_IE = 0x00002000, ETOG_IE = 0x00004000, + EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000 +}; + +enum miscint_bits { + PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, + SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, + OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, + ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100, + REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400, + MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000, + ST_TARGET_REACHED = 0x00008000, + PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000, + ACGPIO_IRQ = 0x01000000 +}; + +/* T2 legacy dma control registers. */ +#define LEGACY_DMAR0 0x00 // ADR0 +#define LEGACY_DMAR4 0x04 // CNT0 +#define LEGACY_DMAR6 0x06 // CNT0 - High bits +#define LEGACY_DMAR11 0x0b // MOD +#define LEGACY_DMAR15 0x0f // MMR + +#define T4D_START_A 0x80 +#define T4D_STOP_A 0x84 +#define T4D_DLY_A 0x88 +#define T4D_SIGN_CSO_A 0x8c +#define T4D_CSPF_A 0x90 +#define T4D_CSPF_B 0xbc +#define T4D_CEBC_A 0x94 +#define T4D_AINT_A 0x98 +#define T4D_AINTEN_A 0x9c +#define T4D_LFO_GC_CIR 0xa0 +#define T4D_MUSICVOL_WAVEVOL 0xa8 +#define T4D_SBDELTA_DELTA_R 0xac +#define T4D_MISCINT 0xb0 +#define T4D_START_B 0xb4 +#define T4D_STOP_B 0xb8 +#define T4D_SBBL_SBCL 0xc0 +#define T4D_SBCTRL_SBE2R_SBDD 0xc4 +#define T4D_STIMER 0xc8 +#define T4D_AINT_B 0xd8 +#define T4D_AINTEN_B 0xdc +#define T4D_RCI 0x70 + +/* MPU-401 UART */ +#define T4D_MPU401_BASE 0x20 +#define T4D_MPUR0 0x20 +#define T4D_MPUR1 0x21 +#define T4D_MPUR2 0x22 +#define T4D_MPUR3 0x23 + +/* S/PDIF Registers */ +#define NX_SPCTRL_SPCSO 0x24 +#define NX_SPLBA 0x28 +#define NX_SPESO 0x2c +#define NX_SPCSTATUS 0x64 + +/* NX Specific Registers */ +#define NX_TLBC 0x6c + +/* Channel Registers */ + +#define CH_START 0xe0 + +#define CH_DX_CSO_ALPHA_FMS 0xe0 +#define CH_DX_ESO_DELTA 0xe8 +#define CH_DX_FMC_RVOL_CVOL 0xec + +#define CH_NX_DELTA_CSO 0xe0 +#define CH_NX_DELTA_ESO 0xe8 +#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec + +#define CH_LBA 0xe4 +#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 +#define CH_EBUF1 0xf4 +#define CH_EBUF2 0xf8 + +/* AC-97 Registers */ + +#define DX_ACR0_AC97_W 0x40 +#define DX_ACR1_AC97_R 0x44 +#define DX_ACR2_AC97_COM_STAT 0x48 + +#define NX_ACR0_AC97_COM_STAT 0x40 +#define NX_ACR1_AC97_W 0x44 +#define NX_ACR2_AC97_R_PRIMARY 0x48 +#define NX_ACR3_AC97_R_SECONDARY 0x4c + +#define SI_AC97_WRITE 0x40 +#define SI_AC97_READ 0x44 +#define SI_SERIAL_INTF_CTRL 0x48 +#define SI_AC97_GPIO 0x4c +#define SI_ASR0 0x50 +#define SI_SPDIF_CS 0x70 +#define SI_GPIO 0x7c + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, + NX_AC97_BUSY_READ = 0x0800, + NX_AC97_BUSY_DATA = 0x0400, + NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, + NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, + NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, + DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, + DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x00008000, + SI_AC97_AUDIO_BUSY = 0x00004000, + SI_AC97_MODEM_BUSY = 0x00002000, + SI_AC97_BUSY_READ = 0x00008000, + SI_AC97_SECONDARY = 0x00000080, +}; + +enum serial_intf_ctrl_bits { + WARM_RESET = 0x00000001, + COLD_RESET = 0x00000002, + I2S_CLOCK = 0x00000004, + PCM_SEC_AC97 = 0x00000008, + AC97_DBL_RATE = 0x00000010, + SPDIF_EN = 0x00000020, + I2S_OUTPUT_EN = 0x00000040, + I2S_INPUT_EN = 0x00000080, + PCMIN = 0x00000100, + LINE1IN = 0x00000200, + MICIN = 0x00000400, + LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, + GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00004000, */ + SECONDARY_ID = 0x00004000, + PCMOUT = 0x00010000, + SURROUT = 0x00020000, + CENTEROUT = 0x00040000, + LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, + LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, + SI_AC97_POWERDOWN = 0x04000000, +}; + +/* PCM defaults */ + +#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */ +#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */ + +typedef struct _snd_trident trident_t; +typedef struct _snd_trident_voice snd_trident_voice_t; +typedef struct _snd_trident_pcm_mixer snd_trident_pcm_mixer_t; + +typedef struct { + void (*sample_start)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_position_t position); + void (*sample_stop)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_stop_mode_t mode); + void (*sample_freq)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_frequency_t freq); + void (*sample_volume)(trident_t *gus, snd_trident_voice_t *voice, snd_seq_ev_volume_t *volume); + void (*sample_loop)(trident_t *card, snd_trident_voice_t *voice, snd_seq_ev_loop_t *loop); + void (*sample_pos)(trident_t *card, snd_trident_voice_t *voice, snd_seq_position_t position); + void (*sample_private1)(trident_t *card, snd_trident_voice_t *voice, unsigned char *data); +} snd_trident_sample_ops_t; + +typedef struct { + snd_midi_channel_set_t * chset; + trident_t * trident; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + int midi_has_voices: 1; +} snd_trident_port_t; + +typedef struct snd_trident_memblk_arg { + short first_page, last_page; +} snd_trident_memblk_arg_t; + +typedef struct { + unsigned int * entries; /* 16k-aligned TLB table */ + dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ + unsigned long * shadow_entries; /* shadow entries with virtual addresses */ + void * buffer; /* pointer for table calloc */ + dma_addr_t buffer_dmaaddr; /* not accessible PCI BUS physical address */ + snd_util_memhdr_t * memhdr; /* page allocation list */ + void * silent_page; /* silent page */ + dma_addr_t silent_page_dmaaddr; /* not accessible PCI BUS physical address */ +} snd_trident_tlb_t; + +struct _snd_trident_voice { + unsigned int number; + int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + + snd_seq_instr_t instr; + snd_trident_sample_ops_t *sample_ops; + + /* channel parameters */ + unsigned int CSO; /* 24 bits (16 on DX) */ + unsigned int ESO; /* 24 bits (16 on DX) */ + unsigned int LBA; /* 30 bits */ + unsigned short EC; /* 12 bits */ + unsigned short Alpha; /* 12 bits */ + unsigned short Delta; /* 16 bits */ + unsigned short Attribute; /* 16 bits - SiS 7018 */ + unsigned short Vol; /* 12 bits (6.6) */ + unsigned char Pan; /* 7 bits (1.4.2) */ + unsigned char GVSel; /* 1 bit */ + unsigned char RVol; /* 7 bits (5.2) */ + unsigned char CVol; /* 7 bits (5.2) */ + unsigned char FMC; /* 2 bits */ + unsigned char CTRL; /* 4 bits */ + unsigned char FMS; /* 4 bits */ + unsigned char LFO; /* 8 bits */ + + unsigned int negCSO; /* nonzero - use negative CSO */ + + snd_util_memblk_t *memblk; /* memory block if TLB enabled */ + + /* PCM data */ + + trident_t *trident; + snd_pcm_substream_t *substream; + snd_trident_voice_t *extra; /* extra PCM voice (acts as interrupt generator) */ + int running: 1, + capture: 1, + spdif: 1, + foldback: 1; + int foldback_chan; /* foldback subdevice number */ + unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ + unsigned int spurious_threshold; /* spurious threshold */ + + /* --- */ + + void *private_data; + void (*private_free)(snd_trident_voice_t *voice); +}; + +struct _snd_4dwave { + int seq_client; + + snd_trident_port_t seq_ports[4]; + snd_simple_ops_t simple_ops; + snd_seq_kinstr_list_t *ilist; + + snd_trident_voice_t voices[64]; + + int ChanSynthCount; /* number of allocated synth channels */ + int max_size; /* maximum synth memory size in bytes */ + int current_size; /* current allocated synth mem in bytes */ +}; + +struct _snd_trident_pcm_mixer { + snd_trident_voice_t *voice; /* active voice */ + unsigned short vol; /* front volume */ + unsigned char pan; /* pan control */ + unsigned char rvol; /* rear volume */ + unsigned char cvol; /* center volume */ + unsigned char pad; + snd_kcontrol_t *ctl_vol; /* front volume */ + snd_kcontrol_t *ctl_pan; /* pan */ + snd_kcontrol_t *ctl_rvol; /* rear volume */ + snd_kcontrol_t *ctl_cvol; /* center volume */ +}; + +struct _snd_trident { + int irq; + + unsigned int device; /* device ID */ + + unsigned char bDMAStart; + + unsigned long port; + struct resource *res_port; + unsigned long midi_port; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + snd_trident_tlb_t tlb; /* TLB entries for NX cards */ + + unsigned char spdif_ctrl; + unsigned char spdif_pcm_ctrl; + unsigned int spdif_bits; + unsigned int spdif_pcm_bits; + snd_kcontrol_t *spdif_pcm_ctl; /* S/PDIF settings */ + unsigned int ac97_ctrl; + + unsigned int ChanMap[2]; /* allocation map for hardware channels */ + + int ChanPCM; /* max number of PCM channels */ + int ChanPCMcnt; /* actual number of PCM channels */ + + struct _snd_4dwave synth; /* synth specific variables */ + + spinlock_t event_lock; + spinlock_t voice_alloc; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; /* ADC/DAC PCM */ + snd_pcm_t *foldback; /* Foldback PCM */ + snd_pcm_t *spdif; /* SPDIF PCM */ + snd_rawmidi_t *rmidi; + snd_seq_device_t *seq_dev; + + ac97_t *ac97; + + unsigned int musicvol_wavevol; + snd_trident_pcm_mixer_t pcm_mixer[32]; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; +}; + +int snd_trident_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + trident_t ** rtrident); +int snd_trident_free(trident_t *trident); + +int snd_trident_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t **rpcm); +int snd_trident_attach_synthesizer(trident_t * trident); +int snd_trident_detach_synthesizer(trident_t * trident); +snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port); +void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice); +void snd_trident_start_voice(trident_t * trident, unsigned int voice); +void snd_trident_stop_voice(trident_t * trident, unsigned int voice); +void snd_trident_write_voice_regs(trident_t * trident, snd_trident_voice_t *voice); +void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max); + +/* TLB memory allocation */ +snd_util_memblk_t *snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size); +int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk); +snd_util_memblk_t *snd_trident_synth_alloc(trident_t *trident, unsigned int size); +int snd_trident_synth_free(trident_t *trident, snd_util_memblk_t *blk); +int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size); +int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size); + +/* Power Management */ +#ifdef CONFIG_PM +void snd_trident_suspend(trident_t *trident); +void snd_trident_resume(trident_t *trident); +#endif + +#endif /* __SOUND_TRIDENT_H */ diff -Nru linux/include/sound/util_mem.h linux-2.4.19-pre5-mjc/include/sound/util_mem.h --- linux/include/sound/util_mem.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/util_mem.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,64 @@ +#ifndef __SOUND_UTIL_MEM_H +#define __SOUND_UTIL_MEM_H +/* + * Copyright (C) 2000 Takashi Iwai + * + * Generic memory management routines for soundcard memory allocation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct snd_util_memblk snd_util_memblk_t; +typedef struct snd_util_memhdr snd_util_memhdr_t; +typedef unsigned int snd_util_unit_t; + +/* + * memory block + */ +struct snd_util_memblk { + snd_util_unit_t size; /* size of this block */ + snd_util_unit_t offset; /* zero-offset of this block */ + struct list_head list; /* link */ +}; + +#define snd_util_memblk_argptr(blk) (void*)((char*)(blk) + sizeof(snd_util_memblk_t)) + +/* + * memory management information + */ +struct snd_util_memhdr { + snd_util_unit_t size; /* size of whole data */ + struct list_head block; /* block linked-list header */ + int nblocks; /* # of allocated blocks */ + snd_util_unit_t used; /* used memory size */ + int block_extra_size; /* extra data size of chunk */ + struct semaphore block_mutex; /* lock */ +}; + +/* + * prototypes + */ +snd_util_memhdr_t *snd_util_memhdr_new(int memsize); +void snd_util_memhdr_free(snd_util_memhdr_t *hdr); +snd_util_memblk_t *snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size); +int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk); +int snd_util_mem_avail(snd_util_memhdr_t *hdr); + +/* functions without mutex */ +snd_util_memblk_t *__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size); +void __snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk); +snd_util_memblk_t *__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, struct list_head *prev); + +#endif /* __SOUND_UTIL_MEM_H */ diff -Nru linux/include/sound/version.h linux-2.4.19-pre5-mjc/include/sound/version.h --- linux/include/sound/version.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/version.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,3 @@ +/* include/version.h. Generated automatically by configure. */ +#define CONFIG_SND_VERSION "0.9.0beta12" +#define CONFIG_SND_DATE " (Wed Mar 06 07:56:20 2002 UTC)" diff -Nru linux/include/sound/wavefront.h linux-2.4.19-pre5-mjc/include/sound/wavefront.h --- linux/include/sound/wavefront.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/wavefront.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,695 @@ +#ifndef __SOUND_WAVEFRONT_H__ +#define __SOUND_WAVEFRONT_H__ + +/* + * Driver for Turtle Beach Wavefront cards (Maui,Tropez,Tropez+) + * + * Copyright (c) by Paul Barton-Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if (!defined(__GNUC__) && !defined(__GNUG__)) + + You will not be able to compile this file correctly without gcc, because + it is necessary to pack the "wavefront_alias" structure to a size + of 22 bytes, corresponding to 16-bit alignment (as would have been + the case on the original platform, MS-DOS). If this is not done, + then WavePatch-format files cannot be read/written correctly. + The method used to do this here ("__attribute__((packed)") is + completely compiler dependent. + + All other wavefront_* types end up aligned to 32 bit values and + still have the same (correct) size. + +#else + + /* However, note that as of G++ 2.7.3.2, g++ was unable to + correctly parse *type* __attribute__ tags. It will do the + right thing if we use the "packed" attribute on each struct + member, which has the same semantics anyway. + */ + +#endif /* __GNUC__ */ + +/***************************** WARNING ******************************** + PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO + BE USED WITH EITHER C *OR* C++. + **********************************************************************/ + +#ifndef NUM_MIDIKEYS +#define NUM_MIDIKEYS 128 +#endif /* NUM_MIDIKEYS */ + +#ifndef NUM_MIDICHANNELS +#define NUM_MIDICHANNELS 16 +#endif /* NUM_MIDICHANNELS */ + +/* These are very useful/important. the original wavefront interface + was developed on a 16 bit system, where sizeof(int) = 2 + bytes. Defining things like this makes the code much more portable, and + easier to understand without having to toggle back and forth + between a 16-bit view of the world and a 32-bit one. + */ + +#ifndef __KERNEL__ +/* keep them for compatibility */ +typedef short s16; +typedef unsigned short u16; +typedef int s32; +typedef unsigned int u32; +typedef char s8; +typedef unsigned char u8; +typedef s16 INT16; +typedef u16 UINT16; +typedef s32 INT32; +typedef u32 UINT32; +typedef s8 CHAR8; +typedef u8 UCHAR8; +#endif + +/* Pseudo-commands not part of the WaveFront command set. + These are used for various driver controls and direct + hardware control. + */ + +#define WFC_DEBUG_DRIVER 0 +#define WFC_FX_IOCTL 1 +#define WFC_PATCH_STATUS 2 +#define WFC_PROGRAM_STATUS 3 +#define WFC_SAMPLE_STATUS 4 +#define WFC_DISABLE_INTERRUPTS 5 +#define WFC_ENABLE_INTERRUPTS 6 +#define WFC_INTERRUPT_STATUS 7 +#define WFC_ROMSAMPLES_RDONLY 8 +#define WFC_IDENTIFY_SLOT_TYPE 9 + +/* Wavefront synth commands + */ + +#define WFC_DOWNLOAD_SAMPLE 0x80 +#define WFC_DOWNLOAD_BLOCK 0x81 +#define WFC_DOWNLOAD_MULTISAMPLE 0x82 +#define WFC_DOWNLOAD_SAMPLE_ALIAS 0x83 +#define WFC_DELETE_SAMPLE 0x84 +#define WFC_REPORT_FREE_MEMORY 0x85 +#define WFC_DOWNLOAD_PATCH 0x86 +#define WFC_DOWNLOAD_PROGRAM 0x87 +#define WFC_SET_SYNTHVOL 0x89 +#define WFC_SET_NVOICES 0x8B +#define WFC_DOWNLOAD_DRUM 0x90 +#define WFC_GET_SYNTHVOL 0x92 +#define WFC_GET_NVOICES 0x94 +#define WFC_DISABLE_CHANNEL 0x9A +#define WFC_ENABLE_CHANNEL 0x9B +#define WFC_MISYNTH_OFF 0x9D +#define WFC_MISYNTH_ON 0x9E +#define WFC_FIRMWARE_VERSION 0x9F +#define WFC_GET_NSAMPLES 0xA0 +#define WFC_DISABLE_DRUM_PROGRAM 0xA2 +#define WFC_UPLOAD_PATCH 0xA3 +#define WFC_UPLOAD_PROGRAM 0xA4 +#define WFC_SET_TUNING 0xA6 +#define WFC_GET_TUNING 0xA7 +#define WFC_VMIDI_ON 0xA8 +#define WFC_VMIDI_OFF 0xA9 +#define WFC_MIDI_STATUS 0xAA +#define WFC_GET_CHANNEL_STATUS 0xAB +#define WFC_DOWNLOAD_SAMPLE_HEADER 0xAC +#define WFC_UPLOAD_SAMPLE_HEADER 0xAD +#define WFC_UPLOAD_MULTISAMPLE 0xAE +#define WFC_UPLOAD_SAMPLE_ALIAS 0xAF +#define WFC_IDENTIFY_SAMPLE_TYPE 0xB0 +#define WFC_DOWNLOAD_EDRUM_PROGRAM 0xB1 +#define WFC_UPLOAD_EDRUM_PROGRAM 0xB2 +#define WFC_SET_EDRUM_CHANNEL 0xB3 +#define WFC_INSTOUT_LEVELS 0xB4 +#define WFC_PEAKOUT_LEVELS 0xB5 +#define WFC_REPORT_CHANNEL_PROGRAMS 0xB6 +#define WFC_HARDWARE_VERSION 0xCF +#define WFC_UPLOAD_SAMPLE_PARAMS 0xD7 +#define WFC_DOWNLOAD_OS 0xF1 +#define WFC_NOOP 0xFF + +#define WF_MAX_SAMPLE 512 +#define WF_MAX_PATCH 256 +#define WF_MAX_PROGRAM 128 + +#define WF_SECTION_MAX 44 /* longest OS section length */ + +/* # of bytes we send to the board when sending it various kinds of + substantive data, such as samples, patches and programs. +*/ + +#define WF_PROGRAM_BYTES 32 +#define WF_PATCH_BYTES 132 +#define WF_SAMPLE_BYTES 27 +#define WF_SAMPLE_HDR_BYTES 25 +#define WF_ALIAS_BYTES 25 +#define WF_DRUM_BYTES 9 +#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */ + +#define WF_ACK 0x80 +#define WF_DMA_ACK 0x81 + +/* OR-values for MIDI status bits */ + +#define WF_MIDI_VIRTUAL_ENABLED 0x1 +#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2 +#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4 + +/* slot indexes for struct address_info: makes code a little more mnemonic */ + +#define WF_SYNTH_SLOT 0 +#define WF_INTERNAL_MIDI_SLOT 1 +#define WF_EXTERNAL_MIDI_SLOT 2 + +/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401 + emulation. Note these NEVER show up in output from the device and + should NEVER be used in input unless Virtual MIDI mode has been + disabled. If they do show up as input, the results are unpredictable. +*/ + +#define WF_EXTERNAL_SWITCH 0xFD +#define WF_INTERNAL_SWITCH 0xF9 + +/* Debugging flags */ + +#define WF_DEBUG_CMD 0x1 +#define WF_DEBUG_DATA 0x2 +#define WF_DEBUG_LOAD_PATCH 0x4 +#define WF_DEBUG_IO 0x8 + +/* WavePatch file format stuff */ + +#define WF_WAVEPATCH_VERSION 120; /* Current version number (1.2) */ +#define WF_MAX_COMMENT 64 /* Comment length */ +#define WF_NUM_LAYERS 4 +#define WF_NAME_LENGTH 32 +#define WF_SOURCE_LENGTH 260 + +#define BankFileID "Bank" +#define DrumkitFileID "DrumKit" +#define ProgramFileID "Program" + +struct wf_envelope +{ + u8 attack_time:7; + u8 Unused1:1; + + u8 decay1_time:7; + u8 Unused2:1; + + u8 decay2_time:7; + u8 Unused3:1; + + u8 sustain_time:7; + u8 Unused4:1; + + u8 release_time:7; + u8 Unused5:1; + + u8 release2_time:7; + u8 Unused6:1; + + s8 attack_level; + s8 decay1_level; + s8 decay2_level; + s8 sustain_level; + s8 release_level; + + u8 attack_velocity:7; + u8 Unused7:1; + + u8 volume_velocity:7; + u8 Unused8:1; + + u8 keyboard_scaling:7; + u8 Unused9:1; +}; +typedef struct wf_envelope wavefront_envelope; + +struct wf_lfo +{ + u8 sample_number; + + u8 frequency:7; + u8 Unused1:1; + + u8 am_src:4; + u8 fm_src:4; + + s8 fm_amount; + s8 am_amount; + s8 start_level; + s8 end_level; + + u8 ramp_delay:7; + u8 wave_restart:1; /* for LFO2 only */ + + u8 ramp_time:7; + u8 Unused2:1; +}; +typedef struct wf_lfo wavefront_lfo; + +struct wf_patch +{ + s16 frequency_bias; /* ** THIS IS IN MOTOROLA FORMAT!! ** */ + + u8 amplitude_bias:7; + u8 Unused1:1; + + u8 portamento:7; + u8 Unused2:1; + + u8 sample_number; + + u8 pitch_bend:4; + u8 sample_msb:1; + u8 Unused3:3; + + u8 mono:1; + u8 retrigger:1; + u8 nohold:1; + u8 restart:1; + u8 filterconfig:2; /* SDK says "not used" */ + u8 reuse:1; + u8 reset_lfo:1; + + u8 fm_src2:4; + u8 fm_src1:4; + + s8 fm_amount1; + s8 fm_amount2; + + u8 am_src:4; + u8 Unused4:4; + + s8 am_amount; + + u8 fc1_mode:4; + u8 fc2_mode:4; + + s8 fc1_mod_amount; + s8 fc1_keyboard_scaling; + s8 fc1_bias; + s8 fc2_mod_amount; + s8 fc2_keyboard_scaling; + s8 fc2_bias; + + u8 randomizer:7; + u8 Unused5:1; + + struct wf_envelope envelope1; + struct wf_envelope envelope2; + struct wf_lfo lfo1; + struct wf_lfo lfo2; +}; +typedef struct wf_patch wavefront_patch; + +struct wf_layer +{ + u8 patch_number; + + u8 mix_level:7; + u8 mute:1; + + u8 split_point:7; + u8 play_below:1; + + u8 pan_mod_src:2; + u8 pan_or_mod:1; + u8 pan:4; + u8 split_type:1; +}; +typedef struct wf_layer wavefront_layer; + +struct wf_program +{ + struct wf_layer layer[WF_NUM_LAYERS]; +}; +typedef struct wf_program wavefront_program; + +struct wf_sample_offset +{ + s32 Fraction:4; + s32 Integer:20; + s32 Unused:8; +}; +typedef struct wf_sample_offset wavefront_sample_offset; + +/* Sample slot types */ + +#define WF_ST_SAMPLE 0 +#define WF_ST_MULTISAMPLE 1 +#define WF_ST_ALIAS 2 +#define WF_ST_EMPTY 3 + +/* pseudo's */ + +#define WF_ST_DRUM 4 +#define WF_ST_PROGRAM 5 +#define WF_ST_PATCH 6 +#define WF_ST_SAMPLEHDR 7 + +#define WF_ST_MASK 0xf + +/* Flags for slot status. These occupy the upper bits of the same byte + as a sample type. +*/ + +#define WF_SLOT_USED 0x80 /* XXX don't rely on this being accurate */ +#define WF_SLOT_FILLED 0x40 +#define WF_SLOT_ROM 0x20 + +#define WF_SLOT_MASK 0xf0 + +/* channel constants */ + +#define WF_CH_MONO 0 +#define WF_CH_LEFT 1 +#define WF_CH_RIGHT 2 + +/* Sample formats */ + +#define LINEAR_16BIT 0 +#define WHITE_NOISE 1 +#define LINEAR_8BIT 2 +#define MULAW_8BIT 3 + +#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2) + + +/* + + Because most/all of the sample data we pass in via pointers has + never been copied (just mmap-ed into user space straight from the + disk), it would be nice to allow handling of multi-channel sample + data without forcing user-level extraction of the relevant bytes. + + So, we need a way of specifying which channel to use (the WaveFront + only handles mono samples in a given slot), and the only way to do + this without using some struct other than wavefront_sample as the + interface is the awful hack of using the unused bits in a + wavefront_sample: + + Val Meaning + --- ------- + 0 no channel selection (use channel 1, sample is MONO) + 1 use first channel, and skip one + 2 use second channel, and skip one + 3 use third channel, and skip two + 4 use fourth channel, skip three + 5 use fifth channel, skip four + 6 use six channel, skip five + + + This can handle up to 4 channels, and anyone downloading >4 channels + of sample data just to select one of them needs to find some tools + like sox ... + + NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is + important. + +*/ + +#define WF_SET_CHANNEL(samp,chn) \ + (samp)->Unused1 = chn & 0x1; \ + (samp)->Unused2 = chn & 0x2; \ + (samp)->Unused3 = chn & 0x4 + +#define WF_GET_CHANNEL(samp) \ + (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1) + +typedef struct wf_sample { + struct wf_sample_offset sampleStartOffset; + struct wf_sample_offset loopStartOffset; + struct wf_sample_offset loopEndOffset; + struct wf_sample_offset sampleEndOffset; + s16 FrequencyBias; + u8 SampleResolution:2; /* sample_format */ + u8 Unused1:1; + u8 Loop:1; + u8 Bidirectional:1; + u8 Unused2:1; + u8 Reverse:1; + u8 Unused3:1; +} wavefront_sample; + +typedef struct wf_multisample { + s16 NumberOfSamples; /* log2 of the number of samples */ + s16 SampleNumber[NUM_MIDIKEYS]; +} wavefront_multisample; + +typedef struct wf_alias { + s16 OriginalSample __attribute__ ((packed)); + + struct wf_sample_offset sampleStartOffset __attribute__ ((packed)); + struct wf_sample_offset loopStartOffset __attribute__ ((packed)); + struct wf_sample_offset sampleEndOffset __attribute__ ((packed)); + struct wf_sample_offset loopEndOffset __attribute__ ((packed)); + + s16 FrequencyBias __attribute__ ((packed)); + + u8 SampleResolution:2 __attribute__ ((packed)); + u8 Unused1:1 __attribute__ ((packed)); + u8 Loop:1 __attribute__ ((packed)); + u8 Bidirectional:1 __attribute__ ((packed)); + u8 Unused2:1 __attribute__ ((packed)); + u8 Reverse:1 __attribute__ ((packed)); + u8 Unused3:1 __attribute__ ((packed)); + + /* This structure is meant to be padded only to 16 bits on their + original. Of course, whoever wrote their documentation didn't + realize that sizeof(struct) can be >= + sum(sizeof(struct-fields)) and so thought that giving a C level + description of the structs used in WavePatch files was + sufficient. I suppose it was, as long as you remember the + standard 16->32 bit issues. + */ + + u8 sixteen_bit_padding __attribute__ ((packed)); +} wavefront_alias; + +typedef struct wf_drum { + u8 PatchNumber; + u8 MixLevel:7; + u8 Unmute:1; + u8 Group:4; + u8 Unused1:4; + u8 PanModSource:2; + u8 PanModulated:1; + u8 PanAmount:4; + u8 Unused2:1; +} wavefront_drum; + +typedef struct wf_drumkit { + struct wf_drum drum[NUM_MIDIKEYS]; +} wavefront_drumkit; + +typedef struct wf_channel_programs { + u8 Program[NUM_MIDICHANNELS]; +} wavefront_channel_programs; + +/* How to get MIDI channel status from the data returned by + a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs) +*/ + +#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7)) + +typedef union wf_any { + wavefront_sample s; + wavefront_multisample ms; + wavefront_alias a; + wavefront_program pr; + wavefront_patch p; + wavefront_drum d; +} wavefront_any; + +/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h + might work for other wave-table based patch loading situations. + Alas, his fears were correct. The WaveFront doesn't even come with + just "patches", but several different kind of structures that + control the sound generation process. + */ + +typedef struct wf_patch_info { + + /* the first two fields are used by the OSS "patch loading" interface + only, and are unused by the current user-level library. + */ + + s16 key; /* Use WAVEFRONT_PATCH here */ + u16 devno; /* fill in when sending */ + u8 subkey; /* WF_ST_{SAMPLE,ALIAS,etc.} */ + +#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999 + + u16 number; /* patch/sample/prog number */ + + u32 size; /* size of any data included in + one of the fields in `hdrptr', or + as `dataptr'. + + NOTE: for actual samples, this is + the size of the *SELECTED CHANNEL* + even if more data is actually available. + + So, a stereo sample (2 channels) of + 6000 bytes total has `size' = 3000. + + See the macros and comments for + WF_{GET,SET}_CHANNEL above. + + */ + wavefront_any *hdrptr; /* user-space ptr to hdr bytes */ + u16 *dataptr; /* actual sample data */ + + wavefront_any hdr; /* kernel-space copy of hdr bytes */ +} wavefront_patch_info; + +/* The maximum number of bytes we will ever move to or from user space + in response to a WFC_* command. This obviously doesn't cover + actual sample data. +*/ + +#define WF_MAX_READ sizeof(wavefront_multisample) +#define WF_MAX_WRITE sizeof(wavefront_multisample) + +/* + This allows us to execute any WF command except the download/upload + ones, which are handled differently due to copyin/copyout issues as + well as data-nybbling to/from the card. + */ + +typedef struct wavefront_control { + int cmd; /* WFC_* */ + char status; /* return status to user-space */ + unsigned char rbuf[WF_MAX_READ]; /* bytes read from card */ + unsigned char wbuf[WF_MAX_WRITE]; /* bytes written to card */ +} wavefront_control; + +#define WFCTL_WFCMD 0x1 +#define WFCTL_LOAD_SPP 0x2 + +/* Modulator table */ + +#define WF_MOD_LFO1 0 +#define WF_MOD_LFO2 1 +#define WF_MOD_ENV1 2 +#define WF_MOD_ENV2 3 +#define WF_MOD_KEYBOARD 4 +#define WF_MOD_LOGKEY 5 +#define WF_MOD_VELOCITY 6 +#define WF_MOD_LOGVEL 7 +#define WF_MOD_RANDOM 8 +#define WF_MOD_PRESSURE 9 +#define WF_MOD_MOD_WHEEL 10 +#define WF_MOD_1 WF_MOD_MOD_WHEEL +#define WF_MOD_BREATH 11 +#define WF_MOD_2 WF_MOD_BREATH +#define WF_MOD_FOOT 12 +#define WF_MOD_4 WF_MOD_FOOT +#define WF_MOD_VOLUME 13 +#define WF_MOD_7 WF_MOD_VOLUME +#define WF_MOD_PAN 14 +#define WF_MOD_10 WF_MOD_PAN +#define WF_MOD_EXPR 15 +#define WF_MOD_11 WF_MOD_EXPR + +/* FX-related material */ + +typedef struct wf_fx_info { + int request; /* see list below */ + long data[4]; /* we don't need much */ +} wavefront_fx_info; + +/* support for each of these will be forthcoming once I or someone + else has figured out which of the addresses on page 6 and page 7 of + the YSS225 control each parameter. Incidentally, these come from + the Windows driver interface, but again, Turtle Beach didn't + document the API to use them. +*/ + +#define WFFX_SETOUTGAIN 0 +#define WFFX_SETSTEREOOUTGAIN 1 +#define WFFX_SETREVERBIN1GAIN 2 +#define WFFX_SETREVERBIN2GAIN 3 +#define WFFX_SETREVERBIN3GAIN 4 +#define WFFX_SETCHORUSINPORT 5 +#define WFFX_SETREVERBIN1PORT 6 +#define WFFX_SETREVERBIN2PORT 7 +#define WFFX_SETREVERBIN3PORT 8 +#define WFFX_SETEFFECTPORT 9 +#define WFFX_SETAUXPORT 10 +#define WFFX_SETREVERBTYPE 11 +#define WFFX_SETREVERBDELAY 12 +#define WFFX_SETCHORUSLFO 13 +#define WFFX_SETCHORUSPMD 14 +#define WFFX_SETCHORUSAMD 15 +#define WFFX_SETEFFECT 16 +#define WFFX_SETBASEALL 17 +#define WFFX_SETREVERBALL 18 +#define WFFX_SETCHORUSALL 20 +#define WFFX_SETREVERBDEF 22 +#define WFFX_SETCHORUSDEF 23 +#define WFFX_DELAYSETINGAIN 24 +#define WFFX_DELAYSETFBGAIN 25 +#define WFFX_DELAYSETFBLPF 26 +#define WFFX_DELAYSETGAIN 27 +#define WFFX_DELAYSETTIME 28 +#define WFFX_DELAYSETFBTIME 29 +#define WFFX_DELAYSETALL 30 +#define WFFX_DELAYSETDEF 32 +#define WFFX_SDELAYSETINGAIN 33 +#define WFFX_SDELAYSETFBGAIN 34 +#define WFFX_SDELAYSETFBLPF 35 +#define WFFX_SDELAYSETGAIN 36 +#define WFFX_SDELAYSETTIME 37 +#define WFFX_SDELAYSETFBTIME 38 +#define WFFX_SDELAYSETALL 39 +#define WFFX_SDELAYSETDEF 41 +#define WFFX_DEQSETINGAIN 42 +#define WFFX_DEQSETFILTER 43 +#define WFFX_DEQSETALL 44 +#define WFFX_DEQSETDEF 46 +#define WFFX_MUTE 47 +#define WFFX_FLANGESETBALANCE 48 +#define WFFX_FLANGESETDELAY 49 +#define WFFX_FLANGESETDWFFX_TH 50 +#define WFFX_FLANGESETFBGAIN 51 +#define WFFX_FLANGESETINGAIN 52 +#define WFFX_FLANGESETLFO 53 +#define WFFX_FLANGESETALL 54 +#define WFFX_FLANGESETDEF 56 +#define WFFX_PITCHSETSHIFT 57 +#define WFFX_PITCHSETBALANCE 58 +#define WFFX_PITCHSETALL 59 +#define WFFX_PITCHSETDEF 61 +#define WFFX_SRSSETINGAIN 62 +#define WFFX_SRSSETSPACE 63 +#define WFFX_SRSSETCENTER 64 +#define WFFX_SRSSETGAIN 65 +#define WFFX_SRSSETMODE 66 +#define WFFX_SRSSETDEF 68 + +/* Allow direct user-space control over FX memory/coefficient data. + In theory this could be used to download the FX microprogram, + but it would be a little slower, and involve some wierd code. + */ + +#define WFFX_MEMSET 69 + +#endif /* __SOUND_WAVEFRONT_H__ */ diff -Nru linux/include/sound/wavefront_fx.h linux-2.4.19-pre5-mjc/include/sound/wavefront_fx.h --- linux/include/sound/wavefront_fx.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/wavefront_fx.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,9 @@ +#ifndef __SOUND_WAVEFRONT_FX_H +#define __SOUND_WAVEFRONT_FX_H + +extern int snd_wavefront_fx_detect (snd_wavefront_t *); +extern void snd_wavefront_fx_ioctl (snd_synth_t *sdev, + unsigned int cmd, + unsigned long arg); + +#endif __SOUND_WAVEFRONT_FX_H diff -Nru linux/include/sound/ymfpci.h linux-2.4.19-pre5-mjc/include/sound/ymfpci.h --- linux/include/sound/ymfpci.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/ymfpci.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,369 @@ +#ifndef __SOUND_YMFPCI_H +#define __SOUND_YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "pcm.h" +#include "rawmidi.h" +#include "ac97_codec.h" + +#ifndef PCI_VENDOR_ID_YAMAHA +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_724 +#define PCI_DEVICE_ID_YAMAHA_724 0x0004 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_724F +#define PCI_DEVICE_ID_YAMAHA_724F 0x000d +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_740 +#define PCI_DEVICE_ID_YAMAHA_740 0x000a +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_740C +#define PCI_DEVICE_ID_YAMAHA_740C 0x000c +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_744 +#define PCI_DEVICE_ID_YAMAHA_744 0x0010 +#endif +#ifndef PCI_DEVICE_ID_YAMAHA_754 +#define PCI_DEVICE_ID_YAMAHA_754 0x0012 +#endif + +/* + * Direct registers + */ + +#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg) + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_ZVOUTVOL 0x0088 +#define YDSXGR_ZVOUTVOLL 0x0088 +#define YDSXGR_ZVOUTVOLR 0x008A +#define YDSXGR_SECADCOUTVOL 0x008C +#define YDSXGR_SECADCOUTVOLL 0x008C +#define YDSXGR_SECADCOUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_ZVLOOPVOL 0x009C +#define YDSXGR_ZVLOOPVOLL 0x009E +#define YDSXGR_ZVLOOPVOLR 0x009E +#define YDSXGR_SECADCLOOPVOL 0x00A0 +#define YDSXGR_SECADCLOOPVOLL 0x00A0 +#define YDSXGR_SECADCLOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL 0x00B8 +#define YDSXGR_SPDIFOUTVOLL 0x00B8 +#define YDSXGR_SPDIFOUTVOLR 0x00BA +#define YDSXGR_SPDIFLOOPVOL 0x00BC +#define YDSXGR_SPDIFLOOPVOLL 0x00BC +#define YDSXGR_SPDIFLOOPVOLR 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_DSXG_LEGACY 0x40 +#define PCIR_DSXG_ELEGACY 0x42 +#define PCIR_DSXG_CTRL 0x48 +#define PCIR_DSXG_PWRCTRL1 0x4a +#define PCIR_DSXG_PWRCTRL2 0x4e +#define PCIR_DSXG_FMBASE 0x60 +#define PCIR_DSXG_SBBASE 0x62 +#define PCIR_DSXG_MPU401BASE 0x64 +#define PCIR_DSXG_JOYBASE 0x66 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +/* + * + */ + +typedef struct _snd_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; + u32 num_of_frames; + u32 loop_count; + u32 start; + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; +} snd_ymfpci_playback_bank_t; + +typedef struct _snd_ymfpci_capture_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +} snd_ymfpci_capture_bank_t; + +typedef struct _snd_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +} snd_ymfpci_effect_bank_t; + +typedef struct _snd_ymfpci_voice ymfpci_voice_t; +typedef struct _snd_ymfpci_pcm ymfpci_pcm_t; +typedef struct _snd_ymfpci ymfpci_t; + +typedef enum { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +} ymfpci_voice_type_t; + +struct _snd_ymfpci_voice { + ymfpci_t *chip; + int number; + int use: 1, + pcm: 1, + synth: 1, + midi: 1; + snd_ymfpci_playback_bank_t *bank; + dma_addr_t bank_addr; + void (*interrupt)(ymfpci_t *chip, ymfpci_voice_t *voice); + ymfpci_pcm_t *ypcm; +}; + +typedef enum { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +} snd_ymfpci_pcm_type_t; + +struct _snd_ymfpci_pcm { + ymfpci_t *chip; + snd_ymfpci_pcm_type_t type; + snd_pcm_substream_t *substream; + ymfpci_voice_t *voices[2]; /* playback only */ + int running: 1, + spdif: 1, + mode4ch : 1; + u32 period_size; /* cached from runtime->period_size */ + u32 buffer_size; /* cached from runtime->buffer_size */ + u32 period_pos; + u32 last_pos; + u32 capture_bank_number; + u32 shift; +}; + +struct _snd_ymfpci { + int irq; + + unsigned int device_id; /* PCI device ID */ + unsigned int rev; /* PCI revision */ + unsigned long reg_area_phys; + unsigned long reg_area_virt; + struct resource *res_reg_area; + + unsigned short old_legacy_ctrl; + unsigned int joystick_port; + + void *work_ptr; + dma_addr_t work_ptr_addr; + unsigned long work_ptr_size; + + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int work_size; + + void *bank_base_playback; + void *bank_base_capture; + void *bank_base_effect; + void *work_base; + dma_addr_t bank_base_playback_addr; + dma_addr_t bank_base_capture_addr; + dma_addr_t bank_base_effect_addr; + dma_addr_t work_base_addr; + void *ac3_tmp_base; + dma_addr_t ac3_tmp_base_addr; + + u32 *ctrl_playback; + snd_ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + snd_ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; + snd_ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + + u32 active_bank; + ymfpci_voice_t voices[64]; + + ac97_t *ac97; + snd_rawmidi_t *rawmidi; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm2; + snd_pcm_t *pcm_spdif; + snd_pcm_t *pcm_4ch; + snd_pcm_substream_t *capture_substream[YDSXG_CAPTURE_VOICES]; + snd_pcm_substream_t *effect_substream[YDSXG_EFFECT_VOICES]; + snd_kcontrol_t *ctl_vol_recsrc; + snd_kcontrol_t *ctl_vol_adcrec; + snd_kcontrol_t *ctl_vol_spdifrec; + unsigned short spdif_bits, spdif_pcm_bits; + snd_kcontrol_t *spdif_pcm_ctl; + + spinlock_t reg_lock; + spinlock_t voice_lock; + wait_queue_head_t interrupt_sleep; + atomic_t interrupt_sleep_count; + snd_info_entry_t *proc_entry; + +#ifdef CONFIG_PM + u32 *saved_regs; + u32 saved_ydsxgr_mode; +#endif +}; + +int snd_ymfpci_create(snd_card_t * card, + struct pci_dev *pci, + unsigned short old_legacy_ctrl, + ymfpci_t ** rcodec); + +int snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t **rpcm); +int snd_ymfpci_mixer(ymfpci_t *chip); +int snd_ymfpci_joystick(ymfpci_t *chip); + +int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice); +int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice); + +#ifdef CONFIG_PM +void snd_ymfpci_suspend(ymfpci_t *chip); +void snd_ymfpci_resume(ymfpci_t *chip); +#endif + +#endif /* __SOUND_YMFPCI_H */ diff -Nru linux/include/sound/yss225.h linux-2.4.19-pre5-mjc/include/sound/yss225.h --- linux/include/sound/yss225.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/include/sound/yss225.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,23 @@ +#ifndef __SOUND_YSS225_H +#define __SOUND_YSS225_H + +extern unsigned char page_zero[256]; +extern unsigned char page_one[256]; +extern unsigned char page_two[128]; +extern unsigned char page_three[128]; +extern unsigned char page_four[128]; +extern unsigned char page_six[192]; +extern unsigned char page_seven[256]; +extern unsigned char page_zero_v2[96]; +extern unsigned char page_one_v2[96]; +extern unsigned char page_two_v2[48]; +extern unsigned char page_three_v2[48]; +extern unsigned char page_four_v2[48]; +extern unsigned char page_seven_v2[96]; +extern unsigned char mod_v2[304]; +extern unsigned char coefficients[364]; +extern unsigned char coefficients2[56]; +extern unsigned char coefficients3[404]; + + +#endif /* __SOUND_YSS225_H */ diff -Nru linux/sound/Config.help linux-2.4.19-pre5-mjc/sound/Config.help --- linux/sound/Config.help Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/Config.help Mon Apr 8 22:31:23 2002 @@ -0,0 +1,7 @@ +CONFIG_SOUND_ALSA_PRIME + Say 'Y' or 'M' to enable Open Sound System drivers. + +CONFIG_SOUND_ALSA_SND + Say 'Y' or 'M' to enable Advanced Linux Sound Architecture (ALSA) drivers. + You need to install also alsa-lib and alsa-utils packages available on + the ALSA website at . diff -Nru linux/sound/Config.in linux-2.4.19-pre5-mjc/sound/Config.in --- linux/sound/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/Config.in Mon Apr 8 22:31:23 2002 @@ -0,0 +1,32 @@ +# sound/Config.in +# + +mainmenu_option next_comment +comment 'Open Sound System' + +dep_tristate 'Open Sound System' CONFIG_SOUND_ALSA_PRIME $CONFIG_SOUND_ALSA +if [ "$CONFIG_SOUND_ALSA_PRIME" != "n" ]; then + source sound/oss/Config.in +fi + +endmenu + +mainmenu_option next_comment +comment 'Advanced Linux Sound Architecture' + +dep_tristate 'Advanced Linux Sound Architecture' CONFIG_SND $CONFIG_SOUND_ALSA +if [ "$CONFIG_SND" != "n" ]; then + source sound/core/Config.in + source sound/drivers/Config.in +fi +if [ "$CONFIG_SND" != "n" -a "$CONFIG_ISA" = "y" ]; then + source sound/isa/Config.in +fi +if [ "$CONFIG_SND" != "n" -a "$CONFIG_PCI" = "y" ]; then + source sound/pci/Config.in +fi +if [ "$CONFIG_SND" != "n" -a "$CONFIG_PPC" = "y" ]; then + source sound/ppc/Config.in +fi + +endmenu diff -Nru linux/sound/Makefile linux-2.4.19-pre5-mjc/sound/Makefile --- linux/sound/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,61 @@ +# Makefile for the Linux sound card driver +# + +O_TARGET := sound.o + +export-objs := sound_core.o + +obj-$(CONFIG_SOUND_ALSA) += soundcore.o + +subdir-$(CONFIG_SOUND_ALSA_PRIME) += oss + +ifeq ($(CONFIG_SOUND_ALSA_PRIME),y) + subdir-m += oss + obj-y += oss/sounddrivers.o +endif + +_subdirs := core i2c drivers isa pci ppc synth + +subdir-$(CONFIG_SND) += $(_subdirs) + +ifeq ($(CONFIG_SND),y) + subdir-m += $(_subdirs) + obj-y += core/core.o i2c/_i2c.o + obj-y += drivers/drivers.o \ + drivers/mpu401/_mpu401.o \ + drivers/opl3/_opl3.o + obj-y += isa/isa.o \ + isa/ad1816a/_ad1816a.o \ + isa/ad1848/_ad1848.o \ + isa/cs423x/_cs423x.o \ + isa/es1688/_es1688.o \ + isa/gus/_gus.o \ + isa/opti9xx/_opti9xx.o \ + isa/sb/_sb.o \ + isa/wavefront/_wavefront.o + obj-y += pci/pci.o \ + pci/ac97/_ac97.o \ + pci/ali5451/_ali5451.o \ + pci/cs46xx/_cs46xx.o \ + pci/emu10k1/_emu10k1.o \ + pci/korg1212/_korg1212.o \ + pci/nm256/_nm256.o \ + pci/rme9652/_rme9652.o \ + pci/trident/_trident.o \ + pci/ymfpci/_ymfpci.o + obj-y += ppc/ppc.o + obj-y += synth/synth.o \ + synth/emux/_emux.o + obj-y += last.o +endif + +list-multi := soundcore.o + +soundcore-objs := sound_core.o sound_firmware.o + + +include $(TOPDIR)/Rules.make + + +soundcore.o: $(soundcore-objs) + $(LD) -r -o $@ $(soundcore-objs) diff -Nru linux/sound/core/Config.help linux-2.4.19-pre5-mjc/sound/core/Config.help --- linux/sound/core/Config.help Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/Config.help Mon Apr 8 22:31:23 2002 @@ -0,0 +1,40 @@ +CONFIG_SND_SEQUENCER + Say 'Y' or 'M' to enable MIDI sequencer and router support. This feature + allows routing and enqueing MIDI events. Events can be processed at given + time. + +CONFIG_SND_SEQ_DUMMY + Say 'Y' or 'M' to enable dummy sequencer client. This client is a simple + midi-through client. All normal input events are redirected to output port + immediately. + +CONFIG_SND_OSSEMUL + Say 'Y' to enable OSS (Open Sound System) API emulation code. + +CONFIG_SND_MIXER_OSS + Say 'Y' or 'M' to enable mixer OSS API emulation (/dev/mixer*). + +CONFIG_SND_PCM_OSS + Say 'Y' or 'M' to enable digital audio (PCM) OSS API emulation (/dev/dsp*). + +CONFIG_SND_SEQUENCER_OSS + Say 'Y' or 'M' to enable OSS sequencer emulation (both /dev/sequencer and + /dev/music interfaces). + +CONFIG_SND_RTCTIMER + Say 'Y' or 'M' to enable RTC timer support for ALSA. ALSA code uses RTC + timer as precise timing source and maps the RTC timer to the ALSA's timer + interface. ALSA sequencer code can also use this timing source. + +CONFIG_SND_VERBOSE_PRINTK + Say 'Y' to enable verbose log messages. These messages will help to + identify source file and position containing printed messages. + +CONFIG_SND_DEBUG + Say 'Y' to enable ALSA debug code. + +CONFIG_SND_DEBUG_MEMORY + Say 'Y' to enable debugging of memory allocation. + +CONFIG_SND_DEBUG_DETECTION + Say 'Y' to enable debugging of hardware detection. diff -Nru linux/sound/core/Config.in linux-2.4.19-pre5-mjc/sound/core/Config.in --- linux/sound/core/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/Config.in Mon Apr 8 22:31:24 2002 @@ -0,0 +1,30 @@ +# ALSA soundcard-configuration + +if [ "$CONFIG_X86_64" = "y" -a "$CONFIG_IA32_EMULATION" = "y" ]; then + tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi +if [ "$CONFIG_PPC64" = "y" ]; then + tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi +if [ "$CONFIG_SPARC64" = "y" ]; then + tristate ' Emulation for 32-bit applications' CONFIG_SND_BIT32_EMUL +fi +dep_tristate ' Sequencer support' CONFIG_SND_SEQUENCER $CONFIG_SND +if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then + dep_tristate ' Sequencer dummy client' CONFIG_SND_SEQ_DUMMY $CONFIG_SND_SEQUENCER +fi +bool ' OSS API emulation' CONFIG_SND_OSSEMUL $CONFIG_SND +if [ "$CONFIG_SND_OSSEMUL" = "y" ]; then + dep_tristate ' OSS Mixer API' CONFIG_SND_MIXER_OSS $CONFIG_SND + dep_tristate ' OSS PCM API' CONFIG_SND_PCM_OSS $CONFIG_SND + if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then + dep_tristate ' OSS Sequencer API' CONFIG_SND_SEQUENCER_OSS $CONFIG_SND_SEQUENCER + fi +fi +dep_tristate ' RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND +bool ' Verbose printk' CONFIG_SND_VERBOSE_PRINTK +bool ' Debug' CONFIG_SND_DEBUG +if [ "$CONFIG_SND_DEBUG" = "y" ]; then + bool ' Debug memory' CONFIG_SND_DEBUG_MEMORY + bool ' Debug detection' CONFIG_SND_DEBUG_DETECT +fi diff -Nru linux/sound/core/Makefile linux-2.4.19-pre5-mjc/sound/core/Makefile --- linux/sound/core/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,131 @@ +# +# Makefile for ALSA +# Copyright (c) 1999,2001 by Jaroslav Kysela +# + +O_TARGET := core.o + +export-objs := sound.o pcm.o pcm_lib.o rawmidi.o timer.o rtctimer.o hwdep.o + +list-multi := snd.o snd-pcm.o snd-rawmidi.o snd-timer.o snd-rtctimer.o snd-hwdep.o + +snd-objs := sound.o init.o isadma.o memory.o info.o control.o misc.o \ + device.o wrappers.o +ifeq ($(CONFIG_SND_OSSEMUL),y) +snd-objs += sound_oss.o info_oss.o +endif + +snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ + pcm_memory.o + +snd-rawmidi-objs := rawmidi.o +snd-timer-objs := timer.o +snd-rtctimer-objs := rtctimer.o +snd-hwdep-objs := hwdep.o + +obj-$(CONFIG_SND) += snd.o +ifeq ($(CONFIG_RTC),y) + obj-$(CONFIG_SND_RTCTIMER) += snd-timer.o + obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o +endif +obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o + +subdir-$(CONFIG_SND_MIXER_OSS) += oss +subdir-$(CONFIG_SND_PCM_OSS) += oss +ifeq ($(filter $(subdir-y),oss),oss) + subdir-m += oss + obj-y += oss/oss.o +endif + +subdir-$(CONFIG_SND_SEQUENCER) += seq +ifeq ($(CONFIG_SND_SEQUENCER),y) + subdir-m += seq + obj-y += seq/sq.o +endif + +obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o + +subdir-$(CONFIG_SND_BIT32_EMUL) += ioctl32 +ifeq ($(CONFIG_SND_BIT32_EMUL),y) + obj-y += ioctl32/_ioctl32.o +endif + +# Toplevel Module Dependency +obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o +obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_DT0197H) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o +obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_VIA686) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_VIA8233) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-pcm.o +obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o +obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o +obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd.o +ifeq ($(CONFIG_SND_SB16_CSP),y) + obj-$(CONFIG_SND_SB16) += snd-hwdep.o + obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o +endif + +include $(TOPDIR)/Rules.make + +snd.o: $(snd-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-objs) + +snd-pcm.o: $(snd-pcm-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-objs) + +snd-rawmidi.o: $(snd-rawmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rawmidi-objs) + +snd-timer.o: $(snd-timer-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-timer-objs) + +snd-rtctimer.o: $(snd-rtctimer-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rtctimer-objs) + +snd-hwdep.o: $(snd-hwdep-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-hwdep-objs) diff -Nru linux/sound/core/control.c linux-2.4.19-pre5-mjc/sound/core/control.c --- linux/sound/core/control.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/control.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,815 @@ +/* + * Routines for driver control interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _snd_kctl_ioctl { + struct list_head list; /* list of all ioctls */ + snd_kctl_ioctl_func_t fioctl; +} snd_kctl_ioctl_t; + +#define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list) + +static rwlock_t snd_ioctl_rwlock = RW_LOCK_UNLOCKED; +static LIST_HEAD(snd_control_ioctls); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static int snd_ctl_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + unsigned long flags; + snd_card_t *card; + snd_ctl_file_t *ctl; + int err; + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + card = snd_cards[cardnum]; + if (!card) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(card->module)) { + err = -EFAULT; + goto __error1; + } + ctl = snd_magic_kcalloc(snd_ctl_file_t, 0, GFP_KERNEL); + if (ctl == NULL) { + err = -ENOMEM; + goto __error; + } + INIT_LIST_HEAD(&ctl->events); + init_waitqueue_head(&ctl->change_sleep); + spin_lock_init(&ctl->read_lock); + ctl->card = card; + ctl->pid = current->pid; + file->private_data = ctl; + write_lock_irqsave(&card->control_rwlock, flags); + list_add_tail(&ctl->list, &card->ctl_files); + write_unlock_irqrestore(&card->control_rwlock, flags); + return 0; + + __error: + dec_mod_count(card->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +static void snd_ctl_empty_read_queue(snd_ctl_file_t * ctl) +{ + snd_kctl_event_t *cread; + + spin_lock(&ctl->read_lock); + while (!list_empty(&ctl->events)) { + cread = snd_kctl_event(ctl->events.next); + list_del(&cread->list); + kfree(cread); + } + spin_unlock(&ctl->read_lock); +} + +static int snd_ctl_release(struct inode *inode, struct file *file) +{ + unsigned long flags; + struct list_head *list; + snd_card_t *card; + snd_ctl_file_t *ctl; + snd_kcontrol_t *control; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + fasync_helper(-1, file, 0, &ctl->fasync); + file->private_data = NULL; + card = ctl->card; + write_lock_irqsave(&card->control_rwlock, flags); + list_del(&ctl->list); + write_unlock_irqrestore(&card->control_rwlock, flags); + write_lock(&card->control_owner_lock); + list_for_each(list, &card->controls) { + control = snd_kcontrol(list); + if (control->owner == ctl) + control->owner = NULL; + } + write_unlock(&card->control_owner_lock); + snd_ctl_empty_read_queue(ctl); + snd_magic_kfree(ctl); + dec_mod_count(card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +void snd_ctl_notify(snd_card_t *card, unsigned int mask, snd_ctl_elem_id_t *id) +{ + unsigned long flags; + struct list_head *flist; + snd_ctl_file_t *ctl; + snd_kctl_event_t *ev; + + snd_runtime_check(card != NULL && id != NULL, return); + read_lock_irqsave(&card->control_rwlock, flags); +#ifdef CONFIG_SND_OSSEMUL + card->mixer_oss_change_count++; +#endif + list_for_each(flist, &card->ctl_files) { + struct list_head *elist; + ctl = snd_ctl_file(flist); + if (!ctl->subscribed) + continue; + spin_lock(&ctl->read_lock); + list_for_each(elist, &ctl->events) { + ev = snd_kctl_event(elist); + if (ev->id.numid == id->numid) { + ev->mask |= mask; + goto _found; + } + } + ev = snd_kcalloc(sizeof(*ev), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (ev) { + ev->id = *id; + ev->mask = mask; + list_add_tail(&ev->list, &ctl->events); + } else { + snd_printk(KERN_ERR "No memory available to allocate event\n"); + } + _found: + wake_up(&ctl->change_sleep); + kill_fasync(&ctl->fasync, SIGIO, POLL_IN); + spin_unlock(&ctl->read_lock); + } + read_unlock_irqrestore(&card->control_rwlock, flags); +} + +snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control) +{ + snd_kcontrol_t *kctl; + + snd_runtime_check(control != NULL, return NULL); + kctl = (snd_kcontrol_t *)snd_magic_kmalloc(snd_kcontrol_t, 0, GFP_KERNEL); + if (kctl == NULL) + return NULL; + *kctl = *control; + return kctl; +} + +snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data) +{ + snd_kcontrol_t kctl; + + snd_runtime_check(ncontrol != NULL, return NULL); + snd_assert(ncontrol->info != NULL, return NULL); + memset(&kctl, 0, sizeof(kctl)); + kctl.id.iface = ncontrol->iface; + kctl.id.device = ncontrol->device; + kctl.id.subdevice = ncontrol->subdevice; + strncpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)-1); + kctl.id.index = ncontrol->index; + kctl.access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : + (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|SNDRV_CTL_ELEM_ACCESS_INDIRECT)); + kctl.info = ncontrol->info; + kctl.get = ncontrol->get; + kctl.put = ncontrol->put; + kctl.private_value = ncontrol->private_value; + kctl.private_data = private_data; + return snd_ctl_new(&kctl); +} + +void snd_ctl_free_one(snd_kcontrol_t * kcontrol) +{ + if (kcontrol) { + if (kcontrol->private_free) + kcontrol->private_free(kcontrol); + snd_magic_kfree(kcontrol); + } +} + +int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol) +{ + snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); + snd_assert(kcontrol->info != NULL, return -EINVAL); + snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_READ) || kcontrol->get != NULL, return -EINVAL); + snd_assert(!(kcontrol->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kcontrol->put != NULL, return -EINVAL); + write_lock(&card->control_rwlock); + list_add_tail(&kcontrol->list, &card->controls); + card->controls_count++; + kcontrol->id.numid = ++card->last_numid; + write_unlock(&card->control_rwlock); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &kcontrol->id); + return 0; +} + +int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol) +{ + snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL); + write_lock(&card->control_rwlock); + list_del(&kcontrol->list); + card->controls_count--; + write_unlock(&card->control_rwlock); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &kcontrol->id); + snd_ctl_free_one(kcontrol); + return 0; +} + +int snd_ctl_remove_id(snd_card_t * card, snd_ctl_elem_id_t *id) +{ + snd_kcontrol_t *kctl; + + kctl = snd_ctl_find_id(card, id); + if (kctl == NULL) + return -ENOENT; + return snd_ctl_remove(card, kctl); +} + +int snd_ctl_rename_id(snd_card_t * card, snd_ctl_elem_id_t *src_id, snd_ctl_elem_id_t *dst_id) +{ + snd_kcontrol_t *kctl; + + kctl = snd_ctl_find_id(card, src_id); + if (kctl == NULL) + return -ENOENT; + write_lock(&card->control_rwlock); + kctl->id = *dst_id; + kctl->id.numid = ++card->last_numid; + write_unlock(&card->control_rwlock); + return 0; +} + +snd_kcontrol_t *snd_ctl_find_numid(snd_card_t * card, unsigned int numid) +{ + struct list_head *list; + snd_kcontrol_t *kctl; + + snd_runtime_check(card != NULL && numid != 0, return NULL); + read_lock(&card->control_rwlock); + list_for_each(list, &card->controls) { + kctl = snd_kcontrol(list); + if (kctl->id.numid == numid) { + read_unlock(&card->control_rwlock); + return kctl; + } + } + read_unlock(&card->control_rwlock); + return NULL; +} + +snd_kcontrol_t *snd_ctl_find_id(snd_card_t * card, snd_ctl_elem_id_t *id) +{ + struct list_head *list; + snd_kcontrol_t *kctl; + + snd_runtime_check(card != NULL && id != NULL, return NULL); + if (id->numid != 0) + return snd_ctl_find_numid(card, id->numid); + read_lock(&card->control_rwlock); + list_for_each(list, &card->controls) { + kctl = snd_kcontrol(list); + if (kctl->id.iface != id->iface) + continue; + if (kctl->id.device != id->device) + continue; + if (kctl->id.subdevice != id->subdevice) + continue; + if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name))) + continue; + if (kctl->id.index != id->index) + continue; + read_unlock(&card->control_rwlock); + return kctl; + } + read_unlock(&card->control_rwlock); + return NULL; +} + +static int snd_ctl_card_info(snd_card_t * card, snd_ctl_file_t * ctl, + unsigned int cmd, unsigned long arg) +{ + snd_ctl_card_info_t info; + + memset(&info, 0, sizeof(info)); + read_lock(&snd_ioctl_rwlock); + info.card = card->number; + strncpy(info.id, card->id, sizeof(info.id) - 1); + strncpy(info.driver, card->driver, sizeof(info.driver) - 1); + strncpy(info.name, card->shortname, sizeof(info.name) - 1); + strncpy(info.longname, card->longname, sizeof(info.longname) - 1); + strncpy(info.mixername, card->mixername, sizeof(info.mixername) - 1); + strncpy(info.components, card->components, sizeof(info.components) - 1); + read_unlock(&snd_ioctl_rwlock); + if (copy_to_user((void *) arg, &info, sizeof(snd_ctl_card_info_t))) + return -EFAULT; + return 0; +} + +static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t *_list) +{ + struct list_head *plist; + snd_ctl_elem_list_t list; + snd_kcontrol_t *kctl; + snd_ctl_elem_id_t *dst, *id; + int offset, space; + + if (copy_from_user(&list, _list, sizeof(list))) + return -EFAULT; + offset = list.offset; + space = list.space; + /* try limit maximum space */ + if (space > 16384) + return -ENOMEM; + if (space > 0) { + /* allocate temporary buffer for atomic operation */ + dst = vmalloc(space * sizeof(snd_ctl_elem_id_t)); + if (dst == NULL) + return -ENOMEM; + read_lock(&card->control_rwlock); + list.count = card->controls_count; + plist = card->controls.next; + while (offset-- > 0 && plist != &card->controls) + plist = plist->next; + list.used = 0; + id = dst; + while (space > 0 && plist != &card->controls) { + kctl = snd_kcontrol(plist); + memcpy(id, &kctl->id, sizeof(snd_ctl_elem_id_t)); + id++; + plist = plist->next; + space--; + list.used++; + } + read_unlock(&card->control_rwlock); + if (list.used > 0 && copy_to_user(list.pids, dst, list.used * sizeof(snd_ctl_elem_id_t))) + return -EFAULT; + vfree(dst); + } else { + read_lock(&card->control_rwlock); + list.count = card->controls_count; + read_unlock(&card->control_rwlock); + } + if (copy_to_user(_list, &list, sizeof(list))) + return -EFAULT; + return 0; +} + +static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *_info) +{ + snd_card_t *card = ctl->card; + snd_ctl_elem_info_t info; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &info.id); + if (kctl == NULL) { + read_unlock(&card->control_rwlock); + return -ENOENT; + } +#ifdef CONFIG_SND_DEBUG + info.access = 0; +#endif + result = kctl->info(kctl, &info); + if (result >= 0) { + snd_assert(info.access == 0, ); + info.id = kctl->id; + info.access = kctl->access; + if (kctl->owner) { + info.access |= SNDRV_CTL_ELEM_ACCESS_LOCK; + if (kctl->owner == ctl) + info.access |= SNDRV_CTL_ELEM_ACCESS_OWNER; + info.owner = kctl->owner_pid; + } else { + info.owner = -1; + } + } + read_unlock(&card->control_rwlock); + if (result >= 0) + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return result; +} + +static int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *_control) +{ + snd_ctl_elem_value_t control; + snd_kcontrol_t *kctl; + int result, indirect; + + if (copy_from_user(&control, _control, sizeof(control))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &control.id); + if (kctl == NULL) { + result = -ENOENT; + } else { + indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; + if (control.indirect != indirect) { + result = -EACCES; + } else { + if ((kctl->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) { + result = kctl->get(kctl, &control); + if (result >= 0) + control.id = kctl->id; + } else + result = -EPERM; + } + } + read_unlock(&card->control_rwlock); + if (result >= 0) + if (copy_to_user(_control, &control, sizeof(control))) + return -EFAULT; + return result; +} + +static int snd_ctl_elem_write(snd_ctl_file_t *file, snd_ctl_elem_value_t *_control) +{ + snd_card_t *card = file->card; + snd_ctl_elem_value_t control; + snd_kcontrol_t *kctl; + int result, indirect; + + if (copy_from_user(&control, _control, sizeof(control))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &control.id); + if (kctl == NULL) { + result = -ENOENT; + } else { + indirect = kctl->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; + if (control.indirect != indirect) { + result = -EACCES; + } else { + read_lock(&card->control_owner_lock); + if (!(kctl->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || + kctl->put == NULL || + (kctl->owner != NULL && kctl->owner != file)) { + result = -EPERM; + } else { + result = kctl->put(kctl, &control); + if (result >= 0) + control.id = kctl->id; + } + read_unlock(&card->control_owner_lock); + if (result > 0) { + result = 0; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + } + } + } + read_unlock(&card->control_rwlock); + if (result >= 0) + if (copy_to_user(_control, &control, sizeof(control))) + return -EFAULT; + return result; +} + +static int snd_ctl_elem_lock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id) +{ + snd_card_t *card = file->card; + snd_ctl_elem_id_t id; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &id); + if (kctl == NULL) { + result = -ENOENT; + } else { + write_lock(&card->control_owner_lock); + if (kctl->owner != NULL) + result = -EBUSY; + else { + kctl->owner = file; + kctl->owner_pid = current->pid; + result = 0; + } + write_unlock(&card->control_owner_lock); + } + read_unlock(&card->control_rwlock); + return result; +} + +static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t *_id) +{ + snd_card_t *card = file->card; + snd_ctl_elem_id_t id; + snd_kcontrol_t *kctl; + int result; + + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + read_lock(&card->control_rwlock); + kctl = snd_ctl_find_id(card, &id); + if (kctl == NULL) { + result = -ENOENT; + } else { + write_lock(&card->control_owner_lock); + if (kctl->owner == NULL) + result = -EINVAL; + else if (kctl->owner != file) + result = -EPERM; + else { + kctl->owner = NULL; + kctl->owner_pid = 0; + result = 0; + } + write_unlock(&card->control_owner_lock); + } + read_unlock(&card->control_rwlock); + return result; +} + +static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int *ptr) +{ + int subscribe; + if (get_user(subscribe, ptr)) + return -EFAULT; + if (subscribe < 0) { + subscribe = file->subscribed; + if (put_user(subscribe, ptr)) + return -EFAULT; + return 0; + } + if (subscribe) { + file->subscribed = 1; + return 0; + } else if (file->subscribed) { + snd_ctl_empty_read_queue(file); + file->subscribed = 0; + } + return 0; +} + +static int snd_ctl_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_ctl_file_t *ctl; + snd_card_t *card; + struct list_head *list; + snd_kctl_ioctl_t *p; + int err; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + card = ctl->card; + snd_assert(card != NULL, return -ENXIO); + switch (cmd) { + case SNDRV_CTL_IOCTL_PVERSION: + return put_user(SNDRV_CTL_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_CTL_IOCTL_CARD_INFO: + return snd_ctl_card_info(card, ctl, cmd, arg); + case SNDRV_CTL_IOCTL_ELEM_LIST: + return snd_ctl_elem_list(ctl->card, (snd_ctl_elem_list_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_INFO: + return snd_ctl_elem_info(ctl, (snd_ctl_elem_info_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_READ: + return snd_ctl_elem_read(ctl->card, (snd_ctl_elem_value_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_WRITE: + return snd_ctl_elem_write(ctl, (snd_ctl_elem_value_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_LOCK: + return snd_ctl_elem_lock(ctl, (snd_ctl_elem_id_t *) arg); + case SNDRV_CTL_IOCTL_ELEM_UNLOCK: + return snd_ctl_elem_unlock(ctl, (snd_ctl_elem_id_t *) arg); + case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: + return snd_ctl_subscribe_events(ctl, (int *) arg); + case SNDRV_CTL_IOCTL_POWER: + if (get_user(err, (int *)arg)) + return -EFAULT; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; +#ifdef CONFIG_PM + if (card->set_power_state == NULL) + return -ENOPROTOOPT; + return card->set_power_state(card, err); +#else + return -ENOPROTOOPT; +#endif + case SNDRV_CTL_IOCTL_POWER_STATE: +#ifdef CONFIG_PM + return put_user(card->power_state, (int *)arg) ? -EFAULT : 0; +#else + return put_user(SNDRV_CTL_POWER_D0, (int *)arg) ? -EFAULT : 0; +#endif + } + read_lock(&snd_ioctl_rwlock); + list_for_each(list, &snd_control_ioctls) { + p = list_entry(list, snd_kctl_ioctl_t, list); + err = p->fioctl(card, ctl, cmd, arg); + if (err != -ENOIOCTLCMD) { + read_unlock(&snd_ioctl_rwlock); + return err; + } + } + read_unlock(&snd_ioctl_rwlock); + snd_printd("unknown ioctl = 0x%x\n", cmd); + return -ENOTTY; +} + +static ssize_t snd_ctl_read(struct file *file, char *buffer, size_t count, loff_t * offset) +{ + snd_ctl_file_t *ctl; + int err = 0; + ssize_t result = 0; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO); + if (!ctl->subscribed) + return -EBADFD; + if (count < sizeof(snd_ctl_event_t)) + return -EINVAL; + spin_lock_irq(&ctl->read_lock); + while (count >= sizeof(snd_ctl_event_t)) { + snd_ctl_event_t ev; + snd_kctl_event_t *kev; + while (list_empty(&ctl->events)) { + wait_queue_t wait; + if (file->f_flags & O_NONBLOCK) { + err = -EAGAIN; + goto __end; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&ctl->change_sleep, &wait); + spin_unlock_irq(&ctl->read_lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ctl->change_sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + spin_lock_irq(&ctl->read_lock); + } + kev = snd_kctl_event(ctl->events.next); + ev.type = SNDRV_CTL_EVENT_ELEM; + ev.data.elem.mask = kev->mask; + ev.data.elem.id = kev->id; + list_del(&kev->list); + spin_unlock_irq(&ctl->read_lock); + kfree(kev); + if (copy_to_user(buffer, &ev, sizeof(snd_ctl_event_t))) { + err = -EFAULT; + goto __end; + } + spin_lock_irq(&ctl->read_lock); + buffer += sizeof(snd_ctl_event_t); + count -= sizeof(snd_ctl_event_t); + result += sizeof(snd_ctl_event_t); + } + __end: + spin_unlock_irq(&ctl->read_lock); + return result > 0 ? result : err; +} + +static unsigned int snd_ctl_poll(struct file *file, poll_table * wait) +{ + unsigned int mask; + snd_ctl_file_t *ctl; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return 0); + if (!ctl->subscribed) + return 0; + poll_wait(file, &ctl->change_sleep, wait); + + mask = 0; + if (!list_empty(&ctl->events)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) +{ + snd_kctl_ioctl_t *pn; + + pn = (snd_kctl_ioctl_t *) + snd_kcalloc(sizeof(snd_kctl_ioctl_t), GFP_KERNEL); + if (pn == NULL) + return -ENOMEM; + pn->fioctl = fcn; + write_lock(&snd_ioctl_rwlock); + list_add_tail(&pn->list, &snd_control_ioctls); + write_unlock(&snd_ioctl_rwlock); + return 0; +} + +int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) +{ + struct list_head *list; + snd_kctl_ioctl_t *p; + + snd_runtime_check(fcn != NULL, return -EINVAL); + write_lock(&snd_ioctl_rwlock); + list_for_each(list, &snd_control_ioctls) { + p = list_entry(list, snd_kctl_ioctl_t, list); + if (p->fioctl == fcn) { + list_del(&p->list); + write_unlock(&snd_ioctl_rwlock); + kfree(p); + return 0; + } + } + write_unlock(&snd_ioctl_rwlock); + snd_BUG(); + return -EINVAL; +} + +static int snd_ctl_fasync(int fd, struct file * file, int on) +{ + snd_ctl_file_t *ctl; + int err; + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + err = fasync_helper(fd, file, on, &ctl->fasync); + if (err < 0) + return err; + return 0; +} + +/* + * INIT PART + */ + +static struct file_operations snd_ctl_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_ctl_read, + open: snd_ctl_open, + release: snd_ctl_release, + poll: snd_ctl_poll, + ioctl: snd_ctl_ioctl, + fasync: snd_ctl_fasync, +}; + +static snd_minor_t snd_ctl_reg = +{ + comment: "ctl", + f_ops: &snd_ctl_f_ops, +}; + +int snd_ctl_register(snd_card_t *card) +{ + int err, cardnum; + char name[16]; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + sprintf(name, "controlC%i", cardnum); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, + card, 0, &snd_ctl_reg, name)) < 0) + return err; + return 0; +} + +int snd_ctl_unregister(snd_card_t *card) +{ + int err, cardnum; + snd_kcontrol_t *control; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0) + return err; + while (!list_empty(&card->controls)) { + control = snd_kcontrol(card->controls.next); + snd_ctl_remove(card, control); + } + return 0; +} diff -Nru linux/sound/core/device.c linux-2.4.19-pre5-mjc/sound/core/device.c --- linux/sound/core/device.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/device.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,135 @@ +/* + * Device management routines + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +int snd_device_new(snd_card_t *card, snd_device_type_t type, + void *device_data, snd_device_ops_t *ops) +{ + snd_device_t *dev; + + snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO); + dev = (snd_device_t *) snd_magic_kcalloc(snd_device_t, 0, GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + dev->card = card; + dev->type = type; + dev->state = SNDRV_DEV_BUILD; + dev->device_data = device_data; + dev->ops = ops; + list_add(&dev->list, &card->devices); /* add to the head of list */ + return 0; +} + +int snd_device_free(snd_card_t *card, void *device_data) +{ + struct list_head *list; + snd_device_t *dev; + + snd_assert(card != NULL, return -ENXIO); + snd_assert(device_data != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->device_data != device_data) + continue; + /* unlink */ + list_del(&dev->list); + if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_unregister) { + if (dev->ops->dev_unregister(dev)) + snd_printk(KERN_ERR "device unregister failure\n"); + } else { + if (dev->ops->dev_free) { + if (dev->ops->dev_free(dev)) + snd_printk(KERN_ERR "device free failure\n"); + } + } + snd_magic_kfree(dev); + return 0; + } + snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0)); + return -ENXIO; +} + +int snd_device_register(snd_card_t *card, void *device_data) +{ + struct list_head *list; + snd_device_t *dev; + int err; + + snd_assert(card != NULL && device_data != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->device_data != device_data) + continue; + if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { + if ((err = dev->ops->dev_register(dev)) < 0) + return err; + dev->state = SNDRV_DEV_REGISTERED; + return 0; + } + return -EBUSY; + } + snd_BUG(); + return -ENXIO; +} + +int snd_device_register_all(snd_card_t *card) +{ + struct list_head *list; + snd_device_t *dev; + int err; + + snd_assert(card != NULL, return -ENXIO); + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) { + if ((err = dev->ops->dev_register(dev)) < 0) + return err; + dev->state = SNDRV_DEV_REGISTERED; + } + } + return 0; +} + +int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd) +{ + snd_device_t *dev; + struct list_head *list; + int err, range_low, range_high; + + snd_assert(card != NULL, return -ENXIO); + range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; + range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; + __again: + list_for_each(list, &card->devices) { + dev = snd_device(list); + if (dev->type >= range_low && dev->type <= range_high) { + if ((err = snd_device_free(card, dev->device_data)) < 0) + return err; + goto __again; + } + } + return 0; +} diff -Nru linux/sound/core/hwdep.c linux-2.4.19-pre5-mjc/sound/core/hwdep.c --- linux/sound/core/hwdep.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/hwdep.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,436 @@ +/* + * Hardware dependent layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Hardware dependent layer"); +MODULE_LICENSE("GPL"); + +snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS]; + +static DECLARE_MUTEX(register_mutex); + +static int snd_hwdep_free(snd_hwdep_t *hwdep); +static int snd_hwdep_dev_free(snd_device_t *device); +static int snd_hwdep_dev_register(snd_device_t *device); +static int snd_hwdep_dev_unregister(snd_device_t *device); + +/* + + */ + +static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.llseek) + return hw->ops.llseek(hw, file, offset, orig); + return -ENXIO; +} + +static ssize_t snd_hwdep_read(struct file * file, char *buf, size_t count, loff_t *offset) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.read) + return hw->ops.read(hw, buf, count, offset); + return -ENXIO; +} + +static ssize_t snd_hwdep_write(struct file * file, const char *buf, size_t count, loff_t *offset) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.write) + return hw->ops.write(hw, buf, count, offset); + return -ENXIO; +} + +static int snd_hwdep_open(struct inode *inode, struct file * file) +{ + int major = major(inode->i_rdev); + int cardnum; + int device; + snd_hwdep_t *hw; + int err; + wait_queue_t wait; + + switch (major) { + case CONFIG_SND_MAJOR: + cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_HWDEP; + break; +#ifdef CONFIG_SND_OSSEMUL + case SOUND_MAJOR: + cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + device = 0; + break; +#endif + default: + return -ENXIO; + } + cardnum %= SNDRV_CARDS; + device %= SNDRV_MINOR_HWDEPS; + hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device]; + + snd_assert(hw != NULL, return -ENODEV); + if (!hw->ops.open) + return -ENXIO; +#ifdef CONFIG_SND_OSSEMUL + if (major == SOUND_MAJOR && hw->oss_type < 0) + return -ENXIO; +#endif + init_waitqueue_entry(&wait, current); + add_wait_queue(&hw->open_wait, &wait); + while (1) { + err = hw->ops.open(hw, file); + if (err >= 0) + break; + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&hw->open_wait, &wait); + if (err >= 0) + file->private_data = hw; + return err; +} + +static int snd_hwdep_release(struct inode *inode, struct file * file) +{ + int err; + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.release) { + err = hw->ops.release(hw, file); + wake_up(&hw->open_wait); + return err; + } + return -ENXIO; +} + +static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return 0); + if (hw->ops.poll) + return hw->ops.poll(hw, file, wait); + return 0; +} + +static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t *_info) +{ + snd_hwdep_info_t info; + + memset(&info, 0, sizeof(info)); + info.card = hw->card->number; + strncpy(info.id, hw->id, sizeof(info.id) - 1); + strncpy(info.name, hw->name, sizeof(info.name) - 1); + info.iface = hw->iface; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_hwdep_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (cmd == SNDRV_HWDEP_IOCTL_PVERSION) + return put_user(SNDRV_HWDEP_VERSION, (int *)arg); + if (cmd == SNDRV_HWDEP_IOCTL_INFO) + return snd_hwdep_info(hw, (snd_hwdep_info_t *)arg); + if (hw->ops.ioctl) + return hw->ops.ioctl(hw, file, cmd, arg); + return -ENOTTY; +} + +static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) +{ + snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + if (hw->ops.mmap) + return hw->ops.mmap(hw, file, vma); + return -ENXIO; +} + +static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_MINOR_HWDEPS; + switch (cmd) { + case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_MINOR_HWDEPS) { + if (snd_hwdep_devices[tmp + device]) + break; + device++; + } + if (device >= SNDRV_MINOR_HWDEPS) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_HWDEP_INFO: + { + snd_hwdep_info_t *info = (snd_hwdep_info_t *)arg; + int device; + snd_hwdep_t *hwdep; + + if (get_user(device, &info->device)) + return -EFAULT; + if (device < 0 || device >= SNDRV_MINOR_HWDEPS) + return -ENXIO; + hwdep = snd_hwdep_devices[tmp + device]; + if (hwdep == NULL) + return -ENXIO; + return snd_hwdep_info(hwdep, info); + } + } + return -ENOIOCTLCMD; +} + +/* + + */ + +static struct file_operations snd_hwdep_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + llseek: snd_hwdep_llseek, + read: snd_hwdep_read, + write: snd_hwdep_write, + open: snd_hwdep_open, + release: snd_hwdep_release, + poll: snd_hwdep_poll, + ioctl: snd_hwdep_ioctl, + mmap: snd_hwdep_mmap, +}; + +static snd_minor_t snd_hwdep_reg = +{ + comment: "hardware dependent", + f_ops: &snd_hwdep_f_ops, +}; + +int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hwdep; + int err; + static snd_device_ops_t ops = { + dev_free: snd_hwdep_dev_free, + dev_register: snd_hwdep_dev_register, + dev_unregister: snd_hwdep_dev_unregister + }; + + snd_assert(rhwdep != NULL, return -EINVAL); + *rhwdep = NULL; + snd_assert(card != NULL, return -ENXIO); + hwdep = snd_magic_kcalloc(snd_hwdep_t, 0, GFP_KERNEL); + if (hwdep == NULL) + return -ENOMEM; + hwdep->card = card; + hwdep->device = device; + if (id) { + strncpy(hwdep->id, id, sizeof(hwdep->id) - 1); + } +#ifdef CONFIG_SND_OSSEMUL + hwdep->oss_type = -1; +#endif + if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) { + snd_hwdep_free(hwdep); + return err; + } + init_waitqueue_head(&hwdep->open_wait); + *rhwdep = hwdep; + return 0; +} + +static int snd_hwdep_free(snd_hwdep_t *hwdep) +{ + snd_assert(hwdep != NULL, return -ENXIO); + if (hwdep->private_free) + hwdep->private_free(hwdep); + snd_magic_kfree(hwdep); + return 0; +} + +static int snd_hwdep_dev_free(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + return snd_hwdep_free(hwdep); +} + +static int snd_hwdep_dev_register(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + int idx, err; + char name[32]; + + down(®ister_mutex); + idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; + if (snd_hwdep_devices[idx]) { + up(®ister_mutex); + return -EBUSY; + } + snd_hwdep_devices[idx] = hwdep; + sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, + hwdep->card, hwdep->device, + &snd_hwdep_reg, name)) < 0) { + snd_printk(KERN_ERR "unable to register hardware dependant device %i:%i\n", + hwdep->card->number, hwdep->device); + snd_hwdep_devices[idx] = NULL; + up(®ister_mutex); + return err; + } +#ifdef CONFIG_SND_OSSEMUL + hwdep->ossreg = 0; + if (hwdep->oss_type >= 0) { + if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) { + snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n"); + } else { + if (snd_register_oss_device(hwdep->oss_type, + hwdep->card, hwdep->device, + &snd_hwdep_reg, hwdep->oss_dev) < 0) { + snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n", + hwdep->card->number, hwdep->device); + } else { + snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number, hwdep->name); + hwdep->ossreg = 1; + } + } + } +#endif + up(®ister_mutex); + return 0; +} + +static int snd_hwdep_dev_unregister(snd_device_t *device) +{ + snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + int idx; + + snd_assert(hwdep != NULL, return -ENXIO); + down(®ister_mutex); + idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device; + if (snd_hwdep_devices[idx] != hwdep) { + up(®ister_mutex); + return -EINVAL; + } +#ifdef CONFIG_SND_OSSEMUL + if (hwdep->ossreg) { + snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number); + } +#endif + snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); + snd_hwdep_devices[idx] = NULL; + up(®ister_mutex); + return snd_hwdep_free(hwdep); +} + +/* + * Info interface + */ + +static void snd_hwdep_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + int idx; + snd_hwdep_t *hwdep; + + down(®ister_mutex); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) { + hwdep = snd_hwdep_devices[idx]; + if (hwdep == NULL) + continue; + snd_iprintf(buffer, "%02i-%02i: %s\n", + idx / SNDRV_MINOR_HWDEPS, + idx % SNDRV_MINOR_HWDEPS, + hwdep->name); + } + up(®ister_mutex); +} + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_hwdep_proc_entry = NULL; + +static int __init alsa_hwdep_init(void) +{ + snd_info_entry_t *entry; + + memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices)); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 512; + entry->c.text.read = snd_hwdep_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_hwdep_proc_entry = entry; + snd_ctl_register_ioctl(snd_hwdep_control_ioctl); + return 0; +} + +static void __exit alsa_hwdep_exit(void) +{ + snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl); + if (snd_hwdep_proc_entry) { + snd_info_unregister(snd_hwdep_proc_entry); + snd_hwdep_proc_entry = NULL; + } +} + +module_init(alsa_hwdep_init) +module_exit(alsa_hwdep_exit) + +EXPORT_SYMBOL(snd_hwdep_new); diff -Nru linux/sound/core/info.c linux-2.4.19-pre5-mjc/sound/core/info.c --- linux/sound/core/info.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/info.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1074 @@ +/* + * Information interface for ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DEVFS_FS +#include +#endif +#include + +/* + * + */ + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_info_check_reserved_words(const char *str) +{ + static char *reserved[] = + { + "dev", + "version", + "meminfo", + "memdebug", + "detect", + "devices", + "oss", + "cards", + "timers", + "synth", + "pcm", + "seq", + NULL + }; + char **xstr = reserved; + + while (*xstr) { + if (!strcmp(*xstr, str)) + return 0; + xstr++; + } + if (!strncmp(str, "card", 4)) + return 0; + return 1; +} + +#ifdef CONFIG_PROC_FS + +extern int snd_major; +extern struct file_operations snd_fops; + +static DECLARE_MUTEX(info_mutex); + +typedef struct _snd_info_private_data { + snd_info_buffer_t *rbuffer; + snd_info_buffer_t *wbuffer; + snd_info_entry_t *entry; + void *file_private_data; +} snd_info_private_data_t; + +static int snd_info_version_init(void); +static int snd_info_version_done(void); + +/* + + */ + +int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...) +{ + va_list args; + int res; + char sbuffer[512]; + + if (buffer->stop || buffer->error) + return 0; + va_start(args, fmt); + res = vsprintf(sbuffer, fmt, args); + va_end(args); + if (buffer->size + res >= buffer->len) { + buffer->stop = 1; + return 0; + } + strcpy(buffer->curr, sbuffer); + buffer->curr += res; + buffer->size += res; + return res; +} + +/* + + */ + +struct proc_dir_entry *snd_proc_root = NULL; +struct proc_dir_entry *snd_proc_dev = NULL; +snd_info_entry_t *snd_seq_root = NULL; +#ifdef CONFIG_SND_OSSEMUL +snd_info_entry_t *snd_oss_root = NULL; +#endif + +#ifdef LINUX_2_2 +static void snd_info_fill_inode(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + +static inline void snd_info_entry_prepare(struct proc_dir_entry *de) +{ + de->fill_inode = snd_info_fill_inode; +} + +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) +{ + if (parent && de) + proc_unregister(parent, de->low_ino); +} +#else +static inline void snd_info_entry_prepare(struct proc_dir_entry *de) +{ + de->owner = THIS_MODULE; +} + +void snd_remove_proc_entry(struct proc_dir_entry *parent, + struct proc_dir_entry *de) +{ + if (de) + remove_proc_entry(de->name, parent); +} +#endif + +static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + loff_t ret; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + entry = data->entry; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) + lock_kernel(); +#endif + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + switch (orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + ret = file->f_pos; + goto out; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + ret = file->f_pos; + goto out; + case 2: /* SEEK_END */ + default: + ret = -EINVAL; + goto out; + } + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->llseek) { + ret = entry->c.ops->llseek(entry, + data->file_private_data, + file, offset, orig); + goto out; + } + break; + } + ret = -ENXIO; +out: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) + unlock_kernel(); +#endif + return ret; +} + +static ssize_t snd_info_entry_read(struct file *file, char *buffer, + size_t count, loff_t * offset) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + snd_info_buffer_t *buf; + long size = 0, size1; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + snd_assert(data != NULL, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + buf = data->rbuffer; + if (buf == NULL) + return -EIO; + if (file->f_pos >= buf->size) + return 0; + size = buf->size < count ? buf->size : count; + size1 = buf->size - file->f_pos; + if (size1 < size) + size = size1; + if (copy_to_user(buffer, buf->buffer + file->f_pos, size)) + return -EFAULT; + file->f_pos += size; + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->read) + return entry->c.ops->read(entry, + data->file_private_data, + file, buffer, count); + if (size > 0) + file->f_pos += size; + break; + } + return size; +} + +static ssize_t snd_info_entry_write(struct file *file, const char *buffer, + size_t count, loff_t * offset) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + snd_info_buffer_t *buf; + long size = 0, size1; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + snd_assert(data != NULL, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + buf = data->wbuffer; + if (buf == NULL) + return -EIO; + if (file->f_pos < 0) + return -EINVAL; + if (file->f_pos >= buf->len) + return -ENOMEM; + size = buf->len < count ? buf->len : count; + size1 = buf->len - file->f_pos; + if (size1 < size) + size = size1; + if (copy_from_user(buf->buffer + file->f_pos, buffer, size)) + return -EFAULT; + if (buf->size < file->f_pos + size) + buf->size = file->f_pos + size; + file->f_pos += size; + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->write) + return entry->c.ops->write(entry, + data->file_private_data, + file, buffer, count); + if (size > 0) + file->f_pos += size; + break; + } + return size; +} + +static int snd_info_entry_open(struct inode *inode, struct file *file) +{ + snd_info_entry_t *entry; + snd_info_private_data_t *data; + snd_info_buffer_t *buffer; + struct proc_dir_entry *p; + int mode, err; + + down(&info_mutex); + p = PDE(inode); + entry = p == NULL ? NULL : (snd_info_entry_t *)p->data; + if (entry == NULL) { + up(&info_mutex); + return -ENODEV; + } +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + if (entry->module && !try_inc_mod_count(entry->module)) { + err = -EFAULT; + goto __error1; + } + mode = file->f_flags & O_ACCMODE; + if (mode == O_RDONLY || mode == O_RDWR) { + if ((entry->content == SNDRV_INFO_CONTENT_TEXT && + !entry->c.text.read_size) || + (entry->content == SNDRV_INFO_CONTENT_DATA && + entry->c.ops->read == NULL) || + entry->content == SNDRV_INFO_CONTENT_DEVICE) { + err = -ENODEV; + goto __error; + } + } + if (mode == O_WRONLY || mode == O_RDWR) { + if ((entry->content == SNDRV_INFO_CONTENT_TEXT && + !entry->c.text.write_size) || + (entry->content == SNDRV_INFO_CONTENT_DATA && + entry->c.ops->write == NULL) || + entry->content == SNDRV_INFO_CONTENT_DEVICE) { + err = -ENODEV; + goto __error; + } + } + data = snd_magic_kcalloc(snd_info_private_data_t, 0, GFP_KERNEL); + if (data == NULL) { + err = -ENOMEM; + goto __error; + } + data->entry = entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + if (mode == O_RDONLY || mode == O_RDWR) { + buffer = (snd_info_buffer_t *) + snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + if (buffer == NULL) { + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->len = (entry->c.text.read_size + + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + buffer->buffer = vmalloc(buffer->len); + if (buffer->buffer == NULL) { + kfree(buffer); + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->curr = buffer->buffer; + data->rbuffer = buffer; + } + if (mode == O_WRONLY || mode == O_RDWR) { + buffer = (snd_info_buffer_t *) + snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + if (buffer == NULL) { + if (mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->len = (entry->c.text.write_size + + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + buffer->buffer = vmalloc(buffer->len); + if (buffer->buffer == NULL) { + if (mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + kfree(buffer); + snd_magic_kfree(data); + err = -ENOMEM; + goto __error; + } + buffer->curr = buffer->buffer; + data->wbuffer = buffer; + } + break; + case SNDRV_INFO_CONTENT_DATA: /* data */ + if (entry->c.ops->open) { + if ((err = entry->c.ops->open(entry, mode, + &data->file_private_data)) < 0) { + snd_magic_kfree(data); + goto __error; + } + } + break; + } + file->private_data = data; + up(&info_mutex); + if (entry->content == SNDRV_INFO_CONTENT_TEXT && + (mode == O_RDONLY || mode == O_RDWR)) { + if (entry->c.text.read) { + down(&entry->access); + entry->c.text.read(entry, data->rbuffer); + up(&entry->access); + } + } + return 0; + + __error: + dec_mod_count(entry->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + up(&info_mutex); + return err; +} + +static int snd_info_entry_release(struct inode *inode, struct file *file) +{ + snd_info_entry_t *entry; + snd_info_private_data_t *data; + int mode; + + mode = file->f_flags & O_ACCMODE; + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_TEXT: + if (mode == O_RDONLY || mode == O_RDWR) { + vfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + if (mode == O_WRONLY || mode == O_RDWR) { + if (entry->c.text.write) { + entry->c.text.write(entry, data->wbuffer); + if (data->wbuffer->error) { + snd_printk(KERN_WARNING "data write error to %s (%i)\n", + entry->name, + data->wbuffer->error); + } + } + vfree(data->wbuffer->buffer); + kfree(data->wbuffer); + } + break; + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->release) + entry->c.ops->release(entry, mode, + data->file_private_data); + break; + } + dec_mod_count(entry->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + snd_magic_kfree(data); + return 0; +} + +static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + unsigned int mask; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + mask = 0; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->poll) + return entry->c.ops->poll(entry, + data->file_private_data, + file, wait); + if (entry->c.ops->read) + mask |= POLLIN | POLLRDNORM; + if (entry->c.ops->write) + mask |= POLLOUT | POLLWRNORM; + break; + } + return mask; +} + +static int snd_info_entry_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_info_private_data_t *data; + struct snd_info_entry *entry; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->ioctl) + return entry->c.ops->ioctl(entry, + data->file_private_data, + file, cmd, arg); + break; + } + return -ENOTTY; +} + +static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file->f_dentry->d_inode; + snd_info_private_data_t *data; + struct snd_info_entry *entry; + + data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + if (data == NULL) + return 0; + entry = data->entry; + switch (entry->content) { + case SNDRV_INFO_CONTENT_DATA: + if (entry->c.ops->mmap) + return entry->c.ops->mmap(entry, + data->file_private_data, + inode, file, vma); + break; + } + return -ENXIO; +} + +static struct file_operations snd_info_entry_operations = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + llseek: snd_info_entry_llseek, + read: snd_info_entry_read, + write: snd_info_entry_write, + poll: snd_info_entry_poll, + ioctl: snd_info_entry_ioctl, + mmap: snd_info_entry_mmap, + open: snd_info_entry_open, + release: snd_info_entry_release, +}; + +#ifdef LINUX_2_2 +static struct inode_operations snd_info_entry_inode_operations = +{ + &snd_info_entry_operations, /* default sound info directory file-ops */ +}; + +static struct inode_operations snd_info_device_inode_operations = +{ + &snd_fops, /* default sound info directory file-ops */ +}; +#endif /* LINUX_2_2 */ + +static int snd_info_card_readlink(struct dentry *dentry, + char *buffer, int buflen) +{ + char *s = PDE(dentry->d_inode)->data; +#ifndef LINUX_2_2 + return vfs_readlink(dentry, buffer, buflen, s); +#else + int len; + + if (s == NULL) + return -EIO; + len = strlen(s); + if (len > buflen) + len = buflen; + if (copy_to_user(buffer, s, len)) + return -EFAULT; + return len; +#endif +} + +#ifndef LINUX_2_2 +static int snd_info_card_followlink(struct dentry *dentry, + struct nameidata *nd) +{ + char *s = PDE(dentry->d_inode)->data; + return vfs_follow_link(nd, s); +} +#else +static struct dentry *snd_info_card_followlink(struct dentry *dentry, + struct dentry *base, + unsigned int follow) +{ + char *s = PDE(dentry->d_inode)->data; + return lookup_dentry(s, base, follow); +} +#endif + +#ifdef LINUX_2_2 +static struct file_operations snd_info_card_link_operations = +{ + NULL +}; +#endif + +struct inode_operations snd_info_card_link_inode_operations = +{ +#ifdef LINUX_2_2 + default_file_ops: &snd_info_card_link_operations, +#endif + readlink: snd_info_card_readlink, + follow_link: snd_info_card_followlink, +}; + +struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *p; + p = create_proc_entry(name, mode, parent); + if (p) + snd_info_entry_prepare(p); + return p; +} + +int __init snd_info_init(void) +{ + struct proc_dir_entry *p; + + p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root); + if (p == NULL) + return -ENOMEM; + snd_proc_root = p; + p = snd_create_proc_entry("dev", S_IFDIR | S_IRUGO | S_IXUGO, snd_proc_root); + if (p == NULL) + return -ENOMEM; + snd_proc_dev = p; +#ifdef CONFIG_SND_OSSEMUL + { + snd_info_entry_t *entry; + if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_oss_root = entry; + } +#endif +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + { + snd_info_entry_t *entry; + if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_seq_root = entry; + } +#endif + snd_info_version_init(); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_info_init(); +#endif + snd_minor_info_init(); +#ifdef CONFIG_SND_OSSEMUL + snd_minor_info_oss_init(); +#endif + snd_card_info_init(); + return 0; +} + +int __exit snd_info_done(void) +{ + snd_card_info_done(); +#ifdef CONFIG_SND_OSSEMUL + snd_minor_info_oss_done(); +#endif + snd_minor_info_done(); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_info_done(); +#endif + snd_info_version_done(); + if (snd_proc_root) { +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_root) + snd_info_unregister(snd_seq_root); +#endif +#ifdef CONFIG_SND_OSSEMUL + if (snd_oss_root) + snd_info_unregister(snd_oss_root); +#endif + snd_remove_proc_entry(snd_proc_root, snd_proc_dev); + snd_remove_proc_entry(&proc_root, snd_proc_root); + } + return 0; +} + +/* + + */ + + +int snd_info_card_register(snd_card_t * card) +{ + char str[8]; + char *s; + snd_info_entry_t *entry; + struct proc_dir_entry *p; + + snd_assert(card != NULL, return -ENXIO); + + sprintf(str, "card%i", card->number); + if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + card->proc_root = entry; + + if (!strcmp(card->id, str)) + return 0; + + s = snd_kmalloc_strdup(str, GFP_KERNEL); + if (s == NULL) + return -ENOMEM; + p = snd_create_proc_entry(card->id, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, snd_proc_root); + if (p == NULL) + return -ENOMEM; + p->data = s; +#ifndef LINUX_2_2 + p->owner = card->module; + p->proc_iops = &snd_info_card_link_inode_operations; +#else + p->ops = &snd_info_card_link_inode_operations; +#endif + card->proc_root_link = p; + return 0; +} + +int snd_info_card_unregister(snd_card_t * card) +{ + void *data; + + snd_assert(card != NULL, return -ENXIO); + if (card->proc_root_link) { + data = card->proc_root_link->data; + card->proc_root_link->data = NULL; + kfree(data); + snd_remove_proc_entry(snd_proc_root, card->proc_root_link); + card->proc_root_link = NULL; + } + if (card->proc_root) { + snd_info_unregister(card->proc_root); + card->proc_root = NULL; + } + return 0; +} + +/* + + */ + +int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) +{ + int c = -1; + + if (len <= 0 || buffer->stop || buffer->error) + return 1; + while (--len > 0) { + c = *buffer->curr++; + if (c == '\n') { + if ((buffer->curr - buffer->buffer) >= buffer->size) { + buffer->stop = 1; + } + break; + } + *line++ = c; + if ((buffer->curr - buffer->buffer) >= buffer->size) { + buffer->stop = 1; + break; + } + } + while (c != '\n' && !buffer->stop) { + c = *buffer->curr++; + if ((buffer->curr - buffer->buffer) >= buffer->size) { + buffer->stop = 1; + } + } + *line = '\0'; + return 0; +} + +char *snd_info_get_str(char *dest, char *src, int len) +{ + int c; + + while (*src == ' ' || *src == '\t') + src++; + if (*src == '"' || *src == '\'') { + c = *src++; + while (--len > 0 && *src && *src != c) { + *dest++ = *src++; + } + if (*src == c) + src++; + } else { + while (--len > 0 && *src && *src != ' ' && *src != '\t') { + *dest++ = *src++; + } + } + *dest = 0; + while (*src == ' ' || *src == '\t') + src++; + return src; +} + +static snd_info_entry_t *snd_info_create_entry(const char *name) +{ + snd_info_entry_t *entry; + entry = (snd_info_entry_t *) snd_kcalloc(sizeof(snd_info_entry_t), GFP_KERNEL); + if (entry == NULL) + return NULL; + entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); + if (entry->name == NULL) { + kfree(entry); + return NULL; + } + entry->mode = S_IFREG | S_IRUGO; + entry->content = SNDRV_INFO_CONTENT_TEXT; + init_MUTEX(&entry->access); + return entry; +} + +snd_info_entry_t *snd_info_create_module_entry(struct module * module, + const char *name, + snd_info_entry_t *parent) +{ + snd_info_entry_t *entry = snd_info_create_entry(name); + if (entry) { + entry->module = module; + entry->parent = parent; + } + return entry; +} + +snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card, + const char *name, + snd_info_entry_t * parent) +{ + snd_info_entry_t *entry = snd_info_create_entry(name); + if (entry) { + entry->module = card->module; + entry->card = card; + entry->parent = parent; + } + return entry; +} + +void snd_info_free_entry(snd_info_entry_t * entry) +{ + if (entry == NULL) + return; + if (entry->name) + kfree((char *)entry->name); + if (entry->private_free) + entry->private_free(entry); + kfree(entry); +} + +#ifdef LINUX_2_2 +static void snd_info_device_fill_inode(struct inode *inode, int fill) +{ + struct proc_dir_entry *de; + snd_info_entry_t *entry; + + if (!fill) { + MOD_DEC_USE_COUNT; + return; + } + MOD_INC_USE_COUNT; + de = PDE(inode); + if (de == NULL) + return; + entry = (snd_info_entry_t *) de->data; + if (entry == NULL) + return; + inode->i_gid = snd_device_gid; + inode->i_uid = snd_device_uid; + inode->i_rdev = MKDEV(entry->c.device.major, entry->c.device.minor); +} + +static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry) +{ + de->fill_inode = snd_info_device_fill_inode; +} +#else +static inline void snd_info_device_entry_prepare(struct proc_dir_entry *de, snd_info_entry_t *entry) +{ + de->rdev = mk_kdev(entry->c.device.major, entry->c.device.minor); + de->owner = THIS_MODULE; +} +#endif /* LINUX_2_2 */ + +snd_info_entry_t *snd_info_create_device(const char *name, unsigned int number, unsigned int mode) +{ +#ifdef CONFIG_DEVFS_FS + char dname[32]; +#endif + unsigned short major = number >> 16; + unsigned short minor = (unsigned short) number; + snd_info_entry_t *entry; + struct proc_dir_entry *p = NULL; + + if (!major) + major = snd_major; + if (!mode) + mode = S_IFCHR | S_IRUGO | S_IWUGO; + mode &= (snd_device_mode & (S_IRUGO | S_IWUGO)) | S_IFCHR | S_IFBLK; + entry = snd_info_create_module_entry(THIS_MODULE, name, NULL); + if (entry == NULL) + return NULL; + entry->content = SNDRV_INFO_CONTENT_DEVICE; + entry->mode = mode; + entry->c.device.major = major; + entry->c.device.minor = minor; + down(&info_mutex); + p = create_proc_entry(entry->name, entry->mode, snd_proc_dev); + if (p) { + snd_info_device_entry_prepare(p, entry); +#ifndef LINUX_2_2 + /* we should not set this - at least on 2.4.14 or later it causes + problems! */ + /* p->proc_fops = &snd_fops; */ +#else + p->ops = &snd_info_device_inode_operations; +#endif + } else { + up(&info_mutex); + snd_info_free_entry(entry); + return NULL; + } + p->gid = snd_device_gid; + p->uid = snd_device_uid; + p->data = (void *) entry; + entry->p = p; + up(&info_mutex); +#ifdef CONFIG_DEVFS_FS + if (strncmp(name, "controlC", 8)) { /* created in sound.c */ + sprintf(dname, "snd/%s", name); + devfs_register(NULL, dname, DEVFS_FL_DEFAULT, + major, minor, mode, + &snd_fops, NULL); + } +#endif + return entry; +} + +void snd_info_free_device(snd_info_entry_t * entry) +{ +#ifdef CONFIG_DEVFS_FS + char dname[32]; + devfs_handle_t master; +#endif + + snd_runtime_check(entry, return); + down(&info_mutex); + snd_remove_proc_entry(snd_proc_dev, entry->p); + up(&info_mutex); +#ifdef CONFIG_DEVFS_FS + if (entry->p && strncmp(entry->name, "controlC", 8)) { + sprintf(dname, "snd/%s", entry->name); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + master = devfs_find_handle(NULL, dname, strlen(dname), 0, 0, DEVFS_SPECIAL_CHR, 0); +#else + master = devfs_find_handle(NULL, dname, 0, 0, DEVFS_SPECIAL_CHR, 0); +#endif + devfs_unregister(master); + } +#endif + snd_info_free_entry(entry); +} + +int snd_info_register(snd_info_entry_t * entry) +{ + struct proc_dir_entry *root, *p = NULL; + + snd_assert(entry != NULL, return -ENXIO); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + down(&info_mutex); + p = snd_create_proc_entry(entry->name, entry->mode, root); + if (!p) { + up(&info_mutex); + return -ENOMEM; + } +#ifndef LINUX_2_2 + p->owner = entry->module; +#endif + if (!S_ISDIR(entry->mode)) { +#ifndef LINUX_2_2 + p->proc_fops = &snd_info_entry_operations; +#else + p->ops = &snd_info_entry_inode_operations; +#endif + } + p->size = entry->size; + p->data = entry; + entry->p = p; + up(&info_mutex); + return 0; +} + +int snd_info_unregister(snd_info_entry_t * entry) +{ + struct proc_dir_entry *root; + + snd_assert(entry != NULL && entry->p != NULL, return -ENXIO); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + snd_assert(root, return -ENXIO); + down(&info_mutex); + snd_remove_proc_entry(root, entry->p); + up(&info_mutex); + snd_info_free_entry(entry); + return 0; +} + +/* + + */ + +static snd_info_entry_t *snd_info_version_entry = NULL; + +static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + static char *kernel_version = UTS_RELEASE; + + snd_iprintf(buffer, + "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n" + "Compiled on " __DATE__ " for kernel %s" +#ifdef __SMP__ + " (SMP)" +#endif +#ifdef MODVERSIONS + " with versioned symbols" +#endif + ".\n", kernel_version); +} + +static int __init snd_info_version_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); + if (entry == NULL) + return -ENOMEM; + entry->c.text.read_size = 256; + entry->c.text.read = snd_info_version_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_info_version_entry = entry; + return 0; +} + +static int __exit snd_info_version_done(void) +{ + if (snd_info_version_entry) + snd_info_unregister(snd_info_version_entry); + return 0; +} + +#endif /* CONFIG_PROC_FS */ diff -Nru linux/sound/core/info_oss.c linux-2.4.19-pre5-mjc/sound/core/info_oss.c --- linux/sound/core/info_oss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/info_oss.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,151 @@ +/* + * Information interface for ALSA driver + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_OSSEMUL + +/* + * OSS compatible part + */ + +static DECLARE_MUTEX(strings); +static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT]; +static snd_info_entry_t *snd_sndstat_proc_entry; + +int snd_oss_info_register(int dev, int num, char *string) +{ + char *x; + + snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO); + snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO); + down(&strings); + if (string == NULL) { + if ((x = snd_sndstat_strings[num][dev]) != NULL) { + kfree(x); + x = NULL; + } + } else { + x = snd_kmalloc_strdup(string, GFP_KERNEL); + if (x == NULL) { + up(&strings); + return -ENOMEM; + } + } + snd_sndstat_strings[num][dev] = x; + up(&strings); + return 0; +} + +extern void snd_card_info_read_oss(snd_info_buffer_t * buffer); + +static int snd_sndstat_show_strings(snd_info_buffer_t * buf, char *id, int dev) +{ + int idx, ok = -1; + char *str; + + snd_iprintf(buf, "\n%s:", id); + down(&strings); + for (idx = 0; idx < SNDRV_CARDS; idx++) { + str = snd_sndstat_strings[idx][dev]; + if (str) { + if (ok < 0) { + snd_iprintf(buf, "\n"); + ok++; + } + snd_iprintf(buf, "%i: %s\n", idx, str); + } + } + up(&strings); + if (ok < 0) + snd_iprintf(buf, " NOT ENABLED IN CONFIG\n"); + return ok; +} + +static void snd_sndstat_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n"); + snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n", + system_utsname.sysname, + system_utsname.nodename, + system_utsname.release, + system_utsname.version, + system_utsname.machine); + snd_iprintf(buffer, "Config options: 0\n"); + snd_iprintf(buffer, "\nInstalled drivers: \n"); + snd_iprintf(buffer, "Type 10: ALSA emulation\n"); + snd_iprintf(buffer, "\nCard config: \n"); + snd_card_info_read_oss(buffer); + snd_sndstat_show_strings(buffer, "Audio devices", SNDRV_OSS_INFO_DEV_AUDIO); + snd_sndstat_show_strings(buffer, "Synth devices", SNDRV_OSS_INFO_DEV_SYNTH); + snd_sndstat_show_strings(buffer, "Midi devices", SNDRV_OSS_INFO_DEV_MIDI); + snd_sndstat_show_strings(buffer, "Timers", SNDRV_OSS_INFO_DEV_TIMERS); + snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS); +} + +int snd_info_minor_register(void) +{ + snd_info_entry_t *entry; + + memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 2048; + entry->c.text.read = snd_sndstat_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_sndstat_proc_entry = entry; + return 0; +} + +int snd_info_minor_unregister(void) +{ + if (snd_sndstat_proc_entry) { + snd_info_unregister(snd_sndstat_proc_entry); + snd_sndstat_proc_entry = NULL; + } + return 0; +} + +#else + +int snd_info_minor_register(void) +{ + return 0; +} + +int snd_info_minor_unregister(void) +{ + return 0; +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -Nru linux/sound/core/init.c linux-2.4.19-pre5-mjc/sound/core/init.c --- linux/sound/core/init.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/init.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,295 @@ +/* + * Initialization routines + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +int snd_cards_count = 0; +static unsigned int snd_cards_lock = 0; /* locked for registering/using */ +snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; +rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED; + +#ifdef CONFIG_SND_OSSEMUL +int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); +#endif + +static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_iprintf(buffer, "%s\n", entry->card->id); +} + +snd_card_t *snd_card_new(int idx, const char *xid, + struct module *module, int extra_size) +{ + snd_card_t *card; + snd_info_entry_t *entry; + int err; + + if (extra_size < 0) + extra_size = 0; + card = (snd_card_t *) snd_kcalloc(sizeof(snd_card_t) + extra_size, GFP_KERNEL); + if (card == NULL) + return NULL; + if (xid) { + if (!snd_info_check_reserved_words(xid)) + goto __error; + strncpy(card->id, xid, sizeof(card->id) - 1); + } + write_lock(&snd_card_rwlock); + if (idx < 0) { + int idx2; + for (idx2 = 0; idx2 < snd_ecards_limit; idx2++) + if (!(snd_cards_lock & (1 << idx2))) { + idx = idx2; + break; + } + } else if (idx < snd_ecards_limit) { + if (snd_cards_lock & (1 << idx)) + idx = -1; /* invalid */ + } + if (idx < 0 || idx >= snd_ecards_limit) { + write_unlock(&snd_card_rwlock); + if (idx >= snd_ecards_limit) + snd_printk(KERN_ERR "card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); + goto __error; + } + snd_cards_lock |= 1 << idx; /* lock it */ + write_unlock(&snd_card_rwlock); + card->number = idx; + if (!card->id[0]) + sprintf(card->id, "card%i", card->number); + card->module = module; + INIT_LIST_HEAD(&card->devices); + rwlock_init(&card->control_rwlock); + rwlock_init(&card->control_owner_lock); + INIT_LIST_HEAD(&card->controls); + INIT_LIST_HEAD(&card->ctl_files); +#ifdef CONFIG_PM + init_MUTEX(&card->power_lock); + init_waitqueue_head(&card->power_sleep); +#endif + /* the control interface cannot be accessed from the user space until */ + /* snd_cards_bitmask and snd_cards are set with snd_card_register */ + if ((err = snd_ctl_register(card)) < 0) { + snd_printd("unable to register control minors\n"); + goto __error; + } + if ((err = snd_info_card_register(card)) < 0) { + snd_printd("unable to register card info\n"); + goto __error_ctl; + } + if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { + snd_printd("unable to create card entry\n"); + goto __error_info; + } + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_card_id_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + goto __error_info; + } + card->proc_id = entry; + if (extra_size > 0) + card->private_data = (char *)card + sizeof(snd_card_t); + return card; + + __error_info: + snd_info_card_unregister(card); + __error_ctl: + snd_ctl_unregister(card); + __error: + kfree(card); + return NULL; +} + +int snd_card_free(snd_card_t * card) +{ + if (card == NULL) + return -EINVAL; + write_lock(&snd_card_rwlock); + snd_cards[card->number] = NULL; + snd_cards_count--; + write_unlock(&snd_card_rwlock); +#ifdef CONFIG_SND_OSSEMUL + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, 1); +#endif + if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { + snd_printk(KERN_ERR "unable to free all devices (pre)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { + snd_printk(KERN_ERR "unable to free all devices (normal)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_ctl_unregister(card) < 0) { + snd_printk(KERN_ERR "unable to unregister control minors\n"); + /* Not fatal error */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { + snd_printk(KERN_ERR "unable to free all devices (post)\n"); + /* Fatal, but this situation should never occur */ + } + if (card->private_free) + card->private_free(card); + snd_info_free_entry(card->proc_id); + if (snd_info_card_unregister(card) < 0) { + snd_printk(KERN_WARNING "unable to unregister card info\n"); + /* Not fatal error */ + } + write_lock(&snd_card_rwlock); + snd_cards_lock &= ~(1 << card->number); + write_unlock(&snd_card_rwlock); + kfree(card); + return 0; +} + +int snd_card_register(snd_card_t * card) +{ + int err; + + snd_runtime_check(card != NULL, return -EINVAL); + if ((err = snd_device_register_all(card)) < 0) + return err; + write_lock(&snd_card_rwlock); + snd_cards[card->number] = card; + snd_cards_count++; + write_unlock(&snd_card_rwlock); +#ifdef CONFIG_SND_OSSEMUL + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, 0); +#endif + return 0; +} + +static snd_info_entry_t *snd_card_info_entry = NULL; + +static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx, count; + snd_card_t *card; + + for (idx = count = 0; idx < SNDRV_CARDS; idx++) { + read_lock(&snd_card_rwlock); + if ((card = snd_cards[idx]) != NULL) { + count++; + snd_iprintf(buffer, "%i [%-15s]: %s - %s\n", + idx, + card->id, + card->driver, + card->shortname); + snd_iprintf(buffer, " %s\n", + card->longname); + } + read_unlock(&snd_card_rwlock); + } + if (!count) + snd_iprintf(buffer, "--- no soundcards ---\n"); +} + +#ifdef CONFIG_SND_OSSEMUL + +void snd_card_info_read_oss(snd_info_buffer_t * buffer) +{ + int idx, count; + snd_card_t *card; + + for (idx = count = 0; idx < SNDRV_CARDS; idx++) { + read_lock(&snd_card_rwlock); + if ((card = snd_cards[idx]) != NULL) { + count++; + snd_iprintf(buffer, "%s\n", card->longname); + } + read_unlock(&snd_card_rwlock); + } + if (!count) { + snd_iprintf(buffer, "--- no soundcards ---\n"); + } +} + +#endif + +int __init snd_card_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); + snd_runtime_check(entry != NULL, return -ENOMEM); + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_card_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + snd_card_info_entry = entry; + + return 0; +} + +int __exit snd_card_info_done(void) +{ + if (snd_card_info_entry) + snd_info_unregister(snd_card_info_entry); + return 0; +} + +int snd_component_add(snd_card_t *card, const char *component) +{ + char *ptr; + int len = strlen(component); + + ptr = strstr(card->components, component); + if (ptr != NULL) { + if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ + return 1; + } + if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { + snd_BUG(); + return -ENOMEM; + } + if (card->components[0] != '\0') + strcat(card->components, " "); + strcat(card->components, component); + return 0; +} + +#ifdef CONFIG_PM +/* the power lock must be active before call */ +void snd_power_wait(snd_card_t *card) +{ + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&card->power_sleep, &wait); + snd_power_unlock(card); + schedule_timeout(30 * HZ); + remove_wait_queue(&card->power_sleep, &wait); + snd_power_lock(card); +} +#endif /* CONFIG_PM */ diff -Nru linux/sound/core/ioctl32/Makefile linux-2.4.19-pre5-mjc/sound/core/ioctl32/Makefile --- linux/sound/core/ioctl32/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,17 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := _ioctl32.o + +list-multi := snd-ioctl32.o + +snd-ioctl32-objs := ioctl32.o pcm32.o rawmidi32.o timer32.o hwdep32.o + +obj-$(CONFIG_SND_BIT32_EMUL) += snd-ioctl32.o + +include $(TOPDIR)/Rules.make + +snd-ioctl32.o: $(snd-ioctl32-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ioctl32-objs) diff -Nru linux/sound/core/ioctl32/hwdep32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/hwdep32.c --- linux/sound/core/ioctl32/hwdep32.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/hwdep32.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,37 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper hwdep_mappers[] = { + { SNDRV_HWDEP_IOCTL_PVERSION, NULL }, + { SNDRV_HWDEP_IOCTL_INFO, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_INFO, NULL }, + { 0 }, +}; diff -Nru linux/sound/core/ioctl32/ioctl32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.c --- linux/sound/core/ioctl32/ioctl32.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,357 @@ +/* + * 32bit -> 64bit ioctl wrapper for control API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include "ioctl32.h" + +/* + * register/unregister mappers + * exported for other modules + */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); +int unregister_ioctl32_conversion(unsigned int cmd); + + +int snd_ioctl32_register(struct ioctl32_mapper *mappers) +{ + int err; + struct ioctl32_mapper *m; + + lock_kernel(); + for (m = mappers; m->cmd; m++) { + err = register_ioctl32_conversion(m->cmd, m->handler); + if (err < 0) { + unlock_kernel(); + return err; + } + m->registered++; + } + return 0; +} + +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers) +{ + struct ioctl32_mapper *m; + + lock_kernel(); + for (m = mappers; m->cmd; m++) { + if (m->registered) { + unregister_ioctl32_conversion(m->cmd); + m->registered = 0; + } + } + unlock_kernel(); +} + + +/* + * Controls + */ + +struct sndrv_ctl_elem_list32 { + u32 offset; + u32 space; + u32 used; + u32 count; + u32 pids; + unsigned char reserved[50]; +}; + +#define CVT_sndrv_ctl_elem_list()\ +{\ + COPY(offset);\ + COPY(space);\ + COPY(used);\ + COPY(count);\ + CPTR(pids);\ +} + +DEFINE_ALSA_IOCTL(ctl_elem_list); + + +/* + * control element info + * it uses union, so the things are not easy.. + */ + +struct sndrv_ctl_elem_info32 { + struct sndrv_ctl_elem_id id; // the size of struct is same + s32 type; + u32 access; + u32 count; + s32 owner; + union { + struct { + s32 min; + s32 max; + s32 step; + } integer; + struct { + u32 items; + u32 item; + char name[64]; + } enumerated; + unsigned char reserved[128]; + } value; + unsigned char reserved[64]; +}; + +static int snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + struct sndrv_ctl_elem_info data; + struct sndrv_ctl_elem_info32 data32; + int err; + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.id = data32.id; + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data); + if (err < 0) + return err; + /* restore info to 32bit */ + data32.type = data.type; + data32.access = data.access; + data32.count = data.count; + data32.owner = data.owner; + switch (data.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + data32.value.integer.min = data.value.integer.min; + data32.value.integer.max = data.value.integer.min; + data32.value.integer.step = data.value.integer.step; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + data32.value.enumerated.items = data.value.enumerated.items; + data32.value.enumerated.item = data.value.enumerated.item; + memcpy(data32.value.enumerated.name, data.value.enumerated.name, + sizeof(data.value.enumerated.name)); + break; + default: + break; + } + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return err; +} + + +struct sndrv_ctl_elem_value32 { + struct sndrv_ctl_elem_id id; + unsigned int indirect: 1; + union { + union { + s32 value[128]; + u32 value_ptr; + } integer; + union { + u32 item[128]; + u32 item_ptr; + } enumerated; + union { + unsigned char data[512]; + u32 data_ptr; + } bytes; + struct sndrv_aes_iec958 iec958; + } value; + unsigned char reserved[128]; +}; + + +/* hmm, it's so hard to retrieve the value type from the control id.. */ +static int get_ctl_type(struct file *file, snd_ctl_elem_id_t *id) +{ + snd_ctl_file_t *ctl; + snd_kcontrol_t *kctl; + snd_ctl_elem_info_t info; + int err; + + ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + + read_lock(&ctl->card->control_rwlock); + kctl = snd_ctl_find_id(ctl->card, id); + if (! kctl) { + read_unlock(&ctl->card->control_rwlock); + return -ENXIO; + } + info.id = *id; + err = kctl->info(kctl, &info); + if (err >= 0) + err = info.type; + read_unlock(&ctl->card->control_rwlock); + return err; +} + + +static int snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + // too big? + struct sndrv_ctl_elem_value data; + struct sndrv_ctl_elem_value32 data32; + int err, i; + int type; + /* FIXME: check the sane ioctl.. */ + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; + memset(&data, 0, sizeof(data)); + data.id = data32.id; + data.indirect = data32.indirect; + if (data.indirect) /* FIXME: this is not correct for long arrays */ + data.value.integer.value_ptr = (void*)TO_PTR(data32.value.integer.value_ptr); + type = get_ctl_type(file, &data.id); + if (type < 0) + return type; + if (! data.indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data.value.integer.value[i] = data32.value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data.value.enumerated.item[i] = data32.value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data.value.bytes.data, data32.value.bytes.data, + sizeof(data.value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data.value.iec958 = data32.value.iec958; + break; + default: + break; + } + } + + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data); + if (err < 0) + return err; + /* restore info to 32bit */ + if (! data.indirect) { + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < 128; i++) + data.value.integer.value[i] = data32.value.integer.value[i]; + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < 128; i++) + data.value.enumerated.item[i] = data32.value.enumerated.item[i]; + break; + case SNDRV_CTL_ELEM_TYPE_BYTES: + memcpy(data.value.bytes.data, data32.value.bytes.data, + sizeof(data.value.bytes.data)); + break; + case SNDRV_CTL_ELEM_TYPE_IEC958: + data.value.iec958 = data32.value.iec958; + break; + default: + break; + } + } + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return err; +} + + +/* + */ + +#define AP(x) snd_ioctl32_##x + +static struct ioctl32_mapper control_mappers[] = { + /* controls (without rawmidi, hwdep, timer releated ones) */ + { SNDRV_CTL_IOCTL_PVERSION, NULL }, + { SNDRV_CTL_IOCTL_CARD_INFO , NULL }, + { SNDRV_CTL_IOCTL_ELEM_LIST, AP(ctl_elem_list) }, + { SNDRV_CTL_IOCTL_ELEM_INFO, AP(ctl_elem_info) }, + { SNDRV_CTL_IOCTL_ELEM_READ, AP(ctl_elem_value) }, + { SNDRV_CTL_IOCTL_ELEM_WRITE, AP(ctl_elem_value) }, + { SNDRV_CTL_IOCTL_ELEM_LOCK, NULL }, + { SNDRV_CTL_IOCTL_ELEM_UNLOCK, NULL }, + { SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, + { SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL }, + { SNDRV_CTL_IOCTL_POWER, NULL }, + { SNDRV_CTL_IOCTL_POWER_STATE, NULL }, + { 0 } +}; + + +/* + */ + +extern struct ioctl32_mapper pcm_mappers[]; +extern struct ioctl32_mapper rawmidi_mappers[]; +extern struct ioctl32_mapper timer_mappers[]; +extern struct ioctl32_mapper hwdep_mappers[]; + +static void snd_ioctl32_done(void) +{ + snd_ioctl32_unregister(hwdep_mappers); + snd_ioctl32_unregister(timer_mappers); + snd_ioctl32_unregister(rawmidi_mappers); + snd_ioctl32_unregister(pcm_mappers); + snd_ioctl32_unregister(control_mappers); +} + +static int __init snd_ioctl32_init(void) +{ + int err; + + err = snd_ioctl32_register(control_mappers); + if (err < 0) + return err; + err = snd_ioctl32_register(pcm_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(rawmidi_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(timer_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } + err = snd_ioctl32_register(hwdep_mappers); + if (err < 0) { + snd_ioctl32_done(); + return err; + } +} + +module_init(snd_ioctl32_init) +module_exit(snd_ioctl32_done) diff -Nru linux/sound/core/ioctl32/ioctl32.h linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.h --- linux/sound/core/ioctl32/ioctl32.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/ioctl32.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,79 @@ +/* + * 32bit -> 64bit ioctl helpers + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This file registers the converters from 32-bit ioctls to 64-bit ones. + * The converter assumes that a 32-bit user-pointer can be casted by A(x) + * macro to a valid 64-bit pointer which is accessible via copy_from/to_user. + * + */ + +#ifndef __ALSA_IOCTL32_H +#define __ALSA_IOCTL32_H + +#define TO_PTR(x) A(x) + +#define COPY(x) (dst->x = src->x) +#define CPTR(x) (dst->x = (typeof(dst->x))A(src->x)) + +#define convert_from_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *dst = dstp;\ + struct sndrv_##type##32 *src = srcp;\ + CVT_##sndrv_##type();\ +} + +#define convert_to_32(type, dstp, srcp)\ +{\ + struct sndrv_##type *src = srcp;\ + struct sndrv_##type##32 *dst = dstp;\ + CVT_##sndrv_##type();\ +} + + +#define DEFINE_ALSA_IOCTL(type) \ +static int snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file)\ +{\ + struct sndrv_##type##32 data32;\ + struct sndrv_##type data;\ + int err;\ + if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\ + return -EFAULT;\ + memset(&data, 0, sizeof(data));\ + convert_from_32(type, &data, &data32);\ + err = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, (unsigned long)&data);\ + if (err < 0)\ + return err;\ + if (cmd & (_IOC_READ << _IOC_DIRSHIFT)) {\ + convert_to_32(type, &data32, &data);\ + if (copy_to_user((void*)arg, &data32, sizeof(data32)))\ + return -EFAULT;\ + }\ + return err;\ +} + +struct ioctl32_mapper { + unsigned int cmd; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + int registered; +}; + +int snd_ioctl32_register(struct ioctl32_mapper *mappers); +void snd_ioctl32_unregister(struct ioctl32_mapper *mappers); + +#endif /* __ALSA_IOCTL32_H */ diff -Nru linux/sound/core/ioctl32/pcm32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/pcm32.c --- linux/sound/core/ioctl32/pcm32.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/pcm32.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,296 @@ +/* + * 32bit -> 64bit ioctl wrapper for PCM API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "ioctl32.h" + + +/* wrapper for sndrv_pcm_[us]frames */ +struct sndrv_pcm_sframes_str { + sndrv_pcm_sframes_t val; +}; +struct sndrv_pcm_sframes_str32 { + s32 val; +}; +struct sndrv_pcm_uframes_str { + sndrv_pcm_uframes_t val; +}; +struct sndrv_pcm_uframes_str32 { + u32 val; +}; + +#define CVT_sndrv_pcm_sframes_str() { COPY(val); } +#define CVT_sndrv_pcm_uframes_str() { COPY(val); } + + +struct sndrv_interval32 { + u32 min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +struct sndrv_pcm_hw_params32 { + u32 flags; + u32 masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct sndrv_interval32 intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + u32 rmask; + u32 cmask; + u32 info; + u32 msbits; + u32 rate_num; + u32 rate_den; + u32 fifo_size; + unsigned char reserved[64]; +}; + +#define numberof(array) (sizeof(array)/sizeof(array[0])) + +#define CVT_sndrv_pcm_hw_params()\ +{\ + int i;\ + COPY(flags);\ + for (i = 0; i < numberof(dst->masks); i++)\ + COPY(masks[i]);\ + for (i = 0; i < numberof(dst->intervals); i++) {\ + COPY(intervals[i].min);\ + COPY(intervals[i].max);\ + COPY(intervals[i].openmin);\ + COPY(intervals[i].openmax);\ + COPY(intervals[i].integer);\ + COPY(intervals[i].empty);\ + }\ + COPY(rmask);\ + COPY(cmask);\ + COPY(info);\ + COPY(msbits);\ + COPY(rate_num);\ + COPY(rate_den);\ + COPY(fifo_size);\ +} + +struct sndrv_pcm_sw_params32 { + s32 tstamp_mode; + u32 period_step; + u32 sleep_min; + u32 avail_min; + u32 xfer_align; + u32 start_threshold; + u32 stop_threshold; + u32 silence_threshold; + u32 silence_size; + u32 boundary; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_pcm_sw_params()\ +{\ + COPY(tstamp_mode);\ + COPY(period_step);\ + COPY(sleep_min);\ + COPY(avail_min);\ + COPY(xfer_align);\ + COPY(start_threshold);\ + COPY(stop_threshold);\ + COPY(silence_threshold);\ + COPY(silence_size);\ + COPY(boundary);\ +} + +struct sndrv_pcm_channel_info32 { + u32 channel; + u32 offset; + u32 first; + u32 step; +}; + +#define CVT_sndrv_pcm_channel_info()\ +{\ + COPY(channel);\ + COPY(offset);\ + COPY(first);\ + COPY(step);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_pcm_status32 { + s32 state; + struct timeval32 trigger_tstamp; + struct timeval32 tstamp; + u32 appl_ptr; + u32 hw_ptr; + s32 delay; + u32 avail; + u32 avail_max; + u32 overrange; + s32 suspended_state; + unsigned char reserved[60]; +}; + +#define CVT_sndrv_pcm_status()\ +{\ + COPY(state);\ + COPY(trigger_tstamp.tv_sec);\ + COPY(trigger_tstamp.tv_usec);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(appl_ptr);\ + COPY(hw_ptr);\ + COPY(delay);\ + COPY(avail);\ + COPY(avail_max);\ + COPY(overrange);\ + COPY(suspended_state);\ +} + +struct sndrv_xferi32 { + s32 result; + u32 buf; + u32 frames; +}; + +#define CVT_sndrv_xferi()\ +{\ + COPY(result);\ + CPTR(buf);\ + COPY(frames);\ +} + +DEFINE_ALSA_IOCTL(pcm_uframes_str); +DEFINE_ALSA_IOCTL(pcm_sframes_str); +DEFINE_ALSA_IOCTL(pcm_hw_params); +DEFINE_ALSA_IOCTL(pcm_sw_params); +DEFINE_ALSA_IOCTL(pcm_channel_info); +DEFINE_ALSA_IOCTL(pcm_status); +DEFINE_ALSA_IOCTL(xferi); + +/* snd_xfern needs remapping of bufs */ +struct sndrv_xfern32 { + s32 result; + u32 bufs; /* this is void **; */ + u32 frames; +}; + +/* + * xfern ioctl nees to copy (up to) 128 pointers on stack. + * although we may pass the copied pointers through f_op->ioctl, but the ioctl + * handler there expands again the same 128 pointers on stack, so it is better + * to handle the function (calling pcm_readv/writev) directly in this handler. + */ +static int snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + struct sndrv_xfern32 data32, *srcptr = (struct sndrv_xfern32*)arg; + void *bufs[128]; + int err = 0, ch, i; + u32 *bufptr; + + /* FIXME: need to check whether fop->ioctl is sane */ + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL && substream->runtime, return -ENXIO); + + /* check validty of the command */ + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + case SNDRV_PCM_IOCTL_READN_FRAMES: + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + break; + } + if ((ch = substream->runtime->channels) > 128) + return -EINVAL; + if (get_user(data32.frames, &srcptr->frames)) + return -EFAULT; + __get_user(data32.bufs, &srcptr->bufs); + bufptr = (u32*)TO_PTR(data32.bufs); + for (i = 0; i < ch; i++) { + u32 ptr; + if (get_user(ptr, bufptr)) + return -EFAULT; + bufs[ch] = (void*)TO_PTR(ptr); + bufptr++; + } + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + err = snd_pcm_lib_writev(substream, bufs, data32.frames); + break; + case SNDRV_PCM_IOCTL_READN_FRAMES: + err = snd_pcm_lib_readv(substream, bufs, data32.frames); + break; + } + + if (err < 0) + return err; + if (put_user(err, &srcptr->result)) + return -EFAULT; + return err < 0 ? err : 0; +} + + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper pcm_mappers[] = { + { SNDRV_PCM_IOCTL_PVERSION, NULL }, + { SNDRV_PCM_IOCTL_INFO, NULL }, + { SNDRV_PCM_IOCTL_HW_REFINE, AP(pcm_hw_params) }, + { SNDRV_PCM_IOCTL_HW_PARAMS, AP(pcm_hw_params) }, + { SNDRV_PCM_IOCTL_HW_FREE, NULL }, + { SNDRV_PCM_IOCTL_SW_PARAMS, AP(pcm_sw_params) }, + { SNDRV_PCM_IOCTL_STATUS, AP(pcm_status) }, + { SNDRV_PCM_IOCTL_DELAY, AP(pcm_sframes_str) }, + { SNDRV_PCM_IOCTL_CHANNEL_INFO, AP(pcm_channel_info) }, + { SNDRV_PCM_IOCTL_PREPARE, NULL }, + { SNDRV_PCM_IOCTL_RESET, NULL }, + { SNDRV_PCM_IOCTL_START, NULL }, + { SNDRV_PCM_IOCTL_DROP, NULL }, + { SNDRV_PCM_IOCTL_DRAIN, NULL }, + { SNDRV_PCM_IOCTL_PAUSE, NULL }, + { SNDRV_PCM_IOCTL_REWIND, AP(pcm_uframes_str) }, + { SNDRV_PCM_IOCTL_RESUME, NULL }, + { SNDRV_PCM_IOCTL_XRUN, NULL }, + { SNDRV_PCM_IOCTL_WRITEI_FRAMES, AP(xferi) }, + { SNDRV_PCM_IOCTL_READI_FRAMES, AP(xferi) }, + { SNDRV_PCM_IOCTL_WRITEN_FRAMES, AP(xfern) }, + { SNDRV_PCM_IOCTL_READN_FRAMES, AP(xfern) }, + { SNDRV_PCM_IOCTL_LINK, NULL }, + { SNDRV_PCM_IOCTL_UNLINK, NULL }, + + { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, + { SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL }, + + { 0 }, +}; diff -Nru linux/sound/core/ioctl32/rawmidi32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/rawmidi32.c --- linux/sound/core/ioctl32/rawmidi32.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/rawmidi32.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,86 @@ +/* + * 32bit -> 64bit ioctl wrapper for raw MIDI API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_rawmidi_params32 { + s32 stream; + u32 buffer_size; + u32 avail_min; + unsigned int no_active_sensing: 1; + unsigned char reserved[16]; +}; + +#define CVT_sndrv_rawmidi_params()\ +{\ + COPY(stream);\ + COPY(buffer_size);\ + COPY(avail_min);\ + COPY(no_active_sensing);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_rawmidi_status32 { + s32 stream; + struct timeval32 tstamp; + u32 avail; + u32 xruns; + unsigned char reserved[16]; +}; + +#define CVT_sndrv_rawmidi_status()\ +{\ + COPY(stream);\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(avail);\ + COPY(xruns);\ +} + +DEFINE_ALSA_IOCTL(rawmidi_params); +DEFINE_ALSA_IOCTL(rawmidi_status); + + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper rawmidi_mappers[] = { + { SNDRV_RAWMIDI_IOCTL_PVERSION, NULL }, + { SNDRV_RAWMIDI_IOCTL_INFO, NULL }, + { SNDRV_RAWMIDI_IOCTL_PARAMS, AP(rawmidi_params) }, + { SNDRV_RAWMIDI_IOCTL_STATUS, AP(rawmidi_status) }, + { SNDRV_RAWMIDI_IOCTL_DROP, NULL }, + { SNDRV_RAWMIDI_IOCTL_DRAIN, NULL }, + + { SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, NULL }, + { SNDRV_CTL_IOCTL_RAWMIDI_INFO, NULL }, + { SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, NULL }, + + { 0 }, +}; diff -Nru linux/sound/core/ioctl32/timer32.c linux-2.4.19-pre5-mjc/sound/core/ioctl32/timer32.c --- linux/sound/core/ioctl32/timer32.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/ioctl32/timer32.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,93 @@ +/* + * 32bit -> 64bit ioctl wrapper for timer API + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "ioctl32.h" + +struct sndrv_timer_info32 { + u32 flags; + s32 card; + unsigned char id[64]; + unsigned char name[80]; + u32 ticks; + u32 resolution; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_info()\ +{\ + COPY(flags);\ + COPY(card);\ + memcpy(dst->id, src->id, sizeof(src->id));\ + memcpy(dst->name, src->name, sizeof(src->name));\ + COPY(ticks);\ + COPY(resolution);\ +} + +struct timeval32 { + s32 tv_sec; + s32 tv_usec; +}; + +struct sndrv_timer_status32 { + struct timeval32 tstamp; + u32 resolution; + u32 lost; + u32 overrun; + u32 queue; + unsigned char reserved[64]; +}; + +#define CVT_sndrv_timer_status()\ +{\ + COPY(tstamp.tv_sec);\ + COPY(tstamp.tv_usec);\ + COPY(resolution);\ + COPY(lost);\ + COPY(overrun);\ + COPY(queue);\ +} + +DEFINE_ALSA_IOCTL(timer_info); +DEFINE_ALSA_IOCTL(timer_status); + + +/* + */ + +#define AP(x) snd_ioctl32_##x + +struct ioctl32_mapper timer_mappers[] = { + { SNDRV_TIMER_IOCTL_PVERSION, NULL }, + { SNDRV_TIMER_IOCTL_NEXT_DEVICE, NULL }, + { SNDRV_TIMER_IOCTL_SELECT, NULL }, + { SNDRV_TIMER_IOCTL_INFO, AP(timer_info) }, + { SNDRV_TIMER_IOCTL_PARAMS, NULL }, + { SNDRV_TIMER_IOCTL_STATUS, AP(timer_status) }, + { SNDRV_TIMER_IOCTL_START, NULL }, + { SNDRV_TIMER_IOCTL_STOP, NULL }, + { SNDRV_TIMER_IOCTL_CONTINUE, NULL }, + { 0 }, +}; diff -Nru linux/sound/core/isadma.c linux-2.4.19-pre5-mjc/sound/core/isadma.c --- linux/sound/core/isadma.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/isadma.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,83 @@ +/* + * ISA DMA support functions + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Defining following add some delay. Maybe this helps for some broken + * ISA DMA controllers. + */ + +#undef HAVE_REALLY_SLOW_DMA_CONTROLLER + +#define __NO_VERSION__ +#include +#include +#include + +#ifdef CONFIG_ISA + +/* + * + */ + +void snd_dma_program(unsigned long dma, + unsigned long addr, unsigned int size, + unsigned short mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma, mode); + set_dma_addr(dma, addr); + set_dma_count(dma, size); + if (!(mode & DMA_MODE_NO_ENABLE)) + enable_dma(dma); + release_dma_lock(flags); +} + +void snd_dma_disable(unsigned long dma) +{ + unsigned long flags; + + flags = claim_dma_lock(); + clear_dma_ff(dma); + disable_dma(dma); + release_dma_lock(flags); +} + +unsigned int snd_dma_residue(unsigned long dma) +{ + unsigned long flags; + unsigned int result; + + flags = claim_dma_lock(); + clear_dma_ff(dma); + if (!isa_dma_bridge_buggy) + disable_dma(dma); + result = get_dma_residue(dma); + if (!isa_dma_bridge_buggy) + enable_dma(dma); + release_dma_lock(flags); + return result; +} + +#endif /* CONFIG_ISA */ diff -Nru linux/sound/core/memory.c linux-2.4.19-pre5-mjc/sound/core/memory.c --- linux/sound/core/memory.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/memory.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,552 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Memory allocation routines. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * memory allocation helpers and debug routines + */ + +#ifdef CONFIG_SND_DEBUG_MEMORY + +struct snd_alloc_track { + unsigned long magic; + void *caller; + size_t size; + struct list_head list; + long data[0]; +}; + +#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data) + +static long snd_alloc_pages; +static long snd_alloc_kmalloc; +static long snd_alloc_vmalloc; +static LIST_HEAD(snd_alloc_kmalloc_list); +static LIST_HEAD(snd_alloc_vmalloc_list); +static spinlock_t snd_alloc_kmalloc_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t snd_alloc_vmalloc_lock = SPIN_LOCK_UNLOCKED; +#define KMALLOC_MAGIC 0x87654321 +#define VMALLOC_MAGIC 0x87654320 +static snd_info_entry_t *snd_memory_info_entry; + +void snd_memory_init(void) +{ + snd_alloc_pages = 0; + snd_alloc_kmalloc = 0; + snd_alloc_vmalloc = 0; +} + +void snd_memory_done(void) +{ + struct list_head *head; + struct snd_alloc_track *t; + if (snd_alloc_pages > 0) + snd_printk(KERN_ERR "Not freed snd_alloc_pages = %li\n", snd_alloc_pages); + if (snd_alloc_kmalloc > 0) + snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); + if (snd_alloc_vmalloc > 0) + snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); + for (head = snd_alloc_kmalloc_list.prev; + head != &snd_alloc_kmalloc_list; head = head->prev) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_ERR "Corrupted kmalloc\n"); + break; + } + snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } + for (head = snd_alloc_vmalloc_list.prev; + head != &snd_alloc_vmalloc_list; head = head->prev) { + t = list_entry(head, struct snd_alloc_track, list); + if (t->magic != VMALLOC_MAGIC) { + snd_printk(KERN_ERR "Corrupted vmalloc\n"); + break; + } + snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); + } +} + +void *__snd_kmalloc(size_t size, int flags, void *caller) +{ + unsigned long cpu_flags; + struct snd_alloc_track *t; + void *ptr; + + ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags); + if (ptr != NULL) { + t = (struct snd_alloc_track *)ptr; + t->magic = KMALLOC_MAGIC; + t->caller = caller; + spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags); + list_add_tail(&t->list, &snd_alloc_kmalloc_list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags); + t->size = size; + snd_alloc_kmalloc += size; + ptr = t->data; + } + return ptr; +} + +#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0)); +void *snd_hidden_kmalloc(size_t size, int flags) +{ + return _snd_kmalloc(size, flags); +} + +void snd_hidden_kfree(const void *obj) +{ + unsigned long flags; + struct snd_alloc_track *t; + if (obj == NULL) { + snd_printk(KERN_WARNING "null kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + t = snd_alloc_track_entry(obj); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags); + list_del(&t->list); + spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags); + t->magic = 0; + snd_alloc_kmalloc -= t->size; + obj = t; + snd_wrapper_kfree(obj); +} + +void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags) +{ + unsigned long *ptr; + ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); + if (ptr) { + *ptr++ = magic; + memset(ptr, 0, size); + } + return ptr; +} + +void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags) +{ + unsigned long *ptr; + ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); + if (ptr) + *ptr++ = magic; + return ptr; +} + +void snd_magic_kfree(void *_ptr) +{ + unsigned long *ptr = _ptr; + if (ptr == NULL) { + snd_printk(KERN_WARNING "null snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + *--ptr = 0; + { + struct snd_alloc_track *t; + t = snd_alloc_track_entry(ptr); + if (t->magic != KMALLOC_MAGIC) { + snd_printk(KERN_ERR "bad snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); + return; + } + } + snd_hidden_kfree(ptr); + return; +} + +void *snd_hidden_vmalloc(unsigned long size) +{ + void *ptr; + ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track)); + if (ptr) { + struct snd_alloc_track *t = (struct snd_alloc_track *)ptr; + t->magic = VMALLOC_MAGIC; + t->caller = __builtin_return_address(0); + spin_lock(&snd_alloc_vmalloc_lock); + list_add_tail(&t->list, &snd_alloc_vmalloc_list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->size = size; + snd_alloc_vmalloc += size; + ptr = t->data; + } + return ptr; +} + +void snd_hidden_vfree(void *obj) +{ + struct snd_alloc_track *t; + if (obj == NULL) { + snd_printk(KERN_WARNING "null vfree (called from %p)\n", __builtin_return_address(0)); + return; + } + t = snd_alloc_track_entry(obj); + if (t->magic != VMALLOC_MAGIC) { + snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0)); + return; + } + spin_lock(&snd_alloc_vmalloc_lock); + list_del(&t->list); + spin_unlock(&snd_alloc_vmalloc_lock); + t->magic = 0; + snd_alloc_vmalloc -= t->size; + obj = t; + snd_wrapper_vfree(obj); +} + +static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + long pages = snd_alloc_pages >> (PAGE_SHIFT-12); + snd_iprintf(buffer, "pages : %li bytes (%li pages per %likB)\n", pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); + snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc); + snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc); +} + +int __init snd_memory_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 256; + entry->c.text.read = snd_memory_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_memory_info_entry = entry; + return 0; +} + +int __exit snd_memory_info_done(void) +{ + if (snd_memory_info_entry) + snd_info_unregister(snd_memory_info_entry); + return 0; +} + +#else + +#define _snd_kmalloc kmalloc + +#endif /* CONFIG_SND_DEBUG_MEMORY */ + + + +void *snd_malloc_pages(unsigned long size, unsigned int dma_flags) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_flags != 0, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if ((res = (void *) __get_free_pages(dma_flags, pg)) != NULL) { + mem_map_t *page = virt_to_page(res); + mem_map_t *last_page = page + (1 << pg); + while (page < last_page) + SetPageReserved(page++); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages += 1 << pg; +#endif + } + return res; +} + +void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size) +{ + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pages(size, dma_flags)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +void snd_free_pages(void *ptr, unsigned long size) +{ + int pg; + mem_map_t *page, *last_page; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + page = virt_to_page(ptr); + last_page = page + (1 << pg); + while (page < last_page) + ClearPageReserved(page++); + free_pages((unsigned long) ptr, pg); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages -= 1 << pg; +#endif +} + +#ifdef CONFIG_ISA + +#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT +#ifdef CONFIG_PCI +#define CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT +#endif +#endif + +void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr) +{ + void *dma_area; + +#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT + dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA); + *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; +#else + { + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + dma_area = pci_alloc_consistent(NULL, size, dma_addr); + if (dma_area != NULL) { + mem_map_t *page = virt_to_page(dma_area); + mem_map_t *last_page = page + (1 << pg); + while (page < last_page) + SetPageReserved(page++); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages += 1 << pg; +#endif + } + } +#endif + return dma_area; +} + +void *snd_malloc_isa_pages_fallback(unsigned long size, + dma_addr_t *dma_addr, + unsigned long *res_size) +{ + void *dma_area; + +#ifndef CONFIG_ISA_USE_PCI_ALLOC_CONSISTENT + dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size); + *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; + return dma_area; +#else + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if ((dma_area = snd_malloc_isa_pages(size, dma_addr)) != NULL) { + *res_size = size; + return dma_area; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +#endif +} + +#endif + +#ifdef CONFIG_PCI + +void *snd_malloc_pci_pages(struct pci_dev *pci, + unsigned long size, + dma_addr_t *dma_addr) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_addr != NULL, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dma_addr); + if (res != NULL) { + mem_map_t *page = virt_to_page(res); + mem_map_t *last_page = page + (1 << pg); + while (page < last_page) + SetPageReserved(page++); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages += 1 << pg; +#endif + } + return res; +} + +void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, + unsigned long size, + dma_addr_t *dma_addr, + unsigned long *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pci_pages(pci, size, dma_addr)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +void snd_free_pci_pages(struct pci_dev *pci, + unsigned long size, + void *ptr, + dma_addr_t dma_addr) +{ + int pg; + mem_map_t *page, *last_page; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + page = virt_to_page(ptr); + last_page = page + (1 << pg); + while (page < last_page) + ClearPageReserved(page++); + pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dma_addr); +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_alloc_pages -= 1 << pg; +#endif +} + +#endif /* CONFIG_PCI */ + +void *snd_kcalloc(size_t size, int flags) +{ + void *ptr; + + ptr = _snd_kmalloc(size, flags); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +char *snd_kmalloc_strdup(const char *string, int flags) +{ + size_t len; + char *ptr; + + if (!string) + return NULL; + len = strlen(string) + 1; + ptr = _snd_kmalloc(len, flags); + if (ptr) + memcpy(ptr, string, len); + return ptr; +} + +int copy_to_user_fromio(void *dst, unsigned long src, size_t count) +{ +#if defined(__i386_) || defined(CONFIG_SPARC32) + return copy_to_user(dst, (const void*) src, count); +#else + char buf[1024]; + while (count) { + size_t c = count; + int err; + if (c > sizeof(buf)) + c = sizeof(buf); + memcpy_fromio(buf, src, c); + err = copy_to_user(dst, buf, c); + if (err) + return err; + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} + +int copy_from_user_toio(unsigned long dst, const void *src, size_t count) +{ +#if defined(__i386_) || defined(CONFIG_SPARC32) + return copy_from_user((void*)dst, src, count); +#else + char buf[1024]; + while (count) { + size_t c = count; + int err; + if (c > sizeof(buf)) + c = sizeof(buf); + err = copy_from_user(buf, src, c); + if (err) + return err; + memcpy_toio(dst, buf, c); + count -= c; + dst += c; + src += c; + } + return 0; +#endif +} + +#ifdef HACK_PCI_ALLOC_CONSISTENT +/* + * A dirty hack... when the kernel code is fixed this should be removed. + * + * since pci_alloc_consistent always tries GFP_DMA when the requested + * pci memory region is below 32bit, it happens quite often that even + * 2 order or pages cannot be allocated. + * + * so in the following, GFP_DMA is used only when the first allocation + * doesn't match the requested region. + */ +#ifdef __i386__ +#define get_phys_addr(x) virt_to_phys(x) +#else /* ppc */ +#define get_phys_addr(x) virt_to_bus(x) +#endif +void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + if (hwdev == NULL) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + if (ret) { + if (hwdev && ((get_phys_addr(ret) + size - 1) & ~hwdev->dma_mask)) { + free_pages((unsigned long)ret, get_order(size)); + ret = (void *)__get_free_pages(gfp | GFP_DMA, get_order(size)); + } + } + if (ret) { + memset(ret, 0, size); + *dma_handle = get_phys_addr(ret); + } + return ret; +} +#endif /* hack */ diff -Nru linux/sound/core/misc.c linux-2.4.19-pre5-mjc/sound/core/misc.c --- linux/sound/core/misc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/misc.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,53 @@ +/* + * Misc and compatibility things + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +int snd_task_name(struct task_struct *task, char *name, size_t size) +{ + unsigned int idx; + + snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL); + for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++) + name[idx] = task->comm[idx]; + name[idx] = '\0'; + return 0; +} + +#ifdef CONFIG_SND_VERBOSE_PRINTK +int snd_verbose_printk(const char *file, int line, const char *format) +{ + if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') { + char tmp[] = "<0>ALSA %s:%d: "; + tmp[1] = format[1]; + printk("%sALSA %s:%d: ", tmp, file, line); + return 1; + } else { + printk("ALSA %s:%d: ", file, line); + return 0; + } +} +#endif diff -Nru linux/sound/core/oss/Makefile linux-2.4.19-pre5-mjc/sound/core/oss/Makefile --- linux/sound/core/oss/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,26 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := oss.o + +export-objs := mixer_oss.o + +list-multi := snd-mixer-oss.o snd-pcm-oss.o + +snd-mixer-oss-objs := mixer_oss.o + +snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \ + io.o copy.o linear.o mulaw.o route.o rate.o + +obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o +obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o + +include $(TOPDIR)/Rules.make + +snd-mixer-oss.o: $(snd-mixer-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mixer-oss-objs) + +snd-pcm-oss.o: $(snd-pcm-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-pcm-oss-objs) diff -Nru linux/sound/core/oss/copy.c linux-2.4.19-pre5-mjc/sound/core/oss/copy.c --- linux/sound/core/oss/copy.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/copy.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,88 @@ +/* + * Linear conversion Plug-In + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + unsigned int channel; + unsigned int nchannels; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; + nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; channel++) { + snd_assert(src_channels->area.first % 8 == 0 && + src_channels->area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels->area.first % 8 == 0 && + dst_channels->area.step % 8 == 0, + return -ENXIO); + if (!src_channels->enabled) { + if (dst_channels->wanted) + snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format); + dst_channels->enabled = 0; + continue; + } + dst_channels->enabled = 1; + snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format); + src_channels++; + dst_channels++; + } + return frames; +} + +int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + snd_pcm_plugin_t *plugin; + int width; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->format == dst_format->format, return -ENXIO); + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + + width = snd_pcm_format_physical_width(src_format->format); + snd_assert(width > 0, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format, + 0, &plugin); + if (err < 0) + return err; + plugin->transfer = copy_transfer; + *r_plugin = plugin; + return 0; +} diff -Nru linux/sound/core/oss/io.c linux-2.4.19-pre5-mjc/sound/core/oss/io.c --- linux/sound/core/oss/io.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/io.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,134 @@ +/* + * PCM I/O Plug-In Interface + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1) +#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1) +#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1) +#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1) + +/* + * Basic io plugin + */ + +static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED, + snd_pcm_uframes_t frames) +{ + snd_assert(plugin != NULL, return -ENXIO); + snd_assert(src_channels != NULL, return -ENXIO); + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + return pcm_write(plugin->plug, src_channels->area.addr, frames); + } else { + int channel, channels = plugin->dst_format.channels; + void **bufs = (void**)plugin->extra_data; + snd_assert(bufs != NULL, return -ENXIO); + for (channel = 0; channel < channels; channel++) { + if (src_channels[channel].enabled) + bufs[channel] = src_channels[channel].area.addr; + else + bufs[channel] = NULL; + } + return pcm_writev(plugin->plug, bufs, frames); + } +} + +static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + snd_assert(plugin != NULL, return -ENXIO); + snd_assert(dst_channels != NULL, return -ENXIO); + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + return pcm_read(plugin->plug, dst_channels->area.addr, frames); + } else { + int channel, channels = plugin->dst_format.channels; + void **bufs = (void**)plugin->extra_data; + snd_assert(bufs != NULL, return -ENXIO); + for (channel = 0; channel < channels; channel++) { + if (dst_channels[channel].enabled) + bufs[channel] = dst_channels[channel].area.addr; + else + bufs[channel] = NULL; + } + return pcm_readv(plugin->plug, bufs, frames); + } + return 0; +} + +static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels) +{ + int err; + unsigned int channel; + snd_pcm_plugin_channel_t *v; + err = snd_pcm_plugin_client_channels(plugin, frames, &v); + if (err < 0) + return err; + *channels = v; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v) + v->wanted = 1; + } + return frames; +} + +int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug, + snd_pcm_hw_params_t *params, + snd_pcm_plugin_t **r_plugin) +{ + int err; + snd_pcm_plugin_format_t format; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + snd_assert(plug != NULL && params != NULL, return -ENXIO); + format.format = params_format(params); + format.rate = params_rate(params); + format.channels = params_channels(params); + err = snd_pcm_plugin_build(plug, "I/O io", + &format, &format, + sizeof(void *) * format.channels, + &plugin); + if (err < 0) + return err; + plugin->access = params_access(params); + if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { + plugin->transfer = io_playback_transfer; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) + plugin->client_channels = io_src_channels; + } else { + plugin->transfer = io_capture_transfer; + } + + *r_plugin = plugin; + return 0; +} diff -Nru linux/sound/core/oss/linear.c linux-2.4.19-pre5-mjc/sound/core/oss/linear.c --- linux/sound/core/oss/linear.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/linear.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,159 @@ +/* + * Linear conversion Plug-In + * Copyright (c) 1999 by Jaroslav Kysela , + * Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +/* + * Basic linear conversion plugin + */ + +typedef struct linear_private_data { + int conv; +} linear_t; + +static void convert(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + linear_t *data = (linear_t *)plugin->extra_data; + void *conv = conv_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + linear_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + data = (linear_t *)plugin->extra_data; + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + convert(plugin, src_channels, dst_channels, frames); + return frames; +} + +int conv_index(int src_format, int dst_format) +{ + int src_endian, dst_endian, sign, src_width, dst_width; + + sign = (snd_pcm_format_signed(src_format) != + snd_pcm_format_signed(dst_format)); +#ifdef SNDRV_LITTLE_ENDIAN + src_endian = snd_pcm_format_big_endian(src_format); + dst_endian = snd_pcm_format_big_endian(dst_format); +#else + src_endian = snd_pcm_format_little_endian(src_format); + dst_endian = snd_pcm_format_little_endian(dst_format); +#endif + + if (src_endian < 0) + src_endian = 0; + if (dst_endian < 0) + dst_endian = 0; + + src_width = snd_pcm_format_width(src_format) / 8 - 1; + dst_width = snd_pcm_format_width(dst_format) / 8 - 1; + + return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian; +} + +int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + struct linear_private_data *data; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) && + snd_pcm_format_linear(dst_format->format), return -ENXIO); + + err = snd_pcm_plugin_build(plug, "linear format conversion", + src_format, dst_format, + sizeof(linear_t), &plugin); + if (err < 0) + return err; + data = (linear_t *)plugin->extra_data; + data->conv = conv_index(src_format->format, dst_format->format); + plugin->transfer = linear_transfer; + *r_plugin = plugin; + return 0; +} diff -Nru linux/sound/core/oss/mixer_oss.c linux-2.4.19-pre5-mjc/sound/core/oss/mixer_oss.c --- linux/sound/core/oss/mixer_oss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/mixer_oss.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1045 @@ +/* + * OSS emulation layer for the mixer interface + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Mixer OSS emulation for ALSA."); +MODULE_LICENSE("GPL"); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static int snd_mixer_oss_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + snd_card_t *card; + snd_mixer_oss_file_t *fmixer; + + if ((card = snd_cards[cardnum]) == NULL) + return -ENODEV; + if (card->mixer_oss == NULL) + return -ENODEV; + fmixer = (snd_mixer_oss_file_t *)snd_kcalloc(sizeof(*fmixer), GFP_KERNEL); + if (fmixer == NULL) + return -ENOMEM; + fmixer->card = card; + fmixer->mixer = card->mixer_oss; + file->private_data = fmixer; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + if (!try_inc_mod_count(card->module)) { + kfree(fmixer); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return -EFAULT; + } + return 0; +} + +static int snd_mixer_oss_release(struct inode *inode, struct file *file) +{ + snd_mixer_oss_file_t *fmixer; + + if (file->private_data) { + fmixer = (snd_mixer_oss_file_t *) file->private_data; + dec_mod_count(fmixer->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + kfree(fmixer); + } + return 0; +} + +static int snd_mixer_oss_info(snd_mixer_oss_file_t *fmixer, + mixer_info *_info) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + struct mixer_info info; + + memset(&info, 0, sizeof(info)); + strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1); + strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1); + info.modify_counter = card->mixer_oss_change_count; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_mixer_oss_info_obsolete(snd_mixer_oss_file_t *fmixer, + _old_mixer_info *_info) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + _old_mixer_info info; + + memset(&info, 0, sizeof(info)); + strncpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id) - 1); + strncpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name) - 1); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_mixer_oss_caps(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->get_recsrc && mixer->put_recsrc) + result |= SOUND_CAP_EXCL_INPUT; + return result; +} + +static int snd_mixer_oss_devmask(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, chn; + + if (mixer == NULL) + return -EIO; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_volume || pslot->put_recsrc) + result |= 1 << chn; + } + return result; +} + +static int snd_mixer_oss_stereodevs(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, chn; + + if (mixer == NULL) + return -EIO; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_volume && pslot->stereo) + result |= 1 << chn; + } + return result; +} + +static int snd_mixer_oss_recmask(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ + result = mixer->mask_recsrc; + } else { + snd_mixer_oss_slot_t *pslot; + int chn; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_recsrc) + result |= 1 << chn; + } + } + return result; +} + +static int snd_mixer_oss_get_recsrc(snd_mixer_oss_file_t *fmixer) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ + int err; + if ((err = mixer->get_recsrc(fmixer, &result)) < 0) + return err; + result = 1 << result; + } else { + snd_mixer_oss_slot_t *pslot; + int chn; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->get_recsrc) { + int active = 0; + pslot->get_recsrc(fmixer, pslot, &active); + if (active) + result |= 1 << chn; + } + } + } + return mixer->oss_recsrc = result; +} + +static int snd_mixer_oss_set_recsrc(snd_mixer_oss_file_t *fmixer, int recsrc) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + int result = 0; + + if (mixer == NULL) + return -EIO; + if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */ + if (recsrc & ~mixer->oss_recsrc) + recsrc &= ~mixer->oss_recsrc; + mixer->put_recsrc(fmixer, ffz(~recsrc)); + mixer->get_recsrc(fmixer, &result); + result = 1 << result; + } else { + snd_mixer_oss_slot_t *pslot; + int chn, active; + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->put_recsrc) { + active = (recsrc & (1 << chn)) ? 1 : 0; + pslot->put_recsrc(fmixer, pslot, active); + } + } + for (chn = 0; chn < 31; chn++) { + pslot = &mixer->slots[chn]; + if (pslot->get_recsrc) { + active = 0; + pslot->get_recsrc(fmixer, pslot, &active); + if (active) + result |= 1 << chn; + } + } + } + return result; +} + +static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, left, right; + + if (mixer == NULL || slot > 30) + return -EIO; + pslot = &mixer->slots[slot]; + left = fmixer->volume[slot][0]; + right = fmixer->volume[slot][1]; + if (pslot->get_volume) + result = pslot->get_volume(fmixer, pslot, &left, &right); + if (!pslot->stereo) + right = left; + snd_assert(left >= 0 && left <= 100, return -EIO); + snd_assert(right >= 0 && right <= 100, return -EIO); + if (result >= 0) { + fmixer->volume[slot][0] = left; + fmixer->volume[slot][1] = right; + result = (left & 0xff) | ((right & 0xff) << 8); + } + return result; +} + +static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer, + int slot, int volume) +{ + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_mixer_oss_slot_t *pslot; + int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff; + + if (mixer == NULL || slot > 30) + return -EIO; + pslot = &mixer->slots[slot]; + if (left > 100) + left = 100; + if (right > 100) + right = 100; + if (!pslot->stereo) + left = right = left; + if (pslot->put_volume) + result = pslot->put_volume(fmixer, pslot, left, right); + if (result < 0) + return result; + fmixer->volume[slot][0] = left; + fmixer->volume[slot][1] = right; + return (left & 0xff) | ((right & 0xff) << 8); +} + +static int snd_mixer_oss_ioctl1(snd_mixer_oss_file_t *fmixer, unsigned int cmd, unsigned long arg) +{ + int tmp; + + snd_assert(fmixer != NULL, return -ENXIO); + if (((cmd >> 8) & 0xff) == 'M') { + switch (cmd) { + case SOUND_MIXER_INFO: + return snd_mixer_oss_info(fmixer, (mixer_info *)arg); + case SOUND_OLD_MIXER_INFO: + return snd_mixer_oss_info_obsolete(fmixer, (_old_mixer_info *)arg); + case SOUND_MIXER_WRITE_RECSRC: + if (get_user(tmp, (int *)arg)) + return -EFAULT; + tmp = snd_mixer_oss_set_recsrc(fmixer, tmp); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case OSS_GETVERSION: + return put_user(SNDRV_OSS_VERSION, (int *) arg); + case SOUND_MIXER_READ_DEVMASK: + tmp = snd_mixer_oss_devmask(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_STEREODEVS: + tmp = snd_mixer_oss_stereodevs(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_RECMASK: + tmp = snd_mixer_oss_recmask(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_CAPS: + tmp = snd_mixer_oss_caps(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + case SOUND_MIXER_READ_RECSRC: + tmp = snd_mixer_oss_get_recsrc(fmixer); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } + } + if (cmd & IOC_IN) { + if (get_user(tmp, (int *)arg)) + return -EFAULT; + tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } else if (cmd & IOC_OUT) { + tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff); + if (tmp < 0) + return tmp; + return put_user(tmp, (int *)arg) ? -EFAULT : 0; + } + return -ENXIO; +} + +int snd_mixer_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); +} + +int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg) +{ + snd_mixer_oss_file_t fmixer; + + snd_assert(card != NULL, return -ENXIO); + if (card->mixer_oss == NULL) + return -ENXIO; + memset(&fmixer, 0, sizeof(fmixer)); + fmixer.card = card; + fmixer.mixer = card->mixer_oss; + return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); +} + +/* + * REGISTRATION PART + */ + +static struct file_operations snd_mixer_oss_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + open: snd_mixer_oss_open, + release: snd_mixer_oss_release, + ioctl: snd_mixer_oss_ioctl, +}; + +static snd_minor_t snd_mixer_oss_reg = +{ + comment: "mixer", + f_ops: &snd_mixer_oss_f_ops, +}; + +/* + * utilities + */ + +static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax) +{ + long orange = omax - omin, nrange = nmax - nmin; + + if (orange == 0) + return 0; + return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin; +} + +static long snd_mixer_oss_conv1(long val, long min, long max, int *old) +{ + if (val == snd_mixer_oss_conv(*old, 0, 100, min, max)) + return *old; + return snd_mixer_oss_conv(val, min, max, 0, 100); +} + +static long snd_mixer_oss_conv2(long val, long min, long max) +{ + return snd_mixer_oss_conv(val, 0, 100, min, max); +} + +#if 0 +static void snd_mixer_oss_recsrce_set(snd_card_t *card, int slot) +{ + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer) + mixer->mask_recsrc |= 1 << slot; +} + +static int snd_mixer_oss_recsrce_get(snd_card_t *card, int slot) +{ + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer && (mixer->mask_recsrc & (1 << slot))) + return 1; + return 0; +} +#endif + +#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250 + +#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0 +#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1 +#define SNDRV_MIXER_OSS_ITEM_GROUTE 2 +#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3 +#define SNDRV_MIXER_OSS_ITEM_PSWITCH 4 +#define SNDRV_MIXER_OSS_ITEM_PROUTE 5 +#define SNDRV_MIXER_OSS_ITEM_PVOLUME 6 +#define SNDRV_MIXER_OSS_ITEM_CSWITCH 7 +#define SNDRV_MIXER_OSS_ITEM_CROUTE 8 +#define SNDRV_MIXER_OSS_ITEM_CVOLUME 9 +#define SNDRV_MIXER_OSS_ITEM_CAPTURE 10 + +#define SNDRV_MIXER_OSS_ITEM_COUNT 11 + +#define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0) +#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1) +#define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2) +#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3) +#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4) +#define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5) +#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6) +#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7) +#define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8) +#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9) +#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10) + +struct slot { + unsigned int signature; + unsigned int present; + int channels; + snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT]; + unsigned int capture_item; +}; + +static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index) +{ + snd_card_t * card = mixer->card; + snd_ctl_elem_id_t id; + + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, name); + id.index = index; + return snd_ctl_find_id(card, &id); +} + +static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int *left, int *right) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + snd_runtime_check(!kctl->get(kctl, &uctl), return); + snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return); + *left = snd_mixer_oss_conv1(uctl.value.integer.value[0], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][0]); + if (uinfo.count > 1) + *right = snd_mixer_oss_conv1(uctl.value.integer.value[1], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][1]); +} + +static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int *left, int *right, + int route) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + snd_runtime_check(!kctl->get(kctl, &uctl), return); + if (!uctl.value.integer.value[0]) { + *left = 0; + if (uinfo.count == 1) + *right = 0; + } + if (uinfo.count > 1 && !uctl.value.integer.value[route ? 3 : 1]) + *right = 0; +} + +static int snd_mixer_oss_get_volume1(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *left, int *right) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + *left = *right = 100; + read_lock(&card->control_rwlock); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { + snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); + } + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } + read_unlock(&card->control_rwlock); + return 0; +} + +static void snd_mixer_oss_put_volume1_vol(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int left, int right) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int res; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return); + uctl.value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo.value.integer.min, uinfo.value.integer.max); + if (uinfo.count > 1) + uctl.value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo.value.integer.min, uinfo.value.integer.max); + snd_runtime_check((res = kctl->put(kctl, &uctl)) >= 0, return); + if (res > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); +} + +static void snd_mixer_oss_put_volume1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + snd_kcontrol_t *kctl, + int left, int right, + int route) +{ + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int res; + + snd_runtime_check(kctl != NULL, return); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!kctl->info(kctl, &uinfo), return); + if (uinfo.count > 1) { + uctl.value.integer.value[0] = left > 0 ? 1 : 0; + uctl.value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0; + if (route) { + uctl.value.integer.value[1] = + uctl.value.integer.value[2] = 0; + } + } else { + uctl.value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0; + } + snd_runtime_check((res = kctl->put(kctl, &uctl)) >= 0, return); + if (res > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); +} + +static int snd_mixer_oss_put_volume1(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int left, int right) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + read_lock(&card->control_rwlock); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { + snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); + } + if (left || right) { + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } else { + if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); + } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); + } + } + read_unlock(&card->control_rwlock); + return 0; +} + +static int snd_mixer_oss_get_recsrc1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + int left, right; + + left = right = 1; + read_lock(&card->control_rwlock); + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0); + read_unlock(&card->control_rwlock); + *active = (left || right) ? 1 : 0; + return 0; +} + +static int snd_mixer_oss_get_recsrc1_route(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int *active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + int left, right; + + left = right = 1; + read_lock(&card->control_rwlock); + snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1); + read_unlock(&card->control_rwlock); + *active = (left || right) ? 1 : 0; + return 0; +} + +static int snd_mixer_oss_put_recsrc1_sw(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + read_lock(&card->control_rwlock); + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0); + read_unlock(&card->control_rwlock); + return 0; +} + +static int snd_mixer_oss_put_recsrc1_route(snd_mixer_oss_file_t *fmixer, + snd_mixer_oss_slot_t *pslot, + int active) +{ + snd_card_t *card = fmixer->card; + struct slot *slot = (struct slot *)pslot->private_data; + + read_lock(&card->control_rwlock); + snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->kcontrol[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1); + read_unlock(&card->control_rwlock); + return 0; +} + +static int snd_mixer_oss_get_recsrc2(snd_mixer_oss_file_t *fmixer, int *active_index) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *pslot; + struct slot *slot; + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int err, idx; + + read_lock(&card->control_rwlock); + kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); + snd_runtime_check(kctl != NULL, return -ENOENT); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!(err = kctl->info(kctl, &uinfo)), read_unlock(&card->control_rwlock); return err); + snd_runtime_check(!(err = kctl->get(kctl, &uctl)), read_unlock(&card->control_rwlock); return err); + read_unlock(&card->control_rwlock); + for (idx = 0; idx < 32; idx++) { + if (!(mixer->mask_recsrc & (1 << idx))) + continue; + pslot = &fmixer->mixer->slots[idx]; + slot = (struct slot *)pslot->private_data; + if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) + continue; + if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) + continue; + if (slot->capture_item == uctl.value.enumerated.item[0]) { + *active_index = idx; + break; + } + } + return 0; +} + +static int snd_mixer_oss_put_recsrc2(snd_mixer_oss_file_t *fmixer, int active_index) +{ + snd_card_t *card = fmixer->card; + snd_mixer_oss_t *mixer = fmixer->mixer; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *pslot; + struct slot *slot = NULL; + snd_ctl_elem_info_t uinfo; + snd_ctl_elem_value_t uctl; + int err, idx; + + read_lock(&card->control_rwlock); + kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); + snd_runtime_check(kctl != NULL, read_unlock(&card->control_rwlock); return -ENOENT); + memset(&uinfo, 0, sizeof(uinfo)); + memset(&uctl, 0, sizeof(uctl)); + snd_runtime_check(!(err = kctl->info(kctl, &uinfo)), read_unlock(&card->control_rwlock); return err); + for (idx = 0; idx < 32; idx++) { + if (!(mixer->mask_recsrc & (1 << idx))) + continue; + pslot = &fmixer->mixer->slots[idx]; + slot = (struct slot *)pslot->private_data; + if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) + continue; + if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) + continue; + if (idx == active_index) + break; + slot = NULL; + } + snd_runtime_check(slot != NULL, goto __unlock); + for (idx = 0; idx < uinfo.count; idx++) + uctl.value.enumerated.item[idx] = slot->capture_item; + snd_runtime_check((err = kctl->put(kctl, &uctl)) >= 0, ); + if (err > 0) + snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + __unlock: + read_unlock(&card->control_rwlock); + return 0; +} + +struct snd_mixer_oss_assign_table { + int oss_id; + const char *name; + int index; +}; + +static int snd_mixer_oss_build_test(snd_mixer_oss_t *mixer, struct slot *slot, const char *name, int index, int item) +{ + snd_ctl_elem_info_t info; + snd_kcontrol_t *kcontrol; + int err; + + kcontrol = snd_mixer_oss_test_id(mixer, name, index); + if (kcontrol == NULL) + return 0; + snd_runtime_check((err = kcontrol->info(kcontrol, &info)) >= 0, return err); + slot->kcontrol[item] = kcontrol; + if (info.count > slot->channels) + slot->channels = info.count; + slot->present |= 1 << item; + return 0; +} + +static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn) +{ + kfree(chn->private_data); +} + +static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr) +{ + struct slot slot; + struct slot *pslot; + snd_kcontrol_t *kctl; + snd_mixer_oss_slot_t *rslot; + char str[64]; + + memset(&slot, 0, sizeof(slot)); + if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index, + SNDRV_MIXER_OSS_ITEM_GLOBAL)) + return; + sprintf(str, "%s Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GSWITCH)) + return; + sprintf(str, "%s Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GROUTE)) + return; + sprintf(str, "%s Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_GVOLUME)) + return; + sprintf(str, "%s Playback Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PSWITCH)) + return; + sprintf(str, "%s Playback Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PROUTE)) + return; + sprintf(str, "%s Playback Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_PVOLUME)) + return; + sprintf(str, "%s Capture Switch", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CSWITCH)) + return; + sprintf(str, "%s Capture Route", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CROUTE)) + return; + sprintf(str, "%s Capture Volume", ptr->name); + if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index, + SNDRV_MIXER_OSS_ITEM_CVOLUME)) + return; + if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) { + snd_ctl_elem_info_t uinfo; + + memset(&uinfo, 0, sizeof(uinfo)); + if (kctl->info(kctl, &uinfo)) + return; + strcpy(str, ptr->name); + if (!strcmp(str, "Master")) + strcpy(str, "Mix"); + if (!strcmp(str, "Master Mono")) + strcpy(str, "Mix Mono"); + slot.capture_item = 0; + if (!strcmp(uinfo.value.enumerated.name, str)) { + slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; + } else { + for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) { + uinfo.value.enumerated.item = slot.capture_item; + if (kctl->info(kctl, &uinfo)) + return; + if (!strcmp(uinfo.value.enumerated.name, str)) { + slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; + break; + } + } + } + } + if (slot.present != 0) { + pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL); + snd_runtime_check(pslot != NULL, return); + *pslot = slot; + pslot->signature = SNDRV_MIXER_OSS_SIGNATURE; + rslot = &mixer->slots[ptr->oss_id]; + rslot->stereo = slot.channels > 1 ? 1 : 0; + rslot->get_volume = snd_mixer_oss_get_volume1; + rslot->put_volume = snd_mixer_oss_put_volume1; + /* note: ES18xx have both Capture Source and XX Capture Volume !!! */ + if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) { + rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw; + rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw; + } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) { + rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route; + rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route; + } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) { + mixer->mask_recsrc |= 1 << ptr->oss_id; + } + rslot->private_data = pslot; + rslot->private_free = snd_mixer_oss_slot_free; + } +} + +static void snd_mixer_oss_build(snd_mixer_oss_t *mixer) +{ + static struct snd_mixer_oss_assign_table table[] = { + { SOUND_MIXER_VOLUME, "Master", 0 }, + { SOUND_MIXER_BASS, "Tone Control - Bass", 0 }, + { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 }, + { SOUND_MIXER_SYNTH, "Synth", 0 }, + { SOUND_MIXER_PCM, "PCM", 0 }, + { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, + { SOUND_MIXER_LINE, "Line", 0 }, + { SOUND_MIXER_MIC, "Mic", 0 }, + { SOUND_MIXER_CD, "CD", 0 }, + { SOUND_MIXER_IMIX, "Monitor Mix", 0 }, + { SOUND_MIXER_ALTPCM, "PCM", 1 }, + { SOUND_MIXER_RECLEV, "-- nothing --", 0 }, + { SOUND_MIXER_IGAIN, "Capture", 0 }, + { SOUND_MIXER_OGAIN, "Playback", 0 }, + { SOUND_MIXER_LINE1, "Aux", 0 }, + { SOUND_MIXER_LINE2, "Aux", 1 }, + { SOUND_MIXER_LINE3, "Aux", 2 }, + { SOUND_MIXER_DIGITAL1, "Digital", 0 }, + { SOUND_MIXER_DIGITAL2, "Digital", 1 }, + { SOUND_MIXER_DIGITAL3, "Digital", 2 }, + { SOUND_MIXER_PHONEIN, "Phone", 0 }, + { SOUND_MIXER_PHONEOUT, "Phone", 1 }, + { SOUND_MIXER_VIDEO, "Video", 0 }, + { SOUND_MIXER_RADIO, "Radio", 0 }, + { SOUND_MIXER_MONITOR, "Monitor", 0 } + }; + static struct snd_mixer_oss_assign_table fm_table = { + SOUND_MIXER_SYNTH, "FM", 0 + }; + int idx; + + for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++) + snd_mixer_oss_build_input(mixer, &table[idx]); + if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL) + snd_mixer_oss_build_input(mixer, &fm_table); + if (mixer->mask_recsrc) { + mixer->get_recsrc = snd_mixer_oss_get_recsrc2; + mixer->put_recsrc = snd_mixer_oss_put_recsrc2; + } +} + +/* + * + */ + +static int snd_mixer_oss_free1(void *private) +{ + snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, private, return -ENXIO); + snd_card_t * card; + int idx; + + snd_assert(mixer != NULL, return -ENXIO); + card = mixer->card; + snd_assert(mixer == card->mixer_oss, return -ENXIO); + card->mixer_oss = NULL; + for (idx = 0; idx < 31; idx++) { + snd_mixer_oss_slot_t *chn = &mixer->slots[idx]; + if (chn->private_free) + chn->private_free(chn); + } + snd_magic_kfree(mixer); + return 0; +} + +static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag) +{ + if (!free_flag) { + snd_mixer_oss_t *mixer; + char name[128]; + int idx, err; + + mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL); + if (mixer == NULL) + return -ENOMEM; + sprintf(name, "mixer%i%i", card->number, 0); + if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, + card, 0, + &snd_mixer_oss_reg, + name)) < 0) { + snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0); + snd_magic_kfree(mixer); + return err; + } + mixer->card = card; + strcpy(mixer->name, name); + snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, + card->number, + name); + for (idx = 0; idx < 31; idx++) + mixer->slots[idx].number = idx; + card->mixer_oss = mixer; + snd_mixer_oss_build(mixer); + } else { + snd_mixer_oss_t *mixer = card->mixer_oss; + if (mixer == NULL) + return 0; + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); + return snd_mixer_oss_free1(mixer); + } + return 0; +} + +static int __init alsa_mixer_oss_init(void) +{ + int idx; + + snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler; + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (snd_cards[idx]) + snd_mixer_oss_notify_handler(snd_cards[idx], 0); + } + return 0; +} + +static void __exit alsa_mixer_oss_exit(void) +{ + int idx; + + snd_mixer_oss_notify_callback = NULL; + for (idx = 0; idx < SNDRV_CARDS; idx++) { + if (snd_cards[idx]) + snd_mixer_oss_notify_handler(snd_cards[idx], 1); + } +} + +module_init(alsa_mixer_oss_init) +module_exit(alsa_mixer_oss_exit) + +EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); diff -Nru linux/sound/core/oss/mulaw.c linux-2.4.19-pre5-mjc/sound/core/oss/mulaw.c --- linux/sound/core/oss/mulaw.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/mulaw.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,308 @@ +/* + * Mu-Law conversion Plug-In Interface + * Copyright (c) 1999 by Jaroslav Kysela + * Uros Bizjak + * + * Based on reference implementation by Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of u-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + if (pcm_val > 0x7FFF) + pcm_val = 0x7FFF; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return uval ^ mask; +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static int ulaw2linear(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +/* + * Basic Mu-Law plugin + */ + +typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames); + +typedef struct mulaw_private_data { + mulaw_f func; + int conv; +} mulaw_t; + +static void mulaw_decode(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef PUT_S16_LABELS + mulaw_t *data = (mulaw_t *)plugin->extra_data; + void *put = put_s16_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + signed short sample = ulaw2linear(*src); + goto *put; +#define PUT_S16_END after +#include "plugin_ops.h" +#undef PUT_S16_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static void mulaw_encode(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ +#define GET_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS + mulaw_t *data = (mulaw_t *)plugin->extra_data; + void *get = get_s16_labels[data->conv]; + int channel; + int nchannels = plugin->src_format.channels; + signed short sample = 0; + for (channel = 0; channel < nchannels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + snd_pcm_uframes_t frames1; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + frames1 = frames; + while (frames1-- > 0) { + goto *get; +#define GET_S16_END after +#include "plugin_ops.h" +#undef GET_S16_END + after: + *dst = linear2ulaw(sample); + src += src_step; + dst += dst_step; + } + } +} + +static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + mulaw_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + data = (mulaw_t *)plugin->extra_data; + data->func(plugin, src_channels, dst_channels, frames); + return frames; +} + +int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + mulaw_t *data; + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_format_t *format; + mulaw_f func; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + + if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) { + format = src_format; + func = mulaw_encode; + } + else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) { + format = dst_format; + func = mulaw_decode; + } + else { + snd_BUG(); + return -EINVAL; + } + snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion", + src_format, dst_format, + sizeof(mulaw_t), &plugin); + if (err < 0) + return err; + data = (mulaw_t*)plugin->extra_data; + data->func = func; + data->conv = getput_index(format->format); + plugin->transfer = mulaw_transfer; + *r_plugin = plugin; + return 0; +} diff -Nru linux/sound/core/oss/pcm_oss.c linux-2.4.19-pre5-mjc/sound/core/oss/pcm_oss.c --- linux/sound/core/oss/pcm_oss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/pcm_oss.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2195 @@ +/* + * Digital Audio (PCM) abstract layer / OSS compatible + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if 0 +#define PLUGIN_DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcm_plugin.h" +#include +#include +#include + +static int snd_dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int snd_adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; +static int snd_nonblock_open = 0; + +MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara "); +MODULE_DESCRIPTION("PCM OSS emulation for ALSA."); +MODULE_LICENSE("GPL"); +MODULE_PARM(snd_dsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dsp_map, "PCM device number assigned to 1st OSS device."); +MODULE_PARM_SYNTAX(snd_dsp_map, "default:0,skill:advanced"); +MODULE_PARM(snd_adsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_adsp_map, "PCM device number assigned to 2nd OSS device."); +MODULE_PARM_SYNTAX(snd_adsp_map, "default:1,skill:advanced"); +MODULE_PARM(snd_nonblock_open, "i"); +MODULE_PARM_DESC(snd_nonblock_open, "Don't block opening busy PCM devices."); +MODULE_PARM_SYNTAX(snd_nonblock_open, "default:0,skill:advanced"); + +extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg); +static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file); +static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file); +static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file); + +EXPORT_NO_SYMBOLS; + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_plugin_t *plugin, *next; + + plugin = runtime->oss.plugin_first; + while (plugin) { + next = plugin->next; + snd_pcm_plugin_free(plugin); + plugin = next; + } + runtime->oss.plugin_first = runtime->oss.plugin_last = NULL; + return 0; +} + +int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin) +{ + snd_pcm_runtime_t *runtime = plugin->plug->runtime; + plugin->next = runtime->oss.plugin_first; + plugin->prev = NULL; + if (runtime->oss.plugin_first) { + runtime->oss.plugin_first->prev = plugin; + runtime->oss.plugin_first = plugin; + } else { + runtime->oss.plugin_last = + runtime->oss.plugin_first = plugin; + } + return 0; +} + +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin) +{ + snd_pcm_runtime_t *runtime = plugin->plug->runtime; + plugin->next = NULL; + plugin->prev = runtime->oss.plugin_last; + if (runtime->oss.plugin_last) { + runtime->oss.plugin_last->next = plugin; + runtime->oss.plugin_last = plugin; + } else { + runtime->oss.plugin_last = + runtime->oss.plugin_first = plugin; + } + return 0; +} + +static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->period_size == runtime->oss.period_bytes) + return frames; + if (runtime->period_size < runtime->oss.period_bytes) + return frames * (runtime->oss.period_bytes / runtime->period_size); + return frames / (runtime->period_size / runtime->oss.period_bytes); +} + +static int snd_pcm_oss_format_from(int format) +{ + switch (format) { + case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; + case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW; + case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM; + case AFMT_U8: return SNDRV_PCM_FORMAT_U8; + case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE; + case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE; + case AFMT_S8: return SNDRV_PCM_FORMAT_S8; + case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE; + case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE; + case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG; + default: return SNDRV_PCM_FORMAT_U8; + } +} + +static int snd_pcm_oss_format_to(int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; + case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW; + case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM; + case SNDRV_PCM_FORMAT_U8: return AFMT_U8; + case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE; + case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE; + case SNDRV_PCM_FORMAT_S8: return AFMT_S8; + case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE; + case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE; + case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG; + default: return -EINVAL; + } +} + +static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *oss_params, + snd_pcm_hw_params_t *slave_params) +{ + size_t s; + size_t oss_buffer_size, oss_period_size, oss_periods; + size_t min_period_size, max_period_size; + snd_pcm_runtime_t *runtime = substream->runtime; + size_t oss_frame_size; + + oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) * + params_channels(oss_params) / 8; + + oss_buffer_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0)) * oss_frame_size; + oss_buffer_size = 1 << ld2(oss_buffer_size); + if (atomic_read(&runtime->mmap_count)) { + if (oss_buffer_size > runtime->oss.mmap_bytes) + oss_buffer_size = runtime->oss.mmap_bytes; + } + + if (substream->oss.setup && + substream->oss.setup->period_size > 16) + oss_period_size = substream->oss.setup->period_size; + else if (runtime->oss.fragshift) { + oss_period_size = 1 << runtime->oss.fragshift; + if (oss_period_size > oss_buffer_size / 2) + oss_period_size = oss_buffer_size / 2; + } else { + int sd; + size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8; + + oss_period_size = oss_buffer_size; + do { + oss_period_size /= 2; + } while (oss_period_size > bytes_per_sec); + if (runtime->oss.subdivision == 0) { + sd = 4; + if (oss_period_size / sd > 4096) + sd *= 2; + if (oss_period_size / sd < 4096) + sd = 1; + } else + sd = runtime->oss.subdivision; + oss_period_size /= sd; + if (oss_period_size < 16) + oss_period_size = 16; + } + + min_period_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); + min_period_size *= oss_frame_size; + min_period_size = 1 << (ld2(min_period_size - 1) + 1); + if (oss_period_size < min_period_size) + oss_period_size = min_period_size; + + max_period_size = snd_pcm_plug_client_size(substream, + snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); + max_period_size *= oss_frame_size; + max_period_size = 1 << ld2(max_period_size); + if (oss_period_size > max_period_size) + oss_period_size = max_period_size; + + oss_periods = oss_buffer_size / oss_period_size; + + if (substream->oss.setup) { + if (substream->oss.setup->periods > 1) + oss_periods = substream->oss.setup->periods; + } + + s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); + if (runtime->oss.maxfrags && s > runtime->oss.maxfrags) + s = runtime->oss.maxfrags; + if (oss_periods > s) + oss_periods = s; + + s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); + if (s < 2) + s = 2; + if (oss_periods < s) + oss_periods = s; + + while (oss_period_size * oss_periods > oss_buffer_size) + oss_period_size /= 2; + + snd_assert(oss_period_size >= 16, return -EINVAL); + runtime->oss.period_bytes = oss_period_size; + runtime->oss.periods = oss_periods; + return 0; +} + +static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hw_params_t params, sparams; + snd_pcm_sw_params_t sw_params; + ssize_t oss_buffer_size, oss_period_size; + size_t oss_frame_size; + int err; + int direct; + int format, sformat, n; + unsigned int sformat_mask; + unsigned int mask; + + if (atomic_read(&runtime->mmap_count)) { + direct = 1; + } else { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + direct = (setup != NULL && setup->direct); + } + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_param_setinteger(&sparams, SNDRV_PCM_HW_PARAM_PERIODS); + _snd_pcm_hw_param_min(&sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); + if (atomic_read(&runtime->mmap_count)) + mask = 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + else { + mask = 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; + if (!direct) + mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + } + err = snd_pcm_hw_param_mask(substream, &sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); + if (err < 0) { + snd_printd("No usable accesses\n"); + return -EINVAL; + } + snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_RATE, runtime->oss.rate, 0); + snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); + + format = snd_pcm_oss_format_from(runtime->oss.format); + + sformat_mask = *hw_param_mask(&sparams, SNDRV_PCM_HW_PARAM_FORMAT); + if (direct) + sformat = format; + else + sformat = snd_pcm_plug_slave_format(format, sformat_mask); + + if (sformat < 0 || !(sformat_mask & (1 << sformat))) { + for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { + if ((sformat_mask & (1 << sformat)) && + snd_pcm_oss_format_to(sformat) >= 0) + break; + } + if (sformat > SNDRV_PCM_FORMAT_LAST) { + snd_printd("Cannot find a format!!!\n"); + return -EINVAL; + } + } + err = _snd_pcm_hw_param_set(&sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); + snd_assert(err >= 0, return err); + + if (direct) { + params = sparams; + } else { + _snd_pcm_hw_params_any(¶ms); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pcm_oss_format_from(runtime->oss.format), 0); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, + runtime->oss.channels, 0); + _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_RATE, + runtime->oss.rate, 0); + pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n", + params_access(¶ms), params_format(¶ms), + params_channels(¶ms), params_rate(¶ms)); + } + pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n", + params_access(&sparams), params_format(&sparams), + params_channels(&sparams), params_rate(&sparams)); + + oss_frame_size = snd_pcm_format_physical_width(params_format(¶ms)) * + params_channels(¶ms) / 8; + + snd_pcm_oss_plugin_clear(substream); + if (!direct) { + /* add necessary plugins */ + snd_pcm_oss_plugin_clear(substream); + if ((err = snd_pcm_plug_format_plugins(substream, + ¶ms, + &sparams)) < 0) { + snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err); + snd_pcm_oss_plugin_clear(substream); + return err; + } + if (runtime->oss.plugin_first) { + snd_pcm_plugin_t *plugin; + if ((err = snd_pcm_plugin_build_io(substream, &sparams, &plugin)) < 0) { + snd_printd("snd_pcm_plugin_build_io failed: %i\n", err); + snd_pcm_oss_plugin_clear(substream); + return err; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + err = snd_pcm_plugin_append(plugin); + } else { + err = snd_pcm_plugin_insert(plugin); + } + if (err < 0) { + snd_pcm_oss_plugin_clear(substream); + return err; + } + } + } + + err = snd_pcm_oss_period_size(substream, ¶ms, &sparams); + if (err < 0) + return err; + + n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size); + err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, 0); + snd_assert(err >= 0, return err); + + err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIODS, + runtime->oss.periods, 0); + snd_assert(err >= 0, return err); + + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + + if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, &sparams)) < 0) { + snd_printd("HW_PARAMS failed: %i\n", err); + return err; + } + + memset(&sw_params, 0, sizeof(sw_params)); + if (runtime->oss.trigger) { + sw_params.start_threshold = 1; + } else { + sw_params.start_threshold = runtime->boundary; + } + if (atomic_read(&runtime->mmap_count)) + sw_params.stop_threshold = runtime->boundary; + else + sw_params.stop_threshold = runtime->buffer_size; + sw_params.tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + sw_params.period_step = 1; + sw_params.sleep_min = 0; + sw_params.avail_min = runtime->period_size; + sw_params.xfer_align = 1; + sw_params.silence_threshold = 0; + sw_params.silence_size = 0; + + if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params)) < 0) { + snd_printd("SW_PARAMS failed: %i\n", err); + return err; + } + runtime->control->avail_min = runtime->period_size; + + runtime->oss.periods = params_periods(&sparams); + oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(&sparams)); + snd_assert(oss_period_size >= 0, return -EINVAL); + if (runtime->oss.plugin_first) { + err = snd_pcm_plug_alloc(substream, oss_period_size); + if (err < 0) + return err; + } + oss_period_size *= oss_frame_size; + + oss_buffer_size = oss_period_size * runtime->oss.periods; + snd_assert(oss_buffer_size >= 0, return -EINVAL); + + runtime->oss.period_bytes = oss_period_size; + runtime->oss.buffer_bytes = oss_buffer_size; + + pdprintf("oss: period bytes = %i, buffer bytes = %i\n", + runtime->oss.period_bytes, + runtime->oss.buffer_bytes); + pdprintf("slave: period_size = %i, buffer_size = %i\n", + params_period_size(&sparams), + params_buffer_size(&sparams)); + + runtime->oss.format = snd_pcm_oss_format_to(params_format(¶ms)); + runtime->oss.channels = params_channels(¶ms); + runtime->oss.rate = params_rate(¶ms); + + runtime->oss.params = 0; + runtime->oss.prepare = 1; + if (runtime->oss.buffer != NULL) + vfree(runtime->oss.buffer); + runtime->oss.buffer = vmalloc(runtime->oss.period_bytes); + runtime->oss.buffer_used = 0; + snd_assert(runtime->dma_area != NULL, return -EIO); + snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); + return 0; +} + +static int snd_pcm_oss_get_active_substream(snd_pcm_oss_file_t *pcm_oss_file, snd_pcm_substream_t **r_substream) +{ + int idx, err; + snd_pcm_substream_t *asubstream = NULL, *substream; + + for (idx = 0; idx < 2; idx++) { + substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if (asubstream == NULL) + asubstream = substream; + if (substream->runtime->oss.params) { + err = snd_pcm_oss_change_params(substream); + if (err < 0) + return err; + } + } + snd_assert(asubstream != NULL, return -EIO); + if (r_substream) + *r_substream = asubstream; + return 0; +} + +static int snd_pcm_oss_prepare(snd_pcm_substream_t *substream) +{ + int err; + snd_pcm_runtime_t *runtime = substream->runtime; + + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, 0); + if (err < 0) { + snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n"); + return err; + } + runtime->oss.prepare = 0; + runtime->oss.prev_hw_ptr_interrupt = 0; + + return 0; +} + +static int snd_pcm_oss_make_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + int err; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (runtime->oss.params) { + err = snd_pcm_oss_change_params(substream); + if (err < 0) + return err; + } + if (runtime->oss.prepare) { + err = snd_pcm_oss_prepare(substream); + if (err < 0) + return err; + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_write(substream, ptr, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_write(substream, ptr, frames); + } + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + /* test, if we can't store new data, because the stream */ + /* has not been started */ + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) + return -EAGAIN; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + if (ret < 0) + break; + } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + ret = snd_pcm_lib_read(substream, ptr, frames); + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_read(substream, ptr, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_read(substream, ptr, frames); + } + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_writev(substream, bufs, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_writev(substream, bufs, frames); + } + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + + /* test, if we can't store new data, because the stream */ + /* has not been started */ + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED) + return -EAGAIN; + } + return ret; +} + +snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + while (1) { + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + if (ret < 0) + break; + } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) { + ret = snd_pcm_oss_prepare(substream); + if (ret < 0) + break; + } + if (in_kernel) { + mm_segment_t fs; + fs = snd_enter_user(); + ret = snd_pcm_lib_readv(substream, bufs, frames); + snd_leave_user(fs); + } else { + ret = snd_pcm_lib_readv(substream, bufs, frames); + } + if (ret != -EPIPE && ret != -ESTRPIPE) + break; + } + return ret; +} + +static ssize_t snd_pcm_oss_write2(snd_pcm_substream_t *substream, const char *buf, size_t bytes, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t frames, frames1; + if (runtime->oss.plugin_first) { + snd_pcm_plugin_channel_t *channels; + size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; + if (!in_kernel) { + if (copy_from_user(runtime->oss.buffer, buf, bytes)) + return -EFAULT; + buf = runtime->oss.buffer; + } + frames = bytes / oss_frame_bytes; + frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels); + if (frames1 < 0) + return frames1; + frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1); + if (frames1 <= 0) + return frames1; + bytes = frames1 * oss_frame_bytes; + } else { + frames = bytes_to_frames(runtime, bytes); + frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel); + if (frames1 <= 0) + return frames1; + bytes = frames_to_bytes(runtime, frames1); + } + return bytes; +} + +static ssize_t snd_pcm_oss_write1(snd_pcm_substream_t *substream, const char *buf, size_t bytes) +{ + size_t xfer = 0; + ssize_t tmp; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (atomic_read(&runtime->mmap_count)) + return -ENXIO; + + if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) + return tmp; + while (bytes > 0) { + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + tmp = bytes; + if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) + tmp = runtime->oss.period_bytes - runtime->oss.buffer_used; + if (tmp > 0) { + if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) + return xfer > 0 ? xfer : -EFAULT; + } + runtime->oss.buffer_used += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + if (runtime->oss.buffer_used == runtime->oss.period_bytes) { + tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + runtime->oss.buffer_used = 0; + } + } else { + tmp = snd_pcm_oss_write2(substream, (char *)buf, runtime->oss.period_bytes, 0); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + } + } + return xfer; +} + +static ssize_t snd_pcm_oss_read2(snd_pcm_substream_t *substream, char *buf, size_t bytes, int in_kernel) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t frames, frames1; + char *final_dst = buf; + if (runtime->oss.plugin_first) { + snd_pcm_plugin_channel_t *channels; + size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; + if (!in_kernel) + buf = runtime->oss.buffer; + frames = bytes / oss_frame_bytes; + frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels); + if (frames1 < 0) + return frames1; + frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1); + if (frames1 <= 0) + return frames1; + bytes = frames1 * oss_frame_bytes; + if (!in_kernel && copy_to_user(final_dst, buf, bytes)) + return -EFAULT; + } else { + frames = bytes_to_frames(runtime, bytes); + frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel); + if (frames1 <= 0) + return frames1; + bytes = frames_to_bytes(runtime, frames1); + } + return bytes; +} + +static ssize_t snd_pcm_oss_read1(snd_pcm_substream_t *substream, char *buf, size_t bytes) +{ + size_t xfer = 0; + ssize_t tmp; + snd_pcm_runtime_t *runtime = substream->runtime; + + if (atomic_read(&runtime->mmap_count)) + return -ENXIO; + + if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) + return tmp; + while (bytes > 0) { + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + if (runtime->oss.buffer_used == 0) { + tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + runtime->oss.buffer_used = runtime->oss.period_bytes; + } + tmp = bytes; + if ((size_t) tmp > runtime->oss.buffer_used) + tmp = runtime->oss.buffer_used; + if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_bytes - runtime->oss.buffer_used), tmp)) + return xfer > 0 ? xfer : -EFAULT; + buf += tmp; + bytes -= tmp; + xfer += tmp; + runtime->oss.buffer_used -= tmp; + } else { + tmp = snd_pcm_oss_read2(substream, (char *)buf, runtime->oss.period_bytes, 0); + if (tmp <= 0) + return xfer > 0 ? xfer : tmp; + runtime->oss.bytes += tmp; + buf += tmp; + bytes -= tmp; + xfer += tmp; + } + } + return xfer; +} + +static int snd_pcm_oss_reset(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream != NULL) { + snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + substream->runtime->oss.prepare = 1; + } + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream != NULL) { + snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + substream->runtime->oss.prepare = 1; + } + return 0; +} + +static int snd_pcm_oss_sync(snd_pcm_oss_file_t *pcm_oss_file) +{ + int err = 0; + unsigned int saved_f_flags; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + ssize_t result; + wait_queue_t wait; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream != NULL) { + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + + runtime = substream->runtime; + if (runtime->oss.buffer_used > 0) { + snd_pcm_format_set_silence(runtime->format, + runtime->oss.buffer + runtime->oss.buffer_used, + bytes_to_samples(runtime, runtime->oss.period_bytes - runtime->oss.buffer_used)); + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + result = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); + if (result > 0) { + runtime->oss.buffer_used = 0; + break; + } + if (result != 0 && result != -EAGAIN) + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + result = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (result < 0) + return result; + } + saved_f_flags = substream->ffile->f_flags; + substream->ffile->f_flags &= ~O_NONBLOCK; + err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, 0); + substream->ffile->f_flags = saved_f_flags; + if (err < 0) + return err; + runtime->oss.prepare = 1; + } + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream != NULL) { + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); + if (err < 0) + return err; + runtime->oss.buffer_used = 0; + runtime->oss.prepare = 1; + } + return 0; +} + +static int snd_pcm_oss_set_rate(snd_pcm_oss_file_t *pcm_oss_file, int rate) +{ + int idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (rate < 1000) + rate = 1000; + else if (rate > 48000) + rate = 48000; + if (runtime->oss.rate != rate) { + runtime->oss.params = 1; + runtime->oss.rate = rate; + } + } + return snd_pcm_oss_get_rate(pcm_oss_file); +} + +static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.rate; +} + +static int snd_pcm_oss_set_channels(snd_pcm_oss_file_t *pcm_oss_file, int channels) +{ + int idx; + if (channels < 1) + channels = 1; + if (channels > 128) + return -EINVAL; + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (runtime->oss.channels != channels) { + runtime->oss.params = 1; + runtime->oss.channels = channels; + } + } + return snd_pcm_oss_get_channels(pcm_oss_file); +} + +static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.channels; +} + +static int snd_pcm_oss_get_block_size(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.period_bytes; +} + +static int snd_pcm_oss_get_formats(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + int direct; + snd_pcm_hw_params_t params; + unsigned int formats = 0; + unsigned int format_mask; + int fmt; + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + if (atomic_read(&substream->runtime->mmap_count)) { + direct = 1; + } else { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + direct = (setup != NULL && setup->direct); + } + if (!direct) + return AFMT_MU_LAW | AFMT_U8 | + AFMT_S16_LE | AFMT_S16_BE | + AFMT_S8 | AFMT_U16_LE | + AFMT_U16_BE; + _snd_pcm_hw_params_any(¶ms); + err = snd_pcm_hw_refine(substream, ¶ms); + snd_assert(err >= 0, return err); + format_mask = *hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT); + for (fmt = 0; fmt < 32; ++fmt) { + if (format_mask & (1 << fmt)) { + int f = snd_pcm_oss_format_to(fmt); + if (f >= 0) + formats |= f; + } + } + return formats; +} + +static int snd_pcm_oss_set_format(snd_pcm_oss_file_t *pcm_oss_file, int format) +{ + int formats, idx; + + if (format != AFMT_QUERY) { + formats = snd_pcm_oss_get_formats(pcm_oss_file); + if (!(formats & format)) + format = AFMT_U8; + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + if (runtime->oss.format != format) { + runtime->oss.params = 1; + runtime->oss.format = format; + } + } + } + return snd_pcm_oss_get_format(pcm_oss_file); +} + +static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + int err; + + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) + return err; + return substream->runtime->oss.format; +} + +static int snd_pcm_oss_set_subdivide1(snd_pcm_substream_t *substream, int subdivide) +{ + snd_pcm_runtime_t *runtime; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (subdivide == 0) { + subdivide = runtime->oss.subdivision; + if (subdivide == 0) + subdivide = 1; + return subdivide; + } + if (runtime->oss.subdivision || runtime->oss.fragshift) + return -EINVAL; + if (subdivide != 1 && subdivide != 2 && subdivide != 4 && + subdivide != 8 && subdivide != 16) + return -EINVAL; + runtime->oss.subdivision = subdivide; + runtime->oss.params = 1; + return subdivide; +} + +static int snd_pcm_oss_set_subdivide(snd_pcm_oss_file_t *pcm_oss_file, int subdivide) +{ + int err = -EINVAL, idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0) + return err; + } + return err; +} + +static int snd_pcm_oss_set_fragment1(snd_pcm_substream_t *substream, unsigned int val) +{ + snd_pcm_runtime_t *runtime; + + if (substream == NULL) + return 0; + runtime = substream->runtime; + if (runtime->oss.subdivision || runtime->oss.fragshift) + return -EINVAL; + runtime->oss.fragshift = val & 0xffff; + runtime->oss.maxfrags = (val >> 16) & 0xffff; + if (runtime->oss.fragshift < 4) /* < 16 */ + runtime->oss.fragshift = 4; + if (runtime->oss.maxfrags < 2) + runtime->oss.maxfrags = 2; + runtime->oss.params = 1; + return 0; +} + +static int snd_pcm_oss_set_fragment(snd_pcm_oss_file_t *pcm_oss_file, unsigned int val) +{ + int err = -EINVAL, idx; + + for (idx = 1; idx >= 0; --idx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + if (substream == NULL) + continue; + if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0) + return err; + } + return err; +} + +static int snd_pcm_oss_nonblock(struct file * file) +{ + file->f_flags |= O_NONBLOCK; + return 0; +} + +static int snd_pcm_oss_get_caps1(snd_pcm_substream_t *substream, int res) +{ + + if (substream == NULL) { + res &= ~DSP_CAP_DUPLEX; + return res; + } +#ifdef DSP_CAP_MULTI + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->pstr->substream_count > 1) + res |= DSP_CAP_MULTI; +#endif + /* DSP_CAP_REALTIME is set all times: */ + /* all ALSA drivers can return actual pointer in ring buffer */ +#if defined(DSP_CAP_REALTIME) && 0 + { + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH)) + res &= ~DSP_CAP_REALTIME; + } +#endif + return res; +} + +static int snd_pcm_oss_get_caps(snd_pcm_oss_file_t *pcm_oss_file) +{ + int result, idx; + + result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME; + for (idx = 0; idx < 2; idx++) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[idx]; + result = snd_pcm_oss_get_caps1(substream, result); + } + result |= 0x0001; /* revision - same as SB AWE 64 */ + return result; +} + +static void snd_pcm_oss_simulate_fill(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr; + appl_ptr = runtime->hw_ptr_interrupt + runtime->buffer_size; + appl_ptr %= runtime->boundary; + runtime->control->appl_ptr = appl_ptr; +} + +static int snd_pcm_oss_set_trigger(snd_pcm_oss_file_t *pcm_oss_file, int trigger) +{ + snd_pcm_runtime_t *runtime; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + int err, cmd; + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + + if (psubstream) { + if ((err = snd_pcm_oss_make_ready(psubstream)) < 0) + return err; + if (atomic_read(&psubstream->runtime->mmap_count)) + snd_pcm_oss_simulate_fill(psubstream); + } + if (csubstream) { + if ((err = snd_pcm_oss_make_ready(csubstream)) < 0) + return err; + } + if (psubstream) { + runtime = psubstream->runtime; + if (trigger & PCM_ENABLE_OUTPUT) { + if (runtime->oss.trigger) + goto _skip1; + runtime->oss.trigger = 1; + runtime->start_threshold = 1; + cmd = SNDRV_PCM_IOCTL_START; + } else { + if (!runtime->oss.trigger) + goto _skip1; + runtime->oss.trigger = 0; + runtime->start_threshold = runtime->boundary; + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } + err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, 0); + if (err < 0) + return err; + } + _skip1: + if (csubstream) { + runtime = csubstream->runtime; + if (trigger & PCM_ENABLE_INPUT) { + if (runtime->oss.trigger) + goto _skip2; + runtime->oss.trigger = 1; + runtime->start_threshold = 1; + cmd = SNDRV_PCM_IOCTL_START; + } else { + if (!runtime->oss.trigger) + goto _skip2; + runtime->oss.trigger = 0; + runtime->start_threshold = runtime->boundary; + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } + err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, 0); + if (err < 0) + return err; + } + _skip2: + return 0; +} + +static int snd_pcm_oss_get_trigger(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + int result = 0; + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger) + result |= PCM_ENABLE_OUTPUT; + if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger) + result |= PCM_ENABLE_INPUT; + return result; +} + +static int snd_pcm_oss_get_odelay(snd_pcm_oss_file_t *pcm_oss_file) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t delay; + int err; + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + return -EINVAL; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + if (runtime->oss.params || runtime->oss.prepare) + return 0; + err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay); + if (err == -EPIPE) + delay = 0; /* hack for broken OSS applications */ + else if (err < 0) + return err; + return snd_pcm_oss_bytes(substream, delay); +} + +static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct count_info * _info) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_status_t status; + struct count_info info; + int err; + + if (_info == NULL) + return -EFAULT; + substream = pcm_oss_file->streams[stream]; + if (substream == NULL) + return -EINVAL; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; + runtime = substream->runtime; + if (runtime->oss.params || runtime->oss.prepare) { + memset(&info, 0, sizeof(info)); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; + } + memset(&status, 0, sizeof(status)); + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status); + if (err < 0) + return err; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + info.bytes = runtime->oss.bytes - snd_pcm_oss_bytes(substream, runtime->buffer_size - status.avail); + } else { + info.bytes = runtime->oss.bytes + snd_pcm_oss_bytes(substream, status.avail); + } + info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); + if (atomic_read(&runtime->mmap_count)) { + snd_pcm_sframes_t n; + n = runtime->hw_ptr_interrupt - runtime->oss.prev_hw_ptr_interrupt; + if (n < 0) + n += runtime->boundary; + info.blocks = n / runtime->period_size; + runtime->oss.prev_hw_ptr_interrupt = runtime->hw_ptr_interrupt; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_pcm_oss_simulate_fill(substream); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + info.blocks = (runtime->buffer_size - status.avail) / runtime->period_size; + else + info.blocks = status.avail / runtime->period_size; + } + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_pcm_oss_get_space(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct audio_buf_info *_info) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_status_t status; + struct audio_buf_info info; + int err; + + if (_info == NULL) + return -EFAULT; + substream = pcm_oss_file->streams[stream]; + if (substream == NULL) + return -EINVAL; + runtime = substream->runtime; + + if (runtime->oss.params && + (err = snd_pcm_oss_change_params(substream)) < 0) + return err; + + info.fragsize = runtime->oss.period_bytes; + info.fragstotal = runtime->periods; + memset(&status, 0, sizeof(status)); + if (runtime->oss.prepare) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + info.bytes = runtime->oss.period_bytes * runtime->periods; + info.fragments = runtime->periods; + } else { + info.bytes = 0; + info.fragments = 0; + } + } else { + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status); + if (err < 0) + return err; + info.bytes = snd_pcm_oss_bytes(substream, status.avail); + info.fragments = status.avail / runtime->period_size; + } + +#if 0 + /* very experimental stuff to get Quake2 working */ + runtime->oss.period = (info.periods - 1) << 16; + for (tmp = info.fragsize; tmp > 1; tmp >>= 1) + runtime->oss.period++; + runtime->oss.subdivision = 1; /* disable SUBDIVIDE */ +#endif + // printk("space: bytes = %i, periods = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.periods, info.fragstotal, info.fragsize); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_pcm_oss_get_mapbuf(snd_pcm_oss_file_t *pcm_oss_file, int stream, struct buffmem_desc * _info) +{ + // it won't be probably implemented + // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n"); + return -EINVAL; +} + +static snd_pcm_oss_setup_t *snd_pcm_oss_look_for_setup(snd_pcm_t *pcm, int stream, const char *task_name) +{ + const char *ptr, *ptrl; + snd_pcm_oss_setup_t *setup; + + down(&pcm->streams[stream].oss.setup_mutex); + for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, task_name)) { + up(&pcm->streams[stream].oss.setup_mutex); + return setup; + } + } + ptr = ptrl = task_name; + while (*ptr) { + if (*ptr == '/') + ptrl = ptr + 1; + ptr++; + } + if (ptrl == task_name) { + goto __not_found; + return NULL; + } + for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, ptrl)) { + up(&pcm->streams[stream].oss.setup_mutex); + return setup; + } + } + __not_found: + up(&pcm->streams[stream].oss.setup_mutex); + return NULL; +} + +static void snd_pcm_oss_init_substream(snd_pcm_substream_t *substream, + snd_pcm_oss_setup_t *setup, + int minor) +{ + snd_pcm_runtime_t *runtime; + + substream->oss.oss = 1; + substream->oss.setup = setup; + runtime = substream->runtime; + runtime->oss.params = 1; + runtime->oss.trigger = 1; + runtime->oss.rate = 8000; + switch (SNDRV_MINOR_OSS_DEVICE(minor)) { + case SNDRV_MINOR_OSS_PCM_8: + runtime->oss.format = AFMT_U8; + break; + case SNDRV_MINOR_OSS_PCM_16: + runtime->oss.format = AFMT_S16_LE; + break; + default: + runtime->oss.format = AFMT_MU_LAW; + } + runtime->oss.channels = 1; + runtime->oss.fragshift = 0; + runtime->oss.maxfrags = 0; + runtime->oss.subdivision = 0; +} + +static void snd_pcm_oss_release_substream(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + runtime = substream->runtime; + if (runtime->oss.buffer) + vfree(runtime->oss.buffer); + snd_pcm_oss_plugin_clear(substream); + substream->oss.file = NULL; + substream->oss.oss = 0; +} + +static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file) +{ + int cidx; + snd_assert(pcm_oss_file != NULL, return -ENXIO); + for (cidx = 0; cidx < 2; ++cidx) { + snd_pcm_substream_t *substream = pcm_oss_file->streams[cidx]; + snd_pcm_runtime_t *runtime; + if (substream == NULL) + continue; + runtime = substream->runtime; + + spin_lock_irq(&runtime->lock); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + spin_unlock_irq(&runtime->lock); + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + substream->ops->close(substream); + substream->ffile = NULL; + snd_pcm_oss_release_substream(substream); + snd_pcm_release_substream(substream); + } + snd_magic_kfree(pcm_oss_file); + return 0; +} + +static int snd_pcm_oss_open_file(struct file *file, + snd_pcm_t *pcm, + snd_pcm_oss_file_t **rpcm_oss_file, + int minor, + snd_pcm_oss_setup_t *psetup, + snd_pcm_oss_setup_t *csetup) +{ + int err = 0; + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + unsigned int f_mode = file->f_mode; + + snd_assert(rpcm_oss_file != NULL, return -EINVAL); + *rpcm_oss_file = NULL; + + pcm_oss_file = snd_magic_kcalloc(snd_pcm_oss_file_t, 0, GFP_KERNEL); + if (pcm_oss_file == NULL) + return -ENOMEM; + + if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) && + (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)) + f_mode = FMODE_WRITE; + if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) { + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &psubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream; + } + if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) { + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE, + &csubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream; + } + + if (psubstream == NULL && csubstream == NULL) { + snd_pcm_oss_release_file(pcm_oss_file); + return -EINVAL; + } + if (psubstream != NULL) { + psubstream->oss.file = pcm_oss_file; + err = snd_pcm_hw_constraints_init(psubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_init failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + if ((err = psubstream->ops->open(psubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + err = snd_pcm_hw_constraints_complete(psubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_complete failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + psubstream->ffile = file; + snd_pcm_oss_init_substream(psubstream, psetup, minor); + } + if (csubstream != NULL) { + csubstream->oss.file = pcm_oss_file; + err = snd_pcm_hw_constraints_init(csubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_init failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + if ((err = csubstream->ops->open(csubstream)) < 0) { + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + err = snd_pcm_hw_constraints_complete(csubstream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraint_complete failed\n"); + snd_pcm_oss_release_file(pcm_oss_file); + return err; + } + csubstream->ffile = file; + snd_pcm_oss_init_substream(csubstream, csetup, minor); + } + + file->private_data = pcm_oss_file; + *rpcm_oss_file = pcm_oss_file; + return 0; +} + + +static int snd_pcm_oss_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + int cardnum = SNDRV_MINOR_OSS_CARD(minor); + int device; + int err; + char task_name[32]; + snd_pcm_t *pcm; + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_oss_setup_t *psetup = NULL, *csetup = NULL; + int nonblock; + wait_queue_t wait; + + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); + device = SNDRV_MINOR_OSS_DEVICE(minor) == SNDRV_MINOR_OSS_PCM1 ? + snd_adsp_map[cardnum] : snd_dsp_map[cardnum]; + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + device]; + if (pcm == NULL) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(pcm->card->module)) { + err = -EFAULT; + goto __error1; + } + if (snd_task_name(current, task_name, sizeof(task_name)) < 0) { + err = -EFAULT; + goto __error1; + } + if (file->f_mode & FMODE_WRITE) + psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name); + if (file->f_mode & FMODE_READ) + csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name); + + nonblock = !!(file->f_flags & O_NONBLOCK); + if (psetup && !psetup->disable) { + if (psetup->nonblock) + nonblock = 1; + else if (psetup->block) + nonblock = 0; + else if (!nonblock) + nonblock = snd_nonblock_open; + } else if (csetup && !csetup->disable) { + if (csetup->nonblock) + nonblock = 1; + else if (csetup->block) + nonblock = 0; + else if (!nonblock) + nonblock = snd_nonblock_open; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&pcm->open_wait, &wait); + while (1) { + down(&pcm->open_mutex); + err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file, + minor, psetup, csetup); + if (err >= 0) + break; + up(&pcm->open_mutex); + if (err == -EAGAIN) { + if (nonblock) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pcm->open_wait, &wait); + if (err < 0) + goto __error; + up(&pcm->open_mutex); + return err; + + __error: + dec_mod_count(pcm->card->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +static int snd_pcm_oss_release(struct inode *inode, struct file *file) +{ + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + snd_pcm_oss_file_t *pcm_oss_file; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + snd_assert(substream != NULL, return -ENXIO); + pcm = substream->pcm; + snd_pcm_oss_sync(pcm_oss_file); + down(&pcm->open_mutex); + snd_pcm_oss_release_file(pcm_oss_file); + up(&pcm->open_mutex); + wake_up(&pcm->open_wait); + dec_mod_count(pcm->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static int snd_pcm_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_oss_file_t *pcm_oss_file; + int res; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + if (cmd == OSS_GETVERSION) + return put_user(SNDRV_OSS_VERSION, (int *)arg) ? -EFAULT : 0; + if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS (grrr) compatibility */ + snd_pcm_substream_t *substream; + int idx; + for (idx = 0; idx < 2; ++idx) { + substream = pcm_oss_file->streams[idx]; + if (substream != NULL) + break; + } + snd_assert(substream != NULL, return -ENXIO); + return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg); + } + if (((cmd >> 8) & 0xff) != 'P') + return -EINVAL; + switch (cmd) { + case SNDCTL_DSP_RESET: + return snd_pcm_oss_reset(pcm_oss_file); + case SNDCTL_DSP_SYNC: + return snd_pcm_oss_sync(pcm_oss_file); + case SNDCTL_DSP_SPEED: + if (get_user(res, (int *)arg)) + return -EFAULT; + if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_RATE: + res = snd_pcm_oss_get_rate(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_STEREO: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = res > 0 ? 2 : 1; + if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0) + return res; + return put_user(--res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETBLKSIZE: + res = snd_pcm_oss_get_block_size(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETFMT: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_format(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_BITS: + res = snd_pcm_oss_get_format(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_CHANNELS: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_channels(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_READ_CHANNELS: + res = snd_pcm_oss_get_channels(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EIO; + case SNDCTL_DSP_POST: /* to do */ + return 0; + case SNDCTL_DSP_SUBDIVIDE: + if (get_user(res, (int *)arg)) + return -EFAULT; + res = snd_pcm_oss_set_subdivide(pcm_oss_file, res); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(res, (int *)arg)) + return -EFAULT; + return snd_pcm_oss_set_fragment(pcm_oss_file, res); + case SNDCTL_DSP_GETFMTS: + res = snd_pcm_oss_get_formats(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + return snd_pcm_oss_get_space(pcm_oss_file, + cmd == SNDCTL_DSP_GETISPACE ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct audio_buf_info *) arg); + case SNDCTL_DSP_NONBLOCK: + return snd_pcm_oss_nonblock(file); + case SNDCTL_DSP_GETCAPS: + res = snd_pcm_oss_get_caps(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_GETTRIGGER: + res = snd_pcm_oss_get_trigger(pcm_oss_file); + if (res < 0) + return res; + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_SETTRIGGER: + if (get_user(res, (int *)arg)) + return -EFAULT; + return snd_pcm_oss_set_trigger(pcm_oss_file, res); + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + return snd_pcm_oss_get_ptr(pcm_oss_file, + cmd == SNDCTL_DSP_GETIPTR ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct count_info *) arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return snd_pcm_oss_get_mapbuf(pcm_oss_file, + cmd == SNDCTL_DSP_MAPINBUF ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, + (struct buffmem_desc *) arg); + case SNDCTL_DSP_SETSYNCRO: + /* stop DMA now.. */ + return 0; + case SNDCTL_DSP_SETDUPLEX: + if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX) + return 0; + return -EIO; + case SNDCTL_DSP_GETODELAY: + res = snd_pcm_oss_get_odelay(pcm_oss_file); + if (res < 0) { + /* it's for sure, some broken apps don't check for error codes */ + put_user(0, (int *)arg); + return res; + } + return put_user(res, (int *)arg) ? -EFAULT : 0; + case SNDCTL_DSP_PROFILE: + return 0; /* silently ignore */ + default: + snd_printd("pcm_oss: unknown command = 0x%x\n", cmd); + } + return -EINVAL; +} + +static ssize_t snd_pcm_oss_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream == NULL) + return -ENXIO; + return snd_pcm_oss_read1(substream, buf, count); +} + +static ssize_t snd_pcm_oss_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream; + long result; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream == NULL) + return -ENXIO; + up(&file->f_dentry->d_inode->i_sem); + result = snd_pcm_oss_write1(substream, buf, count); + down(&file->f_dentry->d_inode->i_sem); + return result; +} + +static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (atomic_read(&runtime->mmap_count)) + return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + else + return snd_pcm_playback_ready(substream); +} + +static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (atomic_read(&runtime->mmap_count)) + return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + else + return snd_pcm_capture_ready(substream); +} + +static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) +{ + snd_pcm_oss_file_t *pcm_oss_file; + unsigned int mask; + snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return 0); + + psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + + mask = 0; + if (psubstream != NULL) { + snd_pcm_runtime_t *runtime = psubstream->runtime; + spin_lock_irq(&runtime->lock); + poll_wait(file, &runtime->sleep, wait); + if (runtime->status->state != SNDRV_PCM_STATE_DRAINING && + (runtime->status->state != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_playback_ready(psubstream))) + mask |= POLLOUT | POLLWRNORM; + spin_unlock_irq(&runtime->lock); + } + if (csubstream != NULL) { + snd_pcm_runtime_t *runtime = csubstream->runtime; + spin_lock_irq(&runtime->lock); + poll_wait(file, &runtime->sleep, wait); + if (runtime->status->state != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_capture_ready(csubstream)) + mask |= POLLIN | POLLRDNORM; + spin_unlock_irq(&runtime->lock); + } + + return mask; +} + +static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) +{ + snd_pcm_oss_file_t *pcm_oss_file; + snd_pcm_substream_t *substream = NULL; + snd_pcm_runtime_t *runtime; + int err; + + pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + switch ((area->vm_flags & (VM_READ | VM_WRITE))) { + case VM_READ | VM_WRITE: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream) + break; + /* Fall through */ + case VM_READ: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; + break; + case VM_WRITE: + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; + break; + default: + return -EINVAL; + } + /* set VM_READ access as well to fix memset() routines that do + reads before writes (to improve performance) */ + area->vm_flags |= VM_READ; + if (substream == NULL) + return -ENXIO; + runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID)) + return -EIO; + if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED) + runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + else + return -EIO; + + if (runtime->oss.params) { + if ((err = snd_pcm_oss_change_params(substream)) < 0) + return err; + } + if (runtime->oss.plugin_first != NULL) + return -EIO; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + if (area->vm_pgoff != 0) +#else + if (area->vm_offset != 0) +#endif + return -EINVAL; + + err = snd_pcm_mmap_data(substream, file, area); + if (err < 0) + return err; + runtime->oss.mmap_bytes = area->vm_end - area->vm_start; + /* In mmap mode we never stop */ + runtime->stop_threshold = runtime->boundary; + + return 0; +} + +/* + * /proc interface + */ + +static void snd_pcm_oss_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; + snd_pcm_oss_setup_t *setup = pstr->oss.setup_list; + down(&pstr->oss.setup_mutex); + while (setup) { + snd_iprintf(buffer, "%s %u %u%s%s%s%s\n", + setup->task_name, + setup->periods, + setup->period_size, + setup->disable ? " disable" : "", + setup->direct ? " direct" : "", + setup->block ? " block" : "", + setup->nonblock ? " non-block" : ""); + setup = setup->next; + } + up(&pstr->oss.setup_mutex); +} + +static void snd_pcm_oss_proc_free_setup_list(snd_pcm_str_t * pstr) +{ + unsigned int idx; + snd_pcm_substream_t *substream; + snd_pcm_oss_setup_t *setup, *setupn; + + for (idx = 0, substream = pstr->substream; + idx < pstr->substream_count; idx++, substream = substream->next) + substream->oss.setup = NULL; + for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL; + setup; setup = setupn) { + setupn = setup->next; + kfree(setup->task_name); + kfree(setup); + } + pstr->oss.setup_list = NULL; +} + +static void snd_pcm_oss_proc_write(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; + char line[512], str[32], task_name[32], *ptr; + int idx1; + snd_pcm_oss_setup_t *setup, *setup1, template; + + while (!snd_info_get_line(buffer, line, sizeof(line))) { + down(&pstr->oss.setup_mutex); + memset(&template, 0, sizeof(template)); + ptr = snd_info_get_str(task_name, line, sizeof(task_name)); + if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) { + snd_pcm_oss_proc_free_setup_list(pstr); + up(&pstr->oss.setup_mutex); + continue; + } + for (setup = pstr->oss.setup_list; setup; setup = setup->next) { + if (!strcmp(setup->task_name, task_name)) { + template = *setup; + break; + } + } + ptr = snd_info_get_str(str, ptr, sizeof(str)); + template.periods = simple_strtoul(str, NULL, 10); + ptr = snd_info_get_str(str, ptr, sizeof(str)); + template.period_size = simple_strtoul(str, NULL, 10); + for (idx1 = 31; idx1 >= 0; idx1--) + if (template.period_size & (1 << idx1)) + break; + for (idx1--; idx1 >= 0; idx1--) + template.period_size &= ~(1 << idx1); + do { + ptr = snd_info_get_str(str, ptr, sizeof(str)); + if (!strcmp(str, "disable")) { + template.disable = 1; + } else if (!strcmp(str, "direct")) { + template.direct = 1; + } else if (!strcmp(str, "block")) { + template.block = 1; + } else if (!strcmp(str, "non-block")) { + template.nonblock = 1; + } + } while (*str); + if (setup == NULL) { + setup = (snd_pcm_oss_setup_t *) kmalloc(sizeof(snd_pcm_oss_setup_t), GFP_KERNEL); + if (setup) { + if (pstr->oss.setup_list == NULL) { + pstr->oss.setup_list = setup; + } else { + for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next); + setup1->next = setup; + } + template.task_name = snd_kmalloc_strdup(task_name, GFP_KERNEL); + } else { + buffer->error = -ENOMEM; + } + } + if (setup) + *setup = template; + up(&pstr->oss.setup_mutex); + } +} + +static void snd_pcm_oss_proc_init(snd_pcm_t *pcm) +{ + int stream; + for (stream = 0; stream < 2; ++stream) { + snd_info_entry_t *entry; + snd_pcm_str_t *pstr = &pcm->streams[stream]; + if (pstr->substream_count == 0) + continue; + if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 8192; + entry->c.text.read = snd_pcm_oss_proc_read; + entry->c.text.write_size = 8192; + entry->c.text.write = snd_pcm_oss_proc_write; + entry->private_data = pstr; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + pstr->oss.proc_entry = entry; + } +} + +static void snd_pcm_oss_proc_done(snd_pcm_t *pcm) +{ + int stream; + for (stream = 0; stream < 2; ++stream) { + snd_pcm_str_t *pstr = &pcm->streams[stream]; + if (pstr->oss.proc_entry) { + snd_info_unregister(pstr->oss.proc_entry); + pstr->oss.proc_entry = NULL; + snd_pcm_oss_proc_free_setup_list(pstr); + } + } +} + +/* + * ENTRY functions + */ + +static struct file_operations snd_pcm_oss_f_reg = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_pcm_oss_read, + write: snd_pcm_oss_write, + open: snd_pcm_oss_open, + release: snd_pcm_oss_release, + poll: snd_pcm_oss_poll, + ioctl: snd_pcm_oss_ioctl, + mmap: snd_pcm_oss_mmap, +}; + +static snd_minor_t snd_pcm_oss_reg = +{ + comment: "digital audio", + f_ops: &snd_pcm_oss_f_reg, +}; + +static void register_oss_dsp(unsigned short native_minor, snd_pcm_t *pcm, int index) +{ + char name[128]; + sprintf(name, "dsp%i%i", pcm->card->number, pcm->device); + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, index, &snd_pcm_oss_reg, + name) < 0) { + snd_printk("unable to register OSS PCM device %i:%i\n", pcm->card->number, pcm->device); + } +} + +static int snd_pcm_oss_register_minor(unsigned short native_minor, + snd_pcm_t * pcm) +{ + pcm->oss.reg = 0; + if (snd_dsp_map[pcm->card->number] == pcm->device) { + char name[128]; + int duplex; + register_oss_dsp(native_minor, pcm, 0); + duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 && + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count && + !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)); + sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : ""); + snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO, + pcm->card->number, + name); + pcm->oss.reg++; + } + if (snd_adsp_map[pcm->card->number] == pcm->device) { + register_oss_dsp(native_minor, pcm, 1); + pcm->oss.reg++; + } + + if (pcm->oss.reg) + snd_pcm_oss_proc_init(pcm); + + return 0; +} + +static int snd_pcm_oss_unregister_minor(unsigned short native_minor, + snd_pcm_t * pcm) +{ + if (pcm->oss.reg) { + if (snd_dsp_map[pcm->card->number] == pcm->device) { + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, 0); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number); + } + if (snd_adsp_map[pcm->card->number] == pcm->device) + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, + pcm->card, 1); + pcm->oss.reg = 0; + snd_pcm_oss_proc_done(pcm); + } + return 0; +} + +static snd_pcm_notify_t snd_pcm_oss_notify = +{ + n_register: snd_pcm_oss_register_minor, + n_unregister: snd_pcm_oss_unregister_minor, +}; + +static int __init alsa_pcm_oss_init(void) +{ + int i; + int err; + + if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0) + return err; + /* check device map table */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (snd_dsp_map[i] < 0 || snd_dsp_map[i] >= SNDRV_PCM_DEVICES) { + snd_printk("invalid dsp_map[%d] = %d\n", i, snd_dsp_map[i]); + snd_dsp_map[i] = 0; + } + if (snd_adsp_map[i] < 0 || snd_adsp_map[i] >= SNDRV_PCM_DEVICES) { + snd_printk("invalid adsp_map[%d] = %d\n", i, snd_adsp_map[i]); + snd_adsp_map[i] = 1; + } + } + return 0; +} + +static void __exit alsa_pcm_oss_exit(void) +{ + snd_pcm_notify(&snd_pcm_oss_notify, 1); +} + +module_init(alsa_pcm_oss_init) +module_exit(alsa_pcm_oss_exit) diff -Nru linux/sound/core/oss/pcm_plugin.c linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.c --- linux/sound/core/oss/pcm_plugin.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1089 @@ +/* + * PCM Plug-In shared (kernel/library) code + * Copyright (c) 1999 by Jaroslav Kysela + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if 0 +#define PLUGIN_DEBUG +#endif + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) +#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) + +static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + bitset_t *vmask = plugin->src_vmask; + bitset_copy(vmask, dst_vmask, plugin->src_format.channels); + *src_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + bitset_t *vmask = plugin->dst_vmask; + bitset_copy(vmask, src_vmask, plugin->dst_format.channels); + *dst_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + snd_pcm_plugin_format_t *format; + ssize_t width; + size_t size; + unsigned int channel; + snd_pcm_plugin_channel_t *c; + + if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { + format = &plugin->src_format; + } else { + format = &plugin->dst_format; + } + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + size = frames * format->channels * width; + snd_assert((size % 8) == 0, return -ENXIO); + size /= 8; + if (plugin->buf_frames < frames) { + if (plugin->buf) + vfree(plugin->buf); + plugin->buf = vmalloc(size); + plugin->buf_frames = frames; + } + if (!plugin->buf) + return -ENOMEM; + c = plugin->buf_channels; + if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { + for (channel = 0; channel < format->channels; channel++, c++) { + c->enabled = 1; + c->wanted = 0; + c->area.addr = plugin->buf; + c->area.first = channel * width; + c->area.step = format->channels * width; + } + } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { + snd_assert((size % format->channels) == 0,); + size /= format->channels; + for (channel = 0; channel < format->channels; channel++, c++) { + c->enabled = 1; + c->wanted = 0; + c->area.addr = plugin->buf + (channel * size); + c->area.first = 0; + c->area.step = width; + } + } else + return -EINVAL; + return 0; +} + +int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames) +{ + int err; + snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); + if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + while (plugin->next) { + if (plugin->dst_frames) + frames = plugin->dst_frames(plugin, frames); + snd_assert(frames > 0, return -ENXIO); + plugin = plugin->next; + err = snd_pcm_plugin_alloc(plugin, frames); + if (err < 0) + return err; + } + } else { + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + while (plugin->prev) { + if (plugin->src_frames) + frames = plugin->src_frames(plugin, frames); + snd_assert(frames > 0, return -ENXIO); + plugin = plugin->prev; + err = snd_pcm_plugin_alloc(plugin, frames); + if (err < 0) + return err; + } + } + return 0; +} + + +snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels) +{ + *channels = plugin->buf_channels; + return frames; +} + +int snd_pcm_plugin_build(snd_pcm_plug_t *plug, + const char *name, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + size_t extra, + snd_pcm_plugin_t **ret) +{ + snd_pcm_plugin_t *plugin; + unsigned int channels; + + snd_assert(plug != NULL, return -ENXIO); + snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); + plugin = (snd_pcm_plugin_t *)snd_kcalloc(sizeof(*plugin) + extra, GFP_KERNEL); + if (plugin == NULL) + return -ENOMEM; + plugin->name = name; + plugin->plug = plug; + plugin->stream = snd_pcm_plug_stream(plug); + plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + plugin->src_format = *src_format; + plugin->src_width = snd_pcm_format_physical_width(src_format->format); + snd_assert(plugin->src_width > 0, ); + plugin->dst_format = *dst_format; + plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); + snd_assert(plugin->dst_width > 0, ); + if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) + channels = src_format->channels; + else + channels = dst_format->channels; + plugin->buf_channels = snd_kcalloc(channels * sizeof(*plugin->buf_channels), GFP_KERNEL); + if (plugin->buf_channels == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->src_vmask = bitset_alloc(src_format->channels); + if (plugin->src_vmask == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->dst_vmask = bitset_alloc(dst_format->channels); + if (plugin->dst_vmask == NULL) { + snd_pcm_plugin_free(plugin); + return -ENOMEM; + } + plugin->client_channels = snd_pcm_plugin_client_channels; + plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; + plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; + *ret = plugin; + return 0; +} + +int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) +{ + if (! plugin) + return 0; + if (plugin->private_free) + plugin->private_free(plugin); + if (plugin->buf_channels) + kfree(plugin->buf_channels); + if (plugin->buf) + vfree(plugin->buf); + if (plugin->src_vmask) + kfree(plugin->src_vmask); + if (plugin->dst_vmask) + kfree(plugin->dst_vmask); + kfree(plugin); + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames) +{ + snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(plug != NULL, return -ENXIO); + if (drv_frames == 0) + return 0; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_last(plug); + while (plugin && drv_frames > 0) { + plugin_prev = plugin->prev; + if (plugin->src_frames) + drv_frames = plugin->src_frames(plugin, drv_frames); + plugin = plugin_prev; + } + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + plugin = snd_pcm_plug_first(plug); + while (plugin && drv_frames > 0) { + plugin_next = plugin->next; + if (plugin->dst_frames) + drv_frames = plugin->dst_frames(plugin, drv_frames); + plugin = plugin_next; + } + } else + snd_BUG(); + return drv_frames; +} + +snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames) +{ + snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; + snd_pcm_sframes_t frames; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(plug != NULL, return -ENXIO); + if (clt_frames == 0) + return 0; + frames = clt_frames; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + plugin_next = plugin->next; + if (plugin->dst_frames) { + frames = plugin->dst_frames(plugin, frames); + if (frames < 0) + return frames; + } + plugin = plugin_next; + } + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + plugin = snd_pcm_plug_last(plug); + while (plugin) { + plugin_prev = plugin->prev; + if (plugin->src_frames) { + frames = plugin->src_frames(plugin, frames); + if (frames < 0) + return frames; + } + plugin = plugin_prev; + } + } else + snd_BUG(); + return frames; +} + +unsigned int snd_pcm_plug_formats(unsigned int formats) +{ + int linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); + formats |= SNDRV_PCM_FMTBIT_MU_LAW; + + if (formats & linfmts) + formats |= linfmts; + return formats; +} + +static int preferred_formats[] = { + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_U8 +}; + +int snd_pcm_plug_slave_format(int format, unsigned int format_mask) +{ + if (format_mask & (1 << format)) + return format; + if ((snd_pcm_plug_formats(format_mask) & (1 << format)) == 0) + return -EINVAL; + if (snd_pcm_format_linear(format)) { + int width = snd_pcm_format_width(format); + int unsignd = snd_pcm_format_unsigned(format); + int big = snd_pcm_format_big_endian(format); + int format1; + int wid, width1=width; + int dwidth1 = 8; + for (wid = 0; wid < 4; ++wid) { + int end, big1 = big; + for (end = 0; end < 2; ++end) { + int sgn, unsignd1 = unsignd; + for (sgn = 0; sgn < 2; ++sgn) { + format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); + if (format1 >= 0 && + format_mask & (1 << format1)) + goto _found; + unsignd1 = !unsignd1; + } + big1 = !big1; + } + if (width1 == 32) { + dwidth1 = -dwidth1; + width1 = width; + } + width1 += dwidth1; + } + return -EINVAL; + _found: + return format1; + } else { + unsigned int i; + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { + int format1 = preferred_formats[i]; + if (format_mask & (1 << format1)) + return format1; + } + default: + return -EINVAL; + } + } +} + +int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *slave_params) +{ + snd_pcm_plugin_format_t tmpformat; + snd_pcm_plugin_format_t dstformat; + snd_pcm_plugin_format_t srcformat; + int src_access, dst_access; + snd_pcm_plugin_t *plugin = NULL; + int err, first; + int stream = snd_pcm_plug_stream(plug); + int slave_interleaved = (params_channels(slave_params) == 1 || + params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + dstformat.format = params_format(slave_params); + dstformat.rate = params_rate(slave_params); + dstformat.channels = params_channels(slave_params); + srcformat.format = params_format(params); + srcformat.rate = params_rate(params); + srcformat.channels = params_channels(params); + src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + break; + case SNDRV_PCM_STREAM_CAPTURE: + dstformat.format = params_format(params); + dstformat.rate = params_rate(params); + dstformat.channels = params_channels(params); + srcformat.format = params_format(slave_params); + srcformat.rate = params_rate(slave_params); + srcformat.channels = params_channels(slave_params); + src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : + SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + break; + default: + snd_BUG(); + return -EINVAL; + } + tmpformat = srcformat; + + pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", + srcformat.format, + srcformat.rate, + srcformat.channels); + pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", + dstformat.format, + dstformat.rate, + dstformat.channels); + + /* Format change (linearization) */ + if ((srcformat.format != dstformat.format || + srcformat.rate != dstformat.rate || + srcformat.channels != dstformat.channels) && + !snd_pcm_format_linear(srcformat.format)) { + if (snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + else + tmpformat.format = SNDRV_PCM_FORMAT_S16; + first = plugin == NULL; + switch (srcformat.format) { + case SNDRV_PCM_FORMAT_MU_LAW: + err = snd_pcm_plugin_build_mulaw(plug, + &srcformat, &tmpformat, + &plugin); + break; + default: + return -EINVAL; + } + pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* channels reduction */ + if (srcformat.channels > dstformat.channels) { + int sv = srcformat.channels; + int dv = dstformat.channels; + route_ttable_entry_t *ttable = snd_kcalloc(dv*sv*sizeof(*ttable), GFP_KERNEL); + if (ttable == NULL) + return -ENOMEM; +#if 1 + if (sv == 2 && dv == 1) { + ttable[0] = HALF; + ttable[1] = HALF; + } else +#endif + { + int v; + for (v = 0; v < dv; ++v) + ttable[v * sv + v] = FULL; + } + tmpformat.channels = dstformat.channels; + if (srcformat.rate == dstformat.rate && + snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_route(plug, + &srcformat, &tmpformat, + ttable, &plugin); + kfree(ttable); + pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* rate resampling */ + if (srcformat.rate != dstformat.rate) { + tmpformat.rate = dstformat.rate; + if (srcformat.channels == dstformat.channels && + snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_rate(plug, + &srcformat, &tmpformat, + &plugin); + pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* channels extension */ + if (srcformat.channels < dstformat.channels) { + int sv = srcformat.channels; + int dv = dstformat.channels; + route_ttable_entry_t *ttable = snd_kcalloc(dv * sv * sizeof(*ttable), GFP_KERNEL); + if (ttable == NULL) + return -ENOMEM; +#if 0 + { + int v; + for (v = 0; v < sv; ++v) + ttable[v * sv + v] = FULL; + } +#else + { + /* Playback is spreaded on all channels */ + int vd, vs; + for (vd = 0, vs = 0; vd < dv; ++vd) { + ttable[vd * sv + vs] = FULL; + vs++; + if (vs == sv) + vs = 0; + } + } +#endif + tmpformat.channels = dstformat.channels; + if (snd_pcm_format_linear(dstformat.format)) + tmpformat.format = dstformat.format; + err = snd_pcm_plugin_build_route(plug, + &srcformat, &tmpformat, + ttable, &plugin); + kfree(ttable); + pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* format change */ + if (srcformat.format != dstformat.format) { + tmpformat.format = dstformat.format; + if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { + err = snd_pcm_plugin_build_mulaw(plug, + &srcformat, &tmpformat, + &plugin); + } + else if (snd_pcm_format_linear(srcformat.format) && + snd_pcm_format_linear(tmpformat.format)) { + err = snd_pcm_plugin_build_linear(plug, + &srcformat, &tmpformat, + &plugin); + } + else + return -EINVAL; + pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcformat = tmpformat; + src_access = dst_access; + } + + /* de-interleave */ + if (src_access != dst_access) { + err = snd_pcm_plugin_build_copy(plug, + &srcformat, + &tmpformat, + &plugin); + pdprintf("interleave change (copy: returns %i)\n", err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + } + + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug, + char *buf, + snd_pcm_uframes_t count, + snd_pcm_plugin_channel_t **channels) +{ + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_channel_t *v; + snd_pcm_plugin_format_t *format; + int width, nchannels, channel; + int stream = snd_pcm_plug_stream(plug); + + snd_assert(buf != NULL, return -ENXIO); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + plugin = snd_pcm_plug_first(plug); + format = &plugin->src_format; + } else { + plugin = snd_pcm_plug_last(plug); + format = &plugin->dst_format; + } + v = plugin->buf_channels; + *channels = v; + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + nchannels = format->channels; + snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); + for (channel = 0; channel < nchannels; channel++, v++) { + v->enabled = 1; + v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); + v->area.addr = buf; + v->area.first = channel * width; + v->area.step = nchannels * width; + } + return count; +} + +int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug, + bitset_t *client_vmask) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + if (plugin == NULL) { + return 0; + } else { + int schannels = plugin->dst_format.channels; + bitset_t bs[bitset_size(schannels)]; + bitset_t *srcmask; + bitset_t *dstmask = bs; + int err; + bitset_one(dstmask, schannels); + if (plugin == NULL) { + bitset_and(client_vmask, dstmask, schannels); + return 0; + } + while (1) { + err = plugin->src_channels_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + if (plugin->prev == NULL) + break; + plugin = plugin->prev; + } + bitset_and(client_vmask, dstmask, plugin->src_format.channels); + return 0; + } +} + +int snd_pcm_plug_capture_channels_mask(snd_pcm_plug_t *plug, + bitset_t *client_vmask) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + if (plugin == NULL) { + return 0; + } else { + int schannels = plugin->src_format.channels; + bitset_t bs[bitset_size(schannels)]; + bitset_t *srcmask = bs; + bitset_t *dstmask; + int err; + bitset_one(srcmask, schannels); + while (1) { + err = plugin->dst_channels_mask(plugin, srcmask, &dstmask); + if (err < 0) + return err; + srcmask = dstmask; + if (plugin->next == NULL) + break; + plugin = plugin->next; + } + bitset_and(client_vmask, srcmask, plugin->dst_format.channels); + return 0; + } +} + +static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug, + snd_pcm_plugin_channel_t *src_channels) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); + unsigned int nchannels = plugin->src_format.channels; + bitset_t bs[bitset_size(nchannels)]; + bitset_t *srcmask = bs; + int err; + unsigned int channel; + for (channel = 0; channel < nchannels; channel++) { + if (src_channels[channel].enabled) + bitset_set(srcmask, channel); + else + bitset_reset(srcmask, channel); + } + err = snd_pcm_plug_playback_channels_mask(plug, srcmask); + if (err < 0) + return err; + for (channel = 0; channel < nchannels; channel++) { + if (!bitset_get(srcmask, channel)) + src_channels[channel].enabled = 0; + } + return 0; +} + +static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug, + snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *client_channels) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); + unsigned int nchannels = plugin->dst_format.channels; + bitset_t bs[bitset_size(nchannels)]; + bitset_t *dstmask = bs; + bitset_t *srcmask; + int err; + unsigned int channel; + for (channel = 0; channel < nchannels; channel++) { + if (client_channels[channel].enabled) + bitset_set(dstmask, channel); + else + bitset_reset(dstmask, channel); + } + while (plugin) { + err = plugin->src_channels_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + plugin = plugin->prev; + } + plugin = snd_pcm_plug_first(plug); + nchannels = plugin->src_format.channels; + for (channel = 0; channel < nchannels; channel++) { + if (!bitset_get(dstmask, channel)) + src_channels[channel].enabled = 0; + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_channel_t *dst_channels; + int err; + snd_pcm_sframes_t frames = size; + + if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0) + return err; + + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + if ((next = plugin->next) != NULL) { + snd_pcm_sframes_t frames1 = frames; + if (plugin->dst_frames) + frames1 = plugin->dst_frames(plugin, frames); + if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { + return err; + } + if (err != frames1) { + frames = err; + if (plugin->src_frames) + frames = plugin->src_frames(plugin, frames1); + } + } else + dst_channels = 0; + pdprintf("write plugin: %s, %li\n", plugin->name, frames); + if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) + return frames; + src_channels = dst_channels; + plugin = next; + } + return snd_pcm_plug_client_size(plug, frames); +} + +snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_channel_t *src_channels, *dst_channels; + snd_pcm_sframes_t frames = size; + int err; + + frames = snd_pcm_plug_slave_size(plug, frames); + if (frames < 0) + return frames; + + src_channels = 0; + plugin = snd_pcm_plug_first(plug); + while (plugin && frames > 0) { + if ((next = plugin->next) != NULL) { + if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { + return err; + } + frames = err; + if (!plugin->prev) { + if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final) < 0)) + return err; + } + } else { + dst_channels = dst_channels_final; + } + pdprintf("read plugin: %s, %li\n", plugin->name, frames); + if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) + return frames; + plugin = next; + src_channels = dst_channels; + } + return frames; +} + +int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *dst; + unsigned int dst_step; + int width; + u_int64_t silence; + if (!dst_area->addr) + return 0; + dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; + width = snd_pcm_format_physical_width(format); + silence = snd_pcm_format_silence_64(format); + if (dst_area->step == (unsigned int) width) { + size_t dwords = samples * width / 64; + samples -= dwords * 64 / width; + while (dwords-- > 0) + *((u_int64_t*)dst)++ = silence; + if (samples == 0) + return 0; + } + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + u_int8_t s0 = silence & 0xf0; + u_int8_t s1 = silence & 0x0f; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + if (dstbit) { + *dst &= 0xf0; + *dst |= s1; + } else { + *dst &= 0x0f; + *dst |= s0; + } + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + break; + } + case 8: { + u_int8_t sil = silence; + while (samples-- > 0) { + *dst = sil; + dst += dst_step; + } + break; + } + case 16: { + u_int16_t sil = silence; + while (samples-- > 0) { + *(u_int16_t*)dst = sil; + dst += dst_step; + } + break; + } + case 32: { + u_int32_t sil = silence; + while (samples-- > 0) { + *(u_int32_t*)dst = sil; + dst += dst_step; + } + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = silence; + dst += dst_step; + } + break; + } + default: + snd_BUG(); + } + return 0; +} + +int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format) +{ + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + void *addr = dst_areas->addr; + unsigned int step = dst_areas->step; + const snd_pcm_channel_area_t *begin = dst_areas; + int vc = channels; + unsigned int v = 0; + int err; + while (1) { + vc--; + v++; + dst_areas++; + if (vc == 0 || + dst_areas->addr != addr || + dst_areas->step != step || + dst_areas->first != dst_areas[-1].first + width) + break; + } + if (v > 1 && v * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t d; + d.addr = begin->addr; + d.first = begin->first; + d.step = width; + err = snd_pcm_area_silence(&d, dst_offset * v, frames * v, format); + channels -= v; + } else { + err = snd_pcm_area_silence(begin, dst_offset, frames, format); + dst_areas = begin + 1; + channels--; + } + if (err < 0) + return err; + } + return 0; +} + + +int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset, + const snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *src, *dst; + int width; + int src_step, dst_step; + src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; + if (!src_area->addr) + return snd_pcm_area_silence(dst_area, dst_offset, samples, format); + dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; + if (!dst_area->addr) + return 0; + width = snd_pcm_format_physical_width(format); + if (src_area->step == (unsigned int) width && + dst_area->step == (unsigned int) width) { + size_t bytes = samples * width / 8; + samples -= bytes * 8 / width; + memcpy(dst, src, bytes); + if (samples == 0) + return 0; + } + src_step = src_area->step / 8; + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + int srcbit = src_area->first % 8; + int srcbit_step = src_area->step % 8; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + unsigned char srcval; + if (srcbit) + srcval = *src & 0x0f; + else + srcval = *src & 0xf0; + if (dstbit) + *dst &= 0xf0; + else + *dst &= 0x0f; + *dst |= srcval; + src += src_step; + srcbit += srcbit_step; + if (srcbit == 8) { + src++; + srcbit = 0; + } + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + break; + } + case 8: { + while (samples-- > 0) { + *dst = *src; + src += src_step; + dst += dst_step; + } + break; + } + case 16: { + while (samples-- > 0) { + *(u_int16_t*)dst = *(u_int16_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + case 32: { + while (samples-- > 0) { + *(u_int32_t*)dst = *(u_int32_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = *(u_int64_t*)src; + src += src_step; + dst += dst_step; + } + break; + } + default: + snd_BUG(); + } + return 0; +} + +int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, + const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format) +{ + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + unsigned int step = src_areas->step; + void *src_addr = src_areas->addr; + const snd_pcm_channel_area_t *src_start = src_areas; + void *dst_addr = dst_areas->addr; + const snd_pcm_channel_area_t *dst_start = dst_areas; + int vc = channels; + unsigned int v = 0; + while (dst_areas->step == step) { + vc--; + v++; + src_areas++; + dst_areas++; + if (vc == 0 || + src_areas->step != step || + src_areas->addr != src_addr || + dst_areas->addr != dst_addr || + src_areas->first != src_areas[-1].first + width || + dst_areas->first != dst_areas[-1].first + width) + break; + } + if (v > 1 && v * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t s, d; + s.addr = src_start->addr; + s.first = src_start->first; + s.step = width; + d.addr = dst_start->addr; + d.first = dst_start->first; + d.step = width; + snd_pcm_area_copy(&s, src_offset * v, &d, dst_offset * v, frames * v, format); + channels -= v; + } else { + snd_pcm_area_copy(src_start, src_offset, dst_start, dst_offset, frames, format); + src_areas = src_start + 1; + dst_areas = dst_start + 1; + channels--; + } + } + return 0; +} diff -Nru linux/sound/core/oss/pcm_plugin.h linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.h --- linux/sound/core/oss/pcm_plugin.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/pcm_plugin.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,257 @@ +#ifndef __PCM_PLUGIN_H +#define __PCM_PLUGIN_H + +/* + * Digital Audio (Plugin interface) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +typedef unsigned int bitset_t; + +static inline size_t bitset_size(int nbits) +{ + return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8); +} + +static inline bitset_t *bitset_alloc(int nbits) +{ + return snd_kcalloc(bitset_size(nbits) * sizeof(bitset_t), GFP_KERNEL); +} + +static inline void bitset_set(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] |= 1 << (pos % bits); +} + +static inline void bitset_reset(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] &= ~(1 << (pos % bits)); +} + +static inline int bitset_get(bitset_t *bitmap, unsigned int pos) +{ + size_t bits = sizeof(*bitmap) * 8; + return !!(bitmap[pos / bits] & (1 << (pos % bits))); +} + +static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits) +{ + memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t)); +} + +static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ &= *bs++; +} + +static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ |= *bs++; +} + +static inline void bitset_zero(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = 0; +} + +static inline void bitset_one(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = ~(bitset_t)0; +} + +#define snd_pcm_plug_t snd_pcm_substream_t +#define snd_pcm_plug_stream(plug) ((plug)->stream) + +typedef enum { + INIT = 0, + PREPARE = 1, +} snd_pcm_plugin_action_t; + +typedef struct _snd_pcm_channel_area { + void *addr; /* base address of channel samples */ + unsigned int first; /* offset to first sample in bits */ + unsigned int step; /* samples distance in bits */ +} snd_pcm_channel_area_t; + +typedef struct _snd_pcm_plugin_channel { + void *aptr; /* pointer to the allocated area */ + snd_pcm_channel_area_t area; + unsigned int enabled:1; /* channel need to be processed */ + unsigned int wanted:1; /* channel is wanted */ +} snd_pcm_plugin_channel_t; + +typedef struct _snd_pcm_plugin_format { + int format; + unsigned int rate; + unsigned int channels; +} snd_pcm_plugin_format_t; + +struct _snd_pcm_plugin { + const char *name; /* plug-in name */ + int stream; + snd_pcm_plugin_format_t src_format; /* source format */ + snd_pcm_plugin_format_t dst_format; /* destination format */ + int src_width; /* sample width in bits */ + int dst_width; /* sample width in bits */ + int access; + snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames); + snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames); + snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels); + int (*src_channels_mask)(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask); + int (*dst_channels_mask)(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask); + snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames); + int (*action)(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_action_t action, + unsigned long data); + snd_pcm_plugin_t *prev; + snd_pcm_plugin_t *next; + snd_pcm_plug_t *plug; + void *private_data; + void (*private_free)(snd_pcm_plugin_t *plugin); + char *buf; + snd_pcm_uframes_t buf_frames; + snd_pcm_plugin_channel_t *buf_channels; + bitset_t *src_vmask; + bitset_t *dst_vmask; + char extra_data[0]; +}; + +int snd_pcm_plugin_build(snd_pcm_plug_t *handle, + const char *name, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + size_t extra, + snd_pcm_plugin_t **ret); +int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin); +int snd_pcm_plugin_clear(snd_pcm_plugin_t **first); +int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames); +snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size); +snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size); + +#define ROUTE_PLUGIN_USE_FLOAT 0 +#define FULL ROUTE_PLUGIN_RESOLUTION +#define HALF ROUTE_PLUGIN_RESOLUTION / 2 +typedef int route_ttable_entry_t; + +int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle, + snd_pcm_hw_params_t *params, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + route_ttable_entry_t *ttable, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); + +unsigned int snd_pcm_plug_formats(unsigned int formats); + +int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *slave_params); + +int snd_pcm_plug_slave_format(int format, unsigned int format_mask); + +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin); + +snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size); +snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size); + +snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle, + char *buf, snd_pcm_uframes_t count, + snd_pcm_plugin_channel_t **channels); + +snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, + snd_pcm_uframes_t frames, + snd_pcm_plugin_channel_t **channels); + +int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format); +int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset, + const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, + size_t samples, int format); +int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_channels, snd_pcm_uframes_t src_offset, + const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset, + unsigned int channels, snd_pcm_uframes_t frames, int format); + +void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size); +void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr); +snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); +snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel); + + + +#define ROUTE_PLUGIN_RESOLUTION 16 + +int getput_index(int format); +int copy_index(int format); +int conv_index(int src_format, int dst_format); + +void zero_channel(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *dst_channel, + size_t samples); + +#ifdef PLUGIN_DEBUG +#define pdprintf( args... ) printk( "plugin: " ##args) +#else +#define pdprintf( args... ) { ; } +#endif + +#endif /* __PCM_PLUGIN_H */ diff -Nru linux/sound/core/oss/plugin_ops.h linux-2.4.19-pre5-mjc/sound/core/oss/plugin_ops.h --- linux/sound/core/oss/plugin_ops.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/plugin_ops.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,536 @@ +/* + * Plugin sample operators with fast switch + * Copyright (c) 2000 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#define as_u8(ptr) (*(u_int8_t*)(ptr)) +#define as_u16(ptr) (*(u_int16_t*)(ptr)) +#define as_u32(ptr) (*(u_int32_t*)(ptr)) +#define as_u64(ptr) (*(u_int64_t*)(ptr)) +#define as_s8(ptr) (*(int8_t*)(ptr)) +#define as_s16(ptr) (*(int16_t*)(ptr)) +#define as_s32(ptr) (*(int32_t*)(ptr)) +#define as_s64(ptr) (*(int64_t*)(ptr)) + +#ifdef COPY_LABELS +static void *copy_labels[4] = { + &©_8, + &©_16, + &©_32, + &©_64 +}; +#endif + +#ifdef COPY_END +while(0) { +copy_8: as_s8(dst) = as_s8(src); goto COPY_END; +copy_16: as_s16(dst) = as_s16(src); goto COPY_END; +copy_32: as_s32(dst) = as_s32(src); goto COPY_END; +copy_64: as_s64(dst) = as_s64(src); goto COPY_END; +} +#endif + +#ifdef CONV_LABELS +/* src_wid src_endswap sign_toggle dst_wid dst_endswap */ +static void *conv_labels[4 * 2 * 2 * 4 * 2] = { + &&conv_xxx1_xxx1, /* 8h -> 8h */ + &&conv_xxx1_xxx1, /* 8h -> 8s */ + &&conv_xxx1_xx10, /* 8h -> 16h */ + &&conv_xxx1_xx01, /* 8h -> 16s */ + &&conv_xxx1_x100, /* 8h -> 24h */ + &&conv_xxx1_001x, /* 8h -> 24s */ + &&conv_xxx1_1000, /* 8h -> 32h */ + &&conv_xxx1_0001, /* 8h -> 32s */ + &&conv_xxx1_xxx9, /* 8h ^> 8h */ + &&conv_xxx1_xxx9, /* 8h ^> 8s */ + &&conv_xxx1_xx90, /* 8h ^> 16h */ + &&conv_xxx1_xx09, /* 8h ^> 16s */ + &&conv_xxx1_x900, /* 8h ^> 24h */ + &&conv_xxx1_009x, /* 8h ^> 24s */ + &&conv_xxx1_9000, /* 8h ^> 32h */ + &&conv_xxx1_0009, /* 8h ^> 32s */ + &&conv_xxx1_xxx1, /* 8s -> 8h */ + &&conv_xxx1_xxx1, /* 8s -> 8s */ + &&conv_xxx1_xx10, /* 8s -> 16h */ + &&conv_xxx1_xx01, /* 8s -> 16s */ + &&conv_xxx1_x100, /* 8s -> 24h */ + &&conv_xxx1_001x, /* 8s -> 24s */ + &&conv_xxx1_1000, /* 8s -> 32h */ + &&conv_xxx1_0001, /* 8s -> 32s */ + &&conv_xxx1_xxx9, /* 8s ^> 8h */ + &&conv_xxx1_xxx9, /* 8s ^> 8s */ + &&conv_xxx1_xx90, /* 8s ^> 16h */ + &&conv_xxx1_xx09, /* 8s ^> 16s */ + &&conv_xxx1_x900, /* 8s ^> 24h */ + &&conv_xxx1_009x, /* 8s ^> 24s */ + &&conv_xxx1_9000, /* 8s ^> 32h */ + &&conv_xxx1_0009, /* 8s ^> 32s */ + &&conv_xx12_xxx1, /* 16h -> 8h */ + &&conv_xx12_xxx1, /* 16h -> 8s */ + &&conv_xx12_xx12, /* 16h -> 16h */ + &&conv_xx12_xx21, /* 16h -> 16s */ + &&conv_xx12_x120, /* 16h -> 24h */ + &&conv_xx12_021x, /* 16h -> 24s */ + &&conv_xx12_1200, /* 16h -> 32h */ + &&conv_xx12_0021, /* 16h -> 32s */ + &&conv_xx12_xxx9, /* 16h ^> 8h */ + &&conv_xx12_xxx9, /* 16h ^> 8s */ + &&conv_xx12_xx92, /* 16h ^> 16h */ + &&conv_xx12_xx29, /* 16h ^> 16s */ + &&conv_xx12_x920, /* 16h ^> 24h */ + &&conv_xx12_029x, /* 16h ^> 24s */ + &&conv_xx12_9200, /* 16h ^> 32h */ + &&conv_xx12_0029, /* 16h ^> 32s */ + &&conv_xx12_xxx2, /* 16s -> 8h */ + &&conv_xx12_xxx2, /* 16s -> 8s */ + &&conv_xx12_xx21, /* 16s -> 16h */ + &&conv_xx12_xx12, /* 16s -> 16s */ + &&conv_xx12_x210, /* 16s -> 24h */ + &&conv_xx12_012x, /* 16s -> 24s */ + &&conv_xx12_2100, /* 16s -> 32h */ + &&conv_xx12_0012, /* 16s -> 32s */ + &&conv_xx12_xxxA, /* 16s ^> 8h */ + &&conv_xx12_xxxA, /* 16s ^> 8s */ + &&conv_xx12_xxA1, /* 16s ^> 16h */ + &&conv_xx12_xx1A, /* 16s ^> 16s */ + &&conv_xx12_xA10, /* 16s ^> 24h */ + &&conv_xx12_01Ax, /* 16s ^> 24s */ + &&conv_xx12_A100, /* 16s ^> 32h */ + &&conv_xx12_001A, /* 16s ^> 32s */ + &&conv_x123_xxx1, /* 24h -> 8h */ + &&conv_x123_xxx1, /* 24h -> 8s */ + &&conv_x123_xx12, /* 24h -> 16h */ + &&conv_x123_xx21, /* 24h -> 16s */ + &&conv_x123_x123, /* 24h -> 24h */ + &&conv_x123_321x, /* 24h -> 24s */ + &&conv_x123_1230, /* 24h -> 32h */ + &&conv_x123_0321, /* 24h -> 32s */ + &&conv_x123_xxx9, /* 24h ^> 8h */ + &&conv_x123_xxx9, /* 24h ^> 8s */ + &&conv_x123_xx92, /* 24h ^> 16h */ + &&conv_x123_xx29, /* 24h ^> 16s */ + &&conv_x123_x923, /* 24h ^> 24h */ + &&conv_x123_329x, /* 24h ^> 24s */ + &&conv_x123_9230, /* 24h ^> 32h */ + &&conv_x123_0329, /* 24h ^> 32s */ + &&conv_123x_xxx3, /* 24s -> 8h */ + &&conv_123x_xxx3, /* 24s -> 8s */ + &&conv_123x_xx32, /* 24s -> 16h */ + &&conv_123x_xx23, /* 24s -> 16s */ + &&conv_123x_x321, /* 24s -> 24h */ + &&conv_123x_123x, /* 24s -> 24s */ + &&conv_123x_3210, /* 24s -> 32h */ + &&conv_123x_0123, /* 24s -> 32s */ + &&conv_123x_xxxB, /* 24s ^> 8h */ + &&conv_123x_xxxB, /* 24s ^> 8s */ + &&conv_123x_xxB2, /* 24s ^> 16h */ + &&conv_123x_xx2B, /* 24s ^> 16s */ + &&conv_123x_xB21, /* 24s ^> 24h */ + &&conv_123x_12Bx, /* 24s ^> 24s */ + &&conv_123x_B210, /* 24s ^> 32h */ + &&conv_123x_012B, /* 24s ^> 32s */ + &&conv_1234_xxx1, /* 32h -> 8h */ + &&conv_1234_xxx1, /* 32h -> 8s */ + &&conv_1234_xx12, /* 32h -> 16h */ + &&conv_1234_xx21, /* 32h -> 16s */ + &&conv_1234_x123, /* 32h -> 24h */ + &&conv_1234_321x, /* 32h -> 24s */ + &&conv_1234_1234, /* 32h -> 32h */ + &&conv_1234_4321, /* 32h -> 32s */ + &&conv_1234_xxx9, /* 32h ^> 8h */ + &&conv_1234_xxx9, /* 32h ^> 8s */ + &&conv_1234_xx92, /* 32h ^> 16h */ + &&conv_1234_xx29, /* 32h ^> 16s */ + &&conv_1234_x923, /* 32h ^> 24h */ + &&conv_1234_329x, /* 32h ^> 24s */ + &&conv_1234_9234, /* 32h ^> 32h */ + &&conv_1234_4329, /* 32h ^> 32s */ + &&conv_1234_xxx4, /* 32s -> 8h */ + &&conv_1234_xxx4, /* 32s -> 8s */ + &&conv_1234_xx43, /* 32s -> 16h */ + &&conv_1234_xx34, /* 32s -> 16s */ + &&conv_1234_x432, /* 32s -> 24h */ + &&conv_1234_234x, /* 32s -> 24s */ + &&conv_1234_4321, /* 32s -> 32h */ + &&conv_1234_1234, /* 32s -> 32s */ + &&conv_1234_xxxC, /* 32s ^> 8h */ + &&conv_1234_xxxC, /* 32s ^> 8s */ + &&conv_1234_xxC3, /* 32s ^> 16h */ + &&conv_1234_xx3C, /* 32s ^> 16s */ + &&conv_1234_xC32, /* 32s ^> 24h */ + &&conv_1234_23Cx, /* 32s ^> 24s */ + &&conv_1234_C321, /* 32s ^> 32h */ + &&conv_1234_123C, /* 32s ^> 32s */ +}; +#endif + +#ifdef CONV_END +while(0) { +conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END; +conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END; +conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END; +conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END; +conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END; +conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END; +conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END; +conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END; +conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END; +conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END; +conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END; +conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END; +conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END; +conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END; +conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END; +conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END; +conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END; +conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END; +conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END; +conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END; +conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END; +conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END; +conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END; +conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END; +conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END; +conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END; +conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END; +conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END; +conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END; +conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END; +conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; +conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END; +conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; +conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END; +conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END; +conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END; +conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END; +conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END; +conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END; +conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; +conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END; +conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END; +conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; +conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END; +conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END; +conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END; +conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END; +conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END; +conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END; +conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END; +conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END; +conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END; +conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; +conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END; +conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; +conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END; +conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END; +conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END; +conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END; +conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END; +conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END; +conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END; +conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END; +conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; +conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END; +conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END; +conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END; +conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END; +} +#endif + +#ifdef GET_S16_LABELS +/* src_wid src_endswap unsigned */ +static void *get_s16_labels[4 * 2 * 2] = { + &&get_s16_xxx1_xx10, /* 8h -> 16h */ + &&get_s16_xxx1_xx90, /* 8h ^> 16h */ + &&get_s16_xxx1_xx10, /* 8s -> 16h */ + &&get_s16_xxx1_xx90, /* 8s ^> 16h */ + &&get_s16_xx12_xx12, /* 16h -> 16h */ + &&get_s16_xx12_xx92, /* 16h ^> 16h */ + &&get_s16_xx12_xx21, /* 16s -> 16h */ + &&get_s16_xx12_xxA1, /* 16s ^> 16h */ + &&get_s16_x123_xx12, /* 24h -> 16h */ + &&get_s16_x123_xx92, /* 24h ^> 16h */ + &&get_s16_123x_xx32, /* 24s -> 16h */ + &&get_s16_123x_xxB2, /* 24s ^> 16h */ + &&get_s16_1234_xx12, /* 32h -> 16h */ + &&get_s16_1234_xx92, /* 32h ^> 16h */ + &&get_s16_1234_xx43, /* 32s -> 16h */ + &&get_s16_1234_xxC3, /* 32s ^> 16h */ +}; +#endif + +#ifdef GET_S16_END +while(0) { +get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END; +get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END; +get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END; +get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END; +get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END; +get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END; +get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END; +get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END; +get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END; +get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END; +get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END; +get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END; +get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END; +get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END; +} +#endif + +#ifdef PUT_S16_LABELS +/* dst_wid dst_endswap unsigned */ +static void *put_s16_labels[4 * 2 * 2 * 4 * 2] = { + &&put_s16_xx12_xxx1, /* 16h -> 8h */ + &&put_s16_xx12_xxx9, /* 16h ^> 8h */ + &&put_s16_xx12_xxx1, /* 16h -> 8s */ + &&put_s16_xx12_xxx9, /* 16h ^> 8s */ + &&put_s16_xx12_xx12, /* 16h -> 16h */ + &&put_s16_xx12_xx92, /* 16h ^> 16h */ + &&put_s16_xx12_xx21, /* 16h -> 16s */ + &&put_s16_xx12_xx29, /* 16h ^> 16s */ + &&put_s16_xx12_x120, /* 16h -> 24h */ + &&put_s16_xx12_x920, /* 16h ^> 24h */ + &&put_s16_xx12_021x, /* 16h -> 24s */ + &&put_s16_xx12_029x, /* 16h ^> 24s */ + &&put_s16_xx12_1200, /* 16h -> 32h */ + &&put_s16_xx12_9200, /* 16h ^> 32h */ + &&put_s16_xx12_0021, /* 16h -> 32s */ + &&put_s16_xx12_0029, /* 16h ^> 32s */ +}; +#endif + +#ifdef PUT_S16_END +while (0) { +put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END; +put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END; +put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END; +put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END; +put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END; +put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END; +put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END; +put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END; +put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END; +put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END; +put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END; +put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END; +put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END; +put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END; +} +#endif + +#if 0 +#ifdef GET32_LABELS +/* src_wid src_endswap unsigned */ +static void *get32_labels[4 * 2 * 2] = { + &&get32_xxx1_1000, /* 8h -> 32h */ + &&get32_xxx1_9000, /* 8h ^> 32h */ + &&get32_xxx1_1000, /* 8s -> 32h */ + &&get32_xxx1_9000, /* 8s ^> 32h */ + &&get32_xx12_1200, /* 16h -> 32h */ + &&get32_xx12_9200, /* 16h ^> 32h */ + &&get32_xx12_2100, /* 16s -> 32h */ + &&get32_xx12_A100, /* 16s ^> 32h */ + &&get32_x123_1230, /* 24h -> 32h */ + &&get32_x123_9230, /* 24h ^> 32h */ + &&get32_123x_3210, /* 24s -> 32h */ + &&get32_123x_B210, /* 24s ^> 32h */ + &&get32_1234_1234, /* 32h -> 32h */ + &&get32_1234_9234, /* 32h ^> 32h */ + &&get32_1234_4321, /* 32s -> 32h */ + &&get32_1234_C321, /* 32s ^> 32h */ +}; +#endif + +#ifdef GET32_END +while (0) { +get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END; +get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END; +get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END; +get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END; +get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END; +get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END; +get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END; +get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END; +get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END; +get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END; +get32_1234_1234: sample = as_u32(src); goto GET32_END; +get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END; +get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END; +get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END; +} +#endif +#endif + +#ifdef PUT_U32_LABELS +/* dst_wid dst_endswap unsigned */ +static void *put_u32_labels[4 * 2 * 2] = { + &&put_u32_1234_xxx9, /* u32h -> s8h */ + &&put_u32_1234_xxx1, /* u32h -> u8h */ + &&put_u32_1234_xxx9, /* u32h -> s8s */ + &&put_u32_1234_xxx1, /* u32h -> u8s */ + &&put_u32_1234_xx92, /* u32h -> s16h */ + &&put_u32_1234_xx12, /* u32h -> u16h */ + &&put_u32_1234_xx29, /* u32h -> s16s */ + &&put_u32_1234_xx21, /* u32h -> u16s */ + &&put_u32_1234_x923, /* u32h -> s24h */ + &&put_u32_1234_x123, /* u32h -> u24h */ + &&put_u32_1234_329x, /* u32h -> s24s */ + &&put_u32_1234_321x, /* u32h -> u24s */ + &&put_u32_1234_9234, /* u32h -> s32h */ + &&put_u32_1234_1234, /* u32h -> u32h */ + &&put_u32_1234_4329, /* u32h -> s32s */ + &&put_u32_1234_4321, /* u32h -> u32s */ +}; +#endif + +#ifdef PUT_U32_END +while (0) { +put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END; +put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END; +put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END; +put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END; +put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END; +put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END; +put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END; +put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END; +put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END; +put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END; +put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END; +put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END; +put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END; +put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END; +} +#endif + +#ifdef GET_U_LABELS +/* width endswap unsigned*/ +static void *get_u_labels[4 * 2 * 2] = { + &&get_u_s8, /* s8 -> u8 */ + &&get_u_u8, /* u8 -> u8 */ + &&get_u_s8, /* s8 -> u8 */ + &&get_u_u8, /* u8 -> u8 */ + &&get_u_s16h, /* s16h -> u16h */ + &&get_u_u16h, /* u16h -> u16h */ + &&get_u_s16s, /* s16s -> u16h */ + &&get_u_u16s, /* u16s -> u16h */ + &&get_u_s24h, /* s24h -> u32h */ + &&get_u_u24h, /* u24h -> u32h */ + &&get_u_s24s, /* s24s -> u32h */ + &&get_u_u24s, /* u24s -> u32h */ + &&get_u_s32h, /* s32h -> u32h */ + &&get_u_u32h, /* u32h -> u32h */ + &&get_u_s32s, /* s32s -> u32h */ + &&get_u_u32s, /* u32s -> u32h */ +}; +#endif + +#ifdef GET_U_END +while (0) { +get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END; +get_u_u8: sample = as_u8(src); goto GET_U_END; +get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END; +get_u_u16h: sample = as_u16(src); goto GET_U_END; +get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END; +get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END; +get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END; +get_u_u24h: sample = as_u32(src); goto GET_U_END; +get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END; +get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END; +get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END; +get_u_u32h: sample = as_u32(src); goto GET_U_END; +get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END; +get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END; +} +#endif + +#if 0 +#ifdef PUT_LABELS +/* width endswap unsigned */ +static void *put_labels[4 * 2 * 2] = { + &&put_s8, /* s8 -> s8 */ + &&put_u8, /* u8 -> s8 */ + &&put_s8, /* s8 -> s8 */ + &&put_u8, /* u8 -> s8 */ + &&put_s16h, /* s16h -> s16h */ + &&put_u16h, /* u16h -> s16h */ + &&put_s16s, /* s16s -> s16h */ + &&put_u16s, /* u16s -> s16h */ + &&put_s24h, /* s24h -> s32h */ + &&put_u24h, /* u24h -> s32h */ + &&put_s24s, /* s24s -> s32h */ + &&put_u24s, /* u24s -> s32h */ + &&put_s32h, /* s32h -> s32h */ + &&put_u32h, /* u32h -> s32h */ + &&put_s32s, /* s32s -> s32h */ + &&put_u32s, /* u32s -> s32h */ +}; +#endif + +#ifdef PUT_END +put_s8: as_s8(dst) = sample; goto PUT_END; +put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END; +put_s16h: as_s16(dst) = sample; goto PUT_END; +put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END; +put_s16s: as_s16(dst) = swab16(sample); goto PUT_END; +put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END; +put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END; +put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END; +put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END; +put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END; +put_s32h: as_s32(dst) = sample; goto PUT_END; +put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END; +put_s32s: as_s32(dst) = swab32(sample); goto PUT_END; +put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END; +#endif +#endif + +#undef as_u8 +#undef as_u16 +#undef as_u32 +#undef as_s8 +#undef as_s16 +#undef as_s32 diff -Nru linux/sound/core/oss/rate.c linux-2.4.19-pre5-mjc/sound/core/oss/rate.c --- linux/sound/core/oss/rate.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/rate.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,387 @@ +/* + * Rate conversion Plug-In + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pcm_plugin.h" + +#define SHIFT 11 +#define BITS (1<extra_data; + data->pos = 0; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + data->channels[channel].last_S1 = 0; + data->channels[channel].last_S2 = 0; + } +} + +static void resample_expand(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + int src_frames, int dst_frames) +{ + unsigned int pos = 0; + signed int val; + signed short S1, S2; + char *src, *dst; + unsigned int channel; + int src_step, dst_step; + int src_frames1, dst_frames1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_channel_t *rchannels = data->channels; + +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[data->get]; + void *put = put_s16_labels[data->put]; + void *get_s16_end = 0; + signed short sample = 0; +#define GET_S16_END *get_s16_end +#include "plugin_ops.h" +#undef GET_S16_END + + for (channel = 0; channel < plugin->src_format.channels; channel++) { + pos = data->pos; + S1 = rchannels->last_S1; + S2 = rchannels->last_S2; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + src_frames1 = src_frames; + dst_frames1 = dst_frames; + if (pos & ~R_MASK) { + get_s16_end = &&after_get1; + goto *get; + after_get1: + pos &= R_MASK; + S1 = S2; + S2 = sample; + src += src_step; + src_frames1--; + } + while (dst_frames1-- > 0) { + if (pos & ~R_MASK) { + pos &= R_MASK; + S1 = S2; + if (src_frames1-- > 0) { + get_s16_end = &&after_get2; + goto *get; + after_get2: + S2 = sample; + src += src_step; + } + } + val = S1 + ((S2 - S1) * (signed int)pos) / BITS; + if (val < -32768) + val = -32768; + else if (val > 32767) + val = 32767; + sample = val; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + pos += data->pitch; + } + rchannels->last_S1 = S1; + rchannels->last_S2 = S2; + rchannels++; + } + data->pos = pos; +} + +static void resample_shrink(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + int src_frames, int dst_frames) +{ + unsigned int pos = 0; + signed int val; + signed short S1, S2; + char *src, *dst; + unsigned int channel; + int src_step, dst_step; + int src_frames1, dst_frames1; + rate_t *data = (rate_t *)plugin->extra_data; + rate_channel_t *rchannels = data->channels; + +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[data->get]; + void *put = put_s16_labels[data->put]; + signed short sample = 0; + + for (channel = 0; channel < plugin->src_format.channels; ++channel) { + pos = data->pos; + S1 = rchannels->last_S1; + S2 = rchannels->last_S2; + if (!src_channels[channel].enabled) { + if (dst_channels[channel].wanted) + snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); + dst_channels[channel].enabled = 0; + continue; + } + dst_channels[channel].enabled = 1; + src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; + dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; + src_step = src_channels[channel].area.step / 8; + dst_step = dst_channels[channel].area.step / 8; + src_frames1 = src_frames; + dst_frames1 = dst_frames; + while (dst_frames1 > 0) { + S1 = S2; + if (src_frames1-- > 0) { + goto *get; +#define GET_S16_END after_get +#include "plugin_ops.h" +#undef GET_S16_END + after_get: + S2 = sample; + src += src_step; + } + if (pos & ~R_MASK) { + pos &= R_MASK; + val = S1 + ((S2 - S1) * (signed int)pos) / BITS; + if (val < -32768) + val = -32768; + else if (val > 32767) + val = 32767; + sample = val; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + dst_frames1--; + } + pos += data->pitch; + } + rchannels->last_S1 = S1; + rchannels->last_S2 = S2; + rchannels++; + } + data->pos = pos; +} + +static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + rate_t *data; + snd_pcm_sframes_t res; + + snd_assert(plugin != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); + } else { + res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); + } + if (data->old_src_frames > 0) { + snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames; + while (data->old_src_frames < frames1) { + frames1 >>= 1; + res1 <<= 1; + } + while (data->old_src_frames > frames1) { + frames1 <<= 1; + res1 >>= 1; + } + if (data->old_src_frames == frames1) + return res1; + } + data->old_src_frames = frames; + data->old_dst_frames = res; + return res; +} + +static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) +{ + rate_t *data; + snd_pcm_sframes_t res; + + snd_assert(plugin != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (rate_t *)plugin->extra_data; + if (plugin->src_format.rate < plugin->dst_format.rate) { + res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); + } else { + res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); + } + if (data->old_dst_frames > 0) { + snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames; + while (data->old_dst_frames < frames1) { + frames1 >>= 1; + res1 <<= 1; + } + while (data->old_dst_frames > frames1) { + frames1 <<= 1; + res1 >>= 1; + } + if (data->old_dst_frames == frames1) + return res1; + } + data->old_dst_frames = frames; + data->old_src_frames = res; + return res; +} + +static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + snd_pcm_uframes_t dst_frames; + rate_t *data; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; +#ifdef CONFIG_SND_DEBUG + { + unsigned int channel; + for (channel = 0; channel < plugin->src_format.channels; channel++) { + snd_assert(src_channels[channel].area.first % 8 == 0 && + src_channels[channel].area.step % 8 == 0, + return -ENXIO); + snd_assert(dst_channels[channel].area.first % 8 == 0 && + dst_channels[channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + + dst_frames = rate_dst_frames(plugin, frames); + data = (rate_t *)plugin->extra_data; + data->func(plugin, src_channels, dst_channels, frames, dst_frames); + return dst_frames; +} + +static int rate_action(snd_pcm_plugin_t *plugin, + snd_pcm_plugin_action_t action, + unsigned long udata ATTRIBUTE_UNUSED) +{ + snd_assert(plugin != NULL, return -ENXIO); + switch (action) { + case INIT: + case PREPARE: + rate_init(plugin); + break; + default: + break; + } + return 0; /* silenty ignore other actions */ +} + +int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + rate_t *data; + snd_pcm_plugin_t *plugin; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + + snd_assert(src_format->channels == dst_format->channels, return -ENXIO); + snd_assert(src_format->channels > 0, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO); + snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO); + snd_assert(src_format->rate != dst_format->rate, return -ENXIO); + + err = snd_pcm_plugin_build(plug, "rate conversion", + src_format, dst_format, + sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t), + &plugin); + if (err < 0) + return err; + data = (rate_t *)plugin->extra_data; + data->get = getput_index(src_format->format); + data->put = getput_index(dst_format->format); + + if (src_format->rate < dst_format->rate) { + data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate; + data->func = resample_expand; + } else { + data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate; + data->func = resample_shrink; + } + data->pos = 0; + rate_init(plugin); + data->old_src_frames = data->old_dst_frames = 0; + plugin->transfer = rate_transfer; + plugin->src_frames = rate_src_frames; + plugin->dst_frames = rate_dst_frames; + plugin->action = rate_action; + *r_plugin = plugin; + return 0; +} diff -Nru linux/sound/core/oss/route.c linux-2.4.19-pre5-mjc/sound/core/oss/route.c --- linux/sound/core/oss/route.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/oss/route.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,581 @@ +/* + * Attenuated route Plug-In + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "pcm_plugin.h" + +/* The best possible hack to support missing optimization in gcc 2.7.2.3 */ +#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0 +#define div(a) a /= ROUTE_PLUGIN_RESOLUTION +#elif ROUTE_PLUGIN_RESOLUTION == 16 +#define div(a) a >>= 4 +#else +#error "Add some code here" +#endif + +typedef struct ttable_dst ttable_dst_t; +typedef struct route_private_data route_t; + +typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames); + +typedef struct { + int channel; + int as_int; +#if ROUTE_PLUGIN_USE_FLOAT + float as_float; +#endif +} ttable_src_t; + +struct ttable_dst { + int att; /* Attenuated */ + unsigned int nsrcs; + ttable_src_t* srcs; + route_channel_f func; +}; + +struct route_private_data { + enum {R_UINT32=0, R_UINT64=1, R_FLOAT=2} sum_type; + int get, put; + int conv; + int src_sample_size; + ttable_dst_t ttable[0]; +}; + +typedef union { + u_int32_t as_uint32; + u_int64_t as_uint64; +#if ROUTE_PLUGIN_USE_FLOAT + float as_float; +#endif +} sum_t; + + +static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames) +{ + if (dst_channel->wanted) + snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format); + dst_channel->enabled = 0; +} + +static void route_to_channel_from_one(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + route_t *data = (route_t *)plugin->extra_data; + void *conv; + const snd_pcm_plugin_channel_t *src_channel = 0; + unsigned int srcidx; + char *src, *dst; + int src_step, dst_step; + for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { + src_channel = &src_channels[ttable->srcs[srcidx].channel]; + if (src_channel->area.addr != NULL) + break; + } + if (srcidx == ttable->nsrcs) { + route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); + return; + } + + dst_channel->enabled = 1; + conv = conv_labels[data->conv]; + src = src_channel->area.addr + src_channel->area.first / 8; + src_step = src_channel->area.step / 8; + dst = dst_channel->area.addr + dst_channel->area.first / 8; + dst_step = dst_channel->area.step / 8; + while (frames-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } +} + +static void route_to_channel(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channel, + ttable_dst_t* ttable, snd_pcm_uframes_t frames) +{ +#define GET_U_LABELS +#define PUT_U32_LABELS +#include "plugin_ops.h" +#undef GET_U_LABELS +#undef PUT_U32_LABELS + static void *zero_labels[3] = { &&zero_int32, &&zero_int64, +#if ROUTE_PLUGIN_USE_FLOAT + &&zero_float +#endif + }; + /* sum_type att */ + static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att, + &&add_int64_noatt, &&add_int64_att, +#if ROUTE_PLUGIN_USE_FLOAT + &&add_float_noatt, &&add_float_att +#endif + }; + /* sum_type att shift */ + static void *norm_labels[3 * 2 * 4] = { 0, + &&norm_int32_8_noatt, + &&norm_int32_16_noatt, + &&norm_int32_24_noatt, + 0, + &&norm_int32_8_att, + &&norm_int32_16_att, + &&norm_int32_24_att, + &&norm_int64_0_noatt, + &&norm_int64_8_noatt, + &&norm_int64_16_noatt, + &&norm_int64_24_noatt, + &&norm_int64_0_att, + &&norm_int64_8_att, + &&norm_int64_16_att, + &&norm_int64_24_att, +#if ROUTE_PLUGIN_USE_FLOAT + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, +#endif + }; + route_t *data = (route_t *)plugin->extra_data; + void *zero, *get, *add, *norm, *put_u32; + int nsrcs = ttable->nsrcs; + char *dst; + int dst_step; + char *srcs[nsrcs]; + int src_steps[nsrcs]; + ttable_src_t src_tt[nsrcs]; + u_int32_t sample = 0; + int srcidx, srcidx1 = 0; + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel]; + if (!src_channel->enabled) + continue; + srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8; + src_steps[srcidx1] = src_channel->area.step / 8; + src_tt[srcidx1] = ttable->srcs[srcidx]; + srcidx1++; + } + nsrcs = srcidx1; + if (nsrcs == 0) { + route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); + return; + } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) { + route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames); + return; + } + + dst_channel->enabled = 1; + zero = zero_labels[data->sum_type]; + get = get_u_labels[data->get]; + add = add_labels[data->sum_type * 2 + ttable->att]; + norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size]; + put_u32 = put_u32_labels[data->put]; + dst = dst_channel->area.addr + dst_channel->area.first / 8; + dst_step = dst_channel->area.step / 8; + + while (frames-- > 0) { + ttable_src_t *ttp = src_tt; + sum_t sum; + + /* Zero sum */ + goto *zero; + zero_int32: + sum.as_uint32 = 0; + goto zero_end; + zero_int64: + sum.as_uint64 = 0; + goto zero_end; +#if ROUTE_PLUGIN_USE_FLOAT + zero_float: + sum.as_float = 0.0; + goto zero_end; +#endif + zero_end: + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + char *src = srcs[srcidx]; + + /* Get sample */ + goto *get; +#define GET_U_END after_get +#include "plugin_ops.h" +#undef GET_U_END + after_get: + + /* Sum */ + goto *add; + add_int32_att: + sum.as_uint32 += sample * ttp->as_int; + goto after_sum; + add_int32_noatt: + if (ttp->as_int) + sum.as_uint32 += sample; + goto after_sum; + add_int64_att: + sum.as_uint64 += (u_int64_t) sample * ttp->as_int; + goto after_sum; + add_int64_noatt: + if (ttp->as_int) + sum.as_uint64 += sample; + goto after_sum; +#if ROUTE_PLUGIN_USE_FLOAT + add_float_att: + sum.as_float += sample * ttp->as_float; + goto after_sum; + add_float_noatt: + if (ttp->as_int) + sum.as_float += sample; + goto after_sum; +#endif + after_sum: + srcs[srcidx] += src_steps[srcidx]; + ttp++; + } + + /* Normalization */ + goto *norm; + norm_int32_8_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_att: + sum.as_uint64 <<= 8; + norm_int64_0_att: + div(sum.as_uint64); + goto norm_int; + + norm_int32_16_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_att: + sum.as_uint64 <<= 16; + div(sum.as_uint64); + goto norm_int; + + norm_int32_24_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_att: + sum.as_uint64 <<= 24; + div(sum.as_uint64); + goto norm_int; + + norm_int32_8_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_noatt: + sum.as_uint64 <<= 8; + goto norm_int; + + norm_int32_16_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_noatt: + sum.as_uint64 <<= 16; + goto norm_int; + + norm_int32_24_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_noatt: + sum.as_uint64 <<= 24; + goto norm_int; + + norm_int64_0_noatt: + norm_int: + if (sum.as_uint64 > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_uint64; + goto after_norm; + +#if ROUTE_PLUGIN_USE_FLOAT + norm_float_8: + sum.as_float *= 1 << 8; + goto norm_float; + norm_float_16: + sum.as_float *= 1 << 16; + goto norm_float; + norm_float_24: + sum.as_float *= 1 << 24; + goto norm_float; + norm_float_0: + norm_float: + sum.as_float = floor(sum.as_float + 0.5); + if (sum.as_float > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_float; + goto after_norm; +#endif + after_norm: + + /* Put sample */ + goto *put_u32; +#define PUT_U32_END after_put_u32 +#include "plugin_ops.h" +#undef PUT_U32_END + after_put_u32: + + dst += dst_step; + } +} + +int route_src_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int schannels = plugin->src_format.channels; + int dchannels = plugin->dst_format.channels; + bitset_t *vmask = plugin->src_vmask; + int channel; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, schannels); + for (channel = 0; channel < dchannels; channel++, dp++) { + unsigned int src; + ttable_src_t *sp; + if (!bitset_get(dst_vmask, channel)) + continue; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) + bitset_set(vmask, sp->channel); + } + *src_vmask = vmask; + return 0; +} + +int route_dst_channels_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int dchannels = plugin->dst_format.channels; + bitset_t *vmask = plugin->dst_vmask; + int channel; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, dchannels); + for (channel = 0; channel < dchannels; channel++, dp++) { + unsigned int src; + ttable_src_t *sp; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) { + if (bitset_get(src_vmask, sp->channel)) { + bitset_set(vmask, channel); + break; + } + } + } + *dst_vmask = vmask; + return 0; +} + +static void route_free(snd_pcm_plugin_t *plugin) +{ + route_t *data = (route_t *)plugin->extra_data; + unsigned int dst_channel; + for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { + if (data->ttable[dst_channel].srcs != NULL) + kfree(data->ttable[dst_channel].srcs); + } +} + +static int route_load_ttable(snd_pcm_plugin_t *plugin, + const route_ttable_entry_t* src_ttable) +{ + route_t *data; + unsigned int src_channel, dst_channel; + const route_ttable_entry_t *sptr; + ttable_dst_t *dptr; + if (src_ttable == NULL) + return 0; + data = (route_t *)plugin->extra_data; + dptr = data->ttable; + sptr = src_ttable; + plugin->private_free = route_free; + for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { + route_ttable_entry_t t = 0; + int att = 0; + int nsrcs = 0; + ttable_src_t srcs[plugin->src_format.channels]; + for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) { + snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO); + if (*sptr != 0) { + srcs[nsrcs].channel = src_channel; +#if ROUTE_PLUGIN_USE_FLOAT + /* Also in user space for non attenuated */ + srcs[nsrcs].as_int = (*sptr == FULL ? ROUTE_PLUGIN_RESOLUTION : 0); + srcs[nsrcs].as_float = *sptr; +#else + srcs[nsrcs].as_int = *sptr; +#endif + if (*sptr != FULL) + att = 1; + t += *sptr; + nsrcs++; + } + sptr++; + } + dptr->att = att; + dptr->nsrcs = nsrcs; + if (nsrcs == 0) + dptr->func = route_to_channel_from_zero; + else if (nsrcs == 1 && !att) + dptr->func = route_to_channel_from_one; + else + dptr->func = route_to_channel; + if (nsrcs > 0) { + int srcidx; + dptr->srcs = snd_kcalloc(nsrcs * sizeof(*srcs), GFP_KERNEL); + for(srcidx = 0; srcidx < nsrcs; srcidx++) + dptr->srcs[srcidx] = srcs[srcidx]; + } else + dptr->srcs = 0; + dptr++; + } + return 0; +} + +static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_channel_t *src_channels, + snd_pcm_plugin_channel_t *dst_channels, + snd_pcm_uframes_t frames) +{ + route_t *data; + int src_nchannels, dst_nchannels; + int dst_channel; + ttable_dst_t *ttp; + snd_pcm_plugin_channel_t *dvp; + + snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); + if (frames == 0) + return 0; + data = (route_t *)plugin->extra_data; + + src_nchannels = plugin->src_format.channels; + dst_nchannels = plugin->dst_format.channels; + +#ifdef CONFIG_SND_DEBUG + { + int src_channel; + for (src_channel = 0; src_channel < src_nchannels; ++src_channel) { + snd_assert(src_channels[src_channel].area.first % 8 == 0 || + src_channels[src_channel].area.step % 8 == 0, + return -ENXIO); + } + for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { + snd_assert(dst_channels[dst_channel].area.first % 8 == 0 || + dst_channels[dst_channel].area.step % 8 == 0, + return -ENXIO); + } + } +#endif + + ttp = data->ttable; + dvp = dst_channels; + for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { + ttp->func(plugin, src_channels, dvp, ttp, frames); + dvp++; + ttp++; + } + return frames; +} + +int getput_index(int format) +{ + int sign, width, endian; + sign = !snd_pcm_format_signed(format); + width = snd_pcm_format_width(format) / 8 - 1; +#ifdef SNDRV_LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(format); +#else + endian = snd_pcm_format_little_endian(format); +#endif + if (endian < 0) + endian = 0; + return width * 4 + endian * 2 + sign; +} + +int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug, + snd_pcm_plugin_format_t *src_format, + snd_pcm_plugin_format_t *dst_format, + route_ttable_entry_t *ttable, + snd_pcm_plugin_t **r_plugin) +{ + route_t *data; + snd_pcm_plugin_t *plugin; + int err; + + snd_assert(r_plugin != NULL, return -ENXIO); + *r_plugin = NULL; + snd_assert(src_format->rate == dst_format->rate, return -ENXIO); + snd_assert(snd_pcm_format_linear(src_format->format) != 0 && + snd_pcm_format_linear(dst_format->format) != 0, + return -ENXIO); + + err = snd_pcm_plugin_build(plug, "attenuated route conversion", + src_format, dst_format, + sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels, + &plugin); + if (err < 0) + return err; + + data = (route_t *) plugin->extra_data; + + data->get = getput_index(src_format->format); + data->put = getput_index(dst_format->format); + data->conv = conv_index(src_format->format, dst_format->format); + +#if ROUTE_PLUGIN_USE_FLOAT + data->sum_type = R_FLOAT; +#else + if (snd_pcm_format_width(src_format->format) == 32) + data->sum_type = R_UINT64; + else + data->sum_type = R_UINT32; +#endif + data->src_sample_size = snd_pcm_format_width(src_format->format) / 8; + + if ((err = route_load_ttable(plugin, ttable)) < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + plugin->transfer = route_transfer; + plugin->src_channels_mask = route_src_channels_mask; + plugin->dst_channels_mask = route_dst_channels_mask; + *r_plugin = plugin; + return 0; +} diff -Nru linux/sound/core/pcm.c linux-2.4.19-pre5-mjc/sound/core/pcm.c --- linux/sound/core/pcm.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/pcm.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,994 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara "); +MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); +MODULE_LICENSE("GPL"); + +snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES]; +static LIST_HEAD(snd_pcm_notify_list); +static DECLARE_MUTEX(register_mutex); + +int snd_pcm_free(snd_pcm_t *pcm); +static int snd_pcm_dev_free(snd_device_t *device); +static int snd_pcm_dev_register(snd_device_t *device); +static int snd_pcm_dev_unregister(snd_device_t *device); + +void snd_pcm_lock(int xup) +{ + if (!xup) { + down(®ister_mutex); + } else { + up(®ister_mutex); + } +} + +static int snd_pcm_control_ioctl(snd_card_t * card, + snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_PCM_DEVICES; + switch (cmd) { + case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_PCM_DEVICES) { + if (snd_pcm_devices[tmp + device]) + break; + device++; + } + if (device == SNDRV_PCM_DEVICES) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_PCM_INFO: + { + snd_pcm_info_t *info = (snd_pcm_info_t *)arg; + unsigned int device, subdevice; + snd_pcm_stream_t stream; + snd_pcm_t *pcm; + snd_pcm_str_t *pstr; + snd_pcm_substream_t *substream; + if (get_user(device, &info->device)) + return -EFAULT; + if (device >= SNDRV_PCM_DEVICES) + return -ENXIO; + pcm = snd_pcm_devices[tmp + device]; + if (pcm == NULL) + return -ENXIO; + if (get_user(stream, &info->stream)) + return -EFAULT; + if (stream < 0 || stream > 1) + return -EINVAL; + pstr = &pcm->streams[stream]; + if (pstr->substream_count == 0) + return -ENOENT; + if (get_user(subdevice, &info->subdevice)) + return -EFAULT; + if (subdevice >= pstr->substream_count) + return -ENXIO; + for (substream = pstr->substream; substream; substream = substream->next) + if (substream->number == subdevice) + break; + if (substream == NULL) + return -ENXIO; + return snd_pcm_info_user(substream, info); + } + case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: + { + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + control->prefer_pcm_subdevice = val; + return 0; + } + } + return -ENOIOCTLCMD; +} +#define STATE(v) [SNDRV_PCM_STATE_##v] = #v +#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v +#define READY(v) [SNDRV_PCM_READY_##v] = #v +#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v +#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v +#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v +#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v +#define START(v) [SNDRV_PCM_START_##v] = #v +#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v +#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v + +char *snd_pcm_stream_names[] = { + STREAM(PLAYBACK), + STREAM(CAPTURE), +}; + +char *snd_pcm_state_names[] = { + STATE(OPEN), + STATE(SETUP), + STATE(PREPARED), + STATE(RUNNING), + STATE(XRUN), + STATE(PAUSED), +}; + +char *snd_pcm_access_names[] = { + ACCESS(MMAP_INTERLEAVED), + ACCESS(MMAP_NONINTERLEAVED), + ACCESS(MMAP_COMPLEX), + ACCESS(RW_INTERLEAVED), + ACCESS(RW_NONINTERLEAVED), +}; + +char *snd_pcm_format_names[] = { + FORMAT(S8), + FORMAT(U8), + FORMAT(S16_LE), + FORMAT(S16_BE), + FORMAT(U16_LE), + FORMAT(U16_BE), + FORMAT(S24_LE), + FORMAT(S24_BE), + FORMAT(U24_LE), + FORMAT(U24_BE), + FORMAT(S32_LE), + FORMAT(S32_BE), + FORMAT(U32_LE), + FORMAT(U32_BE), + FORMAT(FLOAT_LE), + FORMAT(FLOAT_BE), + FORMAT(FLOAT64_LE), + FORMAT(FLOAT64_BE), + FORMAT(IEC958_SUBFRAME_LE), + FORMAT(IEC958_SUBFRAME_BE), + FORMAT(MU_LAW), + FORMAT(A_LAW), + FORMAT(IMA_ADPCM), + FORMAT(MPEG), + FORMAT(GSM), + FORMAT(SPECIAL), +}; + +char *snd_pcm_subformat_names[] = { + SUBFORMAT(STD), +}; + +char *snd_pcm_tstamp_mode_names[] = { + TSTAMP(NONE), + TSTAMP(MMAP), +}; + +const char *snd_pcm_stream_name(snd_pcm_stream_t stream) +{ + snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return 0); + return snd_pcm_stream_names[stream]; +} + +const char *snd_pcm_access_name(snd_pcm_access_t access) +{ + snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return 0); + return snd_pcm_access_names[access]; +} + +const char *snd_pcm_format_name(snd_pcm_format_t format) +{ + snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return 0); + return snd_pcm_format_names[format]; +} + +const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) +{ + snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return 0); + return snd_pcm_subformat_names[subformat]; +} + +const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode) +{ + snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return 0); + return snd_pcm_tstamp_mode_names[mode]; +} + +const char *snd_pcm_state_name(snd_pcm_state_t state) +{ + snd_assert(state <= SNDRV_PCM_STATE_LAST, return 0); + return snd_pcm_state_names[state]; +} + +#ifdef CONFIG_SND_OSSEMUL +#include +const char *snd_pcm_oss_format_name(int format) +{ + switch (format) { + case AFMT_MU_LAW: + return "MU_LAW"; + case AFMT_A_LAW: + return "A_LAW"; + case AFMT_IMA_ADPCM: + return "IMA_ADPCM"; + case AFMT_U8: + return "U8"; + case AFMT_S16_LE: + return "S16_LE"; + case AFMT_S16_BE: + return "S16_BE"; + case AFMT_S8: + return "S8"; + case AFMT_U16_LE: + return "U16_LE"; + case AFMT_U16_BE: + return "U16_BE"; + case AFMT_MPEG: + return "MPEG"; + default: + return "unknown"; + } +} +#endif + + +static void snd_pcm_proc_info_read(snd_pcm_substream_t *substream, snd_info_buffer_t *buffer) +{ + snd_pcm_info_t info; + int err; + snd_runtime_check(substream, return); + err = snd_pcm_info(substream, &info); + if (err < 0) { + snd_iprintf(buffer, "error %d\n", err); + return; + } + snd_iprintf(buffer, "card: %d\n", info.card); + snd_iprintf(buffer, "device: %d\n", info.device); + snd_iprintf(buffer, "subdevice: %d\n", info.subdevice); + snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info.stream)); + snd_iprintf(buffer, "id: %s\n", info.id); + snd_iprintf(buffer, "name: %s\n", info.name); + snd_iprintf(buffer, "subname: %s\n", info.subname); + snd_iprintf(buffer, "class: %d\n", info.dev_class); + snd_iprintf(buffer, "subclass: %d\n", info.dev_subclass); + snd_iprintf(buffer, "subdevices_count: %d\n", info.subdevices_count); + snd_iprintf(buffer, "subdevices_avail: %d\n", info.subdevices_avail); +} + +static void snd_pcm_stream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_proc_info_read(((snd_pcm_str_t *)entry->private_data)->substream, buffer); +} + +static void snd_pcm_substream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_proc_info_read((snd_pcm_substream_t *)entry->private_data, buffer); +} + +static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_iprintf(buffer, "no setup\n"); + spin_unlock_irq(&runtime->lock); + return; + } + snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); + snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); + snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); + snd_iprintf(buffer, "channels: %u\n", runtime->channels); + snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); + snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); + snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); + snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); + snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); + snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); + snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); + snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); + } +#endif + spin_unlock_irq(&runtime->lock); +} + +static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_iprintf(buffer, "no setup\n"); + spin_unlock_irq(&runtime->lock); + return; + } + snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); + snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); + snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min); + snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); + snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align); + snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); + snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); + snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); + snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); + snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); + spin_unlock_irq(&runtime->lock); +} + +static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_status_t status; + int err; + if (!runtime) { + snd_iprintf(buffer, "closed\n"); + return; + } + err = snd_pcm_status(substream, &status); + if (err < 0) { + snd_iprintf(buffer, "error %d\n", err); + return; + } + snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); + snd_iprintf(buffer, "trigger_time: %ld.%06ld\n", + status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_usec); + snd_iprintf(buffer, "tstamp : %ld.%06ld\n", + status.tstamp.tv_sec, status.tstamp.tv_usec); + snd_iprintf(buffer, "delay : %ld\n", status.delay); + snd_iprintf(buffer, "avail : %ld\n", status.avail); + snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); + snd_iprintf(buffer, "-----\n"); + snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); + snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); +} + +static int snd_pcm_stream_proc_init(snd_pcm_str_t *pstr) +{ + snd_pcm_t *pcm = pstr->pcm; + snd_info_entry_t *entry; + char name[16]; + + sprintf(name, "pcm%i%c", pcm->device, + pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); + if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + pstr->proc_root = entry; + + if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_stream_proc_info_read; + entry->private_data = pstr; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + pstr->proc_info_entry = entry; + + return 0; +} + +static int snd_pcm_stream_proc_done(snd_pcm_str_t *pstr) +{ + if (pstr->proc_info_entry) { + snd_info_unregister(pstr->proc_info_entry); + pstr->proc_info_entry = NULL; + } + if (pstr->proc_root) { + snd_info_unregister(pstr->proc_root); + pstr->proc_root = NULL; + } + return 0; +} + +static int snd_pcm_substream_proc_init(snd_pcm_substream_t *substream) +{ + snd_info_entry_t *entry; + snd_card_t *card; + char name[16]; + + card = substream->pcm->card; + + sprintf(name, "sub%i", substream->number); + if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) + return -ENOMEM; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + substream->proc_root = entry; + + if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_info_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_info_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_hw_params_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_hw_params_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_sw_params_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_sw_params_entry = entry; + + if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->mode = S_IFREG | S_IRUGO; + entry->c.text.read_size = 256; + entry->c.text.read = snd_pcm_substream_proc_status_read; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_status_entry = entry; + + return 0; +} + +static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream) +{ + if (substream->proc_info_entry) { + snd_info_unregister(substream->proc_info_entry); + substream->proc_info_entry = 0; + } + if (substream->proc_hw_params_entry) { + snd_info_unregister(substream->proc_hw_params_entry); + substream->proc_hw_params_entry = 0; + } + if (substream->proc_sw_params_entry) { + snd_info_unregister(substream->proc_sw_params_entry); + substream->proc_sw_params_entry = 0; + } + if (substream->proc_status_entry) { + snd_info_unregister(substream->proc_status_entry); + substream->proc_status_entry = 0; + } + if (substream->proc_root) { + snd_info_unregister(substream->proc_root); + substream->proc_root = 0; + } + return 0; +} + +static int snd_pcm_new_stream(snd_pcm_t *pcm, + snd_pcm_str_t *pstr, + int substream_count, + int stream) +{ + int idx, err; + snd_pcm_substream_t *substream, *prev; + +#ifdef CONFIG_SND_OSSEMUL + init_MUTEX(&pstr->oss.setup_mutex); +#endif + pstr->stream = stream; + pstr->pcm = pcm; + pstr->substream_count = substream_count; + pstr->reg = &snd_pcm_reg[stream]; + if (substream_count > 0) { + err = snd_pcm_stream_proc_init(pstr); + if (err < 0) + return err; + } + prev = NULL; + for (idx = 0, prev = NULL; idx < substream_count; idx++) { + substream = snd_magic_kcalloc(snd_pcm_substream_t, 0, GFP_KERNEL); + if (substream == NULL) + return -ENOMEM; + substream->pcm = pcm; + substream->pstr = pstr; + substream->number = idx; + substream->stream = stream; + sprintf(substream->name, "subdevice #%i", idx); + substream->buffer_bytes_max = UINT_MAX; + if (prev == NULL) + pstr->substream = substream; + else + prev->next = substream; + substream->link_next = substream; + substream->link_prev = substream; + err = snd_pcm_substream_proc_init(substream); + if (err < 0) { + snd_magic_kfree(substream); + return err; + } + substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA; + substream->dma_private = NULL; + spin_lock_init(&substream->timer_lock); + prev = substream; + } + return 0; +} + +int snd_pcm_new(snd_card_t * card, char *id, int device, + int playback_count, int capture_count, + snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + static snd_device_ops_t ops = { + dev_free: snd_pcm_dev_free, + dev_register: snd_pcm_dev_register, + dev_unregister: snd_pcm_dev_unregister + }; + + snd_assert(rpcm != NULL, return -EINVAL); + *rpcm = NULL; + snd_assert(card != NULL, return -ENXIO); + pcm = snd_magic_kcalloc(snd_pcm_t, 0, GFP_KERNEL); + if (pcm == NULL) + return -ENOMEM; + pcm->card = card; + pcm->device = device; + if (id) { + strncpy(pcm->id, id, sizeof(pcm->id) - 1); + } + if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK], playback_count, SNDRV_PCM_STREAM_PLAYBACK)) < 0) { + snd_pcm_free(pcm); + return err; + } + if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE], capture_count, SNDRV_PCM_STREAM_CAPTURE)) < 0) { + snd_pcm_free(pcm); + return err; + } + init_MUTEX(&pcm->open_mutex); + init_waitqueue_head(&pcm->open_wait); + if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { + snd_pcm_free(pcm); + return err; + } + *rpcm = pcm; + return 0; +} + +static void snd_pcm_free_stream(snd_pcm_str_t * pstr) +{ + snd_pcm_substream_t *substream, *substream_next; +#ifdef CONFIG_SND_OSSEMUL + snd_pcm_oss_setup_t *setup, *setupn; +#endif + substream = pstr->substream; + while (substream) { + substream_next = substream->next; + snd_pcm_substream_proc_done(substream); + snd_magic_kfree(substream); + substream = substream_next; + } + snd_pcm_stream_proc_done(pstr); +#ifdef CONFIG_SND_OSSEMUL + for (setup = pstr->oss.setup_list; setup; setup = setupn) { + setupn = setup->next; + kfree(setup->task_name); + kfree(setup); + } +#endif +} + +int snd_pcm_free(snd_pcm_t *pcm) +{ + snd_assert(pcm != NULL, return -ENXIO); + if (pcm->private_free) + pcm->private_free(pcm); + snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); + snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); + snd_magic_kfree(pcm); + return 0; +} + +int snd_pcm_dev_free(snd_device_t *device) +{ + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + return snd_pcm_free(pcm); +} + +static void snd_pcm_tick_timer_func(unsigned long data) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t*) data; + snd_pcm_tick_elapsed(substream); +} + +int snd_pcm_open_substream(snd_pcm_t *pcm, int stream, + snd_pcm_substream_t **rsubstream) +{ + snd_pcm_str_t * pstr; + snd_pcm_substream_t * substream; + snd_pcm_runtime_t * runtime; + snd_ctl_file_t *kctl; + snd_card_t *card; + struct list_head *list; + int prefer_subdevice = -1; + size_t size; + + snd_assert(rsubstream != NULL, return -EINVAL); + *rsubstream = NULL; + snd_assert(pcm != NULL, return -ENXIO); + pstr = &pcm->streams[stream]; + if (pstr->substream == NULL) + return -ENODEV; + + card = pcm->card; + read_lock(&card->control_rwlock); + list_for_each(list, &card->ctl_files) { + kctl = snd_ctl_file(list); + if (kctl->pid == current->pid) { + prefer_subdevice = kctl->prefer_pcm_subdevice; + break; + } + } + read_unlock(&card->control_rwlock); + + if (pstr->substream_count == 0) + return -ENODEV; + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; + } + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; + } + } + break; + default: + return -EINVAL; + } + + if (prefer_subdevice >= 0) { + for (substream = pstr->substream; substream; substream = substream->next) + if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) + goto __ok; + } + for (substream = pstr->substream; substream; substream = substream->next) + if (!SUBSTREAM_BUSY(substream)) + break; + __ok: + if (substream == NULL) + return -EAGAIN; + + runtime = snd_kcalloc(sizeof(snd_pcm_runtime_t), GFP_KERNEL); + if (runtime == NULL) + return -ENOMEM; + + size = PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)); + runtime->status = snd_malloc_pages(size, GFP_KERNEL); + if (runtime->status == NULL) { + kfree(runtime); + return -ENOMEM; + } + + size = PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)); + runtime->control = snd_malloc_pages(size, GFP_KERNEL); + if (runtime->control == NULL) { + kfree((void *)runtime->status); + kfree(runtime); + return -ENOMEM; + } + + memset((void*)runtime->status, 0, size); + memset((void*)runtime->control, 0, size); + + init_waitqueue_head(&runtime->sleep); + spin_lock_init(&runtime->lock); + atomic_set(&runtime->mmap_count, 0); + init_timer(&runtime->tick_timer); + runtime->tick_timer.function = snd_pcm_tick_timer_func; + runtime->tick_timer.data = (unsigned long) substream; + + runtime->status->state = SNDRV_PCM_STATE_OPEN; + + substream->runtime = runtime; + pstr->substream_opened++; + *rsubstream = substream; + return 0; +} + +void snd_pcm_release_substream(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t * runtime; + substream->file = NULL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + if (runtime->private_free != NULL) + runtime->private_free(runtime); + snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))); + snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))); + kfree(runtime->hw_constraints.rules); + kfree(runtime); + substream->runtime = NULL; + substream->pstr->substream_opened--; +} + +int snd_pcm_dev_register(snd_device_t *device) +{ + int idx, cidx, err; + unsigned short minor; + snd_pcm_substream_t *substream; + struct list_head *list; + char str[16]; + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + + snd_assert(pcm != NULL && device != NULL, return -ENXIO); + snd_pcm_lock(0); + idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; + if (snd_pcm_devices[idx]) { + snd_pcm_lock(1); + return -EBUSY; + } + snd_pcm_devices[idx] = pcm; + for (cidx = 0; cidx < 2; cidx++) { + int devtype = -1; + if (pcm->streams[cidx].substream == NULL) + continue; + switch (cidx) { + case SNDRV_PCM_STREAM_PLAYBACK: + sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); + minor = SNDRV_MINOR_PCM_PLAYBACK + idx; + devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; + break; + case SNDRV_PCM_STREAM_CAPTURE: + sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); + minor = SNDRV_MINOR_PCM_CAPTURE + idx; + devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; + break; + } + if ((err = snd_register_device(devtype, pcm->card, pcm->device, pcm->streams[cidx].reg, str)) < 0) { + snd_pcm_devices[idx] = NULL; + snd_pcm_lock(1); + return err; + } + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) + snd_pcm_timer_init(substream); + } + list_for_each(list, &snd_pcm_notify_list) { + snd_pcm_notify_t *notify; + notify = list_entry(list, snd_pcm_notify_t, list); + if (notify->n_register) + notify->n_register(-1 /* idx + SNDRV_MINOR_PCM */, pcm); + } + snd_pcm_lock(1); + return 0; +} + +static int snd_pcm_dev_unregister(snd_device_t *device) +{ + int idx, cidx, devtype; + snd_pcm_substream_t *substream; + struct list_head *list; + snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + + snd_assert(pcm != NULL, return -ENXIO); + snd_pcm_lock(0); + idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device; + if (snd_pcm_devices[idx] != pcm) { + snd_pcm_lock(1); + return -EINVAL; + } + for (cidx = 0; cidx < 2; cidx++) { + devtype = -1; + switch (cidx) { + case SNDRV_PCM_STREAM_PLAYBACK: + devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; + break; + case SNDRV_PCM_STREAM_CAPTURE: + devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; + break; + } + snd_unregister_device(devtype, pcm->card, pcm->device); + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) + snd_pcm_timer_done(substream); + } + list_for_each(list, &snd_pcm_notify_list) { + snd_pcm_notify_t *notify; + notify = list_entry(list, snd_pcm_notify_t, list); + if (notify->n_unregister) + notify->n_unregister(-1 /* SNDRV_MINOR_PCM + idx */, pcm); + } + snd_pcm_devices[idx] = NULL; + snd_pcm_lock(1); + return snd_pcm_free(pcm); +} + +int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree) +{ + int idx; + + snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL); + snd_pcm_lock(0); + if (nfree) { + list_del(¬ify->list); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + if (snd_pcm_devices[idx] == NULL) + continue; + notify->n_unregister(-1 /* idx + SNDRV_MINOR_PCM */, + snd_pcm_devices[idx]); + } + } else { + list_add_tail(¬ify->list, &snd_pcm_notify_list); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + if (snd_pcm_devices[idx] == NULL) + continue; + notify->n_register(-1 /* idx + SNDRV_MINOR_PCM */, + snd_pcm_devices[idx]); + } + } + snd_pcm_lock(1); + return 0; +} + +/* + * Info interface + */ + +static void snd_pcm_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx; + snd_pcm_t *pcm; + + down(®ister_mutex); + for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) { + pcm = snd_pcm_devices[idx]; + if (pcm == NULL) + continue; + snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES, + idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name); + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) + snd_iprintf(buffer, " : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) + snd_iprintf(buffer, " : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); + snd_iprintf(buffer, "\n"); + } + up(®ister_mutex); +} + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_pcm_proc_entry = NULL; + +static int __init alsa_pcm_init(void) +{ + snd_info_entry_t *entry; + + snd_ctl_register_ioctl(snd_pcm_control_ioctl); + if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = SNDRV_CARDS * SNDRV_PCM_DEVICES * 128; + entry->c.text.read = snd_pcm_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_pcm_proc_entry = entry; + return 0; +} + +static void __exit alsa_pcm_exit(void) +{ + snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); + if (snd_pcm_proc_entry) { + snd_info_unregister(snd_pcm_proc_entry); + snd_pcm_proc_entry = NULL; + } +} + +module_init(alsa_pcm_init) +module_exit(alsa_pcm_exit) + +EXPORT_SYMBOL(snd_pcm_lock); +EXPORT_SYMBOL(snd_pcm_devices); +EXPORT_SYMBOL(snd_pcm_new); +EXPORT_SYMBOL(snd_pcm_notify); +EXPORT_SYMBOL(snd_pcm_open_substream); +EXPORT_SYMBOL(snd_pcm_release_substream); + /* pcm_native.c */ +EXPORT_SYMBOL(snd_pcm_start); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_pcm_suspend); +EXPORT_SYMBOL(snd_pcm_suspend_all); +#endif +EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl); +EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl); +EXPORT_SYMBOL(snd_pcm_kernel_ioctl); +EXPORT_SYMBOL(snd_pcm_open); +EXPORT_SYMBOL(snd_pcm_release); +EXPORT_SYMBOL(snd_pcm_playback_poll); +EXPORT_SYMBOL(snd_pcm_capture_poll); +EXPORT_SYMBOL(snd_pcm_mmap_data); + /* pcm_misc.c */ +EXPORT_SYMBOL(snd_pcm_format_signed); +EXPORT_SYMBOL(snd_pcm_format_unsigned); +EXPORT_SYMBOL(snd_pcm_format_linear); +EXPORT_SYMBOL(snd_pcm_format_little_endian); +EXPORT_SYMBOL(snd_pcm_format_big_endian); +EXPORT_SYMBOL(snd_pcm_format_width); +EXPORT_SYMBOL(snd_pcm_format_physical_width); +EXPORT_SYMBOL(snd_pcm_format_size); +EXPORT_SYMBOL(snd_pcm_format_silence_64); +EXPORT_SYMBOL(snd_pcm_format_set_silence); +EXPORT_SYMBOL(snd_pcm_build_linear_format); diff -Nru linux/sound/core/pcm_lib.c linux-2.4.19-pre5-mjc/sound/core/pcm_lib.c --- linux/sound/core/pcm_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/pcm_lib.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2376 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void snd_pcm_playback_silence(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frames, ofs; + snd_pcm_sframes_t noise_dist; + if (runtime->silenced_start != runtime->control->appl_ptr) { + snd_pcm_sframes_t n = runtime->control->appl_ptr - runtime->silenced_start; + if (n < 0) + n += runtime->boundary; + if ((snd_pcm_uframes_t)n < runtime->silenced_size) + runtime->silenced_size -= n; + else + runtime->silenced_size = 0; + runtime->silenced_start = runtime->control->appl_ptr; + } + if (runtime->silenced_size == runtime->buffer_size) + return; + snd_assert(runtime->silenced_size <= runtime->buffer_size, return); + noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size; + if (noise_dist > (snd_pcm_sframes_t) runtime->silence_threshold) + return; + frames = runtime->silence_threshold - noise_dist; + if (frames < runtime->silence_size) + frames = runtime->silence_size; + if (runtime->silenced_size + frames > runtime->buffer_size) + frames = runtime->buffer_size - runtime->silenced_size; + ofs = runtime->silenced_start % runtime->buffer_size + runtime->silenced_size; + if (ofs >= runtime->buffer_size) + ofs -= runtime->buffer_size; + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + if (substream->ops->silence) { + int err; + err = substream->ops->silence(substream, -1, ofs, frames); + snd_assert(err >= 0, ); + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); + snd_pcm_format_set_silence(runtime->format, hwbuf, frames * runtime->channels); + } + } else { + unsigned int c; + unsigned int channels = runtime->channels; + if (substream->ops->silence) { + for (c = 0; c < channels; ++c) { + int err; + err = substream->ops->silence(substream, c, ofs, frames); + snd_assert(err >= 0, ); + } + } else { + size_t dma_csize = runtime->dma_bytes / channels; + for (c = 0; c < channels; ++c) { + char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); + snd_pcm_format_set_silence(runtime->format, hwbuf, frames); + } + } + } + runtime->silenced_size += frames; +} + +int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt; + snd_pcm_uframes_t avail; + snd_pcm_sframes_t delta; + + old_hw_ptr = runtime->status->hw_ptr; + pos = substream->ops->pointer(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); +#ifdef CONFIG_SND_DEBUG + if (pos > runtime->buffer_size) { + snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + } else +#endif + snd_runtime_check(pos <= runtime->buffer_size, return 0); + + pos -= pos % runtime->min_align; + new_hw_ptr = runtime->hw_ptr_base + pos; + + hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; + + delta = hw_ptr_interrupt - new_hw_ptr; + if (delta > 0) { + if (delta < runtime->buffer_size / 2) { + snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); + return 0; + } + runtime->hw_ptr_base += runtime->buffer_size; + if (runtime->hw_ptr_base == runtime->boundary) + runtime->hw_ptr_base = 0; + new_hw_ptr = runtime->hw_ptr_base + pos; + } + runtime->status->hw_ptr = new_hw_ptr; + runtime->hw_ptr_interrupt = new_hw_ptr - (runtime->hw_ptr_base + pos) % runtime->period_size; + +#if 0 + if (hw_ptr_interrupt == runtime->boundary) + hw_ptr_interrupt = 0; + + if (runtime->hw_ptr_interrupt != hw_ptr_interrupt) + snd_printd("Lost interrupt: hw_ptr = %d expected %d\n", runtime->hw_ptr_interrupt, hw_ptr_interrupt); +#endif + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail > runtime->avail_max) + runtime->avail_max = avail; + if (avail >= runtime->stop_threshold) { + snd_pcm_stop(substream, + runtime->status->state == SNDRV_PCM_STATE_DRAINING ? + SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN); + return -EPIPE; + } + if (avail >= runtime->control->avail_min) + wake_up(&runtime->sleep); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + return 0; +} + +/* CAUTION: call it with irq disabled */ +int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; + snd_pcm_uframes_t avail; + snd_pcm_sframes_t delta; + old_hw_ptr = runtime->status->hw_ptr; + pos = substream->ops->pointer(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); +#ifdef CONFIG_SND_DEBUG + if (pos > runtime->buffer_size) { + snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); + } else +#endif + snd_runtime_check(pos <= runtime->buffer_size, return 0); + + pos -= pos % runtime->min_align; + new_hw_ptr = runtime->hw_ptr_base + pos; + + delta = old_hw_ptr - new_hw_ptr; + if (delta > 0) { + if (delta < runtime->buffer_size / 2) { + snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); + return 0; + } + runtime->hw_ptr_base += runtime->buffer_size; + if (runtime->hw_ptr_base == runtime->boundary) + runtime->hw_ptr_base = 0; + new_hw_ptr = runtime->hw_ptr_base + pos; + } + runtime->status->hw_ptr = new_hw_ptr; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail > runtime->avail_max) + runtime->avail_max = avail; + if (avail >= runtime->stop_threshold) { + snd_pcm_stop(substream, + runtime->status->state == SNDRV_PCM_STATE_DRAINING ? + SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN); + return -EPIPE; + } + if (avail >= runtime->control->avail_min) + wake_up(&runtime->sleep); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + return 0; +} + +/* + * Operations + */ + +void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) +{ + snd_pcm_str_t *stream = &pcm->streams[direction]; + snd_pcm_substream_t *substream; + + for (substream = stream->substream; substream != NULL; substream = substream->next) + substream->ops = ops; +} + +/* + * Sync + */ + +void snd_pcm_set_sync(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->sync.id32[0] = substream->pcm->card->number; + runtime->sync.id32[1] = -1; + runtime->sync.id32[2] = -1; + runtime->sync.id32[3] = -1; +} + +/* + * Standard ioctl routine + */ + +/* Code taken from alsa-lib */ +#define assert(a) snd_assert((a), return -EINVAL) + +static inline unsigned int div32(unsigned int a, unsigned int b, + unsigned int *r) +{ + if (b == 0) { + *r = 0; + return UINT_MAX; + } + *r = a % b; + return a / b; +} + +static inline unsigned int div_down(unsigned int a, unsigned int b) +{ + if (b == 0) + return UINT_MAX; + return a / b; +} + +static inline unsigned int div_up(unsigned int a, unsigned int b) +{ + unsigned int r; + unsigned int q; + if (b == 0) + return UINT_MAX; + q = div32(a, b, &r); + if (r) + ++q; + return q; +} + +static inline unsigned int mul(unsigned int a, unsigned int b) +{ + if (a == 0) + return 0; + if (div_down(UINT_MAX, a) < b) + return UINT_MAX; + return a * b; +} + +static inline unsigned int muldiv32(unsigned int a, unsigned int b, + unsigned int c, unsigned int *r) +{ + u_int64_t n = (u_int64_t) a * b; + if (c == 0) { + snd_assert(n > 0, ); + *r = 0; + return UINT_MAX; + } + div64_32(&n, c, r); + if (n >= UINT_MAX) { + *r = 0; + return UINT_MAX; + } + return n; +} + +int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->min < min) { + i->min = min; + i->openmin = openmin; + changed = 1; + } else if (i->min == min && !i->openmin && openmin) { + i->openmin = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->max > max) { + i->max = max; + i->openmax = openmax; + changed = 1; + } else if (i->max == max && !i->openmax && openmax) { + i->openmax = 1; + changed = 1; + } + if (i->integer) { + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +/* r <- v */ +int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) +{ + int changed = 0; + assert(!snd_interval_empty(i)); + if (i->min < v->min) { + i->min = v->min; + i->openmin = v->openmin; + changed = 1; + } else if (i->min == v->min && !i->openmin && v->openmin) { + i->openmin = 1; + changed = 1; + } + if (i->max > v->max) { + i->max = v->max; + i->openmax = v->openmax; + changed = 1; + } else if (i->max == v->max && !i->openmax && v->openmax) { + i->openmax = 1; + changed = 1; + } + if (!i->integer && v->integer) { + i->integer = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } else if (!i->openmin && !i->openmax && i->min == i->max) + i->integer = 1; + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +int snd_interval_refine_first(snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + if (snd_interval_single(i)) + return 0; + i->max = i->min; + i->openmax = i->openmin; + if (i->openmax) + i->max++; + return 1; +} + +int snd_interval_refine_last(snd_interval_t *i) +{ + assert(!snd_interval_empty(i)); + if (snd_interval_single(i)) + return 0; + i->min = i->max; + i->openmin = i->openmax; + if (i->openmin) + i->min--; + return 1; +} + +int snd_interval_refine_set(snd_interval_t *i, unsigned int val) +{ + snd_interval_t t; + t.empty = 0; + t.min = t.max = val; + t.openmin = t.openmax = 0; + t.integer = 1; + return snd_interval_refine(i, &t); +} + +void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) +{ + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = mul(a->min, b->min); + c->openmin = (a->openmin || b->openmin); + c->max = mul(a->max, b->max); + c->openmax = (a->openmax || b->openmax); + c->integer = (a->integer && b->integer); +} + +void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = div32(a->min, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = div32(a->max, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +/* a * b / k */ +void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, + unsigned int k, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, b->min, k, &r); + c->openmin = (r || a->openmin || b->openmin); + c->max = muldiv32(a->max, b->max, k, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmax); + c->integer = 0; +} + +/* a * k / b */ +void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, + const snd_interval_t *b, snd_interval_t *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, k, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = muldiv32(a->max, k, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +#undef assert +/* ---- */ + + +int snd_interval_ratnum(snd_interval_t *i, + unsigned int rats_count, ratnum_t *rats, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_diff, best_den; + unsigned int k; + snd_interval_t t; + int err; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->min; + int diff; + if (q == 0) + q = 1; + den = div_down(num, q); + if (den < rats[k].den_min) + continue; + if (den > rats[k].den_max) + den = rats[k].den_max; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den -= r; + } + diff = num - q * den; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->max; + int diff; + if (q == 0) { + i->empty = 1; + return -EINVAL; + } + den = div_up(num, q); + if (den > rats[k].den_max) + continue; + if (den < rats[k].den_min) + den = rats[k].den_min; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den += rats[k].den_step - r; + } + diff = q * den - num; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (nump) + *nump = best_num; + if (denp) + *denp = best_den; + } + return err; +} + +int snd_interval_ratden(snd_interval_t *i, + unsigned int rats_count, ratden_t *rats, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_diff, best_den; + unsigned int k; + snd_interval_t t; + int err; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->min; + int diff; + num = mul(q, den); + if (num > rats[k].num_max) + continue; + if (num < rats[k].num_min) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num += rats[k].num_step - r; + } + diff = num - q * den; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->max; + int diff; + num = mul(q, den); + if (num < rats[k].num_min) + continue; + if (num > rats[k].num_max) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num -= r; + } + diff = q * den - num; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (nump) + *nump = best_num; + if (denp) + *denp = best_den; + } + return err; +} + +int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask) +{ + unsigned int k; + int changed = 0; + for (k = 0; k < count; k++) { + if (mask && !(mask & (1 << k))) + continue; + if (i->min == list[k] && !i->openmin) + goto _l1; + if (i->min < list[k]) { + i->min = list[k]; + i->openmin = 0; + changed = 1; + goto _l1; + } + } + i->empty = 1; + return -EINVAL; + _l1: + for (k = count; k-- > 0;) { + if (mask && !(mask & (1 << k))) + continue; + if (i->max == list[k] && !i->openmax) + goto _l2; + if (i->max > list[k]) { + i->max = list[k]; + i->openmax = 0; + changed = 1; + goto _l2; + } + } + i->empty = 1; + return -EINVAL; + _l2: + if (snd_interval_checkempty(i)) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step) +{ + unsigned int n; + int changed = 0; + n = (i->min - min) % step; + if (n != 0 || i->openmin) { + i->min += step - n; + changed = 1; + } + n = (i->max - min) % step; + if (n != 0 || i->openmax) { + i->max -= n; + changed = 1; + } + if (snd_interval_checkempty(i)) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +/* Info constraints helpers */ + +int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_pcm_hw_rule_t *c; + unsigned int k; + va_list args; + va_start(args, dep); + if (constrs->rules_num >= constrs->rules_all) { + snd_pcm_hw_rule_t *old = constrs->rules; + if (constrs->rules_all == 0) + constrs->rules_all = 32; + else { + old = constrs->rules; + constrs->rules_all += 10; + } + constrs->rules = snd_kcalloc(constrs->rules_all * sizeof(*c), + GFP_KERNEL); + if (!constrs->rules) + return -ENOMEM; + if (old) { + memcpy(constrs->rules, old, + constrs->rules_num * sizeof(*c)); + kfree(old); + } + } + c = &constrs->rules[constrs->rules_num]; + c->cond = cond; + c->func = func; + c->var = var; + c->private = private; + k = 0; + while (1) { + snd_assert(k < sizeof(c->deps) / sizeof(c->deps[0]), return -EINVAL); + c->deps[k++] = dep; + if (dep < 0) + break; + dep = va_arg(args, int); + } + constrs->rules_num++; + va_end(args); + return 0; +} + +int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int mask) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + unsigned int *maskp = constrs_mask(constrs, var); + *maskp &= mask; + if (*maskp == 0) + return -EINVAL; + return 0; +} + +int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + return snd_interval_setinteger(constrs_interval(constrs, var)); +} + +int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max) +{ + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + snd_interval_t t; + t.min = min; + t.max = max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(constrs_interval(constrs, var), &t); +} + +static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_list_t *list = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); +} + + +int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_list_t *l) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_list, l, + var, -1); +} + +static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_ratnums_t *r = rule->private; + unsigned int num = 0, den = 0; + int err; + err = snd_interval_ratnum(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratnums_t *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratnums, r, + var, -1); +} + +static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hw_constraint_ratdens_t *r = rule->private; + unsigned int num = 0, den = 0; + int err = snd_interval_ratden(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + snd_pcm_hw_constraint_ratdens_t *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratdens, r, + var, -1); +} + +static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int l = (unsigned long) rule->private; + unsigned int width = l & 0xffff; + unsigned int msbits = l >> 16; + snd_interval_t *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i) && snd_interval_value(i) == width) + params->msbits = msbits; + return 0; +} + +int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits) +{ + unsigned long l = (msbits << 16) | width; + return snd_pcm_hw_rule_add(runtime, cond, -1, + snd_pcm_hw_rule_msbits, + (void*) l, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); +} + +static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned long step = (unsigned long) rule->private; + return snd_interval_step(hw_param_interval(params, rule->var), 0, step); +} + +int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_step, (void *) step, + var, -1); +} + + +/* To use the same code we have in alsa-lib */ +#define snd_pcm_t snd_pcm_substream_t +#define assert(i) snd_assert((i), return -EINVAL) +#ifndef INT_MIN +#define INT_MIN ((int)((unsigned int)INT_MAX+1)) +#endif + +void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) +{ + if (hw_is_mask(var)) { + snd_mask_any(hw_param_mask(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + return; + } + if (hw_is_interval(var)) { + snd_interval_any(hw_param_interval(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + return; + } + snd_BUG(); +} + +int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + _snd_pcm_hw_param_any(params, var); + return snd_pcm_hw_refine(pcm, params); +} + +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) +{ + unsigned int k; + memset(params, 0, sizeof(*params)); + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST; k++) + _snd_pcm_hw_param_any(params, k); + params->info = ~0U; +} + +/* Fill PARAMS with full configuration space boundaries */ +int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + _snd_pcm_hw_params_any(params); + return snd_pcm_hw_refine(pcm, params); +} + +/* Return the value for field PAR if it's fixed in configuration space + defined by PARAMS. Return -EINVAL otherwise +*/ +int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + const snd_mask_t *mask = hw_param_mask_c(params, var); + if (!snd_mask_single(mask)) + return -EINVAL; + if (dir) + *dir = 0; + return snd_mask_value(mask); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (!snd_interval_single(i)) + return -EINVAL; + if (dir) + *dir = i->openmin; + return snd_interval_value(i); + } + assert(0); + return -EINVAL; +} + +/* Return the minimum value for field PAR. */ +unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_min(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (dir) + *dir = i->openmin; + return snd_interval_min(i); + } + assert(0); + return -EINVAL; +} + +/* Return the maximum value for field PAR. */ +unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_max(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const snd_interval_t *i = hw_param_interval_c(params, var); + if (dir) + *dir = - (int) i->openmax; + return snd_interval_max(i); + } + assert(0); + return -EINVAL; +} + +void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + if (hw_is_mask(var)) { + snd_mask_none(hw_param_mask(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } else if (hw_is_interval(var)) { + snd_interval_none(hw_param_interval(params, var)); + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } else { + snd_BUG(); + } +} + +int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + assert(hw_is_interval(var)); + changed = snd_interval_setinteger(hw_param_interval(params, var)); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + non integer values. Reduce configuration space accordingly. + Return -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed = _snd_pcm_hw_param_setinteger(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + if (hw_is_mask(var)) + changed = snd_mask_refine_first(hw_param_mask(params, var)); + else if (hw_is_interval(var)) + changed = snd_interval_refine_first(hw_param_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + + +/* Inside configuration space defined by PARAMS remove from PAR all + values > minimum. Reduce configuration space accordingly. + Return the minimum. +*/ +int snd_pcm_hw_param_first(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + int changed = _snd_pcm_hw_param_first(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_param_value(params, var, dir); +} + +int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var) +{ + int changed; + if (hw_is_mask(var)) + changed = snd_mask_refine_last(hw_param_mask(params, var)); + else if (hw_is_interval(var)) + changed = snd_interval_refine_last(hw_param_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + + +/* Inside configuration space defined by PARAMS remove from PAR all + values < maximum. Reduce configuration space accordingly. + Return the maximum. +*/ +int snd_pcm_hw_param_last(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, int *dir) +{ + int changed = _snd_pcm_hw_param_last(params, var); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_param_value(params, var, dir); +} + +int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir > 0) { + open = 1; + } else if (dir < 0) { + if (val > 0) { + open = 1; + val--; + } + } + } + if (hw_is_mask(var)) + changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open); + else if (hw_is_interval(var)) + changed = snd_interval_refine_min(hw_param_interval(params, var), val, open); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values < VAL. Reduce configuration space accordingly. + Return new minimum or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int *dir) +{ + int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_min(params, var, dir); +} + +int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir < 0) { + open = 1; + } else if (dir > 0) { + open = 1; + val++; + } + } + if (hw_is_mask(var)) { + if (val == 0 && open) { + snd_mask_none(hw_param_mask(params, var)); + changed = -EINVAL; + } else + changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open); + } else if (hw_is_interval(var)) + changed = snd_interval_refine_max(hw_param_interval(params, var), val, open); + else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values >= VAL + 1. Reduce configuration space accordingly. + Return new maximum or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int *dir) +{ + int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_max(params, var, dir); +} + +int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed; + if (hw_is_mask(var)) { + snd_mask_t *m = hw_param_mask(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_mask_none(m); + } else { + if (dir > 0) + val++; + else if (dir < 0) + val--; + changed = snd_mask_refine_set(hw_param_mask(params, var), val); + } + } else if (hw_is_interval(var)) { + snd_interval_t *i = hw_param_interval(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_interval_none(i); + } else if (dir == 0) + changed = snd_interval_refine_set(i, val); + else { + snd_interval_t t; + t.openmin = 1; + t.openmax = 1; + t.empty = 0; + t.integer = 0; + if (dir < 0) { + t.min = val - 1; + t.max = val; + } else { + t.min = val; + t.max = val+1; + } + changed = snd_interval_refine(i, &t); + } + } else { + assert(0); + return -EINVAL; + } + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values != VAL. Reduce configuration space accordingly. + Return VAL or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int val, int dir) +{ + int changed = _snd_pcm_hw_param_set(params, var, val, dir); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value(params, var, 0); +} + +int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val) +{ + int changed; + assert(hw_is_mask(var)); + changed = snd_mask_refine(hw_param_mask(params, var), val); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all values + not contained in MASK. Reduce configuration space accordingly. + This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + Return 0 on success or -EINVAL + if the configuration space is empty +*/ +int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val) +{ + int changed = _snd_pcm_hw_param_mask(params, var, val); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +static int boundary_sub(int a, int adir, + int b, int bdir, + int *c, int *cdir) +{ + adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); + bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); + *c = a - b; + *cdir = adir - bdir; + if (*cdir == -2) { + assert(*c > INT_MIN); + (*c)--; + } else if (*cdir == 2) { + assert(*c < INT_MAX); + (*c)++; + } + return 0; +} + +static int boundary_lt(unsigned int a, int adir, + unsigned int b, int bdir) +{ + assert(a > 0 || adir >= 0); + assert(b > 0 || bdir >= 0); + if (adir < 0) { + a--; + adir = 1; + } else if (adir > 0) + adir = 1; + if (bdir < 0) { + b--; + bdir = 1; + } else if (bdir > 0) + bdir = 1; + return a < b || (a == b && adir < bdir); +} + +/* Return 1 if min is nearer to best than max */ +static int boundary_nearer(int min, int mindir, + int best, int bestdir, + int max, int maxdir) +{ + int dmin, dmindir; + int dmax, dmaxdir; + boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); + boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); + return boundary_lt(dmin, dmindir, dmax, dmaxdir); +} + +/* Inside configuration space defined by PARAMS set PAR to the available value + nearest to VAL. Reduce configuration space accordingly. + This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + Return the value found. + */ +int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, unsigned int best, int *dir) +{ + snd_pcm_hw_params_t save; + int v; + unsigned int saved_min; + int last = 0; + int min, max; + int mindir, maxdir; + int valdir = dir ? *dir : 0; + /* FIXME */ + if (best > INT_MAX) + best = INT_MAX; + min = max = best; + mindir = maxdir = valdir; + if (maxdir > 0) + maxdir = 0; + else if (maxdir == 0) + maxdir = -1; + else { + maxdir = 1; + max--; + } + save = *params; + saved_min = min; + min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); + if (min >= 0) { + snd_pcm_hw_params_t params1; + if (max < 0) + goto _end; + if ((unsigned int)min == saved_min && mindir == valdir) + goto _end; + params1 = save; + max = snd_pcm_hw_param_max(pcm, ¶ms1, var, max, &maxdir); + if (max < 0) + goto _end; + if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { + *params = params1; + last = 1; + } + } else { + *params = save; + max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); + assert(max >= 0); + last = 1; + } + _end: + if (last) + v = snd_pcm_hw_param_last(pcm, params, var, dir); + else + v = snd_pcm_hw_param_first(pcm, params, var, dir); + assert(v >= 0); + return v; +} + +/* Choose one configuration from configuration space defined by PARAMS + The configuration choosen is that obtained fixing in this order: + first access + first format + first subformat + min channels + min rate + min period time + max buffer size + min tick time +*/ +int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0); + assert(err >= 0); + + err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, 0); + assert(err >= 0); + + return 0; +} + +#undef snd_pcm_t +#undef assert + +static int snd_pcm_lib_ioctl_reset(snd_pcm_substream_t *substream, + void *arg) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (snd_pcm_running(substream) && + snd_pcm_update_hw_ptr(substream) >= 0) { + runtime->status->hw_ptr %= runtime->buffer_size; + return 0; + } + runtime->status->hw_ptr = 0; + return 0; +} + +static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream, + void *arg) +{ + snd_pcm_channel_info_t *info = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + int width; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { + info->offset = -1; + return 0; + } + width = snd_pcm_format_physical_width(runtime->format); + if (width < 0) + return width; + info->offset = 0; + switch (runtime->access) { + case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: + case SNDRV_PCM_ACCESS_RW_INTERLEAVED: + info->first = info->channel * width; + info->step = runtime->channels * width; + break; + case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: + case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: + { + size_t size = runtime->dma_bytes / runtime->channels; + info->first = info->channel * size * 8; + info->step = width; + break; + } + default: + snd_BUG(); + break; + } + return 0; +} + +int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_INFO: + return 0; + case SNDRV_PCM_IOCTL1_RESET: + return snd_pcm_lib_ioctl_reset(substream, arg); + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + return snd_pcm_lib_ioctl_channel_info(substream, arg); + } + return -ENXIO; +} + +/* + * Conditions + */ + +int snd_pcm_playback_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min; +} + +int snd_pcm_capture_ready(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min; +} + +int snd_pcm_playback_data(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) < runtime->buffer_size; +} + +int snd_pcm_playback_empty(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_playback_avail(runtime) >= runtime->buffer_size; +} + +int snd_pcm_capture_empty(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return snd_pcm_capture_avail(runtime) == 0; +} + +static void snd_pcm_system_tick_set(snd_pcm_substream_t *substream, + unsigned long ticks) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (ticks == 0) + del_timer(&runtime->tick_timer); + else { + ticks /= (1000000 / HZ); + if (ticks % (1000000 / HZ)) + ticks++; + mod_timer(&runtime->tick_timer, jiffies + ticks); + } +} + +/* Temporary alias */ +void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks) +{ + snd_pcm_system_tick_set(substream, ticks); +} + +void snd_pcm_tick_prepare(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frames = ULONG_MAX; + snd_pcm_uframes_t avail, dist; + unsigned int ticks; + u_int64_t n; + u_int32_t r; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (runtime->silence_size > 0 && + runtime->silenced_size < runtime->buffer_size) { + snd_pcm_sframes_t noise_dist; + noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silenced_size; + snd_assert(noise_dist <= runtime->silence_threshold, ); + frames = noise_dist - runtime->silence_threshold; + } + avail = snd_pcm_playback_avail(runtime); + } else { + avail = snd_pcm_capture_avail(runtime); + } + if (avail < runtime->control->avail_min) { + snd_pcm_sframes_t n = runtime->control->avail_min - avail; + if (n > 0 && frames > n) + frames = n; + } + if (avail < runtime->buffer_size) { + snd_pcm_sframes_t n = runtime->buffer_size - avail; + if (n > 0 && frames > n) + frames = n; + } + if (frames == ULONG_MAX) { + snd_pcm_tick_set(substream, 0); + return; + } + dist = runtime->status->hw_ptr - runtime->hw_ptr_base; + /* Distance to next interrupt */ + dist = runtime->period_size - dist % runtime->period_size; + if (dist <= frames) { + snd_pcm_tick_set(substream, 0); + return; + } + /* the base time is us */ + n = frames; + n *= 1000000; + div64_32(&n, runtime->tick_time * runtime->rate, &r); + ticks = n + (r > 0 ? 1 : 0); + if (ticks < runtime->sleep_min) + ticks = runtime->sleep_min; + snd_pcm_tick_set(substream, (unsigned long) ticks); +} + +void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return); + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + + spin_lock_irq(&runtime->lock); + if (!snd_pcm_running(substream) || + snd_pcm_update_hw_ptr(substream) < 0) + goto _end; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + _end: + spin_unlock_irq(&runtime->lock); +} + +void snd_pcm_period_elapsed(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return); + runtime = substream->runtime; + snd_assert(runtime != NULL, return); + + if (runtime->transfer_ack_begin) + runtime->transfer_ack_begin(substream); + + spin_lock(&runtime->lock); + if (!snd_pcm_running(substream) || + snd_pcm_update_hw_ptr_interrupt(substream) < 0) + goto _end; + + if (substream->timer_running) + snd_timer_interrupt(substream->timer, 1); + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + _end: + spin_unlock(&runtime->lock); + if (runtime->transfer_ack_end) + runtime->transfer_ack_end(substream); + kill_fasync(&runtime->fasync, SIGIO, POLL_IN); +} + +static int snd_pcm_lib_write_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + char *buf = (char *) data + frames_to_bytes(runtime, off); + if (substream->ops->copy) { + if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) + return err; + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + snd_assert(runtime->dma_area, return -EFAULT); + if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) + return -EFAULT; + } + return 0; +} + +typedef int (*transfer_f)(snd_pcm_substream_t *substream, unsigned int hwoff, + void *data, unsigned int off, snd_pcm_uframes_t size); + +static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, + const void *data, snd_pcm_uframes_t size, + int nonblock, + transfer_f transfer) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + int err = 0; + + if (size == 0) + return 0; + if (size > runtime->xfer_align) + size -= size % runtime->xfer_align; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + err = -EBADFD; + goto _end_unlock; + } + + while (size > 0) { + snd_pcm_uframes_t frames, appl_ptr, appl_ofs; + snd_pcm_uframes_t avail; + snd_pcm_uframes_t cont; + if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_playback_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_PAUSED || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + if (avail < runtime->xfer_align) { + err = -EPIPE; + goto _end_unlock; + } + } else if (((avail < runtime->control->avail_min && size > avail) || + (size >= runtime->xfer_align && avail < runtime->xfer_align))) { + wait_queue_t wait; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + if (nonblock) { + err = -EAGAIN; + goto _end_unlock; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + state = ERROR; + goto _end_loop; + case SNDRV_PCM_STATE_SUSPENDED: + state = SUSPENDED; + goto _end_loop; + default: + break; + } + avail = snd_pcm_playback_avail(runtime); + if (avail >= runtime->control->avail_min) { + state = READY; + break; + } + } + _end_loop: + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case ERROR: + err = -EPIPE; + goto _end_unlock; + case SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + case SIGNALED: + err = -ERESTARTSYS; + goto _end_unlock; + case EXPIRED: + snd_printd("playback write error (DMA or IRQ trouble?)\n"); + err = -EIO; + goto _end_unlock; + default: + break; + } + } + if (avail > runtime->xfer_align) + avail -= avail % runtime->xfer_align; + frames = size > avail ? avail : size; + cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + if (frames > cont) + frames = cont; + if (frames == 0 && runtime->status->state == SNDRV_PCM_STATE_PAUSED) { + err = -EPIPE; + goto _end; + } + snd_assert(frames != 0, + spin_unlock_irq(&runtime->lock); + return -EINVAL); + appl_ptr = runtime->control->appl_ptr; + appl_ofs = appl_ptr % runtime->buffer_size; + spin_unlock_irq(&runtime->lock); + if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0) + goto _end; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + break; + } + appl_ptr += frames; + if (appl_ptr >= runtime->boundary) { + runtime->control->appl_ptr = 0; + } else { + runtime->control->appl_ptr = appl_ptr; + } + + offset += frames; + size -= frames; + xfer += frames; + if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && + snd_pcm_playback_hw_avail(runtime) >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } + if (runtime->sleep_min && + runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_tick_prepare(substream); + } + _end_unlock: + spin_unlock_irq(&runtime->lock); + _end: + return xfer > 0 ? xfer : err; +} + +snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, const void *buf, snd_pcm_uframes_t size) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) + return -EINVAL; + return snd_pcm_lib_write1(substream, buf, size, nonblock, + snd_pcm_lib_write_transfer); +} + +static int snd_pcm_lib_writev_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + void **bufs = data; + int channels = runtime->channels; + int c; + if (substream->ops->copy) { + snd_assert(substream->ops->silence != NULL, return -EINVAL); + for (c = 0; c < channels; ++c, ++bufs) { + if (*bufs == NULL) { + if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) + return err; + } else { + char *buf = *bufs + samples_to_bytes(runtime, off); + if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) + return err; + } + } + } else { + /* default transfer behaviour */ + size_t dma_csize = runtime->dma_bytes / channels; + snd_assert(runtime->dma_area, return -EFAULT); + for (c = 0; c < channels; ++c, ++bufs) { + char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); + if (*bufs == NULL) { + snd_pcm_format_set_silence(runtime->format, hwbuf, frames); + } else { + char *buf = *bufs + samples_to_bytes(runtime, off); + if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) + return -EFAULT; + } + } + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, void **bufs, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + return snd_pcm_lib_write1(substream, bufs, frames, nonblock, + snd_pcm_lib_writev_transfer); +} + +static int snd_pcm_lib_read_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + char *buf = (char *) data + frames_to_bytes(runtime, off); + if (substream->ops->copy) { + if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) + return err; + } else { + char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + snd_assert(runtime->dma_area, return -EFAULT); + if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) + return -EFAULT; + } + return 0; +} + +static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, void *data, snd_pcm_uframes_t size, int nonblock, + transfer_f transfer) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + int err = 0; + + if (size == 0) + return 0; + if (size > runtime->xfer_align) + size -= size % runtime->xfer_align; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + if (size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } + break; + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + err = -EBADFD; + goto _end_unlock; + } + + while (size > 0) { + snd_pcm_uframes_t frames, appl_ptr, appl_ofs; + snd_pcm_uframes_t avail; + snd_pcm_uframes_t cont; + if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + avail = snd_pcm_capture_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) { + if (avail < runtime->xfer_align) { + err = -EPIPE; + goto _end_unlock; + } + } else if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + if (avail < runtime->xfer_align) { + runtime->status->state = SNDRV_PCM_STATE_SETUP; + err = -EPIPE; + goto _end_unlock; + } + } else if ((avail < runtime->control->avail_min && size > avail) || + (size >= runtime->xfer_align && avail < runtime->xfer_align)) { + wait_queue_t wait; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + if (nonblock) { + err = -EAGAIN; + goto _end_unlock; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + state = ERROR; + goto _end_loop; + case SNDRV_PCM_STATE_SUSPENDED: + state = SUSPENDED; + goto _end_loop; + default: + break; + } + avail = snd_pcm_capture_avail(runtime); + if (avail >= runtime->control->avail_min) { + state = READY; + break; + } + } + _end_loop: + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case ERROR: + err = -EPIPE; + goto _end_unlock; + case SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + case SIGNALED: + err = -ERESTARTSYS; + goto _end_unlock; + case EXPIRED: + snd_printd("capture read error (DMA or IRQ trouble?)\n"); + err = -EIO; + goto _end_unlock; + default: + break; + } + } + if (avail > runtime->xfer_align) + avail -= avail % runtime->xfer_align; + frames = size > avail ? avail : size; + cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + if (frames > cont) + frames = cont; + snd_assert(frames != 0, + spin_unlock_irq(&runtime->lock); + return -EINVAL); + appl_ptr = runtime->control->appl_ptr; + appl_ofs = appl_ptr % runtime->buffer_size; + spin_unlock_irq(&runtime->lock); + if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0) + goto _end; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _end_unlock; + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _end_unlock; + default: + break; + } + appl_ptr += frames; + if (appl_ptr >= runtime->boundary) { + runtime->control->appl_ptr = 0; + } else { + runtime->control->appl_ptr = appl_ptr; + } + + offset += frames; + size -= frames; + xfer += frames; + if (runtime->sleep_min && + runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_tick_prepare(substream); + } + _end_unlock: + spin_unlock_irq(&runtime->lock); + _end: + return xfer > 0 ? xfer : err; +} + +snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, void *buf, snd_pcm_uframes_t size) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) + return -EINVAL; + return snd_pcm_lib_read1(substream, buf, size, nonblock, snd_pcm_lib_read_transfer); +} + +static int snd_pcm_lib_readv_transfer(snd_pcm_substream_t *substream, + unsigned int hwoff, + void *data, unsigned int off, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + void **bufs = data; + int channels = runtime->channels; + int c; + if (substream->ops->copy) { + for (c = 0; c < channels; ++c, ++bufs) { + char *buf; + if (*bufs == NULL) + continue; + buf = *bufs + samples_to_bytes(runtime, off); + if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) + return err; + } + } else { + snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; + snd_assert(runtime->dma_area, return -EFAULT); + for (c = 0; c < channels; ++c, ++bufs) { + char *hwbuf, *buf; + if (*bufs == NULL) + continue; + + hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); + buf = *bufs + samples_to_bytes(runtime, off); + if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) + return -EFAULT; + } + } + return 0; +} + +snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, void **bufs, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime; + int nonblock; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + snd_assert(substream->ffile != NULL, return -ENXIO); + nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); +#ifdef CONFIG_SND_OSSEMUL + if (substream->oss.oss) { + snd_pcm_oss_setup_t *setup = substream->oss.setup; + if (setup != NULL) { + if (setup->nonblock) + nonblock = 1; + else if (setup->block) + nonblock = 0; + } + } +#endif + + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + return snd_pcm_lib_read1(substream, bufs, frames, nonblock, snd_pcm_lib_readv_transfer); +} + +/* + * Exported symbols + */ + +EXPORT_SYMBOL(snd_interval_refine); +EXPORT_SYMBOL(snd_interval_list); +EXPORT_SYMBOL(snd_interval_ratnum); +EXPORT_SYMBOL(snd_interval_ratden); +EXPORT_SYMBOL(snd_interval_muldivk); +EXPORT_SYMBOL(snd_interval_mulkdiv); +EXPORT_SYMBOL(snd_interval_div); +EXPORT_SYMBOL(_snd_pcm_hw_params_any); +EXPORT_SYMBOL(_snd_pcm_hw_param_min); +EXPORT_SYMBOL(_snd_pcm_hw_param_set); +EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); +EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger); +EXPORT_SYMBOL(snd_pcm_hw_param_value_min); +EXPORT_SYMBOL(snd_pcm_hw_param_value_max); +EXPORT_SYMBOL(snd_pcm_hw_param_mask); +EXPORT_SYMBOL(snd_pcm_hw_param_first); +EXPORT_SYMBOL(snd_pcm_hw_param_last); +EXPORT_SYMBOL(snd_pcm_hw_param_near); +EXPORT_SYMBOL(snd_pcm_hw_refine); +EXPORT_SYMBOL(snd_pcm_hw_constraints_init); +EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); +EXPORT_SYMBOL(snd_pcm_hw_constraint_list); +EXPORT_SYMBOL(snd_pcm_hw_constraint_step); +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); +EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); +EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); +EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); +EXPORT_SYMBOL(snd_pcm_hw_rule_add); +EXPORT_SYMBOL(snd_pcm_set_ops); +EXPORT_SYMBOL(snd_pcm_set_sync); +EXPORT_SYMBOL(snd_pcm_lib_ioctl); +EXPORT_SYMBOL(snd_pcm_playback_ready); +EXPORT_SYMBOL(snd_pcm_capture_ready); +EXPORT_SYMBOL(snd_pcm_playback_data); +EXPORT_SYMBOL(snd_pcm_capture_empty); +EXPORT_SYMBOL(snd_pcm_stop); +EXPORT_SYMBOL(snd_pcm_period_elapsed); +EXPORT_SYMBOL(snd_pcm_lib_write); +EXPORT_SYMBOL(snd_pcm_lib_read); +EXPORT_SYMBOL(snd_pcm_lib_writev); +EXPORT_SYMBOL(snd_pcm_lib_readv); +EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes); +EXPORT_SYMBOL(snd_pcm_lib_period_bytes); +/* pcm_memory.c */ +EXPORT_SYMBOL(snd_pcm_lib_preallocate_free); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); +EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); +EXPORT_SYMBOL(snd_pcm_lib_free_pages); +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all); +#endif +#ifdef CONFIG_PCI +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages); +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all); +#endif diff -Nru linux/sound/core/pcm_memory.c linux-2.4.19-pre5-mjc/sound/core/pcm_memory.c --- linux/sound/core/pcm_memory.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/pcm_memory.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,357 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +static int snd_preallocate_dma = 1; +MODULE_PARM(snd_preallocate_dma, "i"); +MODULE_PARM_DESC(snd_preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized."); +MODULE_PARM_SYNTAX(snd_preallocate_dma, SNDRV_BOOLEAN_TRUE_DESC); + +static int snd_maximum_substreams = 4; +MODULE_PARM(snd_maximum_substreams, "i"); +MODULE_PARM_DESC(snd_maximum_substreams, "Maximum substreams with preallocated DMA memory."); +MODULE_PARM_SYNTAX(snd_maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC); + +static int snd_minimum_buffer = 16384; + + +static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream) +{ + if (substream->dma_area == NULL) + return; + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + snd_free_pages(substream->dma_area, substream->dma_bytes); + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + snd_free_isa_pages(substream->dma_bytes, substream->dma_area, substream->dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + snd_free_pci_pages((struct pci_dev *)substream->dma_private, substream->dma_bytes, substream->dma_area, substream->dma_addr); + break; +#endif + } + substream->dma_area = NULL; +} + +int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_preallocate_dma_free(substream); + if (substream->proc_prealloc_entry) { + snd_info_unregister(substream->proc_prealloc_entry); + substream->proc_prealloc_entry = NULL; + } + return 0; +} + +int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *substream; + int stream; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_free(substream); + return 0; +} + +static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_bytes / 1024); +} + +static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; + char line[64], str[64]; + size_t size; + void *dma_area; + dma_addr_t dma_addr; + + if (substream->runtime) { + buffer->error = -EBUSY; + return; + } + if (!snd_info_get_line(buffer, line, sizeof(line))) { + snd_info_get_str(str, line, sizeof(str)); + size = simple_strtoul(str, NULL, 10) * 1024; + if ((size != 0 && size < 8192) || size > substream->dma_max) { + buffer->error = -EINVAL; + return; + } + if (substream->dma_bytes == size) + return; + if (size > 0) { + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); + dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + dma_area = snd_malloc_isa_pages(size, &dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); + break; +#endif + default: + dma_area = NULL; + dma_addr = 0UL; + } + if (dma_area == NULL) { + buffer->error = -ENOMEM; + return; + } + substream->buffer_bytes_max = size; + } else { + dma_area = NULL; + substream->buffer_bytes_max = UINT_MAX; + } + snd_pcm_lib_preallocate_dma_free(substream); + substream->dma_area = dma_area; + substream->dma_addr = dma_addr; + substream->dma_bytes = size; + } else { + buffer->error = -EINVAL; + } +} + +static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + unsigned long rsize = 0; + void *dma_area = NULL; + dma_addr_t dma_addr = 0UL; + snd_info_entry_t *entry; + + if (!size || !snd_preallocate_dma || substream->number >= snd_maximum_substreams) { + size = 0; + } else { + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + dma_area = snd_malloc_pages_fallback(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff), &rsize); + dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + dma_area = snd_malloc_isa_pages_fallback(size, &dma_addr, &rsize); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + dma_area = snd_malloc_pci_pages_fallback((struct pci_dev *)substream->dma_private, size, &dma_addr, &rsize); + break; +#endif + default: + size = 0; + } + if (rsize < snd_minimum_buffer) { + snd_pcm_lib_preallocate_dma_free(substream); + size = 0; + } + } + substream->dma_area = dma_area; + substream->dma_addr = dma_addr; + substream->dma_bytes = rsize; + if (rsize > 0) + substream->buffer_bytes_max = rsize; + substream->dma_max = max; + if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) { + entry->c.text.read_size = 64; + entry->c.text.read = snd_pcm_lib_preallocate_proc_read; + entry->c.text.write_size = 64; + entry->c.text.write = snd_pcm_lib_preallocate_proc_write; + entry->private_data = substream; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_prealloc_entry = entry; + return 0; +} + +int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, + size_t size, size_t max, + unsigned int flags) +{ + substream->dma_type = SNDRV_PCM_DMA_TYPE_CONTINUOUS; + substream->dma_private = (void *)(unsigned long)flags; + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max, + unsigned int flags) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_pages(substream, size, max, flags)) < 0) + return err; + return 0; +} + +#ifdef CONFIG_ISA +int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_type = SNDRV_PCM_DMA_TYPE_ISA; + substream->dma_private = NULL; + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_isa_pages(substream, size, max)) < 0) + return err; + return 0; +} +#endif /* CONFIG_ISA */ + +int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) +{ + snd_pcm_runtime_t *runtime; + void *dma_area = NULL; + dma_addr_t dma_addr = 0UL; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + if (runtime->dma_area != NULL) { + /* perphaps, we might free the large DMA memory region + to save some space here, but the actual solution + costs us less time */ + if (runtime->dma_bytes >= size) + return 0; /* ok, do not change */ + snd_pcm_lib_free_pages(substream); + } + if (substream->dma_area != NULL && substream->dma_bytes >= size) { + dma_area = substream->dma_area; + dma_addr = substream->dma_addr; + } else { + switch (substream->dma_type) { + case SNDRV_PCM_DMA_TYPE_CONTINUOUS: + dma_area = snd_malloc_pages(size, (unsigned int)((unsigned long)substream->dma_private & 0xffffffff)); + dma_addr = 0UL; /* not valid */ + break; +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + dma_area = snd_malloc_isa_pages(size, &dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + dma_area = snd_malloc_pci_pages((struct pci_dev *)substream->dma_private, size, &dma_addr); + break; +#endif + default: + return -ENXIO; + } + } + if (! dma_area) + return -ENOMEM; + runtime->dma_area = dma_area; + runtime->dma_addr = dma_addr; + runtime->dma_bytes = size; + return 1; /* area was changed */ +} + +int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + if (runtime->dma_area == NULL) + return 0; + if (runtime->dma_area != substream->dma_area) { + switch (substream->dma_type) { +#ifdef CONFIG_ISA + case SNDRV_PCM_DMA_TYPE_ISA: + snd_free_isa_pages(runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + break; +#endif +#ifdef CONFIG_PCI + case SNDRV_PCM_DMA_TYPE_PCI: + snd_free_pci_pages((struct pci_dev *)substream->dma_private, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + break; +#endif + } + } + runtime->dma_area = NULL; + runtime->dma_addr = 0UL; + runtime->dma_bytes = 0; + return 0; +} + +#ifdef CONFIG_PCI + +int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, + snd_pcm_substream_t *substream, + size_t size, size_t max) +{ + substream->dma_type = SNDRV_PCM_DMA_TYPE_PCI; + substream->dma_private = pci; + return snd_pcm_lib_preallocate_pages1(substream, size, max); +} + +int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, + snd_pcm_t *pcm, + size_t size, size_t max) +{ + snd_pcm_substream_t *substream; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) + if ((err = snd_pcm_lib_preallocate_pci_pages(pci, substream, size, max)) < 0) + return err; + return 0; +} + +#endif /* CONFIG_PCI */ diff -Nru linux/sound/core/pcm_misc.c linux-2.4.19-pre5-mjc/sound/core/pcm_misc.c --- linux/sound/core/pcm_misc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/pcm_misc.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,442 @@ +/* + * PCM Interface - misc routines + * Copyright (c) 1998 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#define bswap_16 swab16 +#define bswap_32 swab32 +#define bswap_64 swab64 +#define SND_PCM_FORMAT_UNKNOWN (-1) +#define snd_enum_to_int(v) (v) +#define snd_int_to_enum(v) (v) + +int snd_pcm_format_signed(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + return 1; + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + return 0; + default: + return -EINVAL; + } +} + +int snd_pcm_format_unsigned(snd_pcm_format_t format) +{ + int val; + + val = snd_pcm_format_signed(format); + if (val < 0) + return val; + return !val; +} + +int snd_pcm_format_linear(snd_pcm_format_t format) +{ + return snd_pcm_format_signed(format) >= 0; +} + +int snd_pcm_format_little_endian(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + return 1; + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_BE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 0; + default: + return -EINVAL; + } +} + +int snd_pcm_format_big_endian(snd_pcm_format_t format) +{ + int val; + + val = snd_pcm_format_little_endian(format); + if (val < 0) + return val; + return !val; +} + +int snd_pcm_format_cpu_endian(snd_pcm_format_t format) +{ +#ifdef SNDRV_LITTLE_ENDIAN + return snd_pcm_format_little_endian(format); +#else + return snd_pcm_format_big_endian(format); +#endif +} + +int snd_pcm_format_width(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + return 24; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 24; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + +int snd_pcm_format_physical_width(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + +ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return samples; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return samples * 2; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return samples * 8; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return samples; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + if (samples & 1) + return -EINVAL; + return samples / 2; + default: + return -EINVAL; + } +} + +u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format) +{ + switch (snd_enum_to_int(format)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + return 0; + case SNDRV_PCM_FORMAT_U8: + return 0x8080808080808080ULL; +#ifdef SNDRV_LITTLE_ENDIAN + case SNDRV_PCM_FORMAT_U16_LE: + return 0x8000800080008000ULL; + case SNDRV_PCM_FORMAT_U24_LE: + return 0x0080000000800000ULL; + case SNDRV_PCM_FORMAT_U32_LE: + return 0x8000000080000000ULL; + case SNDRV_PCM_FORMAT_U16_BE: + return 0x0080008000800080ULL; + case SNDRV_PCM_FORMAT_U24_BE: + return 0x0000800000008000ULL; + case SNDRV_PCM_FORMAT_U32_BE: + return 0x0000008000000080ULL; +#else + case SNDRV_PCM_FORMAT_U16_LE: + return 0x0080008000800080ULL; + case SNDRV_PCM_FORMAT_U24_LE: + return 0x0000800000008000ULL; + case SNDRV_PCM_FORMAT_U32_LE: + return 0x0000008000000080ULL; + case SNDRV_PCM_FORMAT_U16_BE: + return 0x8000800080008000ULL; + case SNDRV_PCM_FORMAT_U24_BE: + return 0x0080000000800000ULL; + case SNDRV_PCM_FORMAT_U32_BE: + return 0x8000000080000000ULL; +#endif + case SNDRV_PCM_FORMAT_FLOAT_LE: + { + union { + float f; + u_int32_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return u.i; +#else + return bswap_32(u.i); +#endif + } + case SNDRV_PCM_FORMAT_FLOAT64_LE: + { + union { + double f; + u_int64_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return u.i; +#else + return bswap_64(u.i); +#endif + } + case SNDRV_PCM_FORMAT_FLOAT_BE: + { + union { + float f; + u_int32_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return bswap_32(u.i); +#else + return u.i; +#endif + } + case SNDRV_PCM_FORMAT_FLOAT64_BE: + { + union { + double f; + u_int64_t i; + } u; + u.f = 0.0; +#ifdef SNDRV_LITTLE_ENDIAN + return bswap_64(u.i); +#else + return u.i; +#endif + } + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 0; + case SNDRV_PCM_FORMAT_MU_LAW: + return 0x7f7f7f7f7f7f7f7fULL; + case SNDRV_PCM_FORMAT_A_LAW: + return 0x5555555555555555ULL; + case SNDRV_PCM_FORMAT_IMA_ADPCM: /* special case */ + case SNDRV_PCM_FORMAT_MPEG: + case SNDRV_PCM_FORMAT_GSM: + case SNDRV_PCM_FORMAT_SPECIAL: + return 0; + default: + return -EINVAL; + } + return 0; +} + +u_int32_t snd_pcm_format_silence_32(snd_pcm_format_t format) +{ + return (u_int32_t)snd_pcm_format_silence_64(format); +} + +u_int16_t snd_pcm_format_silence_16(snd_pcm_format_t format) +{ + return (u_int16_t)snd_pcm_format_silence_64(format); +} + +u_int8_t snd_pcm_format_silence(snd_pcm_format_t format) +{ + return (u_int8_t)snd_pcm_format_silence_64(format); +} + +int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) +{ + if (samples == 0) + return 0; + switch (snd_pcm_format_width(format)) { + case 4: { + u_int8_t silence = snd_pcm_format_silence_64(format); + unsigned int samples1; + if (samples % 2 != 0) + return -EINVAL; + samples1 = samples / 2; + memset(data, silence, samples1); + break; + } + case 8: { + u_int8_t silence = snd_pcm_format_silence_64(format); + memset(data, silence, samples); + break; + } + case 16: { + u_int16_t silence = snd_pcm_format_silence_64(format); + while (samples-- > 0) + *((u_int16_t *)data)++ = silence; + break; + } + case 32: { + u_int32_t silence = snd_pcm_format_silence_64(format); + while (samples-- > 0) + *((u_int32_t *)data)++ = silence; + break; + } + case 64: { + u_int64_t silence = snd_pcm_format_silence_64(format); + while (samples-- > 0) + *((u_int64_t *)data)++ = silence; + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int linear_formats[4*2*2] = { + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_S8, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE +}; + +snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) +{ + switch (width) { + case 8: + width = 0; + break; + case 16: + width = 1; + break; + case 24: + width = 2; + break; + case 32: + width = 3; + break; + default: + return SND_PCM_FORMAT_UNKNOWN; + } + return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]); +} diff -Nru linux/sound/core/pcm_native.c linux-2.4.19-pre5-mjc/sound/core/pcm_native.c --- linux/sound/core/pcm_native.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/pcm_native.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2762 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +spinlock_t pcm_link_lock = SPIN_LOCK_UNLOCKED; + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info) +{ + snd_pcm_runtime_t * runtime; + snd_pcm_t *pcm = substream->pcm; + snd_pcm_str_t *pstr = substream->pstr; + + snd_assert(substream != NULL, return -ENXIO); + memset(info, 0, sizeof(*info)); + info->card = pcm->card->number; + info->device = pcm->device; + info->stream = substream->stream; + info->subdevice = substream->number; + strncpy(info->id, pcm->id, sizeof(info->id)-1); + strncpy(info->name, pcm->name, sizeof(info->name)-1); + info->dev_class = pcm->dev_class; + info->dev_subclass = pcm->dev_subclass; + info->subdevices_count = pstr->substream_count; + info->subdevices_avail = pstr->substream_count - pstr->substream_opened; + strncpy(info->subname, substream->name, sizeof(info->subname)-1); + runtime = substream->runtime; + /* AB: FIXME!!! This is definitely nonsense */ + if (runtime) { + info->sync = runtime->sync; + substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); + } + return 0; +} + +int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t * _info) +{ + snd_pcm_info_t info; + int err = snd_pcm_info(substream, &info); + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return err; +} + +#undef RULES_DEBUG + +#ifdef RULES_DEBUG +#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v +char *snd_pcm_hw_param_names[] = { + HW_PARAM(ACCESS), + HW_PARAM(FORMAT), + HW_PARAM(SUBFORMAT), + HW_PARAM(SAMPLE_BITS), + HW_PARAM(FRAME_BITS), + HW_PARAM(CHANNELS), + HW_PARAM(RATE), + HW_PARAM(PERIOD_TIME), + HW_PARAM(PERIOD_SIZE), + HW_PARAM(PERIOD_BYTES), + HW_PARAM(PERIODS), + HW_PARAM(BUFFER_TIME), + HW_PARAM(BUFFER_SIZE), + HW_PARAM(BUFFER_BYTES), + HW_PARAM(TICK_TIME), +}; +#endif + +int snd_pcm_hw_refine(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned int k; + snd_pcm_hardware_t *hw; + snd_interval_t *i = NULL; + snd_mask_t *m = NULL; + snd_pcm_hw_constraints_t *constrs = &substream->runtime->hw_constraints; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST + 1]; + unsigned int stamp = 2; + int changed, again; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + m = hw_param_mask(params, k); + if (snd_mask_empty(m)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + printk("%s = ", snd_pcm_hw_param_names[k]); + printk("%x -> ", *m); +#endif + changed = snd_mask_refine(m, constrs_mask(constrs, k)); +#ifdef RULES_DEBUG + printk("%x\n", *m); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + i = hw_param_interval(params, k); + if (snd_interval_empty(i)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + printk("%s = ", snd_pcm_hw_param_names[k]); + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + printk(" -> "); +#endif + changed = snd_interval_refine(i, constrs_interval(constrs, k)); +#ifdef RULES_DEBUG + if (i->empty) + printk("empty\n"); + else + printk("%c%u %u%c\n", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = 0; k < constrs->rules_num; k++) + rstamps[k] = 0; + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST; k++) + vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; + do { + again = 0; + for (k = 0; k < constrs->rules_num; k++) { + snd_pcm_hw_rule_t *r = &constrs->rules[k]; + unsigned int d; + int doit = 0; + if (r->cond && !(r->cond & params->flags)) + continue; + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) { + doit = 1; + break; + } + } + if (!doit) + continue; +#ifdef RULES_DEBUG + printk("Rule %d [%p]: ", k, r->func); + if (r->var >= 0) { + printk("%s = ", snd_pcm_hw_param_names[r->var]); + if (hw_is_mask(r->var)) { + m = hw_param_mask(params, r->var); + printk("%x", *m); + } else { + i = hw_param_interval(params, r->var); + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } +#endif + changed = r->func(params, r); +#ifdef RULES_DEBUG + if (r->var >= 0) { + printk(" -> "); + if (hw_is_mask(r->var)) + printk("%x", *m); + else { + if (i->empty) + printk("empty"); + else + printk("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } + printk("\n"); +#endif + rstamps[k] = stamp; + if (changed && r->var >= 0) { + params->cmask |= (1 << r->var); + vstamps[r->var] = stamp; + again = 1; + } + if (changed < 0) + return changed; + stamp++; + } + } while (again); + if (!params->msbits) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i)) + params->msbits = snd_interval_value(i); + } + + if (!params->rate_den) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (snd_interval_single(i)) { + params->rate_num = snd_interval_value(i); + params->rate_den = 1; + } + } + + hw = &substream->runtime->hw; + if (!params->info) + params->info = hw->info; + if (!params->fifo_size) + params->fifo_size = hw->fifo_size; + params->rmask = 0; + return 0; +} + +static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params) +{ + snd_pcm_hw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_hw_refine(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + snd_pcm_runtime_t *runtime; + int err; + unsigned int bits; + snd_pcm_uframes_t frames; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + break; + default: + return -EBADFD; + } +#ifdef CONFIG_SND_OSSEMUL + if (!substream->oss.oss) +#endif + if (atomic_read(&runtime->mmap_count)) + return -EBADFD; + + params->rmask = ~0U; + err = snd_pcm_hw_refine(substream, params); + if (err < 0) + goto _error; + + err = snd_pcm_hw_params_choose(substream, params); + if (err < 0) + goto _error; + + if (substream->ops->hw_params != NULL) { + err = substream->ops->hw_params(substream, params); + if (err < 0) + goto _error; + } + + runtime->access = params_access(params); + runtime->format = params_format(params); + runtime->subformat = params_subformat(params); + runtime->channels = params_channels(params); + runtime->rate = params_rate(params); + runtime->period_size = params_period_size(params); + runtime->periods = params_periods(params); + runtime->buffer_size = params_buffer_size(params); + runtime->tick_time = params_tick_time(params); + runtime->info = params->info; + runtime->rate_num = params->rate_num; + runtime->rate_den = params->rate_den; + + bits = snd_pcm_format_physical_width(runtime->format); + runtime->sample_bits = bits; + bits *= runtime->channels; + runtime->frame_bits = bits; + frames = 1; + while (bits % 8 != 0) { + bits *= 2; + frames *= 2; + } + runtime->byte_align = bits / 8; + runtime->min_align = frames; + + /* Default sw params */ + runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + runtime->period_step = 1; + runtime->sleep_min = 0; + runtime->control->avail_min = runtime->period_size; + runtime->xfer_align = runtime->period_size; + runtime->start_threshold = 1; + runtime->stop_threshold = runtime->buffer_size; + runtime->silence_threshold = 0; + runtime->silence_size = 0; + runtime->boundary = runtime->buffer_size; + while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) + runtime->boundary *= 2; + + snd_pcm_timer_resolution_change(substream); + runtime->status->state = SNDRV_PCM_STATE_SETUP; + return 0; + _error: + /* hardware might be unuseable from this time, + so we force application to retry to set + the correct hardware parameter settings */ + runtime->status->state = SNDRV_PCM_STATE_OPEN; + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + return err; +} + +static int snd_pcm_hw_params_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params) +{ + snd_pcm_hw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_hw_params(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_pcm_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime; + int result; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + break; + default: + return -EBADFD; + } + if (atomic_read(&runtime->mmap_count)) + return -EBADFD; + if (substream->ops->hw_free == NULL) + return 0; + result = substream->ops->hw_free(substream); + runtime->status->state = SNDRV_PCM_STATE_OPEN; + return result; +} + +static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params) +{ + snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -ENXIO); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + + if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) + return -EINVAL; + if (params->avail_min == 0) + return -EINVAL; + if (params->xfer_align == 0 || + params->xfer_align % runtime->min_align != 0) + return -EINVAL; + if (params->silence_threshold + params->silence_size > runtime->buffer_size) + return -EINVAL; + runtime->tstamp_mode = params->tstamp_mode; + runtime->sleep_min = params->sleep_min; + runtime->period_step = params->period_step; + runtime->control->avail_min = params->avail_min; + runtime->start_threshold = params->start_threshold; + runtime->stop_threshold = params->stop_threshold; + runtime->silence_threshold = params->silence_threshold; + runtime->silence_size = params->silence_size; + runtime->xfer_align = params->xfer_align; + params->boundary = runtime->boundary; + spin_lock_irq(&runtime->lock); + if (snd_pcm_running(substream)) { + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + else + snd_pcm_tick_set(substream, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + wake_up(&runtime->sleep); + } + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t * _params) +{ + snd_pcm_sw_params_t params; + int err; + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + err = snd_pcm_sw_params(substream, ¶ms); + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +int snd_pcm_status(snd_pcm_substream_t *substream, + snd_pcm_status_t *status) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&runtime->lock); + status->state = runtime->status->state; + status->suspended_state = runtime->status->suspended_state; + if (status->state == SNDRV_PCM_STATE_OPEN) + goto _end; + status->trigger_tstamp = runtime->trigger_tstamp; + if (snd_pcm_running(substream)) { + snd_pcm_update_hw_ptr(substream); + if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + status->tstamp = runtime->status->tstamp; + else + snd_timestamp_now(&status->tstamp); + } else + snd_timestamp_now(&status->tstamp); + status->appl_ptr = runtime->control->appl_ptr; + status->hw_ptr = runtime->status->hw_ptr; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + status->avail = snd_pcm_playback_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || + runtime->status->state == SNDRV_PCM_STATE_DRAINING) + status->delay = runtime->buffer_size - status->avail; + } else { + status->avail = snd_pcm_capture_avail(runtime); + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) + status->delay = status->avail; + } + status->avail_max = runtime->avail_max; + status->overrange = runtime->overrange; + runtime->avail_max = 0; + runtime->overrange = 0; + _end: + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t * _status) +{ + snd_pcm_status_t status; + snd_pcm_runtime_t *runtime; + int res; + + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + memset(&status, 0, sizeof(status)); + res = snd_pcm_status(substream, &status); + if (res < 0) + return res; + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t * _info) +{ + snd_pcm_channel_info_t info; + snd_pcm_runtime_t *runtime; + int res; + unsigned int channel; + + snd_assert(substream != NULL, return -ENXIO); + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + channel = info.channel; + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (channel >= runtime->channels) + return -EINVAL; + memset(&info, 0, sizeof(info)); + info.channel = channel; + res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, &info); + if (res < 0) + return res; + if (copy_to_user(_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static void snd_pcm_trigger_time(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->trigger_master == NULL) + return; + if (runtime->trigger_master == substream) { + snd_timestamp_now(&runtime->trigger_tstamp); + } else { + snd_pcm_trigger_time(runtime->trigger_master); + runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; + } + runtime->trigger_master = NULL; +} + +#define _SND_PCM_ACTION(aname, substream, state, res, check_master) { \ + snd_pcm_substream_t *s; \ + res = 0; \ + spin_lock(&pcm_link_lock); \ + s = substream; \ + do { \ + if (s != substream) \ + spin_lock(&s->runtime->lock); \ + res = snd_pcm_pre_##aname(s, state); \ + if (res < 0) \ + break; \ + s = s->link_next; \ + } while (s != substream); \ + if (res < 0) { \ + /* Clean all spin_lock */ \ + while (s != substream) { \ + spin_unlock(&s->runtime->lock); \ + s = s->link_prev; \ + } \ + goto _end; \ + } \ + s = substream; \ + do { \ + snd_pcm_runtime_t *runtime = s->runtime; \ + int err; \ + if (check_master && runtime->trigger_master != s) \ + goto _done; \ + err = snd_pcm_do_##aname(s, state); \ + if (err < 0) { \ + if (res == 0) \ + res = err; \ + } else { \ + _done: \ + snd_pcm_post_##aname(s, state); \ + } \ + if (s != substream) \ + spin_unlock(&runtime->lock); \ + s = s->link_next; \ + } while (s != substream); \ + _end: \ + spin_unlock(&pcm_link_lock); \ +} + +#define SND_PCM_ACTION(aname, substream, state) { \ + int res; \ + _SND_PCM_ACTION(aname, substream, state, res, 1); \ + return res; \ +} + +static inline int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->state != SNDRV_PCM_STATE_PREPARED) + return -EBADFD; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !snd_pcm_playback_data(substream)) + return -EPIPE; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_start(snd_pcm_substream_t *substream, int state) +{ + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); +} + +static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = SNDRV_PCM_STATE_RUNNING; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); +} + +int snd_pcm_start(snd_pcm_substream_t *substream) +{ + SND_PCM_ACTION(start, substream, 0); +} + +static inline int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!snd_pcm_running(substream)) + return -EBADFD; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state) +{ + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); +} + +static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = state; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); +} + +int snd_pcm_stop(snd_pcm_substream_t *substream, int state) +{ + SND_PCM_ACTION(stop, substream, state); +} + +static inline int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_PAUSE)) + return -ENOSYS; + if (push) { + if (runtime->status->state != SNDRV_PCM_STATE_RUNNING) + return -EBADFD; + } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED) + return -EBADFD; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push) +{ + return substream->ops->trigger(substream, + push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : + SNDRV_PCM_TRIGGER_PAUSE_RELEASE); +} + +static inline void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + if (push) { + runtime->status->state = SNDRV_PCM_STATE_PAUSED; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); + } else { + runtime->status->state = SNDRV_PCM_STATE_RUNNING; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); + } +} + +static int snd_pcm_pause(snd_pcm_substream_t *substream, int push) +{ + SND_PCM_ACTION(pause, substream, push); +} + +#ifdef CONFIG_PM +/* suspend */ + +static inline int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) + return -EBUSY; + runtime->status->suspended_state = runtime->status->state; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING) + return 0; + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); +} + +static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; + snd_pcm_tick_set(substream, 0); + wake_up(&runtime->sleep); +} + +int snd_pcm_suspend(snd_pcm_substream_t *substream) +{ + SND_PCM_ACTION(suspend, substream, 0); +} + +int snd_pcm_suspend_all(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + int stream, err; + + for (stream = 0; stream < 2; stream++) { + for (substream = pcm->streams[stream].substream; substream; substream = substream->next) { + /* FIXME: the open/close code should lock this as well */ + if ((runtime = substream->runtime) == NULL) + continue; + spin_lock(&runtime->lock); + if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + spin_unlock(&runtime->lock); + continue; + } + if ((err = snd_pcm_suspend(substream)) < 0) { + spin_unlock(&runtime->lock); + return err; + } + spin_unlock(&runtime->lock); + } + } + return 0; +} + +/* resume */ + +static inline int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) + return -ENOSYS; + runtime->trigger_master = substream; + return 0; +} + +static inline int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING) + return 0; + return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME); +} + +static inline void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_trigger_time(substream); + runtime->status->state = runtime->status->suspended_state; + if (runtime->sleep_min) + snd_pcm_tick_prepare(substream); +} + +static int snd_pcm_resume(snd_pcm_substream_t *substream) +{ + snd_card_t *card = substream->pcm->card; + int res; + + snd_power_lock(card); + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _power_unlock; + } + snd_power_wait(card); + } + + _SND_PCM_ACTION(resume, substream, 0, res, 1); + + _power_unlock: + snd_power_unlock(card); + return res; +} + +#else + +static int snd_pcm_resume(snd_pcm_substream_t *substream) +{ + return -ENOSYS; +} + +#endif /* CONFIG_PM */ + +static int snd_pcm_xrun(snd_pcm_substream_t *substream) +{ + snd_card_t *card = substream->pcm->card; + snd_pcm_runtime_t *runtime = substream->runtime; + int result; + + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + _xrun_recovery: + switch (runtime->status->state) { + case SNDRV_PCM_STATE_XRUN: + result = 0; /* already there */ + break; + case SNDRV_PCM_STATE_RUNNING: + result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + default: + result = -EBADFD; + } + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return result; +} + +static inline int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + case SNDRV_PCM_STATE_SUSPENDED: + return 0; + default: + return -EBADFD; + } +} + +static inline int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, 0); + if (err < 0) + return err; + snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); + runtime->hw_ptr_base = 0; + runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; + return 0; +} + +static inline void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + runtime->control->appl_ptr = runtime->status->hw_ptr; + +} + +static int snd_pcm_reset(snd_pcm_substream_t *substream) +{ + int res; + _SND_PCM_ACTION(reset, substream, 0, res, 0); + return res; +} + +static inline int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + return -EBADFD; + case SNDRV_PCM_STATE_RUNNING: + return -EBUSY; + default: + return 0; + } +} + +static inline int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state) +{ + int err; + err = substream->ops->prepare(substream); + if (err < 0) + return err; + return snd_pcm_do_reset(substream, 0); +} + +static inline void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + runtime->control->appl_ptr = runtime->status->hw_ptr; + runtime->status->state = SNDRV_PCM_STATE_PREPARED; +} + +int snd_pcm_prepare(snd_pcm_substream_t *substream) +{ + int res; + snd_card_t *card = substream->pcm->card; + snd_power_lock(card); + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _power_unlock; + } + snd_power_wait(card); + } + + _SND_PCM_ACTION(prepare, substream, 0, res, 0); + + _power_unlock: + snd_power_unlock(card); + return res; +} + +static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state) +{ + snd_pcm_substream_t *s; + spin_lock(&pcm_link_lock); + s = substream->link_next; + while (s != substream) { + spin_lock(&s->runtime->lock); + s = s->link_next; + } + s = substream; + do { + snd_pcm_runtime_t *runtime = s->runtime; + runtime->status->state = state; + if (s != substream) + spin_unlock(&runtime->lock); + s = s->link_next; + } while (s != substream); + spin_unlock(&pcm_link_lock); +} + +static int snd_pcm_playback_drop(snd_pcm_substream_t *substream); + +static int snd_pcm_playback_drain(snd_pcm_substream_t * substream) +{ + snd_card_t *card; + snd_pcm_runtime_t *runtime; + int err, result = 0; + wait_queue_t wait; + enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY; + + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + runtime = substream->runtime; + card = substream->pcm->card; + + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + case SNDRV_PCM_STATE_OPEN: + result = -EBADFD; + goto _end; + case SNDRV_PCM_STATE_PREPARED: + if (!snd_pcm_playback_empty(substream)) { + err = snd_pcm_start(substream); + if (err < 0) { + result = err; + goto _end; + } + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + /* Fall through */ + case SNDRV_PCM_STATE_SETUP: + goto _end; + } + + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { + if (snd_pcm_playback_empty(substream)) { + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + goto _end; + } + snd_pcm_change_state(substream, SNDRV_PCM_STATE_DRAINING); + } + + if (substream->ffile->f_flags & O_NONBLOCK) { + result = -EAGAIN; + goto _end; + } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + state = SIGNALED; + break; + } + spin_unlock_irq(&runtime->lock); + if (schedule_timeout(10 * HZ) == 0) { + spin_lock_irq(&runtime->lock); + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } + spin_lock_irq(&runtime->lock); + if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) { + state = READY; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + + switch (state) { + case SIGNALED: + result = -ERESTARTSYS; + goto _end; + case SUSPENDED: + result = -ESTRPIPE; + goto _end; + case EXPIRED: + snd_printd("playback drain error (DMA or IRQ trouble?)\n"); + result = -EIO; + goto _end; + default: + break; + } + + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + if (state == EXPIRED) + snd_pcm_playback_drop(substream); + + return result; +} + +static int snd_pcm_playback_drop(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + } + runtime->control->appl_ptr = runtime->status->hw_ptr; + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +static int snd_pcm_capture_drain(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_PREPARED: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + snd_pcm_stop(substream, + snd_pcm_capture_avail(runtime) > 0 ? + SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP); + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + _xrun_recovery: + snd_pcm_change_state(substream, + snd_pcm_capture_avail(runtime) > 0 ? + SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + goto _xrun_recovery; + } + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +static int snd_pcm_capture_drop(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_t *card = substream->pcm->card; + int res = 0; + snd_power_lock(card); + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + res = -EBADFD; + break; + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* Fall through */ + case SNDRV_PCM_STATE_RUNNING: + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + case SNDRV_PCM_STATE_SUSPENDED: + while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) { + if (substream->ffile->f_flags & O_NONBLOCK) { + res = -EAGAIN; + goto _end; + } + spin_unlock_irq(&runtime->lock); + snd_power_wait(card); + spin_lock_irq(&runtime->lock); + } + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_XRUN: + snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); + break; + } + runtime->control->appl_ptr = runtime->status->hw_ptr; + _end: + spin_unlock_irq(&runtime->lock); + snd_power_unlock(card); + return res; +} + +/* WARNING: Don't forget to fput back the file */ +static struct file *snd_pcm_file_fd(int fd) +{ + struct file *file; + struct inode *inode; + unsigned short minor; + file = fget(fd); + if (!file) + return 0; + inode = file->f_dentry->d_inode; + if (!S_ISCHR(inode->i_mode) || + major(inode->i_rdev) != CONFIG_SND_MAJOR) { + fput(file); + return 0; + } + minor = minor(inode->i_rdev); + if (minor >= 256 || + minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK) { + fput(file); + return 0; + } + return file; +} + +static int snd_pcm_link(snd_pcm_substream_t *substream, int fd) +{ + int res = 0; + struct file *file; + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *s, *substream1; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + file = snd_pcm_file_fd(fd); + if (!file) + return -EBADFD; + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream1 = pcm_file->substream; + spin_lock_irq(&pcm_link_lock); + if (substream->runtime->status->state != substream1->runtime->status->state) { + res = -EBADFD; + goto _end; + } + s = substream; + do { + if (s == substream1) { + res = -EALREADY; + goto _end; + } + s = s->link_next; + } while (s != substream); + substream1->link_prev->link_next = substream->link_next; + substream->link_next->link_prev = substream1->link_prev; + substream->link_next = substream1; + substream1->link_prev = substream; + _end: + spin_unlock_irq(&pcm_link_lock); + fput(file); + return res; +} + +static int snd_pcm_unlink(snd_pcm_substream_t *substream) +{ + spin_lock_irq(&pcm_link_lock); + substream->link_prev->link_next = substream->link_next; + substream->link_next->link_prev = substream->link_prev; + substream->link_prev = substream; + substream->link_next = substream; + spin_unlock_irq(&pcm_link_lock); + return 0; +} + + +static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_div(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), + (unsigned long) rule->private, &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), + (unsigned long) rule->private, + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int k; + snd_interval_t *i = hw_param_interval(params, rule->deps[0]); + unsigned int m = ~0U; + unsigned int *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (!(*mask & (1U << k))) + continue; + bits = snd_pcm_format_physical_width(k); + snd_assert(bits > 0, continue); + if ((unsigned)bits < i->min || (unsigned)bits > i->max) + m &= ~(1U << k); + } + return snd_mask_refine(mask, &m); +} + +static int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + unsigned int k; + t.min = UINT_MAX; + t.max = 0; + t.openmin = 0; + t.openmax = 0; + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (!(*hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT) & (1U << k))) + continue; + bits = snd_pcm_format_physical_width(k); + snd_assert(bits > 0, continue); + if (t.min > (unsigned)bits) + t.min = bits; + if (t.max < (unsigned)bits) + t.max = bits; + } + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 +#error "Change this table" +#endif + +static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 }; + +#define RATES (sizeof(rates) / sizeof(rates[0])) + +static int snd_pcm_hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_pcm_hardware_t *hw = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), RATES, rates, hw->rates); +} + +static int snd_pcm_hw_rule_buffer_bytes_max(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t t; + snd_pcm_substream_t *substream = rule->private; + t.min = 0; + t.max = substream->buffer_bytes_max; + t.openmin = 0; + t.openmax = 0; + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; + int k, err; + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + snd_mask_any(constrs_mask(constrs, k)); + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + snd_interval_any(constrs_interval(constrs, k)); + } + + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS)); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pcm_hw_rule_format, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_sample_bits, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mul, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_div, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mul, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + return 0; +} + +int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_hardware_t *hw = &runtime->hw; + int err; + unsigned int mask = 0; + + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_MMAP) { + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_COMPLEX) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; + } + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + hw->channels_min, hw->channels_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, + hw->rate_min, hw->rate_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + hw->period_bytes_min, hw->period_bytes_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, + hw->periods_min, hw->periods_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + hw->period_bytes_min, hw->buffer_bytes_max); + snd_assert(err >= 0, return -EINVAL); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_buffer_bytes_max, substream, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); + if (err < 0) + return err; + + /* FIXME: remove */ + if (runtime->dma_bytes) { + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); + snd_assert(err >= 0, return -EINVAL); + } + + if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_rate, hw, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + } + + /* FIXME: this belong to lowlevel */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME, + 1000000 / HZ, 1000000 / HZ); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + + return 0; +} + +static void snd_pcm_add_file(snd_pcm_str_t *str, + snd_pcm_file_t *pcm_file) +{ + pcm_file->next = str->files; + str->files = pcm_file; +} + +static void snd_pcm_remove_file(snd_pcm_str_t *str, + snd_pcm_file_t *pcm_file) +{ + snd_pcm_file_t * pcm_file1; + if (str->files == pcm_file) { + str->files = pcm_file->next; + } else { + pcm_file1 = str->files; + while (pcm_file1 && pcm_file1->next != pcm_file) + pcm_file1 = pcm_file1->next; + if (pcm_file1 != NULL) + pcm_file1->next = pcm_file->next; + } +} + +static int snd_pcm_release_file(snd_pcm_file_t * pcm_file) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_str_t * str; + + snd_assert(pcm_file != NULL, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + str = substream->pstr; + snd_pcm_unlink(substream); + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + substream->ops->close(substream); + substream->ffile = NULL; + snd_pcm_remove_file(str, pcm_file); + snd_pcm_release_substream(substream); + snd_magic_kfree(pcm_file); + return 0; +} + +static int snd_pcm_open_file(struct file *file, + snd_pcm_t *pcm, + int stream, + snd_pcm_file_t **rpcm_file) +{ + int err = 0; + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_str_t *str; + + snd_assert(rpcm_file != NULL, return -EINVAL); + *rpcm_file = NULL; + + pcm_file = snd_magic_kcalloc(snd_pcm_file_t, 0, GFP_KERNEL); + if (pcm_file == NULL) { + return -ENOMEM; + } + + if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) { + snd_magic_kfree(pcm_file); + return err; + } + + str = substream->pstr; + substream->file = pcm_file; + + pcm_file->substream = substream; + + snd_pcm_add_file(str, pcm_file); + + err = snd_pcm_hw_constraints_init(substream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraints_init failed\n"); + snd_pcm_release_file(pcm_file); + return err; + } + + if ((err = substream->ops->open(substream)) < 0) { + snd_pcm_release_file(pcm_file); + return err; + } + + err = snd_pcm_hw_constraints_complete(substream); + if (err < 0) { + snd_printd("snd_pcm_hw_constraints_complete failed\n"); + substream->ops->close(substream); + snd_pcm_release_file(pcm_file); + return err; + } + + substream->ffile = file; + + file->private_data = pcm_file; + *rpcm_file = pcm_file; + return 0; +} + +int snd_pcm_open(struct inode *inode, struct file *file) +{ + int cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + int device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)); + int err; + snd_pcm_t *pcm; + snd_pcm_file_t *pcm_file; + wait_queue_t wait; + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + snd_runtime_check(device >= SNDRV_MINOR_PCM_PLAYBACK && device < SNDRV_MINOR_DEVICES, return -ENXIO); + pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + (device % SNDRV_MINOR_PCMS)]; + if (pcm == NULL) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(pcm->card->module)) { + err = -EFAULT; + goto __error1; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&pcm->open_wait, &wait); + while (1) { + down(&pcm->open_mutex); + err = snd_pcm_open_file(file, pcm, device >= SNDRV_MINOR_PCM_CAPTURE ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, &pcm_file); + if (err >= 0) + break; + up(&pcm->open_mutex); + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pcm->open_wait, &wait); + if (err < 0) + goto __error; + up(&pcm->open_mutex); + return err; + + __error: + dec_mod_count(pcm->card->module); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +int snd_pcm_release(struct inode *inode, struct file *file) +{ + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + snd_assert(!atomic_read(&substream->runtime->mmap_count), ); + pcm = substream->pcm; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if ((substream->ffile->f_flags & O_NONBLOCK) || + snd_pcm_playback_drain(substream) == -ERESTARTSYS) + snd_pcm_playback_drop(substream); + } else + snd_pcm_capture_drop(substream); + fasync_helper(-1, file, 0, &substream->runtime->fasync); + down(&pcm->open_mutex); + snd_pcm_release_file(pcm_file); + up(&pcm->open_mutex); + wake_up(&pcm->open_wait); + dec_mod_count(pcm->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +snd_pcm_sframes_t snd_pcm_playback_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t hw_avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + break; + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + hw_avail = snd_pcm_playback_hw_avail(runtime); + if (hw_avail <= 0) { + ret = 0; + goto __end; + } + if (frames > hw_avail) + frames = hw_avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr - frames; + if (appl_ptr < 0) + appl_ptr += runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +snd_pcm_sframes_t snd_pcm_capture_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + snd_pcm_sframes_t ret; + snd_pcm_sframes_t hw_avail; + + if (frames == 0) + return 0; + + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_DRAINING: + break; + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) + break; + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + ret = -EPIPE; + goto __end; + default: + ret = -EBADFD; + goto __end; + } + + hw_avail = snd_pcm_capture_hw_avail(runtime); + if (hw_avail <= 0) { + ret = 0; + goto __end; + } + if (frames > hw_avail) + frames = hw_avail; + else + frames -= frames % runtime->xfer_align; + appl_ptr = runtime->control->appl_ptr - frames; + if (appl_ptr < 0) + appl_ptr += runtime->boundary; + runtime->control->appl_ptr = appl_ptr; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && + runtime->sleep_min) + snd_pcm_tick_prepare(substream); + ret = frames; + __end: + spin_unlock_irq(&runtime->lock); + return ret; +} + +static int snd_pcm_playback_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err = 0; + snd_pcm_sframes_t n; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + n = snd_pcm_playback_hw_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + break; + } else { + err = SNDRV_PCM_STATE_RUNNING ? -EPIPE : -EBADFD; + } + break; + case SNDRV_PCM_STATE_SUSPENDED: + if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) { + n = snd_pcm_playback_hw_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + } else { + err = -EBADFD; + } + break; + default: + err = -EBADFD; + break; + } + spin_unlock_irq(&runtime->lock); + return err; +} + +static int snd_pcm_capture_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int err = 0; + snd_pcm_sframes_t n; + spin_lock_irq(&runtime->lock); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + if (snd_pcm_update_hw_ptr(substream) >= 0) { + n = snd_pcm_capture_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + break; + case SNDRV_PCM_STATE_SUSPENDED: + if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) { + n = snd_pcm_capture_avail(runtime); + if (put_user(n, res)) + err = -EFAULT; + } else { + err = -EBADFD; + } + break; + default: + err = -EBADFD; + break; + } + spin_unlock_irq(&runtime->lock); + return err; +} + +static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); +static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg); + +static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + + switch (cmd) { + case SNDRV_PCM_IOCTL_PVERSION: + return put_user(SNDRV_PCM_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_PCM_IOCTL_INFO: + return snd_pcm_info_user(substream, (snd_pcm_info_t *) arg); + case SNDRV_PCM_IOCTL_HW_REFINE: + return snd_pcm_hw_refine_user(substream, (snd_pcm_hw_params_t *) arg); + case SNDRV_PCM_IOCTL_HW_PARAMS: + return snd_pcm_hw_params_user(substream, (snd_pcm_hw_params_t *) arg); + case SNDRV_PCM_IOCTL_HW_FREE: + return snd_pcm_hw_free(substream); + case SNDRV_PCM_IOCTL_SW_PARAMS: + return snd_pcm_sw_params_user(substream, (snd_pcm_sw_params_t *) arg); + case SNDRV_PCM_IOCTL_STATUS: + return snd_pcm_status_user(substream, (snd_pcm_status_t *) arg); + case SNDRV_PCM_IOCTL_CHANNEL_INFO: + return snd_pcm_channel_info(substream, (snd_pcm_channel_info_t *) arg); + case SNDRV_PCM_IOCTL_PREPARE: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_prepare(substream); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_RESET: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_reset(substream); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_START: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_start(substream); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_LINK: + return snd_pcm_link(substream, (long) arg); + case SNDRV_PCM_IOCTL_UNLINK: + return snd_pcm_unlink(substream); + case SNDRV_PCM_IOCTL_RESUME: + return snd_pcm_resume(substream); + case SNDRV_PCM_IOCTL_XRUN: + return snd_pcm_xrun(substream); + } + snd_printd("unknown ioctl = 0x%x\n", cmd); + return -ENOTTY; +} + +static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + switch (cmd) { + case SNDRV_PCM_IOCTL_WRITEI_FRAMES: + { + snd_xferi_t xferi, *_xferi = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (put_user(0, &_xferi->result)) + return -EFAULT; + if (copy_from_user(&xferi, _xferi, sizeof(xferi))) + return -EFAULT; + result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); + __put_user(result, &_xferi->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_WRITEN_FRAMES: + { + snd_xfern_t xfern, *_xfern = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + void *bufs[128]; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (runtime->channels > 128) + return -EINVAL; + if (put_user(0, &_xfern->result)) + return -EFAULT; + if (copy_from_user(&xfern, _xfern, sizeof(xfern))) + return -EFAULT; + if (copy_from_user(bufs, xfern.bufs, sizeof(*bufs) * runtime->channels)) + return -EFAULT; + result = snd_pcm_lib_writev(substream, bufs, xfern.frames); + __put_user(result, &_xfern->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_REWIND: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_playback_rewind(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_PAUSE: + { + int res; + spin_lock_irq(&substream->runtime->lock); + res = snd_pcm_pause(substream, (long) arg); + spin_unlock_irq(&substream->runtime->lock); + return res; + } + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_playback_drain(substream); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_playback_drop(substream); + case SNDRV_PCM_IOCTL_DELAY: + return snd_pcm_playback_delay(substream, (snd_pcm_sframes_t*) arg); + } + return snd_pcm_common_ioctl1(substream, cmd, arg); +} + +static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + snd_assert(substream != NULL, return -ENXIO); + snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); + switch (cmd) { + case SNDRV_PCM_IOCTL_READI_FRAMES: + { + snd_xferi_t xferi, *_xferi = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (put_user(0, &_xferi->result)) + return -EFAULT; + if (copy_from_user(&xferi, _xferi, sizeof(xferi))) + return -EFAULT; + result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); + __put_user(result, &_xferi->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_READN_FRAMES: + { + snd_xfern_t xfern, *_xfern = arg; + snd_pcm_runtime_t *runtime = substream->runtime; + void *bufs[128]; + snd_pcm_sframes_t result; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (runtime->channels > 128) + return -EINVAL; + if (put_user(0, &_xfern->result)) + return -EFAULT; + if (copy_from_user(&xfern, _xfern, sizeof(xfern))) + return -EFAULT; + if (copy_from_user(bufs, xfern.bufs, sizeof(*bufs) * runtime->channels)) + return -EFAULT; + result = snd_pcm_lib_readv(substream, bufs, xfern.frames); + __put_user(result, &_xfern->result); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_REWIND: + { + snd_pcm_uframes_t frames, *_frames = arg; + snd_pcm_sframes_t result; + if (get_user(frames, _frames)) + return -EFAULT; + if (put_user(0, _frames)) + return -EFAULT; + result = snd_pcm_capture_rewind(substream, frames); + __put_user(result, _frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_capture_drain(substream); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_capture_drop(substream); + case SNDRV_PCM_IOCTL_DELAY: + return snd_pcm_capture_delay(substream, (snd_pcm_sframes_t*) arg); + } + return snd_pcm_common_ioctl1(substream, cmd, arg); +} + +static int snd_pcm_playback_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + + if (((cmd >> 8) & 0xff) != 'A') + return -ENOTTY; + + return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void *) arg); +} + +static int snd_pcm_capture_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_pcm_file_t *pcm_file; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + + if (((cmd >> 8) & 0xff) != 'A') + return -ENOTTY; + + return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void *) arg); +} + +int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + mm_segment_t fs; + int result; + + fs = snd_enter_user(); + result = snd_pcm_playback_ioctl1(substream, cmd, arg); + snd_leave_user(fs); + return result; +} + +int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + mm_segment_t fs; + int result; + + fs = snd_enter_user(); + result = snd_pcm_capture_ioctl1(substream, cmd, arg); + snd_leave_user(fs); + return result; +} + +int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + return snd_pcm_kernel_playback_ioctl(substream, cmd, arg); + case SNDRV_PCM_STREAM_CAPTURE: + return snd_pcm_kernel_capture_ioctl(substream, cmd, arg); + default: + return -EINVAL; + } +} + +static ssize_t snd_pcm_read(struct file *file, char *buf, size_t count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!frame_aligned(runtime, count)) + return -EINVAL; + count = bytes_to_frames(runtime, count); + result = snd_pcm_lib_read(substream, buf, count); + if (result > 0) + result = frames_to_bytes(runtime, result); + return result; +} + +static ssize_t snd_pcm_write(struct file *file, const char *buf, size_t count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + + up(&file->f_dentry->d_inode->i_sem); + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + substream = pcm_file->substream; + snd_assert(substream != NULL, result = -ENXIO; goto end); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + result = -EBADFD; + goto end; + } + if (!frame_aligned(runtime, count)) { + result = -EINVAL; + goto end; + } + count = bytes_to_frames(runtime, count); + result = snd_pcm_lib_write(substream, buf, count); + if (result > 0) + result = frames_to_bytes(runtime, result); + end: + down(&file->f_dentry->d_inode->i_sem); + return result; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) +static ssize_t snd_pcm_readv(struct file *file, const struct iovec *_vector, + unsigned long count, loff_t * offset) + +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + unsigned long i; + void *bufs[128]; + snd_pcm_uframes_t frames; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (count > 128 || count != runtime->channels) + return -EINVAL; + if (!frame_aligned(runtime, _vector->iov_len)) + return -EINVAL; + frames = bytes_to_samples(runtime, _vector->iov_len); + for (i = 0; i < count; ++i) + bufs[i] = _vector[i].iov_base; + result = snd_pcm_lib_readv(substream, bufs, frames); + if (result > 0) + result = frames_to_bytes(runtime, result); + return result; +} + +static ssize_t snd_pcm_writev(struct file *file, const struct iovec *_vector, + unsigned long count, loff_t * offset) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + snd_pcm_sframes_t result; + unsigned long i; + void *bufs[128]; + snd_pcm_uframes_t frames; + + up(&file->f_dentry->d_inode->i_sem); + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + substream = pcm_file->substream; + snd_assert(substream != NULL, result = -ENXIO; goto end); + runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + result = -EBADFD; + goto end; + } + if (count > 128 || count != runtime->channels || + !frame_aligned(runtime, _vector->iov_len)) { + result = -EINVAL; + goto end; + } + frames = bytes_to_samples(runtime, _vector->iov_len); + for (i = 0; i < count; ++i) + bufs[i] = _vector[i].iov_base; + result = snd_pcm_lib_writev(substream, bufs, frames); + if (result > 0) + result = frames_to_bytes(runtime, result); + end: + down(&file->f_dentry->d_inode->i_sem); + return result; +} +#endif + +unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + unsigned int mask; + snd_pcm_uframes_t avail; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + poll_wait(file, &runtime->sleep, wait); + + spin_lock_irq(&runtime->lock); + avail = snd_pcm_playback_avail(runtime); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + if (avail >= runtime->control->avail_min) { + mask = POLLOUT | POLLWRNORM; + break; + } + /* Fall through */ + case SNDRV_PCM_STATE_DRAINING: + mask = 0; + break; + case SNDRV_PCM_STATE_PREPARED: + if (avail > 0) { + mask = POLLOUT | POLLWRNORM; + break; + } + /* Fall through */ + default: + mask = POLLOUT | POLLWRNORM | POLLERR; + break; + } + spin_unlock_irq(&runtime->lock); + return mask; +} + +unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) +{ + snd_pcm_file_t *pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + unsigned int mask; + snd_pcm_uframes_t avail; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + poll_wait(file, &runtime->sleep, wait); + + spin_lock_irq(&runtime->lock); + avail = snd_pcm_capture_avail(runtime); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_RUNNING: + if (avail >= runtime->control->avail_min) { + mask = POLLIN | POLLRDNORM; + break; + } + mask = 0; + break; + case SNDRV_PCM_STATE_DRAINING: + if (avail > 0) { + mask = POLLIN | POLLRDNORM; + break; + } + /* Fall through */ + default: + mask = POLLIN | POLLRDNORM | POLLERR; + break; + } + spin_unlock_irq(&runtime->lock); + return mask; +} + +#ifndef VM_RESERVED +#ifndef LINUX_2_2 +static int snd_pcm_mmap_swapout(struct page * page, struct file * file) +#else +static int snd_pcm_mmap_swapout(struct vm_area_struct * area, struct page * page) +#endif +{ + return 0; +} +#endif + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + struct page * page; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; + page = virt_to_page(runtime->status); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_status = +{ + nopage: snd_pcm_mmap_status_nopage, +#ifndef VM_RESERVED + swapout: snd_pcm_mmap_swapout, +#endif +}; + +int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + size = area->vm_end - area->vm_start; + if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t))) + return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_status; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + return 0; +} + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + struct page * page; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; + page = virt_to_page(runtime->control); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_control = +{ + nopage: snd_pcm_mmap_control_nopage, +#ifndef VM_RESERVED + swapout: snd_pcm_mmap_swapout, +#endif +}; + +static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + size = area->vm_end - area->vm_start; + if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t))) + return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_control; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + return 0; +} + +static void snd_pcm_mmap_data_open(struct vm_area_struct *area) +{ +#if 0 + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_inc(&substream->runtime->mmap_count); +#endif +} + +static void snd_pcm_mmap_data_close(struct vm_area_struct *area) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_dec(&substream->runtime->mmap_count); +} + +#ifndef LINUX_2_2 +static struct page * snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#else +static unsigned long snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int no_share) +#endif +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + snd_pcm_runtime_t *runtime; + unsigned long offset; + struct page * page; + size_t dma_bytes; + + if (substream == NULL) + return NOPAGE_OOM; + runtime = substream->runtime; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + offset += address - area->vm_start; + snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM); + dma_bytes = PAGE_ALIGN(runtime->dma_bytes); + if (offset > dma_bytes - PAGE_SIZE) + return NOPAGE_SIGBUS; + page = virt_to_page(runtime->dma_area + offset); + get_page(page); +#ifndef LINUX_2_2 + return page; +#else + return page_address(page); +#endif +} + +static struct vm_operations_struct snd_pcm_vm_ops_data = +{ + open: snd_pcm_mmap_data_open, + close: snd_pcm_mmap_data_close, + nopage: snd_pcm_mmap_data_nopage, +#ifndef VM_RESERVED + swapout: snd_pcm_mmap_swapout, +#endif +}; + +int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) +{ + snd_pcm_runtime_t *runtime; + long size; + unsigned long offset; + size_t dma_bytes; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (!(area->vm_flags & (VM_WRITE|VM_READ))) + return -EINVAL; + } else { + if (!(area->vm_flags & VM_READ)) + return -EINVAL; + } + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EAGAIN); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) + return -ENXIO; + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + size = area->vm_end - area->vm_start; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + dma_bytes = PAGE_ALIGN(runtime->dma_bytes); + if (size > dma_bytes) + return -EINVAL; + if (offset > dma_bytes - size) + return -EINVAL; + + area->vm_ops = &snd_pcm_vm_ops_data; +#ifndef LINUX_2_2 + area->vm_private_data = substream; +#else + area->vm_private_data = (long)substream; +#endif +#ifdef VM_RESERVED + area->vm_flags |= VM_RESERVED; +#endif + atomic_inc(&runtime->mmap_count); + return 0; +} + +static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) +{ + snd_pcm_file_t * pcm_file; + snd_pcm_substream_t *substream; + unsigned long offset; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 25) + offset = area->vm_pgoff << PAGE_SHIFT; +#else + offset = area->vm_offset; +#endif + switch (offset) { + case SNDRV_PCM_MMAP_OFFSET_STATUS: + return snd_pcm_mmap_status(substream, file, area); + case SNDRV_PCM_MMAP_OFFSET_CONTROL: + return snd_pcm_mmap_control(substream, file, area); + default: + return snd_pcm_mmap_data(substream, file, area); + } + return 0; +} + +static int snd_pcm_fasync(int fd, struct file * file, int on) +{ + snd_pcm_file_t * pcm_file; + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + int err; + + pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + substream = pcm_file->substream; + snd_assert(substream != NULL, return -ENXIO); + runtime = substream->runtime; + + err = fasync_helper(fd, file, on, &runtime->fasync); + if (err < 0) + return err; + return 0; +} + +/* + * Register section + */ + +static struct file_operations snd_pcm_f_ops_playback = { +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + write: snd_pcm_write, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) + writev: snd_pcm_writev, +#endif + open: snd_pcm_open, + release: snd_pcm_release, + poll: snd_pcm_playback_poll, + ioctl: snd_pcm_playback_ioctl, + mmap: snd_pcm_mmap, + fasync: snd_pcm_fasync, +}; + +static struct file_operations snd_pcm_f_ops_capture = { +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_pcm_read, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44) + readv: snd_pcm_readv, +#endif + open: snd_pcm_open, + release: snd_pcm_release, + poll: snd_pcm_capture_poll, + ioctl: snd_pcm_capture_ioctl, + mmap: snd_pcm_mmap, + fasync: snd_pcm_fasync, +}; + +snd_minor_t snd_pcm_reg[2] = +{ + { + comment: "digital audio playback", + f_ops: &snd_pcm_f_ops_playback, + }, + { + comment: "digital audio capture", + f_ops: &snd_pcm_f_ops_capture, + } +}; diff -Nru linux/sound/core/pcm_timer.c linux-2.4.19-pre5-mjc/sound/core/pcm_timer.c --- linux/sound/core/pcm_timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/pcm_timer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,159 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#define chip_t snd_pcm_substream_t + +/* + * Timer functions + */ + +/* Greatest common divisor */ +static int gcd(int a, int b) +{ + int r; + if (a < b) { + r = a; + a = b; + b = r; + } + while ((r = a % b) != 0) { + a = b; + b = r; + } + return b; +} + +void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream) +{ + unsigned int rate, mult, fsize, l; + snd_pcm_runtime_t *runtime = substream->runtime; + + mult = 1000000000; + rate = runtime->rate; + snd_assert(rate != 0, return); + l = gcd(mult, rate); + mult /= l; + rate /= l; + fsize = runtime->period_size; + snd_assert(fsize != 0, return); + l = gcd(rate, fsize); + rate /= l; + fsize /= l; + while ((mult * fsize) / fsize != mult) { + mult /= 2; + rate /= 2; + } + snd_assert(rate != 0, return); + runtime->timer_resolution = mult * fsize / rate; +} + +static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer) +{ + snd_pcm_substream_t * substream; + + substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return -ENXIO); + return substream->runtime ? substream->runtime->timer_resolution : 0; +} + +static int snd_pcm_timer_start(snd_timer_t * timer) +{ + unsigned long flags; + snd_pcm_substream_t * substream; + + substream = snd_timer_chip(timer); + spin_lock_irqsave(&substream->timer_lock, flags); + substream->timer_running = 1; + spin_unlock_irqrestore(&substream->timer_lock, flags); + return 0; +} + +static int snd_pcm_timer_stop(snd_timer_t * timer) +{ + unsigned long flags; + snd_pcm_substream_t * substream; + + substream = snd_timer_chip(timer); + spin_lock_irqsave(&substream->timer_lock, flags); + substream->timer_running = 0; + spin_unlock_irqrestore(&substream->timer_lock, flags); + return 0; +} + +static struct _snd_timer_hardware snd_pcm_timer = +{ + flags: SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE, + resolution: 0, + ticks: 1, + c_resolution: snd_pcm_timer_resolution, + start: snd_pcm_timer_start, + stop: snd_pcm_timer_stop, +}; + +/* + * Init functions + */ + +static void snd_pcm_timer_free(snd_timer_t *timer) +{ + snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return); + substream->timer = NULL; +} + +void snd_pcm_timer_init(snd_pcm_substream_t *substream) +{ + snd_timer_id_t tid; + snd_timer_t *timer; + + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.dev_class = SNDRV_TIMER_CLASS_PCM; + tid.card = substream->pcm->card->number; + tid.device = substream->pcm->device; + tid.subdevice = (substream->number << 1) | (substream->stream & 1); + if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0) + return; + sprintf(timer->name, "PCM %s %i-%i-%i", + substream->stream == SNDRV_PCM_STREAM_CAPTURE ? + "capture" : "playback", + tid.card, tid.device, tid.subdevice); + timer->hw = snd_pcm_timer; + if (snd_device_register(timer->card, timer) < 0) { + snd_device_free(timer->card, timer); + return; + } + timer->private_data = substream; + timer->private_free = snd_pcm_timer_free; + substream->timer = timer; +} + +void snd_pcm_timer_done(snd_pcm_substream_t *substream) +{ + if (substream->timer) { + snd_device_free(substream->pcm->card, substream->timer); + substream->timer = NULL; + } +} diff -Nru linux/sound/core/rawmidi.c linux-2.4.19-pre5-mjc/sound/core/rawmidi.c --- linux/sound/core/rawmidi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/rawmidi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1574 @@ +/* + * Abstract layer for MIDI v1.0 stream + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA."); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_SND_OSSEMUL +static int snd_midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int snd_amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; +MODULE_PARM(snd_midi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_midi_map, "Raw MIDI device number assigned to 1st OSS device."); +MODULE_PARM_SYNTAX(snd_midi_map, "default:0,skill:advanced"); +MODULE_PARM(snd_amidi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); +MODULE_PARM_SYNTAX(snd_amidi_map, "default:1,skill:advanced"); +#endif /* CONFIG_SND_OSSEMUL */ + +static int snd_rawmidi_free(snd_rawmidi_t *rawmidi); +static int snd_rawmidi_dev_free(snd_device_t *device); +static int snd_rawmidi_dev_register(snd_device_t *device); +static int snd_rawmidi_dev_unregister(snd_device_t *device); + +snd_rawmidi_t *snd_rawmidi_devices[SNDRV_CARDS * SNDRV_RAWMIDI_DEVICES]; + +static DECLARE_MUTEX(register_mutex); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static inline unsigned short snd_rawmidi_file_flags(struct file *file) +{ + switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { + case FMODE_WRITE: + return SNDRV_RAWMIDI_LFLG_OUTPUT; + case FMODE_READ: + return SNDRV_RAWMIDI_LFLG_INPUT; + default: + return SNDRV_RAWMIDI_LFLG_OPEN; + } +} + +static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + return runtime->avail >= runtime->avail_min; +} + +static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + return runtime->avail >= runtime->avail_min && runtime->avail >= count; +} + +static int snd_rawmidi_init(snd_rawmidi_substream_t *substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + spin_lock_init(&runtime->lock); + init_waitqueue_head(&runtime->sleep); + runtime->event = NULL; + runtime->buffer_size = PAGE_SIZE; + runtime->avail_min = 1; + if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT) + runtime->avail = 0; + else + runtime->avail = runtime->buffer_size; + if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + runtime->appl_ptr = runtime->hw_ptr = 0; + return 0; +} + +static int snd_rawmidi_done_buffer(snd_rawmidi_runtime_t *runtime) +{ + if (runtime->buffer) { + kfree(runtime->buffer); + runtime->buffer = NULL; + } + return 0; +} + +int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + substream->ops->trigger(substream, 0); + runtime->trigger = 0; + runtime->drain = 0; + /* interrupts are not enabled at this moment, + so spinlock is not required */ + runtime->appl_ptr = runtime->hw_ptr = 0; + runtime->avail = runtime->buffer_size; + return 0; +} + +int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream) +{ + int err; + long timeout; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + err = 0; + runtime->drain = 1; + while (runtime->avail < runtime->buffer_size) { + timeout = interruptible_sleep_on_timeout(&runtime->sleep, 10 * HZ); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + if (runtime->avail < runtime->buffer_size && !timeout) { + err = -EIO; + break; + } + } + runtime->drain = 0; + if (err != -ERESTARTSYS) { + /* we need wait a while to make sure that Tx FIFOs are empty */ + if (substream->ops->drain) + substream->ops->drain(substream); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + snd_rawmidi_drop_output(substream); + } + return err; +} + +int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + substream->ops->trigger(substream, 0); + runtime->trigger = 0; + runtime->drain = 0; + /* interrupts aren't enabled at this moment, so spinlock isn't needed */ + runtime->appl_ptr = runtime->hw_ptr = 0; + runtime->avail = 0; + return 0; +} + +int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, + int mode, snd_rawmidi_file_t * rfile) +{ + snd_rawmidi_t *rmidi; + struct list_head *list1, *list2; + snd_rawmidi_substream_t *sinput, *soutput; + snd_rawmidi_runtime_t *input = NULL, *output = NULL; + int err; + + if (rfile) + rfile->input = rfile->output = NULL; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; + if (rmidi == NULL) { + err = -ENODEV; + goto __error1; + } + if (!try_inc_mod_count(rmidi->card->module)) { + err = -EFAULT; + goto __error1; + } + down(&rmidi->open_mutex); + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) { + err = -ENXIO; + goto __error; + } + if (subdevice >= 0 && subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { + err = -ENODEV; + goto __error; + } + if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >= + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) { + err = -EAGAIN; + goto __error; + } + } + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) { + err = -ENXIO; + goto __error; + } + if (subdevice >= 0 && subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { + err = -ENODEV; + goto __error; + } + if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >= + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) { + err = -EAGAIN; + goto __error; + } + } + list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next; + while (1) { + if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + sinput = NULL; + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + err = -EAGAIN; + goto __error; + } + break; + } + sinput = list_entry(list1, snd_rawmidi_substream_t, list); + if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened) + goto __nexti; + if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number)) + break; + __nexti: + list1 = list1->next; + } + list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next; + while (1) { + if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + soutput = NULL; + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + err = -EAGAIN; + goto __error; + } + break; + } + soutput = list_entry(list2, snd_rawmidi_substream_t, list); + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (mode & SNDRV_RAWMIDI_LFLG_APPEND) { + if (soutput->opened && !soutput->append) + goto __nexto; + } else { + if (soutput->opened) + goto __nexto; + } + } + if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number)) + break; + __nexto: + list2 = list2->next; + } + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + input = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + if (input == NULL) { + err = -ENOMEM; + goto __error; + } + sinput->runtime = input; + if (snd_rawmidi_init(sinput) < 0) { + err = -ENOMEM; + goto __error; + } + if ((err = sinput->ops->open(sinput)) < 0) { + sinput->runtime = NULL; + goto __error; + } + sinput->opened = 1; + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++; + } else { + sinput = NULL; + } + if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { + if (soutput->opened) + goto __skip_output; + output = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + if (output == NULL) { + err = -ENOMEM; + goto __error; + } + soutput->runtime = output; + if (snd_rawmidi_init(soutput) < 0) { + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + sinput->ops->close(sinput); + sinput->runtime = NULL; + } + err = -ENOMEM; + goto __error; + } + if ((err = soutput->ops->open(soutput)) < 0) { + if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { + sinput->ops->close(sinput); + sinput->runtime = NULL; + } + soutput->runtime = NULL; + goto __error; + } + __skip_output: + soutput->opened = 1; + if (mode & SNDRV_RAWMIDI_LFLG_APPEND) + soutput->append = 1; + if (soutput->use_count++ == 0) + soutput->active_sensing = 1; + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++; + } else { + soutput = NULL; + } + up(&rmidi->open_mutex); + if (rfile) { + rfile->rmidi = rmidi; + rfile->input = sinput; + rfile->output = soutput; + } + return 0; + + __error: + if (input != NULL) + kfree(input); + if (output != NULL) + kfree(output); + dec_mod_count(rmidi->card->module); + up(&rmidi->open_mutex); + __error1: +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return err; +} + +static int snd_rawmidi_open(struct inode *inode, struct file *file) +{ + int maj = major(inode->i_rdev); + int cardnum; + snd_card_t *card; + int device, subdevice; + unsigned short fflags; + int err; + snd_rawmidi_t *rmidi; + snd_rawmidi_file_t *rawmidi_file; + wait_queue_t wait; + struct list_head *list; + snd_ctl_file_t *kctl; + + switch (maj) { + case CONFIG_SND_MAJOR: + cardnum = SNDRV_MINOR_CARD(minor(inode->i_rdev)); + cardnum %= SNDRV_CARDS; + device = SNDRV_MINOR_DEVICE(minor(inode->i_rdev)) - SNDRV_MINOR_RAWMIDI; + device %= SNDRV_MINOR_RAWMIDIS; + break; +#ifdef CONFIG_SND_OSSEMUL + case SOUND_MAJOR: + cardnum = SNDRV_MINOR_OSS_CARD(minor(inode->i_rdev)); + cardnum %= SNDRV_CARDS; + device = SNDRV_MINOR_OSS_DEVICE(minor(inode->i_rdev)) == SNDRV_MINOR_OSS_MIDI ? + snd_midi_map[cardnum] : snd_amidi_map[cardnum]; + break; +#endif + default: + return -ENXIO; + } + + rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device]; + if (rmidi == NULL) + return -ENODEV; + card = rmidi->card; +#ifdef CONFIG_SND_OSSEMUL + if (maj == SOUND_MAJOR && !rmidi->ossreg) + return -ENXIO; +#endif + fflags = snd_rawmidi_file_flags(file); + if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) + return -EINVAL; /* invalid combination */ + if ((file->f_flags & O_APPEND) || maj != CONFIG_SND_MAJOR) /* OSS emul? */ + fflags |= SNDRV_RAWMIDI_LFLG_APPEND; + rawmidi_file = snd_magic_kmalloc(snd_rawmidi_file_t, 0, GFP_KERNEL); + if (rawmidi_file == NULL) + return -ENOMEM; + init_waitqueue_entry(&wait, current); + add_wait_queue(&rmidi->open_wait, &wait); + while (1) { + subdevice = -1; + read_lock(&card->control_rwlock); + list_for_each(list, &card->ctl_files) { + kctl = snd_ctl_file(list); + if (kctl->pid == current->pid) { + subdevice = kctl->prefer_rawmidi_subdevice; + break; + } + } + read_unlock(&card->control_rwlock); + err = snd_rawmidi_kernel_open(cardnum, device, subdevice, fflags, rawmidi_file); + if (err >= 0) + break; + if (err == -EAGAIN) { + if (file->f_flags & O_NONBLOCK) { + err = -EBUSY; + break; + } + } else + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } +#ifdef CONFIG_SND_OSSEMUL + if (rawmidi_file->input && rawmidi_file->input->runtime) + rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR); + if (rawmidi_file->output && rawmidi_file->output->runtime) + rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR); +#endif + set_current_state(TASK_RUNNING); + remove_wait_queue(&rmidi->open_wait, &wait); + if (err >= 0) { + file->private_data = rawmidi_file; + } else { + snd_magic_kfree(rawmidi_file); + } + return err; +} + +int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + + snd_assert(rfile != NULL, return -ENXIO); + snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO); + rmidi = rfile->rmidi; + down(&rmidi->open_mutex); + if (rfile->input != NULL) { + substream = rfile->input; + rfile->input = NULL; + runtime = substream->runtime; + runtime->trigger = 0; + substream->ops->trigger(substream, 0); + substream->ops->close(substream); + snd_rawmidi_done_buffer(runtime); + if (runtime->private_free != NULL) + runtime->private_free(substream); + kfree(runtime); + substream->runtime = NULL; + substream->opened = 0; + rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--; + } + if (rfile->output != NULL) { + substream = rfile->output; + rfile->output = NULL; + if (--substream->use_count == 0) { + runtime = substream->runtime; + if (substream->active_sensing) { + unsigned char buf = 0xfe; + /* sending single active sensing message to shut the device up */ + snd_rawmidi_kernel_write(substream, &buf, 1); + } + if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS) + substream->ops->trigger(substream, 0); + substream->ops->close(substream); + snd_rawmidi_done_buffer(runtime); + if (runtime->private_free != NULL) + runtime->private_free(substream); + kfree(runtime); + substream->runtime = NULL; + substream->opened = 0; + substream->append = 0; + } + rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--; + } + up(&rmidi->open_mutex); + dec_mod_count(rmidi->card->module); +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static int snd_rawmidi_release(struct inode *inode, struct file *file) +{ + snd_rawmidi_file_t *rfile; + int err; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + err = snd_rawmidi_kernel_release(rfile); + wake_up(&rfile->rmidi->open_wait); + snd_magic_kfree(rfile); + return err; +} + +int snd_rawmidi_info(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t *info) +{ + snd_rawmidi_t *rmidi = substream->rmidi; + memset(info, 0, sizeof(*info)); + info->card = rmidi->card->number; + info->device = rmidi->device; + info->subdevice = substream->number; + info->stream = substream->stream; + info->flags = rmidi->info_flags; + strcpy(info->id, rmidi->id); + strcpy(info->name, rmidi->name); + strcpy(info->subname, substream->name); + info->subdevices_count = substream->pstr->substream_count; + info->subdevices_avail = (substream->pstr->substream_count - + substream->pstr->substream_opened); + return 0; +} + +static int snd_rawmidi_info_user(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t * _info) +{ + snd_rawmidi_info_t info; + int err; + if ((err = snd_rawmidi_info(substream, &info)) < 0) + return err; + if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) + return -EFAULT; + return 0; +} + +int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_str_t *pstr; + snd_rawmidi_substream_t *substream; + struct list_head *list; + if (info->device >= SNDRV_RAWMIDI_DEVICES) + return -ENXIO; + rmidi = snd_rawmidi_devices[card->number * SNDRV_RAWMIDI_DEVICES + info->device]; + if (info->stream < 0 || info->stream > 1) + return -EINVAL; + pstr = &rmidi->streams[info->stream]; + if (pstr->substream_count == 0) + return -ENOENT; + if (info->subdevice >= pstr->substream_count) + return -ENXIO; + list_for_each(list, &pstr->substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + if (substream->number == info->subdevice) + return snd_rawmidi_info(substream, info); + } + return -ENXIO; +} + +static int snd_rawmidi_info_select_user(snd_card_t *card, + snd_rawmidi_info_t *_info) +{ + int err; + snd_rawmidi_info_t info; + if (get_user(info.device, &_info->device)) + return -EFAULT; + if (get_user(info.stream, &_info->stream)) + return -EFAULT; + if (get_user(info.subdevice, &_info->subdevice)) + return -EFAULT; + if ((err = snd_rawmidi_info_select(card, &info)) < 0) + return err; + if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t))) + return -EFAULT; + return 0; +} + +int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream, + snd_rawmidi_params_t * params) +{ + char *newbuf; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (substream->append && substream->use_count > 1) + return -EBUSY; + snd_rawmidi_drain_output(substream); + if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->avail_min < 1 || params->avail_min > params->buffer_size) { + return -EINVAL; + } + if (params->buffer_size != runtime->buffer_size) { + if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + kfree(runtime->buffer); + runtime->buffer = newbuf; + runtime->buffer_size = params->buffer_size; + } + runtime->avail_min = params->avail_min; + substream->active_sensing = !params->no_active_sensing; + return 0; +} + +int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream, + snd_rawmidi_params_t * params) +{ + char *newbuf; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + snd_rawmidi_drain_input(substream); + if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->avail_min < 1 || params->avail_min > params->buffer_size) { + return -EINVAL; + } + if (params->buffer_size != runtime->buffer_size) { + if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL) + return -ENOMEM; + kfree(runtime->buffer); + runtime->buffer = newbuf; + runtime->buffer_size = params->buffer_size; + } + runtime->avail_min = params->avail_min; + return 0; +} + +static int snd_rawmidi_output_status(snd_rawmidi_substream_t * substream, + snd_rawmidi_status_t * status) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + memset(status, 0, sizeof(*status)); + status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + spin_lock_irq(&runtime->lock); + status->avail = runtime->avail; + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_rawmidi_input_status(snd_rawmidi_substream_t * substream, + snd_rawmidi_status_t * status) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + + memset(status, 0, sizeof(*status)); + status->stream = SNDRV_RAWMIDI_STREAM_INPUT; + spin_lock_irq(&runtime->lock); + status->avail = runtime->avail; + status->xruns = runtime->xruns; + runtime->xruns = 0; + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_rawmidi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_rawmidi_file_t *rfile; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + if (((cmd >> 8) & 0xff) != 'W') + return -ENOTTY; + switch (cmd) { + case SNDRV_RAWMIDI_IOCTL_PVERSION: + return put_user(SNDRV_RAWMIDI_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_RAWMIDI_IOCTL_INFO: + { + snd_rawmidi_stream_t stream; + snd_rawmidi_info_t *info = (snd_rawmidi_info_t *) arg; + if (get_user(stream, &info->stream)) + return -EFAULT; + switch (stream) { + case SNDRV_RAWMIDI_STREAM_INPUT: + return snd_rawmidi_info_user(rfile->input, info); + case SNDRV_RAWMIDI_STREAM_OUTPUT: + return snd_rawmidi_info_user(rfile->output, info); + default: + return -EINVAL; + } + } + case SNDRV_RAWMIDI_IOCTL_PARAMS: + { + snd_rawmidi_params_t params; + int err; + if (copy_from_user(¶ms, (snd_rawmidi_params_t *) arg, sizeof(snd_rawmidi_params_t))) + return -EFAULT; + switch (params.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_output_params(rfile->output, ¶ms); + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + return snd_rawmidi_input_params(rfile->input, ¶ms); + default: + return -EINVAL; + } + if (copy_to_user((snd_rawmidi_params_t *) arg, ¶ms, sizeof(snd_rawmidi_params_t))) + return -EFAULT; + return err; + } + case SNDRV_RAWMIDI_IOCTL_STATUS: + { + int err = 0; + snd_rawmidi_status_t status; + if (copy_from_user(&status, (snd_rawmidi_status_t *) arg, sizeof(snd_rawmidi_status_t))) + return -EFAULT; + switch (status.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + err = snd_rawmidi_output_status(rfile->output, &status); + break; + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + err = snd_rawmidi_input_status(rfile->input, &status); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + if (copy_to_user((snd_rawmidi_status_t *) arg, &status, sizeof(snd_rawmidi_status_t))) + return -EFAULT; + return 0; + } + case SNDRV_RAWMIDI_IOCTL_DROP: + { + int val; + if (get_user(val, (long *) arg)) + return -EFAULT; + switch (val) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_drop_output(rfile->output); + default: + return -EINVAL; + } + } + case SNDRV_RAWMIDI_IOCTL_DRAIN: + { + int val; + if (get_user(val, (long *) arg)) + return -EFAULT; + switch (val) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + if (rfile->output == NULL) + return -EINVAL; + return snd_rawmidi_drain_output(rfile->output); + case SNDRV_RAWMIDI_STREAM_INPUT: + if (rfile->input == NULL) + return -EINVAL; + return snd_rawmidi_drain_input(rfile->input); + default: + return -EINVAL; + } + } +#ifdef CONFIG_SND_DEBUG + default: + snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd); +#endif + } + return -ENOTTY; +} + +int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, + unsigned int cmd, unsigned long arg) +{ + unsigned int tmp; + + tmp = card->number * SNDRV_RAWMIDI_DEVICES; + switch (cmd) { + case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE: + { + int device; + + if (get_user(device, (int *)arg)) + return -EFAULT; + device = device < 0 ? 0 : device + 1; + while (device < SNDRV_RAWMIDI_DEVICES) { + if (snd_rawmidi_devices[tmp + device]) + break; + device++; + } + if (device == SNDRV_RAWMIDI_DEVICES) + device = -1; + if (put_user(device, (int *)arg)) + return -EFAULT; + return 0; + } + case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE: + { + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + control->prefer_rawmidi_subdevice = val; + return 0; + } + case SNDRV_CTL_IOCTL_RAWMIDI_INFO: + return snd_rawmidi_info_select_user(card, (snd_rawmidi_info_t *)arg); + } + return -ENOIOCTLCMD; +} + +void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream) +{ + /* TODO: reset current state */ +} + +int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + unsigned long flags; + int result = 0, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_receive: input is not active!!!\n"); + return -EINVAL; + } + spin_lock_irqsave(&runtime->lock, flags); + if (count == 1) { /* special case, faster code */ + substream->bytes++; + if (runtime->avail < runtime->buffer_size) { + runtime->buffer[runtime->hw_ptr++] = buffer[0]; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail++; + result++; + } else { + runtime->xruns++; + } + } else { + substream->bytes += count; + count1 = runtime->buffer_size - runtime->hw_ptr; + if (count1 > count) + count1 = count; + if (count1 > runtime->buffer_size - runtime->avail) + count1 = runtime->buffer_size - runtime->avail; + memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1); + runtime->hw_ptr += count1; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail += count1; + count -= count1; + result += count1; + if (count > 0) { + buffer += count1; + count1 = count; + if (count1 > runtime->buffer_size - runtime->avail) { + count1 = runtime->buffer_size - runtime->avail; + runtime->xruns = count - count1; + } + if (count1 > 0) { + memcpy(runtime->buffer, buffer, count1); + runtime->hw_ptr = count1; + runtime->avail += count1; + result += count1; + } + } + } + if (result > 0 && runtime->event == NULL) { + if (snd_rawmidi_ready(substream)) + wake_up(&runtime->sleep); + } + spin_unlock_irqrestore(&runtime->lock, flags); + if (result > 0 && runtime->event) + runtime->event(substream); + return result; +} + +static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream, + unsigned char *buf, long count, int kernel) +{ + unsigned long flags; + long result = 0, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + while (count > 0 && runtime->avail) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) + count1 = count; + spin_lock_irqsave(&runtime->lock, flags); + if (count1 > runtime->avail) + count1 = runtime->avail; + if (kernel) { + memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1); + } else { + if (copy_to_user(buf + result, runtime->buffer + runtime->appl_ptr, count1)) { + spin_unlock_irqrestore(&runtime->lock, flags); + return result > 0 ? result : -EFAULT; + } + } + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + spin_unlock_irqrestore(&runtime->lock, flags); + result += count1; + count -= count1; + } + return result; +} + +long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count) +{ + substream->runtime->trigger = 1; + substream->ops->trigger(substream, 1); + return snd_rawmidi_kernel_read1(substream, buf, count, 1); +} + +static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + long result; + int count1; + snd_rawmidi_file_t *rfile; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + substream = rfile->input; + if (substream == NULL) + return -EIO; + runtime = substream->runtime; + runtime->trigger = 1; + substream->ops->trigger(substream, 1); + result = 0; + while (count > 0) { + spin_lock_irq(&runtime->lock); + while (!snd_rawmidi_ready(substream)) { + wait_queue_t wait; + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EAGAIN; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + count1 = snd_rawmidi_kernel_read1(substream, buf, count, 0); + result += count1; + buf += count1; + count -= count1; + } + return result; +} + +void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream) +{ + /* TODO: reset current state */ +} + +int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime = substream->runtime; + int result; + unsigned long flags; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n"); + return 1; + } + spin_lock_irqsave(&runtime->lock, flags); + result = runtime->avail >= runtime->buffer_size; + if (result) + runtime->trigger = 1; + spin_unlock_irqrestore(&runtime->lock, flags); + return result; +} + +int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + unsigned long flags; + int result, count1; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n"); + return -EINVAL; + } + result = 0; + spin_lock_irqsave(&runtime->lock, flags); + if (runtime->avail >= runtime->buffer_size) { + /* warning: lowlevel layer MUST trigger down the hardware */ + runtime->trigger = 0; + goto __skip; + } + if (count == 1) { /* special case, faster code */ + *buffer = runtime->buffer[runtime->hw_ptr]; + result++; + } else { + count1 = runtime->buffer_size - runtime->hw_ptr; + if (count1 > count) + count1 = count; + if (count1 > runtime->buffer_size - runtime->avail) + count1 = runtime->buffer_size - runtime->avail; + memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1); + count -= count1; + result += count1; + if (count > 0) + memcpy(buffer + count1, runtime->buffer, count); + } + __skip: + spin_unlock_irqrestore(&runtime->lock, flags); + return result; +} + +int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) +{ + unsigned long flags; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + if (runtime->buffer == NULL) { + snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n"); + return -EINVAL; + } + spin_lock_irqsave(&runtime->lock, flags); + snd_assert(runtime->avail + count <= runtime->buffer_size, ); + runtime->hw_ptr += count; + runtime->hw_ptr %= runtime->buffer_size; + runtime->avail += count; + substream->bytes += count; + if (runtime->drain) + wake_up(&runtime->sleep); + else + if (count > 0 && runtime->event == NULL) + if (snd_rawmidi_ready(substream)) + wake_up(&runtime->sleep); + spin_unlock_irqrestore(&runtime->lock, flags); + if (count > 0 && runtime->event) + runtime->event(substream); + return count; +} + +int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count) +{ + count = snd_rawmidi_transmit_peek(substream, buffer, count); + if (count < 0) + return count; + return snd_rawmidi_transmit_ack(substream, count); +} + +static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count, int kernel) +{ + unsigned long flags; + long count1, result; + snd_rawmidi_runtime_t *runtime = substream->runtime; + + snd_assert(buf != NULL, return -EINVAL); + snd_assert(runtime->buffer != NULL, return -EINVAL); + + result = 0; + spin_lock_irqsave(&runtime->lock, flags); + if (substream->append) { + if (runtime->avail < count) { + spin_unlock_irqrestore(&runtime->lock, flags); + return -EAGAIN; + } + } + while (count > 0 && runtime->avail > 0) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) + count1 = count; + if (count1 > runtime->avail) + count1 = runtime->avail; + if (kernel) { + memcpy(runtime->buffer + runtime->appl_ptr, buf, count1); + } else { + if (copy_from_user(runtime->buffer + runtime->appl_ptr, buf, count1)) { + result = result > 0 ? result : -EFAULT; + goto __end; + } + } + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + result += count1; + buf += count1; + count -= count1; + } + __end: + if (result > 0) + runtime->trigger = 1; + spin_unlock_irqrestore(&runtime->lock, flags); + substream->ops->trigger(substream, 1); + return result; +} + +long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count) +{ + return snd_rawmidi_kernel_write1(substream, buf, count, 1); +} + +static ssize_t snd_rawmidi_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + long result, timeout; + int count1; + snd_rawmidi_file_t *rfile; + snd_rawmidi_runtime_t *runtime; + snd_rawmidi_substream_t *substream; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + substream = rfile->output; + runtime = substream->runtime; + /* we cannot put an atomic message to our buffer */ + if (substream->append && count > runtime->buffer_size) + return -EIO; + result = 0; + while (count > 0) { + spin_lock_irq(&runtime->lock); + while (!snd_rawmidi_ready_append(substream, count)) { + wait_queue_t wait; + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EAGAIN; + } + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(30 * HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail && !timeout) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + count1 = snd_rawmidi_kernel_write1(substream, buf, count, 0); + if (count1 < 0) + continue; + result += count1; + buf += count1; + if (count1 < count && (file->f_flags & O_NONBLOCK)) + break; + count -= count1; + } + while (file->f_flags & O_SYNC) { + spin_lock_irq(&runtime->lock); + while (runtime->avail != runtime->buffer_size) { + wait_queue_t wait; + unsigned int last_avail = runtime->avail; + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + spin_unlock_irq(&runtime->lock); + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(30 * HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&runtime->sleep, &wait); + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (runtime->avail == last_avail && !timeout) + return result > 0 ? result : -EIO; + spin_lock_irq(&runtime->lock); + } + spin_unlock_irq(&runtime->lock); + } + return result; +} + +static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait) +{ + snd_rawmidi_file_t *rfile; + snd_rawmidi_runtime_t *runtime; + unsigned int mask; + + rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return 0); + if (rfile->input != NULL) { + runtime = rfile->input->runtime; + runtime->trigger = 1; + rfile->input->ops->trigger(rfile->input, 1); + poll_wait(file, &runtime->sleep, wait); + } + if (rfile->output != NULL) { + runtime = rfile->output->runtime; + poll_wait(file, &runtime->sleep, wait); + } + mask = 0; + if (rfile->input != NULL) { + if (snd_rawmidi_ready(rfile->input)) + mask |= POLLIN | POLLRDNORM; + } + if (rfile->output != NULL) { + if (snd_rawmidi_ready(rfile->output)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} + +/* + + */ + +static void snd_rawmidi_proc_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream; + snd_rawmidi_runtime_t *runtime; + struct list_head *list; + + rmidi = snd_magic_cast(snd_rawmidi_t, entry->private_data, return); + snd_iprintf(buffer, "%s\n\n", rmidi->name); + down(&rmidi->open_mutex); + if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { + list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_iprintf(buffer, + "Output %d\n" + " Tx bytes : %lu\n", + substream->number, + (unsigned long) substream->bytes); + if (substream->opened) { + runtime = substream->runtime; + snd_iprintf(buffer, + " Mode : %s\n" + " Buffer size : %lu\n" + " Avail : %lu\n", + runtime->oss ? "OSS compatible" : "native", + (unsigned long) runtime->buffer_size, + (unsigned long) runtime->avail); + } + } + } + if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) { + list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_iprintf(buffer, + "Input %d\n" + " Rx bytes : %lu\n", + substream->number, + (unsigned long) substream->bytes); + if (substream->opened) { + runtime = substream->runtime; + snd_iprintf(buffer, + " Buffer size : %lu\n" + " Avail : %lu\n" + " Overruns : %lu\n", + (unsigned long) runtime->buffer_size, + (unsigned long) runtime->avail, + (unsigned long) runtime->xruns); + } + } + } + up(&rmidi->open_mutex); +} + +/* + * Register functions + */ + +static struct file_operations snd_rawmidi_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_rawmidi_read, + write: snd_rawmidi_write, + open: snd_rawmidi_open, + release: snd_rawmidi_release, + poll: snd_rawmidi_poll, + ioctl: snd_rawmidi_ioctl, +}; + +static snd_minor_t snd_rawmidi_reg = +{ + comment: "raw midi", + f_ops: &snd_rawmidi_f_ops, +}; + +static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi, + snd_rawmidi_str_t *stream, + int direction, + int count) +{ + snd_rawmidi_substream_t *substream; + int idx; + + INIT_LIST_HEAD(&stream->substreams); + for (idx = 0; idx < count; idx++) { + substream = snd_kcalloc(sizeof(snd_rawmidi_substream_t), GFP_KERNEL); + if (substream == NULL) + return -ENOMEM; + substream->stream = direction; + substream->number = idx; + substream->rmidi = rmidi; + substream->pstr = stream; + list_add_tail(&substream->list, &stream->substreams); + stream->substream_count++; + } + return 0; +} + +int snd_rawmidi_new(snd_card_t * card, char *id, int device, + int output_count, int input_count, + snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + static snd_device_ops_t ops = { + dev_free: snd_rawmidi_dev_free, + dev_register: snd_rawmidi_dev_register, + dev_unregister: snd_rawmidi_dev_unregister + }; + + snd_assert(rrawmidi != NULL, return -EINVAL); + *rrawmidi = NULL; + snd_assert(card != NULL, return -ENXIO); + rmidi = snd_magic_kcalloc(snd_rawmidi_t, 0, GFP_KERNEL); + if (rmidi == NULL) + return -ENOMEM; + rmidi->card = card; + rmidi->device = device; + init_MUTEX(&rmidi->open_mutex); + init_waitqueue_head(&rmidi->open_wait); + if (id != NULL) + strncpy(rmidi->id, id, sizeof(rmidi->id) - 1); + if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], SNDRV_RAWMIDI_STREAM_INPUT, input_count)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], SNDRV_RAWMIDI_STREAM_OUTPUT, output_count)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) { + snd_rawmidi_free(rmidi); + return err; + } + *rrawmidi = rmidi; + return 0; +} + +static void snd_rawmidi_free_substreams(snd_rawmidi_str_t *stream) +{ + snd_rawmidi_substream_t *substream; + + while (!list_empty(&stream->substreams)) { + substream = list_entry(stream->substreams.next, snd_rawmidi_substream_t, list); + list_del(&substream->list); + kfree(substream); + } +} + +static int snd_rawmidi_free(snd_rawmidi_t *rmidi) +{ + snd_assert(rmidi != NULL, return -ENXIO); + snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); + snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); + if (rmidi->private_free) + rmidi->private_free(rmidi); + snd_magic_kfree(rmidi); + return 0; +} + +static int snd_rawmidi_dev_free(snd_device_t *device) +{ + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + return snd_rawmidi_free(rmidi); +} + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device) +{ + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->private_data, return); + rmidi->seq_dev = NULL; +} +#endif + +static int snd_rawmidi_dev_register(snd_device_t *device) +{ + int idx, err; + snd_info_entry_t *entry; + char name[16]; + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + + if (rmidi->device >= SNDRV_RAWMIDI_DEVICES) + return -ENOMEM; + down(®ister_mutex); + idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; + if (snd_rawmidi_devices[idx] != NULL) { + up(®ister_mutex); + return -EBUSY; + } + snd_rawmidi_devices[idx] = rmidi; + sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI, + rmidi->card, rmidi->device, + &snd_rawmidi_reg, name)) < 0) { + snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); + return err; + } + if (rmidi->ops && rmidi->ops->dev_register && + (err = rmidi->ops->dev_register(rmidi)) < 0) { + snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); + return err; + } +#ifdef CONFIG_SND_OSSEMUL + rmidi->ossreg = 0; + if (rmidi->device == snd_midi_map[rmidi->card->number]) { + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, + rmidi->card, 0, &snd_rawmidi_reg, name) < 0) { + snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0); + } else { + rmidi->ossreg++; + snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name); + } + } + if (rmidi->device == snd_amidi_map[rmidi->card->number]) { + if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, + rmidi->card, 1, &snd_rawmidi_reg, name) < 0) { + snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1); + } else { + rmidi->ossreg++; + } + } +#endif + up(®ister_mutex); + sprintf(name, "midi%d", rmidi->device); + entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = rmidi; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_rawmidi_proc_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + rmidi->proc_entry = entry; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ + if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { + rmidi->seq_dev->private_data = rmidi; + rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free; + sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device); + snd_device_register(rmidi->card, rmidi->seq_dev); + } + } +#endif + return 0; +} + +static int snd_rawmidi_dev_unregister(snd_device_t *device) +{ + int idx; + snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + + snd_assert(rmidi != NULL, return -ENXIO); + down(®ister_mutex); + idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device; + if (snd_rawmidi_devices[idx] != rmidi) { + up(®ister_mutex); + return -EINVAL; + } + if (rmidi->proc_entry) { + snd_info_unregister(rmidi->proc_entry); + rmidi->proc_entry = NULL; + } +#ifdef CONFIG_SND_OSSEMUL + if (rmidi->ossreg) { + if (rmidi->device == snd_midi_map[rmidi->card->number]) { + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number); + } + if (rmidi->device == snd_amidi_map[rmidi->card->number]) + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1); + rmidi->ossreg = 0; + } +#endif + if (rmidi->ops && rmidi->ops->dev_unregister) + rmidi->ops->dev_unregister(rmidi); + snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); + snd_rawmidi_devices[idx] = NULL; + up(®ister_mutex); +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (rmidi->seq_dev) { + snd_device_free(rmidi->card, rmidi->seq_dev); + rmidi->seq_dev = NULL; + } +#endif + return snd_rawmidi_free(rmidi); +} + +void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops) +{ + struct list_head *list; + snd_rawmidi_substream_t *substream; + + list_for_each(list, &rmidi->streams[stream].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + substream->ops = ops; + } +} + +/* + * ENTRY functions + */ + +static int __init alsa_rawmidi_init(void) +{ + int i; + + snd_ctl_register_ioctl(snd_rawmidi_control_ioctl); +#ifdef CONFIG_SND_OSSEMUL + /* check device map table */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (snd_midi_map[i] < 0 || snd_midi_map[i] >= SNDRV_RAWMIDI_DEVICES) { + snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, snd_midi_map[i]); + snd_midi_map[i] = 0; + } + if (snd_amidi_map[i] < 0 || snd_amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) { + snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, snd_amidi_map[i]); + snd_amidi_map[i] = 1; + } + } +#endif /* CONFIG_SND_OSSEMUL */ + return 0; +} + +static void __exit alsa_rawmidi_exit(void) +{ + snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl); +} + +module_init(alsa_rawmidi_init) +module_exit(alsa_rawmidi_exit) + +EXPORT_SYMBOL(snd_rawmidi_output_params); +EXPORT_SYMBOL(snd_rawmidi_input_params); +EXPORT_SYMBOL(snd_rawmidi_drop_output); +EXPORT_SYMBOL(snd_rawmidi_drain_output); +EXPORT_SYMBOL(snd_rawmidi_drain_input); +EXPORT_SYMBOL(snd_rawmidi_receive_reset); +EXPORT_SYMBOL(snd_rawmidi_receive); +EXPORT_SYMBOL(snd_rawmidi_transmit_reset); +EXPORT_SYMBOL(snd_rawmidi_transmit_empty); +EXPORT_SYMBOL(snd_rawmidi_transmit_peek); +EXPORT_SYMBOL(snd_rawmidi_transmit_ack); +EXPORT_SYMBOL(snd_rawmidi_transmit); +EXPORT_SYMBOL(snd_rawmidi_new); +EXPORT_SYMBOL(snd_rawmidi_set_ops); +EXPORT_SYMBOL(snd_rawmidi_info); +EXPORT_SYMBOL(snd_rawmidi_info_select); +EXPORT_SYMBOL(snd_rawmidi_kernel_open); +EXPORT_SYMBOL(snd_rawmidi_kernel_release); +EXPORT_SYMBOL(snd_rawmidi_kernel_read); +EXPORT_SYMBOL(snd_rawmidi_kernel_write); diff -Nru linux/sound/core/rtctimer.c linux-2.4.19-pre5-mjc/sound/core/rtctimer.c --- linux/sound/core/rtctimer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/rtctimer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,219 @@ +/* + * RTC based high-frequency timer + * + * Copyright (C) 2000 Takashi Iwai + * based on rtctimer.c by Steve Ratcliffe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 12) /* FIXME: which 2.2.x kernel? */ +#include +#else +#include +#endif + +/* use tasklet for interrupt handling */ +#define USE_TASKLET + +#define RTC_FREQ 1024 /* default frequency */ +#define NANO_SEC 1000000000L /* 10^9 in sec */ + +/* + * prototypes + */ +static int rtctimer_open(snd_timer_t *t); +static int rtctimer_close(snd_timer_t *t); +static int rtctimer_start(snd_timer_t *t); +static int rtctimer_stop(snd_timer_t *t); + + +/* + * The hardware dependent description for this timer. + */ +static struct _snd_timer_hardware rtc_hw = { + flags: SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, + ticks: 100000000L, /* FIXME: XXX */ + open: rtctimer_open, + close: rtctimer_close, + start: rtctimer_start, + stop: rtctimer_stop, +}; + +int rtctimer_freq = RTC_FREQ; /* frequency */ +static snd_timer_t *rtctimer; +static atomic_t rtc_inc = ATOMIC_INIT(0); +static rtc_task_t rtc_task; + +/* tasklet */ +#ifdef USE_TASKLET +static struct tasklet_struct rtc_tq; +#endif + +static int +rtctimer_open(snd_timer_t *t) +{ + err = rtc_register(&rtc_task); + if (err < 0) + return err; + t->private_data = &rtc_task; + MOD_INC_USE_COUNT; + return 0; +} + +static int +rtctimer_close(snd_timer_t *t) +{ + rtc_task_t *rtc = t->private_data; + if (rtc) { + rtc_unregister(rtc); + t->private_data = NULL; + } + MOD_DEC_USE_COUNT; + return 0; +} + +static int +rtctimer_start(snd_timer_t *timer) +{ + rtc_task_t *rtc = timer->private_data; + snd_assert(rtc != NULL, return -EINVAL); + rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); + rtc_control(rtc, RTC_PIE_ON, 0); + atomic_set(&rtc_inc, 0); + return 0; +} + +static int +rtctimer_stop(snd_timer_t *timer) +{ + rtc_task_t *rtc = timer->private_data; + snd_assert(rtc != NULL, return -EINVAL); + rtc_control(rtc, RTC_PIE_OFF, 0); + return 0; +} + +/* + * interrupt + */ +static void rtctimer_interrupt(void *private_data) +{ + atomic_inc(&rtc_inc); +#ifdef USE_TASKLET + tasklet_hi_schedule(&rtc_tq); +#else + { + int ticks = atomic_read(&rtc_inc); + snd_timer_interrupt((snd_timer_t*)private_data, ticks); + atomic_sub(ticks, &rtc_inc); + } +#endif /* USE_TASKLET */ +} + +#ifdef USE_TASKLET +static void rtctimer_interrupt2(unsigned long private_data) +{ + snd_timer_t *timer = (snd_timer_t *)private_data; + int ticks; + + snd_assert(timer != NULL, return); + do { + ticks = atomic_read(&rtc_inc); + snd_timer_interrupt(timer, ticks); + } while (!atomic_sub_and_test(ticks, &rtc_inc)); +} +#endif /* USE_TASKLET */ + + +/* + * ENTRY functions + */ +static int __init rtctimer_init(void) +{ + int order, err; + snd_timer_t *timer; + + if (rtctimer_freq < 2 || rtctimer_freq > 8192) { + snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); + return -EINVAL; + } + for (order = 1; rtctimer_freq > order; order <<= 1) + ; + if (rtctimer_freq != order) { + snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); + return -EINVAL; + } + + /* Create a new timer and set up the fields */ + err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer); + if (err < 0) + return err; + +#ifdef USE_TASKLET + tasklet_init(&rtc_tq, rtctimer_interrupt2, (unsigned long)timer); +#endif /* USE_TASKLET */ + + strcpy(timer->name, "RTC timer"); + timer->hw = rtc_hw; + timer->hw.resolution = NANO_SEC / rtctimer_freq; + + /* set up RTC callback */ + rtc_task.func = rtctimer_interrupt; + rtc_task.private_data = timer; + + err = snd_timer_global_register(timer); + if (err < 0) { + snd_timer_global_free(timer); + return err; + } + rtctimer = timer; /* remember this */ + + return 0; +} + +static void __exit rtctimer_exit(void) +{ + if (rtctimer) { + snd_timer_global_unregister(rtctimer); + rtctimer = NULL; + } +} + + +/* + * exported stuff + */ +module_init(rtctimer_init) +module_exit(rtctimer_exit) + +MODULE_PARM(rtctimer_freq, "i"); +MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz"); + +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; + +#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */ diff -Nru linux/sound/core/seq/Makefile linux-2.4.19-pre5-mjc/sound/core/seq/Makefile --- linux/sound/core/seq/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,108 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := sq.o + +subdir-y := instr +subdir-m := $(subdir-y) +obj-y += instr/instr.o +subdir-$(CONFIG_SND_SEQUENCER_OSS) += oss +ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) + obj-y += oss/oss.o +endif + +list-multi := snd-seq-device.o snd-seq.o snd-seq-midi.o snd-seq-midi-emul.o \ + snd-seq-midi-event.o snd-seq-instr.o snd-seq-dummy.o \ + snd-seq-virmidi.o + +export-objs := seq_device.o seq.o seq_ports.o seq_instr.o seq_midi_emul.o \ + seq_midi_event.o seq_virmidi.o + +snd-seq-device-objs := seq_device.o +snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ + seq_fifo.o seq_prioq.o seq_timer.o \ + seq_system.o seq_ports.o seq_info.o seq_sync.o \ + seq_midi_clock.o seq_mtc.o seq_dtl.o +snd-seq-midi-objs := seq_midi.o +snd-seq-midi-emul-objs := seq_midi_emul.o +snd-seq-midi-event-objs := seq_midi_event.o +snd-seq-instr-objs := seq_instr.o +snd-seq-dummy-objs := seq_dummy.o +snd-seq-virmidi-objs := seq_virmidi.o + +obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o +obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-midi-event.o +obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_MTPAV) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_MPU401) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ALS100) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_AZT2320) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_DT0197H) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ES18XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPL3SA2) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_AD1816A) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_CS4232) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4236) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ES1688) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSMAX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_INTERWAVE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_OPTI93X) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SB8) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SB16) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_SBAWE) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o snd-seq-virmidi.o +obj-$(CONFIG_SND_ES968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ALS4000) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CMIPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_CS4281) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ENS1370) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ENS1371) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ES1938) += snd-seq-device.o snd-seq-midi-emul.o snd-seq.o snd-seq-instr.o snd-seq-midi.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ES1968) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o + +include $(TOPDIR)/Rules.make + +snd-seq-device.o: $(snd-seq-device-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-device-objs) + +snd-seq.o: $(snd-seq-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-objs) + +snd-seq-midi.o: $(snd-seq-midi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-objs) + +snd-seq-midi-emul.o: $(snd-seq-midi-emul-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-emul-objs) + +snd-seq-midi-event.o: $(snd-seq-midi-event-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-midi-event-objs) + +snd-seq-instr.o: $(snd-seq-instr-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-instr-objs) + +snd-seq-dummy.o: $(snd-seq-dummy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-dummy-objs) + +snd-seq-virmidi.o: $(snd-seq-virmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-virmidi-objs) diff -Nru linux/sound/core/seq/instr/Makefile linux-2.4.19-pre5-mjc/sound/core/seq/instr/Makefile --- linux/sound/core/seq/instr/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,61 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := instr.o + +list-multi := snd-ainstr-fm.o snd-ainstr-simple.o snd-ainstr-gf1.o \ + snd-ainstr-iw.o + +export-objs := ainstr_fm.o ainstr_simple.o ainstr_gf1.o ainstr_iw.o + +snd-ainstr-fm-objs := ainstr_fm.o +snd-ainstr-simple-objs := ainstr_simple.o +snd-ainstr-gf1-objs := ainstr_gf1.o +snd-ainstr-iw-objs := ainstr_iw.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-ainstr-fm.o +obj-$(CONFIG_SND_AZT2320) += snd-ainstr-fm.o +obj-$(CONFIG_SND_DT0197H) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES18XX) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPL3SA2) += snd-ainstr-fm.o +obj-$(CONFIG_SND_AD1816A) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4232) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4236) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES1688) += snd-ainstr-fm.o +obj-$(CONFIG_SND_GUSCLASSIC) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_GUSMAX) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-fm.o +obj-$(CONFIG_SND_INTERWAVE) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-ainstr-fm.o +obj-$(CONFIG_SND_OPTI93X) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SB8) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SB16) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SBAWE) += snd-ainstr-fm.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ALS4000) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CMIPCI) += snd-ainstr-fm.o +obj-$(CONFIG_SND_CS4281) += snd-ainstr-fm.o +obj-$(CONFIG_SND_ES1938) += snd-ainstr-fm.o +obj-$(CONFIG_SND_FM801) += snd-ainstr-fm.o +obj-$(CONFIG_SND_SONICVIBES) += snd-ainstr-fm.o +obj-$(CONFIG_SND_TRIDENT) += snd-ainstr-simple.o +obj-$(CONFIG_SND_YMFPCI) += snd-ainstr-fm.o + +include $(TOPDIR)/Rules.make + +snd-ainstr-fm.o: $(snd-ainstr-fm-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-fm-objs) + +snd-ainstr-simple.o: $(snd-ainstr-simple-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-simple-objs) + +snd-ainstr-gf1.o: $(snd-ainstr-gf1-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-gf1-objs) + +snd-ainstr-iw.o: $(snd-ainstr-iw-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ainstr-iw-objs) diff -Nru linux/sound/core/seq/instr/ainstr_fm.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_fm.c --- linux/sound/core/seq/instr/ainstr_fm.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_fm.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,160 @@ +/* + * FM (OPL2/3) Instrument routines + * Copyright (c) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_fm_id = SNDRV_SEQ_INSTR_ID_OPL2_3; + +static int snd_seq_fm_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + fm_instrument_t *ip; + fm_xinstrument_t ix; + int idx; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != FM_STRU_INSTR) + return -EINVAL; + ip = (fm_instrument_t *)KINSTR_DATA(instr); + ip->share_id[0] = le32_to_cpu(ix.share_id[0]); + ip->share_id[1] = le32_to_cpu(ix.share_id[1]); + ip->share_id[2] = le32_to_cpu(ix.share_id[2]); + ip->share_id[3] = le32_to_cpu(ix.share_id[3]); + ip->type = ix.type; + for (idx = 0; idx < 4; idx++) { + ip->op[idx].am_vib = ix.op[idx].am_vib; + ip->op[idx].ksl_level = ix.op[idx].ksl_level; + ip->op[idx].attack_decay = ix.op[idx].attack_decay; + ip->op[idx].sustain_release = ix.op[idx].sustain_release; + ip->op[idx].wave_select = ix.op[idx].wave_select; + } + for (idx = 0; idx < 2; idx++) { + ip->feedback_connection[idx] = ix.feedback_connection[idx]; + } + ip->echo_delay = ix.echo_delay; + ip->echo_atten = ix.echo_atten; + ip->chorus_spread = ix.chorus_spread; + ip->trnsps = ix.trnsps; + ip->fix_dur = ix.fix_dur; + ip->modes = ix.modes; + ip->fix_key = ix.fix_key; + return 0; +} + +static int snd_seq_fm_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + fm_instrument_t *ip; + fm_xinstrument_t ix; + int idx; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (fm_instrument_t *)KINSTR_DATA(instr); + ix.stype = FM_STRU_INSTR; + ix.share_id[0] = cpu_to_le32(ip->share_id[0]); + ix.share_id[1] = cpu_to_le32(ip->share_id[1]); + ix.share_id[2] = cpu_to_le32(ip->share_id[2]); + ix.share_id[3] = cpu_to_le32(ip->share_id[3]); + ix.type = ip->type; + for (idx = 0; idx < 4; idx++) { + ix.op[idx].am_vib = ip->op[idx].am_vib; + ix.op[idx].ksl_level = ip->op[idx].ksl_level; + ix.op[idx].attack_decay = ip->op[idx].attack_decay; + ix.op[idx].sustain_release = ip->op[idx].sustain_release; + ix.op[idx].wave_select = ip->op[idx].wave_select; + } + for (idx = 0; idx < 2; idx++) { + ix.feedback_connection[idx] = ip->feedback_connection[idx]; + } + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + ix.echo_delay = ip->echo_delay; + ix.echo_atten = ip->echo_atten; + ix.chorus_spread = ip->chorus_spread; + ix.trnsps = ip->trnsps; + ix.fix_dur = ip->fix_dur; + ix.modes = ip->modes; + ix.fix_key = ip->fix_key; + return 0; +} + +static int snd_seq_fm_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + *size = sizeof(fm_xinstrument_t); + return 0; +} + +int snd_seq_fm_init(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + // ops->private_data = private_data; + ops->add_len = sizeof(fm_instrument_t); + ops->instr_type = snd_seq_fm_id; + ops->put = snd_seq_fm_put; + ops->get = snd_seq_fm_get; + ops->get_size = snd_seq_fm_get_size; + // ops->remove = snd_seq_fm_remove; + // ops->notify = snd_seq_fm_notify; + ops->next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_fm_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_fm_exit(void) +{ +} + +module_init(alsa_ainstr_fm_init) +module_exit(alsa_ainstr_fm_exit) + +EXPORT_SYMBOL(snd_seq_fm_id); +EXPORT_SYMBOL(snd_seq_fm_init); diff -Nru linux/sound/core/seq/instr/ainstr_gf1.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_gf1.c --- linux/sound/core/seq/instr/ainstr_gf1.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_gf1.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,361 @@ +/* + * GF1 (GUS) Patch - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_gf1_id = SNDRV_SEQ_INSTR_ID_GUS_PATCH; + +static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & GF1_WAVE_16BIT) + result <<= 1; + if (format & GF1_WAVE_STEREO) + result <<= 1; + return format; +} + +static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + char **data, + long *len, + int atomic) +{ + gf1_wave_t *wp, *prev; + gf1_xwave_t xp; + int err, gfp_mask; + unsigned int real_size; + + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + if (*len < sizeof(xp)) + return -EINVAL; + if (copy_from_user(&xp, *data, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + wp = (gf1_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + if (wp == NULL) + return -ENOMEM; + wp->share_id[0] = le32_to_cpu(xp.share_id[0]); + wp->share_id[1] = le32_to_cpu(xp.share_id[1]); + wp->share_id[2] = le32_to_cpu(xp.share_id[2]); + wp->share_id[3] = le32_to_cpu(xp.share_id[3]); + wp->format = le32_to_cpu(xp.format); + wp->size = le32_to_cpu(xp.size); + wp->start = le32_to_cpu(xp.start); + wp->loop_start = le32_to_cpu(xp.loop_start); + wp->loop_end = le32_to_cpu(xp.loop_end); + wp->loop_repeat = le16_to_cpu(xp.loop_repeat); + wp->flags = xp.flags; + wp->sample_rate = le32_to_cpu(xp.sample_rate); + wp->low_frequency = le32_to_cpu(xp.low_frequency); + wp->high_frequency = le32_to_cpu(xp.high_frequency); + wp->root_frequency = le32_to_cpu(xp.root_frequency); + wp->tune = le16_to_cpu(xp.tune); + wp->balance = xp.balance; + memcpy(wp->envelope_rate, xp.envelope_rate, 6); + memcpy(wp->envelope_offset, xp.envelope_offset, 6); + wp->tremolo_sweep = xp.tremolo_sweep; + wp->tremolo_rate = xp.tremolo_rate; + wp->tremolo_depth = xp.tremolo_depth; + wp->vibrato_sweep = xp.vibrato_sweep; + wp->vibrato_rate = xp.vibrato_rate; + wp->vibrato_depth = xp.vibrato_depth; + wp->scale_frequency = le16_to_cpu(xp.scale_frequency); + wp->scale_factor = le16_to_cpu(xp.scale_factor); + real_size = snd_seq_gf1_size(wp->size, wp->format); + if (real_size > *len) { + kfree(wp); + return -ENOMEM; + } + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) { + kfree(wp); + return err; + } + } + *data += real_size; + *len -= real_size; + prev = ip->wave; + if (prev) { + while (prev->next) prev = prev->next; + prev->next = wp; + } else { + ip->wave = wp; + } + return 0; +} + +static void snd_seq_gf1_wave_free(snd_gf1_ops_t *ops, + gf1_wave_t *wave, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, wave, atomic); + kfree(wave); +} + +static void snd_seq_gf1_instr_free(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + int atomic) +{ + gf1_wave_t *wave; + + while ((wave = ip->wave) != NULL) { + ip->wave = wave->next; + snd_seq_gf1_wave_free(ops, wave, atomic); + } +} + +static int snd_seq_gf1_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + gf1_xinstrument_t ix; + int err, gfp_mask; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != GF1_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + ip->exclusion = le16_to_cpu(ix.exclusion); + ip->exclusion_group = le16_to_cpu(ix.exclusion_group); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + /* copy layers */ + while (len > sizeof(__u32)) { + __u32 stype; + + if (copy_from_user(&stype, instr_data, sizeof(stype))) + return -EFAULT; + if (stype != GF1_STRU_WAVE) { + snd_seq_gf1_instr_free(ops, ip, atomic); + return -EINVAL; + } + err = snd_seq_gf1_copy_wave_from_stream(ops, + ip, + &instr_data, + &len, + atomic); + if (err < 0) { + snd_seq_gf1_instr_free(ops, ip, atomic); + return err; + } + } + return 0; +} + +static int snd_seq_gf1_copy_wave_to_stream(snd_gf1_ops_t *ops, + gf1_instrument_t *ip, + char **data, + long *len, + int atomic) +{ + gf1_wave_t *wp; + gf1_xwave_t xp; + int err; + unsigned int real_size; + + for (wp = ip->wave; wp; wp = wp->next) { + if (*len < sizeof(xp)) + return -ENOMEM; + memset(&xp, 0, sizeof(xp)); + xp.stype = GF1_STRU_WAVE; + xp.share_id[0] = cpu_to_le32(wp->share_id[0]); + xp.share_id[1] = cpu_to_le32(wp->share_id[1]); + xp.share_id[2] = cpu_to_le32(wp->share_id[2]); + xp.share_id[3] = cpu_to_le32(wp->share_id[3]); + xp.format = cpu_to_le32(wp->format); + xp.size = cpu_to_le32(wp->size); + xp.start = cpu_to_le32(wp->start); + xp.loop_start = cpu_to_le32(wp->loop_start); + xp.loop_end = cpu_to_le32(wp->loop_end); + xp.loop_repeat = cpu_to_le32(wp->loop_repeat); + xp.flags = wp->flags; + xp.sample_rate = cpu_to_le32(wp->sample_rate); + xp.low_frequency = cpu_to_le32(wp->low_frequency); + xp.high_frequency = cpu_to_le32(wp->high_frequency); + xp.root_frequency = cpu_to_le32(wp->root_frequency); + xp.tune = cpu_to_le16(wp->tune); + xp.balance = wp->balance; + memcpy(xp.envelope_rate, wp->envelope_rate, 6); + memcpy(xp.envelope_offset, wp->envelope_offset, 6); + xp.tremolo_sweep = wp->tremolo_sweep; + xp.tremolo_rate = wp->tremolo_rate; + xp.tremolo_depth = wp->tremolo_depth; + xp.vibrato_sweep = wp->vibrato_sweep; + xp.vibrato_rate = wp->vibrato_rate; + xp.vibrato_depth = wp->vibrato_depth; + xp.scale_frequency = cpu_to_le16(wp->scale_frequency); + xp.scale_factor = cpu_to_le16(wp->scale_factor); + if (copy_to_user(*data, &xp, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + real_size = snd_seq_gf1_size(wp->size, wp->format); + if (*len < real_size) + return -ENOMEM; + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) + return err; + } + *data += wp->size; + *len -= wp->size; + } + return 0; +} + +static int snd_seq_gf1_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + gf1_xinstrument_t ix; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + ix.stype = GF1_STRU_INSTR; + ix.exclusion = cpu_to_le16(ip->exclusion); + ix.exclusion_group = cpu_to_le16(ip->exclusion_group); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + return snd_seq_gf1_copy_wave_to_stream(ops, + ip, + &instr_data, + &len, + atomic); +} + +static int snd_seq_gf1_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + long result; + gf1_instrument_t *ip; + gf1_wave_t *wp; + + *size = 0; + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + result = sizeof(gf1_xinstrument_t); + for (wp = ip->wave; wp; wp = wp->next) { + result += sizeof(gf1_xwave_t); + result += wp->size; + } + *size = result; + return 0; +} + +static int snd_seq_gf1_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + gf1_instrument_t *ip; + + ip = (gf1_instrument_t *)KINSTR_DATA(instr); + snd_seq_gf1_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_gf1_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_gf1_init(snd_gf1_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(gf1_instrument_t); + ops->kops.instr_type = snd_seq_gf1_id; + ops->kops.put = snd_seq_gf1_put; + ops->kops.get = snd_seq_gf1_get; + ops->kops.get_size = snd_seq_gf1_get_size; + ops->kops.remove = snd_seq_gf1_remove; + ops->kops.notify = snd_seq_gf1_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_gf1_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_gf1_exit(void) +{ +} + +module_init(alsa_ainstr_gf1_init) +module_exit(alsa_ainstr_gf1_exit) + +EXPORT_SYMBOL(snd_seq_gf1_id); +EXPORT_SYMBOL(snd_seq_gf1_init); diff -Nru linux/sound/core/seq/instr/ainstr_iw.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_iw.c --- linux/sound/core/seq/instr/ainstr_iw.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_iw.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,626 @@ +/* + * IWFFFF - AMD InterWave (tm) - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_iwffff_id = SNDRV_SEQ_INSTR_ID_INTERWAVE; + +static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & IWFFFF_WAVE_16BIT) + result <<= 1; + if (format & IWFFFF_WAVE_STEREO) + result <<= 1; + return result; +} + +static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp, + iwffff_xlfo_t *fx) +{ + fp->freq = le16_to_cpu(fx->freq); + fp->depth = le16_to_cpu(fx->depth); + fp->sweep = le16_to_cpu(fx->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_env_t *ep, + iwffff_xenv_t *ex, + char **data, + long *len, + int gfp_mask) +{ + __u32 stype; + iwffff_env_record_t *rp, *rp_last; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ep->flags = ex->flags; + ep->mode = ex->mode; + ep->index = ex->index; + rp_last = NULL; + while (1) { + if (*len < sizeof(__u32)) + return -EINVAL; + if (copy_from_user(&stype, data, sizeof(stype))) + return -EFAULT; + if (stype == IWFFFF_STRU_WAVE) + return 0; + if (req_stype != stype) { + if (stype == IWFFFF_STRU_ENV_RECP || + stype == IWFFFF_STRU_ENV_RECV) + return 0; + } + if (*len < sizeof(rx)) + return -EINVAL; + if (copy_from_user(&rx, *data, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); + if (points_size > *len) + return -EINVAL; + rp = (iwffff_env_record_t *)snd_kcalloc(sizeof(*rp) + points_size, gfp_mask); + if (rp == NULL) + return -ENOMEM; + rp->nattack = le16_to_cpu(rx.nattack); + rp->nrelease = le16_to_cpu(rx.nrelease); + rp->sustain_offset = le16_to_cpu(rx.sustain_offset); + rp->sustain_rate = le16_to_cpu(rx.sustain_rate); + rp->release_rate = le16_to_cpu(rx.release_rate); + rp->hirange = rx.hirange; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + if (copy_from_user(&px, *data, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + pp->offset = le16_to_cpu(px.offset); + pp->rate = le16_to_cpu(px.rate); + } + if (ep->record == NULL) { + ep->record = rp; + } else { + rp_last = rp; + } + rp_last = rp; + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp, *prev; + iwffff_xwave_t xp; + int err, gfp_mask; + unsigned int real_size; + + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + if (*len < sizeof(xp)) + return -EINVAL; + if (copy_from_user(&xp, *data, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + wp = (iwffff_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + if (wp == NULL) + return -ENOMEM; + wp->share_id[0] = le32_to_cpu(xp.share_id[0]); + wp->share_id[1] = le32_to_cpu(xp.share_id[1]); + wp->share_id[2] = le32_to_cpu(xp.share_id[2]); + wp->share_id[3] = le32_to_cpu(xp.share_id[3]); + wp->format = le32_to_cpu(xp.format); + wp->address.memory = le32_to_cpu(xp.offset); + wp->size = le32_to_cpu(xp.size); + wp->start = le32_to_cpu(xp.start); + wp->loop_start = le32_to_cpu(xp.loop_start); + wp->loop_end = le32_to_cpu(xp.loop_end); + wp->loop_repeat = le16_to_cpu(xp.loop_repeat); + wp->sample_ratio = le32_to_cpu(xp.sample_ratio); + wp->attenuation = xp.attenuation; + wp->low_note = xp.low_note; + wp->high_note = xp.high_note; + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if (real_size > *len) { + kfree(wp); + return -ENOMEM; + } + } + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) { + kfree(wp); + return err; + } + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + prev = lp->wave; + if (prev) { + while (prev->next) prev = prev->next; + prev->next = wp; + } else { + lp->wave = wp; + } + return 0; +} + +static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops, + iwffff_env_t *env, + int atomic) +{ + iwffff_env_record_t *rec; + + while ((rec = env->record) != NULL) { + env->record = rec->next; + kfree(rec); + } +} + +static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops, + iwffff_wave_t *wave, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, wave, atomic); + kfree(wave); +} + +static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops, + iwffff_instrument_t *ip, + int atomic) +{ + iwffff_layer_t *layer; + iwffff_wave_t *wave; + + while ((layer = ip->layer) != NULL) { + ip->layer = layer->next; + snd_seq_iwffff_env_free(ops, &layer->penv, atomic); + snd_seq_iwffff_env_free(ops, &layer->venv, atomic); + while ((wave = layer->wave) != NULL) { + layer->wave = wave->next; + snd_seq_iwffff_wave_free(ops, wave, atomic); + } + kfree(layer); + } +} + +static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp, *prev_lp; + iwffff_xlayer_t lx; + int err, gfp_mask; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != IWFFFF_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ip->exclusion = le16_to_cpu(ix.exclusion); + ip->layer_type = le16_to_cpu(ix.layer_type); + ip->exclusion_group = le16_to_cpu(ix.exclusion_group); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + /* copy layers */ + prev_lp = NULL; + while (len > 0) { + if (len < sizeof(iwffff_xlayer_t)) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + if (copy_from_user(&lx, instr_data, sizeof(lx))) + return -EFAULT; + instr_data += sizeof(lx); + len -= sizeof(lx); + if (lx.stype != IWFFFF_STRU_LAYER) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -EINVAL; + } + lp = (iwffff_layer_t *)snd_kcalloc(sizeof(*lp), gfp_mask); + if (lp == NULL) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return -ENOMEM; + } + if (prev_lp) { + prev_lp->next = lp; + } else { + ip->layer = lp; + } + prev_lp = lp; + lp->flags = lx.flags; + lp->velocity_mode = lx.velocity_mode; + lp->layer_event = lx.layer_event; + lp->low_range = lx.low_range; + lp->high_range = lx.high_range; + lp->pan = lx.pan; + lp->pan_freq_scale = lx.pan_freq_scale; + lp->attenuation = lx.attenuation; + snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo); + snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato); + lp->freq_scale = le16_to_cpu(lx.freq_scale); + lp->freq_center = lx.freq_center; + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lp->penv, &lx.penv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lp->venv, &lx.venv, + &instr_data, &len, + gfp_mask); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + while (len > sizeof(__u32)) { + __u32 stype; + + if (copy_from_user(&stype, instr_data, sizeof(stype))) + return -EFAULT; + if (stype != IWFFFF_STRU_WAVE) + break; + err = snd_seq_iwffff_copy_wave_from_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) { + snd_seq_iwffff_instr_free(ops, ip, atomic); + return err; + } + } + } + return 0; +} + +static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx, + iwffff_lfo_t *fp) +{ + fx->freq = cpu_to_le16(fp->freq); + fx->depth = cpu_to_le16(fp->depth); + fx->sweep = cpu_to_le16(fp->sweep); + fp->shape = fx->shape; + fp->delay = fx->delay; +} + +static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, + iwffff_layer_t *lp, + iwffff_xenv_t *ex, + iwffff_env_t *ep, + char **data, + long *len) +{ + iwffff_env_record_t *rp; + iwffff_xenv_record_t rx; + iwffff_env_point_t *pp; + iwffff_xenv_point_t px; + int points_size, idx; + + ex->flags = ep->flags; + ex->mode = ep->mode; + ex->index = ep->index; + for (rp = ep->record; rp; rp = rp->next) { + if (*len < sizeof(rx)) + return -ENOMEM; + memset(&rx, 0, sizeof(rx)); + rx.stype = req_stype; + rx.nattack = cpu_to_le16(rp->nattack); + rx.nrelease = cpu_to_le16(rp->nrelease); + rx.sustain_offset = cpu_to_le16(rp->sustain_offset); + rx.sustain_rate = cpu_to_le16(rp->sustain_rate); + rx.release_rate = cpu_to_le16(rp->release_rate); + rx.hirange = cpu_to_le16(rp->hirange); + if (copy_to_user(*data, &rx, sizeof(rx))) + return -EFAULT; + *data += sizeof(rx); + *len -= sizeof(rx); + points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + if (*len < points_size) + return -ENOMEM; + pp = (iwffff_env_point_t *)(rp + 1); + for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { + px.offset = cpu_to_le16(pp->offset); + px.rate = cpu_to_le16(pp->rate); + if (copy_to_user(*data, &px, sizeof(px))) + return -EFAULT; + *data += sizeof(px); + *len -= sizeof(px); + } + } + return 0; +} + +static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops, + iwffff_layer_t *lp, + char **data, + long *len, + int atomic) +{ + iwffff_wave_t *wp; + iwffff_xwave_t xp; + int err; + unsigned int real_size; + + for (wp = lp->wave; wp; wp = wp->next) { + if (*len < sizeof(xp)) + return -ENOMEM; + memset(&xp, 0, sizeof(xp)); + xp.stype = IWFFFF_STRU_WAVE; + xp.share_id[0] = cpu_to_le32(wp->share_id[0]); + xp.share_id[1] = cpu_to_le32(wp->share_id[1]); + xp.share_id[2] = cpu_to_le32(wp->share_id[2]); + xp.share_id[3] = cpu_to_le32(wp->share_id[3]); + xp.format = cpu_to_le32(wp->format); + if (wp->format & IWFFFF_WAVE_ROM) + xp.offset = cpu_to_le32(wp->address.memory); + xp.size = cpu_to_le32(wp->size); + xp.start = cpu_to_le32(wp->start); + xp.loop_start = cpu_to_le32(wp->loop_start); + xp.loop_end = cpu_to_le32(wp->loop_end); + xp.loop_repeat = cpu_to_le32(wp->loop_repeat); + xp.sample_ratio = cpu_to_le32(wp->sample_ratio); + xp.attenuation = wp->attenuation; + xp.low_note = wp->low_note; + xp.high_note = wp->high_note; + if (copy_to_user(*data, &xp, sizeof(xp))) + return -EFAULT; + *data += sizeof(xp); + *len -= sizeof(xp); + real_size = snd_seq_iwffff_size(wp->size, wp->format); + if (!(wp->format & IWFFFF_WAVE_ROM)) { + if (*len < real_size) + return -ENOMEM; + } + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, wp, + *data, real_size, atomic); + if (err < 0) + return err; + } + if (!(wp->format & IWFFFF_WAVE_ROM)) { + *data += real_size; + *len -= real_size; + } + } + return 0; +} + +static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + iwffff_xinstrument_t ix; + iwffff_layer_t *lp; + iwffff_xlayer_t lx; + char *layer_instr_data; + int err; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + ix.stype = IWFFFF_STRU_INSTR; + ix.exclusion = cpu_to_le16(ip->exclusion); + ix.layer_type = cpu_to_le16(ip->layer_type); + ix.exclusion_group = cpu_to_le16(ip->exclusion_group); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + for (lp = ip->layer; lp; lp = lp->next) { + if (len < sizeof(lx)) + return -ENOMEM; + memset(&lx, 0, sizeof(lx)); + lx.stype = IWFFFF_STRU_LAYER; + lx.flags = lp->flags; + lx.velocity_mode = lp->velocity_mode; + lx.layer_event = lp->layer_event; + lx.low_range = lp->low_range; + lx.high_range = lp->high_range; + lx.pan = lp->pan; + lx.pan_freq_scale = lp->pan_freq_scale; + lx.attenuation = lp->attenuation; + snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo); + snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato); + layer_instr_data = instr_data; + instr_data += sizeof(lx); + len -= sizeof(lx); + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, + lp, + &lx.penv, &lp->penv, + &instr_data, &len); + if (err < 0) + return err; + err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, + lp, + &lx.venv, &lp->venv, + &instr_data, &len); + if (err < 0) + return err; + /* layer structure updating is now finished */ + if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) + return -EFAULT; + err = snd_seq_iwffff_copy_wave_to_stream(ops, + lp, + &instr_data, + &len, + atomic); + if (err < 0) + return err; + } + return 0; +} + +static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep) +{ + long result = 0; + iwffff_env_record_t *rp; + + for (rp = ep->record; rp; rp = rp->next) { + result += sizeof(iwffff_xenv_record_t); + result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); + } + return 0; +} + +static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp) +{ + long result = 0; + iwffff_wave_t *wp; + + for (wp = lp->wave; wp; wp = wp->next) { + result += sizeof(iwffff_xwave_t); + if (!(wp->format & IWFFFF_WAVE_ROM)) + result += wp->size; + } + return result; +} + +static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + long result; + iwffff_instrument_t *ip; + iwffff_layer_t *lp; + + *size = 0; + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + result = sizeof(iwffff_xinstrument_t); + for (lp = ip->layer; lp; lp = lp->next) { + result += sizeof(iwffff_xlayer_t); + result += snd_seq_iwffff_env_size_in_stream(&lp->penv); + result += snd_seq_iwffff_env_size_in_stream(&lp->venv); + result += snd_seq_iwffff_wave_size_in_stream(lp); + } + *size = result; + return 0; +} + +static int snd_seq_iwffff_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + iwffff_instrument_t *ip; + + ip = (iwffff_instrument_t *)KINSTR_DATA(instr); + snd_seq_iwffff_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_iwffff_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_iwffff_init(snd_iwffff_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(iwffff_instrument_t); + ops->kops.instr_type = snd_seq_iwffff_id; + ops->kops.put = snd_seq_iwffff_put; + ops->kops.get = snd_seq_iwffff_get; + ops->kops.get_size = snd_seq_iwffff_get_size; + ops->kops.remove = snd_seq_iwffff_remove; + ops->kops.notify = snd_seq_iwffff_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_iw_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_iw_exit(void) +{ +} + +module_init(alsa_ainstr_iw_init) +module_exit(alsa_ainstr_iw_exit) + +EXPORT_SYMBOL(snd_seq_iwffff_id); +EXPORT_SYMBOL(snd_seq_iwffff_init); diff -Nru linux/sound/core/seq/instr/ainstr_simple.c linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_simple.c --- linux/sound/core/seq/instr/ainstr_simple.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/instr/ainstr_simple.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,218 @@ +/* + * Simple (MOD player) - Instrument routines + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +char *snd_seq_simple_id = SNDRV_SEQ_INSTR_ID_SIMPLE; + +static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format) +{ + unsigned int result = size; + + if (format & SIMPLE_WAVE_16BIT) + result <<= 1; + if (format & SIMPLE_WAVE_STEREO) + result <<= 1; + return result; +} + +static void snd_seq_simple_instr_free(snd_simple_ops_t *ops, + simple_instrument_t *ip, + int atomic) +{ + if (ops->remove_sample) + ops->remove_sample(ops->private_data, ip, atomic); +} + +static int snd_seq_simple_put(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + simple_xinstrument_t ix; + int err, gfp_mask; + unsigned int real_size; + + if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) + return -EINVAL; + gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + /* copy instrument data */ + if (len < sizeof(ix)) + return -EINVAL; + if (copy_from_user(&ix, instr_data, sizeof(ix))) + return -EFAULT; + if (ix.stype != SIMPLE_STRU_INSTR) + return -EINVAL; + instr_data += sizeof(ix); + len -= sizeof(ix); + ip = (simple_instrument_t *)KINSTR_DATA(instr); + ip->share_id[0] = le32_to_cpu(ix.share_id[0]); + ip->share_id[1] = le32_to_cpu(ix.share_id[1]); + ip->share_id[2] = le32_to_cpu(ix.share_id[2]); + ip->share_id[3] = le32_to_cpu(ix.share_id[3]); + ip->format = le32_to_cpu(ix.format); + ip->size = le32_to_cpu(ix.size); + ip->start = le32_to_cpu(ix.start); + ip->loop_start = le32_to_cpu(ix.loop_start); + ip->loop_end = le32_to_cpu(ix.loop_end); + ip->loop_repeat = le16_to_cpu(ix.loop_repeat); + ip->effect1 = ix.effect1; + ip->effect1_depth = ix.effect1_depth; + ip->effect2 = ix.effect2; + ip->effect2_depth = ix.effect2_depth; + real_size = snd_seq_simple_size(ip->size, ip->format); + if (len < real_size) + return -EINVAL; + if (ops->put_sample) { + err = ops->put_sample(ops->private_data, ip, + instr_data, real_size, atomic); + if (err < 0) + return err; + } + return 0; +} + +static int snd_seq_simple_get(void *private_data, snd_seq_kinstr_t *instr, + char *instr_data, long len, int atomic, int cmd) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + simple_xinstrument_t ix; + int err; + unsigned int real_size; + + if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) + return -EINVAL; + if (len < sizeof(ix)) + return -ENOMEM; + memset(&ix, 0, sizeof(ix)); + ip = (simple_instrument_t *)KINSTR_DATA(instr); + ix.stype = SIMPLE_STRU_INSTR; + ix.share_id[0] = cpu_to_le32(ip->share_id[0]); + ix.share_id[1] = cpu_to_le32(ip->share_id[1]); + ix.share_id[2] = cpu_to_le32(ip->share_id[2]); + ix.share_id[3] = cpu_to_le32(ip->share_id[3]); + ix.format = cpu_to_le32(ip->format); + ix.size = cpu_to_le32(ip->size); + ix.start = cpu_to_le32(ip->start); + ix.loop_start = cpu_to_le32(ip->loop_start); + ix.loop_end = cpu_to_le32(ip->loop_end); + ix.loop_repeat = cpu_to_le32(ip->loop_repeat); + ix.effect1 = cpu_to_le16(ip->effect1); + ix.effect1_depth = cpu_to_le16(ip->effect1_depth); + ix.effect2 = ip->effect2; + ix.effect2_depth = ip->effect2_depth; + if (copy_to_user(instr_data, &ix, sizeof(ix))) + return -EFAULT; + instr_data += sizeof(ix); + len -= sizeof(ix); + real_size = snd_seq_simple_size(ip->size, ip->format); + if (len < real_size) + return -ENOMEM; + if (ops->get_sample) { + err = ops->get_sample(ops->private_data, ip, + instr_data, real_size, atomic); + if (err < 0) + return err; + } + return 0; +} + +static int snd_seq_simple_get_size(void *private_data, snd_seq_kinstr_t *instr, + long *size) +{ + simple_instrument_t *ip; + + ip = (simple_instrument_t *)KINSTR_DATA(instr); + *size = sizeof(simple_xinstrument_t) + snd_seq_simple_size(ip->size, ip->format); + return 0; +} + +static int snd_seq_simple_remove(void *private_data, + snd_seq_kinstr_t *instr, + int atomic) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + simple_instrument_t *ip; + + ip = (simple_instrument_t *)KINSTR_DATA(instr); + snd_seq_simple_instr_free(ops, ip, atomic); + return 0; +} + +static void snd_seq_simple_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data; + + if (ops->notify) + ops->notify(ops->private_data, instr, what); +} + +int snd_seq_simple_init(snd_simple_ops_t *ops, + void *private_data, + snd_seq_kinstr_ops_t *next) +{ + memset(ops, 0, sizeof(*ops)); + ops->private_data = private_data; + ops->kops.private_data = ops; + ops->kops.add_len = sizeof(simple_instrument_t); + ops->kops.instr_type = snd_seq_simple_id; + ops->kops.put = snd_seq_simple_put; + ops->kops.get = snd_seq_simple_get; + ops->kops.get_size = snd_seq_simple_get_size; + ops->kops.remove = snd_seq_simple_remove; + ops->kops.notify = snd_seq_simple_notify; + ops->kops.next = next; + return 0; +} + +/* + * Init part + */ + +static int __init alsa_ainstr_simple_init(void) +{ + return 0; +} + +static void __exit alsa_ainstr_simple_exit(void) +{ +} + +module_init(alsa_ainstr_simple_init) +module_exit(alsa_ainstr_simple_exit) + +EXPORT_SYMBOL(snd_seq_simple_id); +EXPORT_SYMBOL(snd_seq_simple_init); diff -Nru linux/sound/core/seq/oss/Makefile linux-2.4.19-pre5-mjc/sound/core/seq/oss/Makefile --- linux/sound/core/seq/oss/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,19 @@ +# +# Makefile for ALSA +# Copyright (c) 1999 by Jaroslav Kysela +# + +O_TARGET := oss.o + +list-multi := snd-seq-oss.o + +snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \ + seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \ + seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o + +obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-oss.o + +include $(TOPDIR)/Rules.make + +snd-seq-oss.o: $(snd-seq-oss-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-seq-oss-objs) diff -Nru linux/sound/core/seq/oss/seq_oss.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss.c --- linux/sound/core/seq/oss/seq_oss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,307 @@ +/* + * OSS compatible sequencer driver + * + * registration of device and proc + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "seq_oss_device.h" +#include "seq_oss_synth.h" + +/* + * module option + */ +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("OSS-compatible sequencer module"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#ifdef SNDRV_SEQ_OSS_DEBUG +MODULE_PARM(seq_oss_debug, "i"); +MODULE_PARM_DESC(seq_oss_debug, "debug option"); +int seq_oss_debug = 0; +#endif + + +/* + * prototypes + */ +static int register_device(void); +static void unregister_device(void); +static int register_proc(void); +static void unregister_proc(void); + +static int odev_open(struct inode *inode, struct file *file); +static int odev_release(struct inode *inode, struct file *file); +static ssize_t odev_read(struct file *file, char *buf, size_t count, loff_t *offset); +static ssize_t odev_write(struct file *file, const char *buf, size_t count, loff_t *offset); +static int odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static unsigned int odev_poll(struct file *file, poll_table * wait); +#ifdef CONFIG_PROC_FS +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf); +#endif + + +/* + * module interface + */ + +static int __init alsa_seq_oss_init(void) +{ + int rc; + static snd_seq_dev_ops_t ops = { + snd_seq_oss_synth_register, + snd_seq_oss_synth_unregister, + }; + + if ((rc = register_device()) < 0) + return rc; + if ((rc = register_proc()) < 0) { + unregister_device(); + return rc; + } + if ((rc = snd_seq_oss_create_client()) < 0) { + unregister_proc(); + unregister_device(); + return rc; + } + + if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops, + sizeof(snd_seq_oss_reg_t))) < 0) { + snd_seq_oss_delete_client(); + unregister_proc(); + unregister_device(); + return rc; + } + + /* success */ + snd_seq_oss_synth_init(); + return 0; +} + +static void __exit alsa_seq_oss_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS); + snd_seq_oss_delete_client(); + unregister_proc(); + unregister_device(); +} + +module_init(alsa_seq_oss_init) +module_exit(alsa_seq_oss_exit) + +/* + * ALSA minor device interface + */ + +static DECLARE_MUTEX(register_mutex); + +static int +odev_open(struct inode *inode, struct file *file) +{ + int level, rc; + + if (minor(inode->i_rdev) == SNDRV_MINOR_OSS_MUSIC) + level = SNDRV_SEQ_OSS_MODE_MUSIC; + else + level = SNDRV_SEQ_OSS_MODE_SYNTH; + + down(®ister_mutex); + rc = snd_seq_oss_open(file, level); + up(®ister_mutex); + + return rc; +} + +static int +odev_release(struct inode *inode, struct file *file) +{ + seq_oss_devinfo_t *dp; + + if ((dp = file->private_data) == NULL) + return 0; + + snd_seq_oss_drain_write(dp); + + down(®ister_mutex); + snd_seq_oss_release(dp); + up(®ister_mutex); + + return 0; +} + +static ssize_t +odev_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_read(dp, buf, count); +} + + +static ssize_t +odev_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_write(dp, buf, count, file); +} + +static int +odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return -EIO); + return snd_seq_oss_ioctl(dp, cmd, arg); +} + + +static unsigned int +odev_poll(struct file *file, poll_table * wait) +{ + seq_oss_devinfo_t *dp; + dp = file->private_data; + snd_assert(dp != NULL, return 0); + return snd_seq_oss_poll(dp, file, wait); +} + +/* + * registration of sequencer minor device + */ + +static struct file_operations seq_oss_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: odev_read, + write: odev_write, + open: odev_open, + release: odev_release, + poll: odev_poll, + ioctl: odev_ioctl, +}; + +static snd_minor_t seq_oss_reg = { + comment: "sequencer", + f_ops: &seq_oss_f_ops, +}; + +static int __init +register_device(void) +{ + int rc; + + down(®ister_mutex); + if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, + NULL, 0, + &seq_oss_reg, + SNDRV_SEQ_OSS_DEVNAME)) < 0) { + snd_printk(KERN_ERR "can't register device seq\n"); + up(®ister_mutex); + return rc; + } + if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, + NULL, 0, + &seq_oss_reg, + SNDRV_SEQ_OSS_DEVNAME)) < 0) { + snd_printk(KERN_ERR "can't register device music\n"); + snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0); + up(®ister_mutex); + return rc; + } + debug_printk(("device registered\n")); + up(®ister_mutex); + return 0; +} + +static void +unregister_device(void) +{ + down(®ister_mutex); + debug_printk(("device unregistered\n")); + if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0) + snd_printk(KERN_ERR "error unregister device music\n"); + if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0) + snd_printk(KERN_ERR "error unregister device seq\n"); + up(®ister_mutex); +} + +/* + * /proc interface + */ + +#ifdef CONFIG_PROC_FS + +static snd_info_entry_t *info_entry; + +static void +info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf) +{ + down(®ister_mutex); + snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR); + snd_seq_oss_system_info_read(buf); + snd_seq_oss_synth_info_read(buf); + snd_seq_oss_midi_info_read(buf); + up(®ister_mutex); +} + +#endif /* CONFIG_PROC_FS */ + +static int __init +register_proc(void) +{ +#ifdef CONFIG_PROC_FS + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root); + if (entry == NULL) + return -ENOMEM; + + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = NULL; + entry->c.text.read_size = 1024; + entry->c.text.read = info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return -ENOMEM; + } + info_entry = entry; +#endif + return 0; +} + +static void +unregister_proc(void) +{ +#ifdef CONFIG_PROC_FS + if (info_entry) + snd_info_unregister(info_entry); + info_entry = NULL; +#endif +} diff -Nru linux/sound/core/seq/oss/seq_oss_device.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_device.h --- linux/sound/core/seq/oss/seq_oss_device.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_device.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,199 @@ +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_DEVICE_H +#define __SEQ_OSS_DEVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* enable debug print */ +#define SNDRV_SEQ_OSS_DEBUG + +/* max. applications */ +#define SNDRV_SEQ_OSS_MAX_CLIENTS 16 +#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS 16 +#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS 32 + +/* version */ +#define SNDRV_SEQ_OSS_MAJOR_VERSION 0 +#define SNDRV_SEQ_OSS_MINOR_VERSION 1 +#define SNDRV_SEQ_OSS_TINY_VERSION 8 +#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8" + +/* device and proc interface name */ +#define SNDRV_SEQ_OSS_DEVNAME "seq_oss" +#define SNDRV_SEQ_OSS_PROCNAME "oss" + + +/* + * type definitions + */ + +typedef struct seq_oss_devinfo_t seq_oss_devinfo_t; +typedef struct seq_oss_writeq_t seq_oss_writeq_t; +typedef struct seq_oss_readq_t seq_oss_readq_t; +typedef struct seq_oss_timer_t seq_oss_timer_t; +typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t; +typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t; +typedef struct seq_oss_chinfo_t seq_oss_chinfo_t; +typedef unsigned int reltime_t; +typedef unsigned int abstime_t; +typedef union evrec_t evrec_t; + + +/* + * synthesizer channel information + */ +struct seq_oss_chinfo_t { + int note, vel; +}; + +/* + * synthesizer information + */ +struct seq_oss_synthinfo_t { + snd_seq_oss_arg_t arg; + seq_oss_chinfo_t *ch; + seq_oss_synth_sysex_t *sysex; + int nr_voices; + int opened; + int is_midi; + int midi_mapped; +}; + + +/* + * sequencer client information + */ + +struct seq_oss_devinfo_t { + + int index; /* application index */ + int cseq; /* sequencer client number */ + int port; /* sequencer port number */ + int queue; /* sequencer queue number */ + + snd_seq_addr_t addr; /* address of this device */ + + int seq_mode; /* sequencer mode */ + int file_mode; /* file access */ + + /* midi device table */ + int max_mididev; + + /* synth device table */ + int max_synthdev; + seq_oss_synthinfo_t synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; + int synth_opened; + + /* output queue */ + seq_oss_writeq_t *writeq; + + /* midi input queue */ + seq_oss_readq_t *readq; + + /* timer */ + seq_oss_timer_t *timer; +}; + + +/* + * function prototypes + */ + +/* create/delete OSS sequencer client */ +int snd_seq_oss_create_client(void); +int snd_seq_oss_delete_client(void); + +/* device file interface */ +int snd_seq_oss_open(struct file *file, int level); +void snd_seq_oss_release(seq_oss_devinfo_t *dp); +int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg); +int snd_seq_oss_read(seq_oss_devinfo_t *dev, char *buf, int count); +int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt); +unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait); + +void snd_seq_oss_reset(seq_oss_devinfo_t *dp); +void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp); + +/* */ +void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time); + + +/* proc interface */ +void snd_seq_oss_system_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf); +void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf); + +/* file mode macros */ +#define is_read_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_READ) +#define is_write_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_WRITE) +#define is_nonblock_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK) + +/* dispatch event */ +inline static int +snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop) +{ + return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop); +} + +/* ioctl */ +inline static int +snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg) +{ + return snd_seq_kernel_client_ctl(dp->cseq, type, arg); +} + +/* fill the addresses in header */ +inline static void +snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, + int dest_client, int dest_port) +{ + ev->queue = dp->queue; + ev->source = dp->addr; + ev->dest.client = dest_client; + ev->dest.port = dest_port; +} + + +/* misc. functions for proc interface */ +char *enabled_str(int bool); +char *filemode_str(int fmode); + + +/* for debug */ +#ifdef SNDRV_SEQ_OSS_DEBUG +extern int seq_oss_debug; +#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0) +#else +#define debug_printk(x) /**/ +#endif + +#endif /* __SEQ_OSS_DEVICE_H */ diff -Nru linux/sound/core/seq/oss/seq_oss_event.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.c --- linux/sound/core/seq/oss/seq_oss_event.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,448 @@ +/* + * OSS compatible sequencer driver + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" + + +/* + * prototypes + */ +static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev); +static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); +static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev); +static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev); +static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev); +static int set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev); + + +/* + * convert an OSS event to ALSA event + * return 0 : enqueued + * non-zero : invalid - ignored + */ + +int +snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->s.code) { + case SEQ_EXTENDED: + return extended_event(dp, q, ev); + + case EV_CHN_VOICE: + return chn_voice_event(dp, q, ev); + + case EV_CHN_COMMON: + return chn_common_event(dp, q, ev); + + case EV_TIMING: + return timing_event(dp, q, ev); + + case EV_SEQ_LOCAL: + return local_event(dp, q, ev); + + case EV_SYSEX: + return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev); + + case SEQ_MIDIPUTC: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + /* put a midi byte */ + if (! is_write_mode(dp->file_mode)) + break; + if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE)) + break; + if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE) + return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev); + break; + + case SEQ_ECHO: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return set_echo_event(dp, q, ev); + + case SEQ_PRIVATE: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev); + + default: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + return old_event(dp, q, ev); + } + return -EINVAL; +} + +/* old type events: mode1 only */ +static int +old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->s.code) { + case SEQ_NOTEOFF: + return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev); + + case SEQ_NOTEON: + return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev); + + case SEQ_WAIT: + /* skip */ + break; + + case SEQ_PGMCHANGE: + return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE, + q->n.chn, 0, q->n.note, ev); + + case SEQ_SYNCTIMER: + return snd_seq_oss_timer_reset(dp->timer); + } + + return -EINVAL; +} + +/* 8bytes extended event: mode1 only */ +static int +extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + int val; + + switch (q->e.cmd) { + case SEQ_NOTEOFF: + return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev); + + case SEQ_NOTEON: + return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev); + + case SEQ_PGMCHANGE: + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE, + q->e.chn, 0, q->e.p1, ev); + + case SEQ_AFTERTOUCH: + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS, + q->e.chn, 0, q->e.p1, ev); + + case SEQ_BALANCE: + /* convert -128:127 to 0:127 */ + val = (char)q->e.p1; + val = (val + 128) / 2; + return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER, + q->e.chn, CTL_PAN, val, ev); + + case SEQ_CONTROLLER: + val = ((short)q->e.p3 << 8) | (short)q->e.p2; + switch (q->e.p1) { + case CTRL_PITCH_BENDER: /* SEQ1 V2 control */ + /* -0x2000:0x1fff */ + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_PITCHBEND, + q->e.chn, 0, val, ev); + case CTRL_PITCH_BENDER_RANGE: + /* conversion: 100/semitone -> 128/semitone */ + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_REGPARAM, + q->e.chn, 0, val*128/100, ev); + default: + return set_control_event(dp, q->e.dev, + SNDRV_SEQ_EVENT_CONTROL14, + q->e.chn, q->e.p1, val, ev); + } + + case SEQ_VOLMODE: + return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev); + + } + return -EINVAL; +} + +/* channel voice events: mode1 and 2 */ +static int +chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + if (q->v.chn >= 32) + return -EINVAL; + switch (q->v.cmd) { + case MIDI_NOTEON: + return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev); + + case MIDI_NOTEOFF: + return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev); + + case MIDI_KEY_PRESSURE: + return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS, + q->v.chn, q->v.note, q->v.parm, ev); + + } + return -EINVAL; +} + +/* channel common events: mode1 and 2 */ +static int +chn_common_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + if (q->l.chn >= 32) + return -EINVAL; + switch (q->l.cmd) { + case MIDI_PGM_CHANGE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE, + q->l.chn, 0, q->l.p1, ev); + + case MIDI_CTL_CHANGE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER, + q->l.chn, q->l.p1, q->l.val, ev); + + case MIDI_PITCH_BEND: + /* conversion: 0:0x3fff -> -0x2000:0x1fff */ + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND, + q->l.chn, 0, q->l.val - 8192, ev); + + case MIDI_CHN_PRESSURE: + return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS, + q->l.chn, 0, q->l.val, ev); + } + return -EINVAL; +} + +/* timer events: mode1 and mode2 */ +static int +timing_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + switch (q->t.cmd) { + case TMR_ECHO: + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return set_echo_event(dp, q, ev); + else { + evrec_t tmp; + memset(&tmp, 0, sizeof(tmp)); + /* XXX: only for little-endian! */ + tmp.echo = (q->t.time << 8) | SEQ_ECHO; + return set_echo_event(dp, &tmp, ev); + } + + case TMR_STOP: + if (dp->seq_mode) + return snd_seq_oss_timer_stop(dp->timer); + return 0; + + case TMR_CONTINUE: + if (dp->seq_mode) + return snd_seq_oss_timer_continue(dp->timer); + return 0; + + case TMR_TEMPO: + if (dp->seq_mode) + return snd_seq_oss_timer_tempo(dp->timer, q->t.time); + return 0; + } + + return -EINVAL; +} + +/* local events: mode1 and 2 */ +static int +local_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev) +{ + return -EINVAL; +} + +/* + * process note-on event for OSS synth + * three different modes are available: + * - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode) + * Accept note 255 as volume change. + * - SNDRV_SEQ_OSS_PASS_EVENTS + * Pass all events to lowlevel driver anyway + * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000) + * Use key-pressure if note >= 128 + */ +static int +note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev) +{ + seq_oss_synthinfo_t *info = &dp->synths[dev]; + switch (info->arg.event_passing) { + case SNDRV_SEQ_OSS_PROCESS_EVENTS: + if (! info->ch || ch < 0 || ch >= info->nr_voices) { + /* pass directly */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + + if (note == 255 && info->ch[ch].note >= 0) { + /* volume control */ + int type; + //if (! vel) + /* set volume to zero -- note off */ + // type = SNDRV_SEQ_EVENT_NOTEOFF; + //else + if (info->ch[ch].vel) + /* sample already started -- volume change */ + type = SNDRV_SEQ_EVENT_KEYPRESS; + else + /* sample not started -- start now */ + type = SNDRV_SEQ_EVENT_NOTEON; + info->ch[ch].vel = vel; + return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev); + } else if (note >= 128) + return -EINVAL; /* invalid */ + + if (note != info->ch[ch].note && info->ch[ch].note >= 0) + /* note changed - note off at beginning */ + set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev); + /* set current status */ + info->ch[ch].note = note; + info->ch[ch].vel = vel; + if (vel) /* non-zero velocity - start the note now */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + return -EINVAL; + + case SNDRV_SEQ_OSS_PASS_EVENTS: + /* pass the event anyway */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + + case SNDRV_SEQ_OSS_PROCESS_KEYPRESS: + if (note >= 128) /* key pressure: shifted by 128 */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev); + else /* normal note-on event */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + return -EINVAL; +} + +/* + * process note-off event for OSS synth + */ +static int +note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev) +{ + seq_oss_synthinfo_t *info = &dp->synths[dev]; + switch (info->arg.event_passing) { + case SNDRV_SEQ_OSS_PROCESS_EVENTS: + if (! info->ch || ch < 0 || ch >= info->nr_voices) { + /* pass directly */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev); + } + + if (info->ch[ch].note >= 0) { + note = info->ch[ch].note; + info->ch[ch].vel = 0; + info->ch[ch].note = -1; + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev); + } + return -EINVAL; /* invalid */ + + case SNDRV_SEQ_OSS_PASS_EVENTS: + case SNDRV_SEQ_OSS_PROCESS_KEYPRESS: + /* pass the event anyway */ + return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev); + + } + return -EINVAL; +} + +/* + * create a note event + */ +static int +set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + ev->type = type; + snd_seq_oss_synth_addr(dp, dev, ev); + ev->data.note.channel = ch; + ev->data.note.note = note; + ev->data.note.velocity = vel; + + return 0; +} + +/* + * create a control event + */ +static int +set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + ev->type = type; + snd_seq_oss_synth_addr(dp, dev, ev); + ev->data.control.channel = ch; + ev->data.control.param = param; + ev->data.control.value = val; + + return 0; +} + +/* + * create an echo event + */ +static int +set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev) +{ + ev->type = SNDRV_SEQ_EVENT_ECHO; + /* echo back to itself */ + snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port); + memcpy(&ev->data, rec, LONG_EVENT_SIZE); + return 0; +} + +/* + * event input callback from ALSA sequencer: + * the echo event is processed here. + */ +int +snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data; + evrec_t *rec; + + if (ev->type != SNDRV_SEQ_EVENT_ECHO) + return snd_seq_oss_midi_input(ev, direct, private_data); + + if (ev->source.client != dp->cseq) + return 0; /* ignored */ + + rec = (evrec_t*)&ev->data; + if (rec->s.code == SEQ_SYNCTIMER) { + /* sync echo back */ + snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time); + + } else { + /* echo back event */ + if (dp->readq == NULL) + return 0; + snd_seq_oss_readq_put_event(dp->readq, rec); + } + return 0; +} + diff -Nru linux/sound/core/seq/oss/seq_oss_event.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.h --- linux/sound/core/seq/oss/seq_oss_event.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_event.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,112 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_event.h - OSS event queue record + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_EVENT_H +#define __SEQ_OSS_EVENT_H + +#include "seq_oss_device.h" + +#define SHORT_EVENT_SIZE 4 +#define LONG_EVENT_SIZE 8 + +/* short event (4bytes) */ +typedef struct evrec_short_t { + unsigned char code; + unsigned char parm1; + unsigned char dev; + unsigned char parm2; +} evrec_short_t; + +/* short note events (4bytes) */ +typedef struct evrec_note_t { + unsigned char code; + unsigned char chn; + unsigned char note; + unsigned char vel; +} evrec_note_t; + +/* long timer events (8bytes) */ +typedef struct evrec_timer_t { + unsigned char code; + unsigned char cmd; + unsigned char dummy1, dummy2; + unsigned int time; +} evrec_timer_t; + +/* long extended events (8bytes) */ +typedef struct evrec_extended_t { + unsigned char code; + unsigned char cmd; + unsigned char dev; + unsigned char chn; + unsigned char p1, p2, p3, p4; +} evrec_extended_t; + +/* long channel events (8bytes) */ +typedef struct evrec_long_t { + unsigned char code; + unsigned char dev; + unsigned char cmd; + unsigned char chn; + unsigned char p1, p2; + unsigned short val; +} evrec_long_t; + +/* channel voice events (8bytes) */ +typedef struct evrec_voice_t { + unsigned char code; + unsigned char dev; + unsigned char cmd; + unsigned char chn; + unsigned char note, parm; + unsigned short dummy; +} evrec_voice_t; + +/* sysex events (8bytes) */ +typedef struct evrec_sysex_t { + unsigned char code; + unsigned char dev; + unsigned char buf[6]; +} evrec_sysex_t; + +/* event record */ +union evrec_t { + evrec_short_t s; + evrec_note_t n; + evrec_long_t l; + evrec_voice_t v; + evrec_timer_t t; + evrec_extended_t e; + evrec_sysex_t x; + unsigned int echo; + unsigned char c[LONG_EVENT_SIZE]; +}; + +#define ev_is_long(ev) ((ev)->s.code >= 128) +#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE) + +int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev); +int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q); +int snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop); + + +#endif /* __SEQ_OSS_EVENT_H */ diff -Nru linux/sound/core/seq/oss/seq_oss_init.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_init.c --- linux/sound/core/seq/oss/seq_oss_init.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_init.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,540 @@ +/* + * OSS compatible sequencer driver + * + * open/close and reset interface + * + * Copyright (C) 1998-1999 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_writeq.h" +#include "seq_oss_readq.h" +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include + +/* + * common variables + */ +MODULE_PARM(maxqlen, "i"); +MODULE_PARM_DESC(maxqlen, "maximum queue length"); + +static int system_client = -1; /* ALSA sequencer client number */ +static int system_port = -1; + +int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN; +static int num_clients = 0; +static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS]; + + +/* + * prototypes + */ +static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +static int translate_mode(struct file *file); +static int create_port(seq_oss_devinfo_t *dp); +static int delete_port(seq_oss_devinfo_t *dp); +static int alloc_seq_queue(seq_oss_devinfo_t *dp); +static int delete_seq_queue(seq_oss_devinfo_t *dp); +static void free_devinfo(void *private); + +#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec) + + +/* + * create sequencer client for OSS sequencer + */ +int __init +snd_seq_oss_create_client(void) +{ + int rc; + snd_seq_client_callback_t callback; + snd_seq_client_info_t info; + snd_seq_port_info_t port; + snd_seq_port_callback_t port_callback; + + /* create ALSA client */ + memset(&callback, 0, sizeof(callback)); + + callback.private_data = NULL; + callback.allow_input = 1; + callback.allow_output = 1; + + rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback); + if (rc < 0) + return rc; + + system_client = rc; + debug_printk(("new client = %d\n", rc)); + + /* set client information */ + memset(&info, 0, sizeof(info)); + info.client = system_client; + info.type = KERNEL_CLIENT; + strcpy(info.name, "OSS sequencer"); + + rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info); + + /* look up midi devices */ + snd_seq_oss_midi_lookup_ports(system_client); + + /* create annoucement receiver port */ + memset(&port, 0, sizeof(port)); + strcpy(port.name, "Receiver"); + port.addr.client = system_client; + port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ + port.type = 0; + + memset(&port_callback, 0, sizeof(port_callback)); + /* don't set port_callback.owner here. otherwise the module counter + * is incremented and we can no longer release the module.. + */ + port_callback.event_input = receive_announce; + port.kernel = &port_callback; + + call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + if ((system_port = port.addr.port) >= 0) { + snd_seq_port_subscribe_t subs; + + memset(&subs, 0, sizeof(subs)); + subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + subs.dest.client = system_client; + subs.dest.port = system_port; + call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs); + } + + + return 0; +} + + +/* + * receive annoucement from system port, and check the midi device + */ +static int +receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop) +{ + snd_seq_port_info_t pinfo; + + if (atomic) + return 0; /* it must not happen */ + + switch (ev->type) { + case SNDRV_SEQ_EVENT_PORT_START: + case SNDRV_SEQ_EVENT_PORT_CHANGE: + if (ev->data.addr.client == system_client) + break; /* ignore myself */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr = ev->data.addr; + if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0) + snd_seq_oss_midi_check_new_port(&pinfo); + break; + + case SNDRV_SEQ_EVENT_PORT_EXIT: + if (ev->data.addr.client == system_client) + break; /* ignore myself */ + snd_seq_oss_midi_check_exit_port(ev->data.addr.client, + ev->data.addr.port); + break; + } + return 0; +} + + +/* + * delete OSS sequencer client + */ +int +snd_seq_oss_delete_client(void) +{ + if (system_client >= 0) + snd_seq_delete_kernel_client(system_client); + + snd_seq_oss_midi_clear_all(); + + return 0; +} + + +/* + * open sequencer device + */ +int +snd_seq_oss_open(struct file *file, int level) +{ + int i, rc; + seq_oss_devinfo_t *dp; + + if ((dp = snd_kcalloc(sizeof(*dp), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc device info\n"); + return -ENOMEM; + } + + for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) { + if (client_table[i] == NULL) + break; + } + if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { + snd_printk(KERN_ERR "too many applications\n"); + return -ENOMEM; + } + + dp->index = i; + dp->cseq = system_client; + dp->port = -1; + dp->queue = -1; + dp->readq = NULL; + dp->writeq = NULL; + + /* look up synth and midi devices */ + snd_seq_oss_synth_setup(dp); + snd_seq_oss_midi_setup(dp); + + if (dp->synth_opened == 0 && dp->max_mididev == 0) { + snd_printk(KERN_ERR "no device found\n"); + kfree(dp); + return -ENODEV; + } + + /* create port */ + if ((rc = create_port(dp)) < 0) { + snd_printk(KERN_ERR "can't create port\n"); + free_devinfo(dp); + return rc; + } + + /* allocate queue */ + if ((rc = alloc_seq_queue(dp)) < 0) { + delete_port(dp); + return rc; + } + + /* set address */ + dp->addr.client = dp->cseq; + dp->addr.port = dp->port; + /*dp->addr.queue = dp->queue;*/ + /*dp->addr.channel = 0;*/ + + dp->seq_mode = level; + + /* set up file mode */ + dp->file_mode = translate_mode(file); + + /* initialize read queue */ + if (is_read_mode(dp->file_mode)) { + if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) { + delete_seq_queue(dp); + delete_port(dp); + return -ENOMEM; + } + } + + /* initialize write queue */ + if (is_write_mode(dp->file_mode)) { + dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen); + if (dp->writeq == NULL) { + delete_seq_queue(dp); + delete_port(dp); + return -ENOMEM; + } + } + + /* initialize timer */ + if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) { + snd_printk(KERN_ERR "can't alloc timer\n"); + delete_seq_queue(dp); + delete_port(dp); + return -ENOMEM; + } + + /* set private data pointer */ + file->private_data = dp; + + /* set up for mode2 */ + if (level == SNDRV_SEQ_OSS_MODE_MUSIC) + snd_seq_oss_synth_setup_midi(dp); + else if (is_read_mode(dp->file_mode)) + snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ); + + client_table[dp->index] = dp; + num_clients++; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + + debug_printk(("open done\n")); + + return 0; +} + +/* + * translate file flags to private mode + */ +static int +translate_mode(struct file *file) +{ + int file_mode = 0; + if ((file->f_flags & O_ACCMODE) != O_RDONLY) + file_mode |= SNDRV_SEQ_OSS_FILE_WRITE; + if ((file->f_flags & O_ACCMODE) != O_WRONLY) + file_mode |= SNDRV_SEQ_OSS_FILE_READ; + if (file->f_flags & O_NONBLOCK) + file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK; + return file_mode; +} + + +/* + * create sequencer port + */ +static int +create_port(seq_oss_devinfo_t *dp) +{ + int rc; + snd_seq_port_info_t port; + snd_seq_port_callback_t callback; + + memset(&port, 0, sizeof(port)); + port.addr.client = dp->cseq; + sprintf(port.name, "Sequencer-%d", dp->index); + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */ + port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; + port.midi_channels = 128; + port.synth_voices = 128; + + memset(&callback, 0, sizeof(callback)); + callback.owner = THIS_MODULE; + callback.private_data = dp; + callback.event_input = snd_seq_oss_event_input; + callback.private_free = free_devinfo; + port.kernel = &callback; + + rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + if (rc < 0) + return rc; + + dp->port = port.addr.port; + debug_printk(("new port = %d\n", port.addr.port)); + + return 0; +} + +/* + * delete ALSA port + */ +static int +delete_port(seq_oss_devinfo_t *dp) +{ + snd_seq_port_info_t port_info; + + if (dp->port < 0) + return 0; + + memset(&port_info, 0, sizeof(port_info)); + port_info.addr.client = dp->cseq; + port_info.addr.port = dp->port; + return snd_seq_kernel_client_ctl(dp->cseq, + SNDRV_SEQ_IOCTL_DELETE_PORT, + &port_info); +} + +/* + * allocate a queue + */ +static int +alloc_seq_queue(seq_oss_devinfo_t *dp) +{ + snd_seq_queue_info_t qinfo; + int rc; + + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.owner = system_client; + qinfo.locked = 1; + strcpy(qinfo.name, "OSS Sequencer Emulation"); + if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0) + return rc; + dp->queue = qinfo.queue; + return 0; +} + +/* + * release queue + */ +static int +delete_seq_queue(seq_oss_devinfo_t *dp) +{ + snd_seq_queue_info_t qinfo; + + if (dp->queue < 0) + return 0; + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.queue = dp->queue; + return call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo); +} + + +/* + * free device informations - private_free callback of port + */ +static void +free_devinfo(void *private) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private; + + if (dp->timer) + snd_seq_oss_timer_delete(dp->timer); + + if (dp->writeq) + snd_seq_oss_writeq_delete(dp->writeq); + + if (dp->readq) + snd_seq_oss_readq_delete(dp->readq); + + kfree(dp); +} + + +/* + * close sequencer device + */ +void +snd_seq_oss_release(seq_oss_devinfo_t *dp) +{ + client_table[dp->index] = NULL; + num_clients--; + + debug_printk(("resetting..\n")); + snd_seq_oss_reset(dp); + + debug_printk(("cleaning up..\n")); + snd_seq_oss_synth_cleanup(dp); + snd_seq_oss_midi_cleanup(dp); + + /* clear slot */ + debug_printk(("releasing resource..\n")); + if (dp->port >= 0) + delete_port(dp); + if (dp->queue >= 0) + delete_seq_queue(dp); + +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + debug_printk(("release done\n")); +} + + +/* + * Wait until the queue is empty (if we don't have nonblock) + */ +void +snd_seq_oss_drain_write(seq_oss_devinfo_t *dp) +{ + if (! dp->timer->running) + return; + if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) && + dp->writeq) { + debug_printk(("syncing..\n")); + while (snd_seq_oss_writeq_sync(dp->writeq)) + ; + } +} + + +/* + * reset sequencer devices + */ +void +snd_seq_oss_reset(seq_oss_devinfo_t *dp) +{ + int i; + + /* reset all synth devices */ + for (i = 0; i < dp->max_synthdev; i++) + snd_seq_oss_synth_reset(dp, i); + + /* reset all midi devices */ + if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) { + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_reset(dp, i); + } + + /* remove queues */ + if (dp->readq) + snd_seq_oss_readq_clear(dp->readq); + if (dp->writeq) + snd_seq_oss_writeq_clear(dp->writeq); + + /* reset timer */ + snd_seq_oss_timer_stop(dp->timer); +} + +/* + * proc interface + */ +void +snd_seq_oss_system_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_devinfo_t *dp; + + snd_iprintf(buf, "ALSA client number %d\n", system_client); + snd_iprintf(buf, "ALSA receiver port %d\n", system_port); + + snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients); + for (i = 0; i < num_clients; i++) { + snd_iprintf(buf, "\nApplication %d: ", i); + if ((dp = client_table[i]) == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue); + snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n", + (dp->seq_mode ? "music" : "synth"), + filemode_str(dp->file_mode)); + if (dp->seq_mode) + snd_iprintf(buf, " timer tempo = %d, timebase = %d\n", + dp->timer->oss_tempo, dp->timer->oss_timebase); + snd_iprintf(buf, " max queue length %d\n", maxqlen); + if (is_read_mode(dp->file_mode) && dp->readq) + snd_seq_oss_readq_info_read(dp->readq, buf); + } +} + +/* + * misc. functions for proc interface + */ +char * +enabled_str(int bool) +{ + return bool ? "enabled" : "disabled"; +} + +char * +filemode_str(int val) +{ + static char *str[] = { + "none", "read", "write", "read/write", + }; + return str[val & SNDRV_SEQ_OSS_FILE_ACMODE]; +} + + diff -Nru linux/sound/core/seq/oss/seq_oss_ioctl.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_ioctl.c --- linux/sound/core/seq/oss/seq_oss_ioctl.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_ioctl.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,191 @@ +/* + * OSS compatible sequencer driver + * + * OSS compatible i/o control + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" +#include "seq_oss_timer.h" +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "seq_oss_event.h" + +int +snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg) +{ + int dev, val; + struct synth_info inf; + struct midi_info minf; + unsigned char ev[8]; + void *arg = (void*)carg; + snd_seq_event_t tmpev; + + switch (cmd) { + case SNDCTL_TMR_TIMEBASE: + case SNDCTL_TMR_TEMPO: + case SNDCTL_TMR_START: + case SNDCTL_TMR_STOP: + case SNDCTL_TMR_CONTINUE: + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SOURCE: + case SNDCTL_TMR_SELECT: + case SNDCTL_SEQ_CTRLRATE: + return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg); + + case SNDCTL_SEQ_PANIC: + debug_printk(("panic\n")); + snd_seq_oss_reset(dp); + return -EINVAL; + + case SNDCTL_SEQ_SYNC: + debug_printk(("sync\n")); + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return 0; + while (snd_seq_oss_writeq_sync(dp->writeq)) + ; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; + + case SNDCTL_SEQ_RESET: + debug_printk(("reset\n")); + snd_seq_oss_reset(dp); + return 0; + + case SNDCTL_SEQ_TESTMIDI: + debug_printk(("test midi\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + return snd_seq_oss_midi_open(dp, dev, dp->file_mode); + + case SNDCTL_SEQ_GETINCOUNT: + debug_printk(("get in count\n")); + if (dp->readq == NULL || ! is_read_mode(dp->file_mode)) + return 0; + return put_user(dp->readq->qlen, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_GETOUTCOUNT: + debug_printk(("get out count\n")); + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return 0; + return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_GETTIME: + debug_printk(("get time\n")); + return put_user(snd_seq_oss_timer_cur_tick(dp->timer), (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_RESETSAMPLES: + debug_printk(("reset samples\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + + case SNDCTL_SEQ_NRSYNTHS: + debug_printk(("nr synths\n")); + return put_user(dp->max_synthdev, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SEQ_NRMIDIS: + debug_printk(("nr midis\n")); + return put_user(dp->max_mididev, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_SYNTH_MEMAVL: + debug_printk(("mem avail\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + return put_user(val, (int *)arg) ? -EFAULT : 0; + + case SNDCTL_FM_4OP_ENABLE: + debug_printk(("4op\n")); + if (get_user(dev, (int *)arg)) + return -EFAULT; + snd_seq_oss_synth_ioctl(dp, dev, cmd, carg); + return 0; + + case SNDCTL_SYNTH_INFO: + case SNDCTL_SYNTH_ID: + debug_printk(("synth info\n")); + if (copy_from_user(&inf, arg, sizeof(inf))) + return -EFAULT; + if (snd_seq_oss_synth_make_info(dp, inf.device, &inf) < 0) + return -EINVAL; + if (copy_to_user(arg, &inf, sizeof(inf))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_OUTOFBAND: + debug_printk(("out of bound\n")); + if (copy_from_user(ev, arg, 8)) + return -EFAULT; + memset(&tmpev, 0, sizeof(tmpev)); + snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client); + tmpev.time.tick = 0; + if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) { + snd_seq_oss_dispatch(dp, &tmpev, 0, 0); + } + return 0; + + case SNDCTL_MIDI_INFO: + debug_printk(("midi info\n")); + if (copy_from_user(&minf, arg, sizeof(minf))) + return -EFAULT; + if (snd_seq_oss_midi_make_info(dp, minf.device, &minf) < 0) + return -EINVAL; + if (copy_to_user(arg, &minf, sizeof(minf))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_THRESHOLD: + debug_printk(("threshold\n")); + if (! is_write_mode(dp->file_mode)) + return 0; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 1) + val = 1; + if (val >= dp->writeq->maxlen) + val = dp->writeq->maxlen - 1; + snd_seq_oss_writeq_set_output(dp->writeq, val); + return 0; + + case SNDCTL_MIDI_PRETIME: + debug_printk(("pretime\n")); + if (dp->readq == NULL || !is_read_mode(dp->file_mode)) + return 0; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val <= 0) + val = -1; + else + val = (HZ * val) / 10; + dp->readq->pre_event_timeout = val; + return put_user(val, (int *)arg) ? -EFAULT : 0; + + default: + debug_printk(("others\n")); + if (! is_write_mode(dp->file_mode)) + return -EIO; + return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg); + } + return 0; +} + diff -Nru linux/sound/core/seq/oss/seq_oss_midi.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.c --- linux/sound/core/seq/oss/seq_oss_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,697 @@ +/* + * OSS compatible sequencer driver + * + * MIDI device handlers + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_midi.h" +#include "seq_oss_readq.h" +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include +#include "../seq_lock.h" +#include + + +/* + * constants + */ +#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30 + +/* + * definition of midi device record + */ +struct seq_oss_midi_t { + int seq_device; /* device number */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + unsigned int flags; /* port capability */ + int opened; /* flag for opening */ + unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME]; + snd_midi_event_t *coder; /* MIDI event coder */ + seq_oss_devinfo_t *devinfo; /* assigned OSSseq device */ + snd_use_lock_t use_lock; +}; + + +/* + * midi device table + */ +static int max_midi_devs = 0; +static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; + +static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; + +/* + * prototypes + */ +static seq_oss_midi_t *get_mdev(int dev); +static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev); +static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev); +static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev); + +/* + * look up the existing ports + * this looks a very exhausting job. + */ +int __init +snd_seq_oss_midi_lookup_ports(int client) +{ + snd_seq_system_info_t sysinfo; + snd_seq_client_info_t clinfo; + snd_seq_port_info_t pinfo; + int rc; + + rc = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SYSTEM_INFO, &sysinfo); + if (rc < 0) + return rc; + + memset(&clinfo, 0, sizeof(clinfo)); + memset(&pinfo, 0, sizeof(pinfo)); + clinfo.client = -1; + while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, &clinfo) == 0) { + if (clinfo.client == client) + continue; /* ignore myself */ + pinfo.addr.client = clinfo.client; + pinfo.addr.port = -1; + while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, &pinfo) == 0) + snd_seq_oss_midi_check_new_port(&pinfo); + } + return 0; +} + + +/* + */ +static seq_oss_midi_t * +get_mdev(int dev) +{ + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + mdev = midi_devs[dev]; + if (mdev) + snd_use_lock_use(&mdev->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return mdev; +} + +/* + * look for the identical slot + */ +static seq_oss_midi_t * +find_slot(int client, int port) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + mdev = midi_devs[i]; + if (mdev && mdev->client == client && mdev->port == port) { + /* found! */ + snd_use_lock_use(&mdev->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return mdev; + } + } + spin_unlock_irqrestore(®ister_lock, flags); + return NULL; +} + + +#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) +#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) +/* + * register a new port if it doesn't exist yet + */ +int +snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port)); + /* the port must include generic midi */ + if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC)) + return 0; + /* either read or write subscribable */ + if ((pinfo->capability & PERM_WRITE) != PERM_WRITE && + (pinfo->capability & PERM_READ) != PERM_READ) + return 0; + + /* + * look for the identical slot + */ + if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) { + /* already exists */ + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + /* + * allocate midi info record + */ + if ((mdev = snd_kcalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc midi info\n"); + return -ENOMEM; + } + + /* copy the port information */ + mdev->client = pinfo->addr.client; + mdev->port = pinfo->addr.port; + mdev->flags = pinfo->capability; + mdev->opened = 0; + snd_use_lock_init(&mdev->use_lock); + + /* copy and truncate the name of synth device */ + strncpy(mdev->name, pinfo->name, sizeof(mdev->name)); + mdev->name[sizeof(mdev->name) - 1] = 0; + + /* create MIDI coder */ + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) { + snd_printk(KERN_ERR "can't malloc midi coder\n"); + kfree(mdev); + return -ENOMEM; + } + + /* + * look for en empty slot + */ + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + if (midi_devs[i] == NULL) + break; + } + if (i >= max_midi_devs) { + if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) { + snd_midi_event_free(mdev->coder); + kfree(mdev); + spin_unlock_irqrestore(®ister_lock, flags); + return -ENOMEM; + } + max_midi_devs++; + } + mdev->seq_device = i; + midi_devs[mdev->seq_device] = mdev; + spin_unlock_irqrestore(®ister_lock, flags); + + /*MOD_INC_USE_COUNT;*/ + return 0; +} + +/* + * release the midi device if it was registered + */ +int +snd_seq_oss_midi_check_exit_port(int client, int port) +{ + seq_oss_midi_t *mdev; + unsigned long flags; + int index; + + if ((mdev = find_slot(client, port)) != NULL) { + spin_lock_irqsave(®ister_lock, flags); + midi_devs[mdev->seq_device] = NULL; + spin_unlock_irqrestore(®ister_lock, flags); + snd_use_lock_free(&mdev->use_lock); + snd_use_lock_sync(&mdev->use_lock); + if (mdev->coder) + snd_midi_event_free(mdev->coder); + kfree(mdev); + } + spin_lock_irqsave(®ister_lock, flags); + for (index = max_midi_devs - 1; index >= 0; index--) { + if (midi_devs[index]) + break; + } + max_midi_devs = index + 1; + spin_unlock_irqrestore(®ister_lock, flags); + return 0; +} + + +/* + * release the midi device if it was registered + */ +void +snd_seq_oss_midi_clear_all(void) +{ + int i; + seq_oss_midi_t *mdev; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_midi_devs; i++) { + if ((mdev = midi_devs[i]) != NULL) { + if (mdev->coder) + snd_midi_event_free(mdev->coder); + kfree(mdev); + midi_devs[i] = NULL; + } + } + max_midi_devs = 0; + spin_unlock_irqrestore(®ister_lock, flags); +} + + +/* + * set up midi tables + */ +void +snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp) +{ + dp->max_mididev = max_midi_devs; +} + +/* + * clean up midi tables + */ +void +snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp) +{ + int i; + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_close(dp, i); + dp->max_mididev = 0; +} + + +/* + * open all midi devices. ignore errors. + */ +void +snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode) +{ + int i; + for (i = 0; i < dp->max_mididev; i++) + snd_seq_oss_midi_open(dp, i, file_mode); +} + + +/* + * get the midi device information + */ +static seq_oss_midi_t * +get_mididev(seq_oss_devinfo_t *dp, int dev) +{ + if (dev < 0 || dev >= dp->max_mididev) + return NULL; + return get_mdev(dev); +} + + +/* + * open the midi device if not opened yet + */ +int +snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode) +{ + int perm; + seq_oss_midi_t *mdev; + snd_seq_port_subscribe_t subs; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + + /* already used? */ + if (mdev->opened && mdev->devinfo != dp) { + snd_use_lock_free(&mdev->use_lock); + return -EBUSY; + } + + perm = 0; + if (is_write_mode(fmode)) + perm |= PERM_WRITE; + if (is_read_mode(fmode)) + perm |= PERM_READ; + perm &= mdev->flags; + if (perm == 0) { + snd_use_lock_free(&mdev->use_lock); + return -ENXIO; + } + + /* already opened? */ + if ((mdev->opened & perm) == perm) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + perm &= ~mdev->opened; + + memset(&subs, 0, sizeof(subs)); + + if (perm & PERM_WRITE) { + subs.sender = dp->addr; + subs.dest.client = mdev->client; + subs.dest.port = mdev->port; + if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) + mdev->opened |= PERM_WRITE; + } + if (perm & PERM_READ) { + subs.sender.client = mdev->client; + subs.sender.port = mdev->port; + subs.dest = dp->addr; + if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) + mdev->opened |= PERM_READ; + } + + if (! mdev->opened) { + snd_use_lock_free(&mdev->use_lock); + return -ENXIO; + } + + mdev->devinfo = dp; + snd_use_lock_free(&mdev->use_lock); + return 0; +} + +/* + * close the midi device if already opened + */ +int +snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + snd_seq_port_subscribe_t subs; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + if (! mdev->opened || mdev->devinfo != dp) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened)); + memset(&subs, 0, sizeof(subs)); + if (mdev->opened & PERM_WRITE) { + subs.sender = dp->addr; + subs.dest.client = mdev->client; + subs.dest.port = mdev->port; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); + } + if (mdev->opened & PERM_READ) { + subs.sender.client = mdev->client; + subs.sender.port = mdev->port; + subs.dest = dp->addr; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs); + } + + mdev->opened = 0; + mdev->devinfo = NULL; + + snd_use_lock_free(&mdev->use_lock); + return 0; +} + +/* + * change seq capability flags to file mode flags + */ +int +snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + int mode; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return 0; + + mode = 0; + if (mdev->opened & PERM_WRITE) + mode |= SNDRV_SEQ_OSS_FILE_WRITE; + if (mdev->opened & PERM_READ) + mode |= SNDRV_SEQ_OSS_FILE_READ; + + snd_use_lock_free(&mdev->use_lock); + return mode; +} + +/* + * reset the midi device and close it: + * so far, only close the device. + */ +void +snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return; + if (! mdev->opened) { + snd_use_lock_free(&mdev->use_lock); + return; + } + + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC && + (mdev->opened & PERM_WRITE)) { + snd_seq_event_t ev; + int c; + + debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port)); + memset(&ev, 0, sizeof(ev)); + ev.dest.client = mdev->client; + ev.dest.port = mdev->port; + ev.queue = dp->queue; + ev.source.port = dp->port; + for (c = 0; c < 16; c++) { + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + ev.data.control.channel = c; + ev.data.control.param = 123; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */ + ev.data.control.param = 121; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */ + ev.type = SNDRV_SEQ_EVENT_PITCHBEND; + ev.data.control.value = 0; + snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */ + } + } + snd_seq_oss_midi_close(dp, dev); + snd_use_lock_free(&mdev->use_lock); +} + + +/* + * get client/port of the specified MIDI device + */ +void +snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return; + addr->client = mdev->client; + addr->port = mdev->port; + snd_use_lock_free(&mdev->use_lock); +} + + +/* + * input callback - this can be atomic + */ +int +snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data) +{ + seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data; + seq_oss_midi_t *mdev; + int rc; + + if (dp->readq == NULL) + return 0; + if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL) + return 0; + if (! (mdev->opened & PERM_READ)) { + snd_use_lock_free(&mdev->use_lock); + return 0; + } + + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + rc = send_synth_event(dp, ev, mdev->seq_device); + else + rc = send_midi_event(dp, ev, mdev); + + snd_use_lock_free(&mdev->use_lock); + return rc; +} + +/* + * convert ALSA sequencer event to OSS synth event + */ +static int +send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev) +{ + evrec_t ossev; + + memset(&ossev, 0, sizeof(ossev)); + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + ossev.v.cmd = MIDI_NOTEON; break; + case SNDRV_SEQ_EVENT_NOTEOFF: + ossev.v.cmd = MIDI_NOTEOFF; break; + case SNDRV_SEQ_EVENT_KEYPRESS: + ossev.v.cmd = MIDI_KEY_PRESSURE; break; + case SNDRV_SEQ_EVENT_CONTROLLER: + ossev.l.cmd = MIDI_CTL_CHANGE; break; + case SNDRV_SEQ_EVENT_PGMCHANGE: + ossev.l.cmd = MIDI_PGM_CHANGE; break; + case SNDRV_SEQ_EVENT_CHANPRESS: + ossev.l.cmd = MIDI_CHN_PRESSURE; break; + case SNDRV_SEQ_EVENT_PITCHBEND: + ossev.l.cmd = MIDI_PITCH_BEND; break; + default: + return 0; /* not supported */ + } + + ossev.v.dev = dev; + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + case SNDRV_SEQ_EVENT_NOTEOFF: + case SNDRV_SEQ_EVENT_KEYPRESS: + ossev.v.code = EV_CHN_VOICE; + ossev.v.note = ev->data.note.note; + ossev.v.parm = ev->data.note.velocity; + break; + case SNDRV_SEQ_EVENT_CONTROLLER: + case SNDRV_SEQ_EVENT_PGMCHANGE: + case SNDRV_SEQ_EVENT_CHANPRESS: + ossev.l.code = EV_CHN_COMMON; + ossev.l.p1 = ev->data.control.param; + ossev.l.val = ev->data.control.value; + break; + case SNDRV_SEQ_EVENT_PITCHBEND: + ossev.l.code = EV_CHN_COMMON; + ossev.l.val = ev->data.control.value + 8192; + break; + } + + snd_seq_oss_readq_put_event(dp->readq, &ossev); + + return 0; +} + +/* + * decode event and send MIDI bytes to read queue + */ +static int +send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev) +{ + char msg[32]; /* enough except for sysex? */ + int len; + + snd_seq_oss_readq_put_timestamp(dp->readq, snd_seq_oss_timer_cur_tick(dp->timer), dp->seq_mode); + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, + ev->data.ext.ptr, ev->data.ext.len); + } else { + len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev); + if (len > 0) + snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len); + } + + return 0; +} + + +/* + * dump midi data + * return 0 : enqueued + * non-zero : invalid - ignored + */ +int +snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENODEV; + if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) { + snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port); + snd_use_lock_free(&mdev->use_lock); + return 0; + } + snd_use_lock_free(&mdev->use_lock); + return -EINVAL; +} + +/* + * create OSS compatible midi_info record + */ +int +snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf) +{ + seq_oss_midi_t *mdev; + + if ((mdev = get_mididev(dp, dev)) == NULL) + return -ENXIO; + inf->device = dev; + inf->dev_type = 0; /* FIXME: ?? */ + inf->capabilities = 0; /* FIXME: ?? */ + strncpy(inf->name, mdev->name, sizeof(inf->name)); + snd_use_lock_free(&mdev->use_lock); + return 0; +} + + +/* + * proc interface + */ +static char * +capmode_str(int val) +{ + val &= PERM_READ|PERM_WRITE; + if (val == (PERM_READ|PERM_WRITE)) + return "read/write"; + else if (val == PERM_READ) + return "read"; + else if (val == PERM_WRITE) + return "write"; + else + return "none"; +} + +void +snd_seq_oss_midi_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_midi_t *mdev; + + snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs); + for (i = 0; i < max_midi_devs; i++) { + snd_iprintf(buf, "\nmidi %d: ", i); + mdev = get_mdev(i); + if (mdev == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name, + mdev->client, mdev->port); + snd_iprintf(buf, " capability %s / opened %s\n", + capmode_str(mdev->flags), + capmode_str(mdev->opened)); + snd_use_lock_free(&mdev->use_lock); + } +} + diff -Nru linux/sound/core/seq/oss/seq_oss_midi.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.h --- linux/sound/core/seq/oss/seq_oss_midi.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_midi.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,49 @@ +/* + * OSS compatible sequencer driver + * + * midi device information + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_MIDI_H +#define __SEQ_OSS_MIDI_H + +#include "seq_oss_device.h" +#include + +typedef struct seq_oss_midi_t seq_oss_midi_t; + +int snd_seq_oss_midi_lookup_ports(int client); +int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo); +int snd_seq_oss_midi_check_exit_port(int client, int port); +void snd_seq_oss_midi_clear_all(void); + +void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp); +void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp); + +int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode); +void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode); +int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev); +void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev); +int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private); +int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf); +void snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr); + +#endif diff -Nru linux/sound/core/seq/oss/seq_oss_misc.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_misc.c --- linux/sound/core/seq/oss/seq_oss_misc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_misc.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,110 @@ +/*---------------------------------------------------------------- + * miscellaneous functions + *----------------------------------------------------------------*/ + +unsigned short snd_seq_oss_semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short snd_seq_oss_cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; + +/* convert from MIDI note to frequency */ +int +snd_seq_oss_note_to_freq(int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ + + int note, octave, note_freq; + static int notes[] = { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* + * note_freq >>= 1; + */ + + return note_freq; +} + +unsigned long +snd_seq_oss_compute_finetune(unsigned long base_freq, int bend, int range, int vibrato_cents) +{ + unsigned long amount; + int negative, semitones, cents, multiplier = 1; + + if (!bend || !range || !base_freq) + return base_freq; + + if (range >= 8192) + range = 8192; + + bend = bend * range / 8192; /* Convert to cents */ + bend += vibrato_cents; + + if (!bend) + return base_freq; + + negative = bend < 0 ? 1 : 0; + + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; + + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) { + multiplier *= 4; + bend -= 2400; + } + + semitones = bend / 100; + if (semitones > 99) + semitones = 99; + cents = bend % 100; + + amount = (int) (snd_seq_oss_semitone_tuning[semitones] * multiplier * + snd_seq_oss_cent_tuning[cents]) / 10000; + + if (negative) + return (base_freq * 10000) / amount; /* Bend down */ + else + return (base_freq * amount) / 10000; /* Bend up */ +} + diff -Nru linux/sound/core/seq/oss/seq_oss_readq.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.c --- linux/sound/core/seq/oss/seq_oss_readq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,245 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_readq.c - MIDI input queue + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_readq.h" +#include "seq_oss_event.h" +#include +#include "../seq_lock.h" + +/* + * constants + */ +//#define SNDRV_SEQ_OSS_MAX_TIMEOUT (unsigned long)(-1) +#define SNDRV_SEQ_OSS_MAX_TIMEOUT (HZ * 3600) + + +/* + * prototypes + */ + + +/* + * create a read queue + */ +seq_oss_readq_t * +snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen) +{ + seq_oss_readq_t *q; + + if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc read queue\n"); + return NULL; + } + + if ((q->q = snd_kcalloc(sizeof(evrec_t) * maxlen, GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc read queue buffer\n"); + kfree(q); + return NULL; + } + + q->maxlen = maxlen; + q->qlen = 0; + q->head = q->tail = 0; + init_waitqueue_head(&q->midi_sleep); + spin_lock_init(&q->lock); + q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT; + q->input_time = (unsigned long)-1; + + return q; +} + +/* + * delete the read queue + */ +void +snd_seq_oss_readq_delete(seq_oss_readq_t *q) +{ + if (q) { + snd_seq_oss_readq_clear(q); /* to be sure */ + if (q->q) + kfree(q->q); + kfree(q); + } +} + +/* + * reset the read queue + */ +void +snd_seq_oss_readq_clear(seq_oss_readq_t *q) +{ + if (q->qlen) { + q->qlen = 0; + q->head = q->tail = 0; + } + /* if someone sleeping, wake'em up */ + if (waitqueue_active(&q->midi_sleep)) + wake_up(&q->midi_sleep); + q->input_time = (unsigned long)-1; +} + +/* + * put a midi byte + */ +int +snd_seq_oss_readq_puts(seq_oss_readq_t *q, int dev, unsigned char *data, int len) +{ + evrec_t rec; + int result; + + rec.c[0] = SEQ_MIDIPUTC; + rec.c[2] = dev; + rec.c[3] = 0; + + while (len-- > 0) { + rec.c[1] = *data++; + result = snd_seq_oss_readq_put_event(q, &rec); + if (result < 0) + return result; + } + return 0; +} + +/* + * copy an event to input queue: + * return zero if enqueued + */ +int +snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->qlen >= q->maxlen - 1) { + spin_unlock_irqrestore(&q->lock, flags); + return -ENOMEM; + } + + memcpy(&q->q[q->tail], ev, ev_length(ev)); + q->tail = (q->tail + 1) % q->maxlen; + q->qlen++; + + /* wake up sleeper */ + if (waitqueue_active(&q->midi_sleep)) + wake_up(&q->midi_sleep); + + spin_unlock_irqrestore(&q->lock, flags); + + return 0; +} + + +/* + * pop queue + */ +evrec_t * +snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags) +{ + evrec_t *p; + + spin_lock_irqsave(&q->lock, *rflags); + if (q->qlen == 0) { + if (blocking) { + snd_seq_sleep_timeout_in_lock(&q->midi_sleep, + &q->lock, + q->pre_event_timeout); + } + if (q->qlen == 0) { + spin_unlock_irqrestore(&q->lock, *rflags); + return NULL; + } + } + p = q->q + q->head; + + return p; +} + +/* + * unlock queue + */ +void +snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags) +{ + spin_unlock_irqrestore(&q->lock, flags); +} + +/* + * drain one record and unlock queue + */ +void +snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags) +{ + if (q->qlen > 0) { + q->head = (q->head + 1) % q->maxlen; + q->qlen--; + } + spin_unlock_irqrestore(&q->lock, flags); +} + +/* + * polling/select: + * return non-zero if readq is not empty. + */ +unsigned int +snd_seq_oss_readq_poll(seq_oss_readq_t *q, struct file *file, poll_table *wait) +{ + poll_wait(file, &q->midi_sleep, wait); + return q->qlen; +} + +/* + * put a timestamp + */ +int +snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *q, unsigned long curt, int seq_mode) +{ + if (curt != q->input_time) { + evrec_t rec; + switch (seq_mode) { + case SNDRV_SEQ_OSS_MODE_SYNTH: + rec.echo = (curt << 8) | SEQ_WAIT; + snd_seq_oss_readq_put_event(q, &rec); + break; + case SNDRV_SEQ_OSS_MODE_MUSIC: + rec.t.code = EV_TIMING; + rec.t.cmd = TMR_WAIT_ABS; + rec.t.time = curt; + snd_seq_oss_readq_put_event(q, &rec); + break; + } + q->input_time = curt; + } + return 0; +} + + +/* + * proc interface + */ +void +snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf) +{ + snd_iprintf(buf, " read queue [%s] length = %d : tick = %ld\n", + (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"), + q->qlen, q->input_time); +} diff -Nru linux/sound/core/seq/oss/seq_oss_readq.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.h --- linux/sound/core/seq/oss/seq_oss_readq.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_readq.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,53 @@ +/* + * OSS compatible sequencer driver + * read fifo queue + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_READQ_H +#define __SEQ_OSS_READQ_H + +#include "seq_oss_device.h" + + +/* + * definition of read queue + */ +struct seq_oss_readq_t { + evrec_t *q; + int qlen; + int maxlen; + int head, tail; + unsigned long pre_event_timeout; + unsigned long input_time; + wait_queue_head_t midi_sleep; + spinlock_t lock; +}; + +seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen); +void snd_seq_oss_readq_delete(seq_oss_readq_t *q); +void snd_seq_oss_readq_clear(seq_oss_readq_t *readq); +unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait); +int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len); +int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev); +int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode); +evrec_t *snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags); +void snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags); +void snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags); + +#endif diff -Nru linux/sound/core/seq/oss/seq_oss_rw.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_rw.c --- linux/sound/core/seq/oss/seq_oss_rw.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_rw.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,197 @@ +/* + * OSS compatible sequencer driver + * + * read/write/select interface to device file + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_device.h" +#include "seq_oss_readq.h" +#include "seq_oss_writeq.h" +#include "seq_oss_synth.h" +#include +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include "../seq_clientmgr.h" + + +/* + * protoypes + */ +static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt); + + +/* + * read interface + */ + +int +snd_seq_oss_read(seq_oss_devinfo_t *dp, char *buf, int count) +{ + seq_oss_readq_t *readq = dp->readq; + int cnt, pos; + evrec_t *q; + unsigned long flags; + + if (readq == NULL || ! is_read_mode(dp->file_mode)) + return -EIO; + + /* copy queued events to read buffer */ + cnt = count; + pos = 0; + q = snd_seq_oss_readq_pick(readq, !is_nonblock_mode(dp->file_mode), &flags); + if (q == NULL) + return 0; + do { + int ev_len; + /* tansfer the data */ + ev_len = ev_length(q); + if (copy_to_user(buf + pos, q, ev_len)) { + snd_seq_oss_readq_unlock(readq, flags); + break; + } + snd_seq_oss_readq_free(readq, flags); + pos += ev_len; + cnt -= ev_len; + if (cnt < ev_len) + break; + } while ((q = snd_seq_oss_readq_pick(readq, 0, &flags)) != NULL); + + return count - cnt; +} + + +/* + * write interface + */ + +int +snd_seq_oss_write(seq_oss_devinfo_t *dp, const char *buf, int count, struct file *opt) +{ + int rc, c, p, ev_size; + evrec_t rec; + + if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) + return -EIO; + + c = count; + p = 0; + while (c >= SHORT_EVENT_SIZE) { + if (copy_from_user(rec.c, buf + p, SHORT_EVENT_SIZE)) + break; + p += SHORT_EVENT_SIZE; + + if (rec.s.code == SEQ_FULLSIZE) { + /* load patch */ + int fmt = (*(unsigned short *)rec.c) & 0xffff; + return snd_seq_oss_synth_load_patch(dp, rec.s.dev, fmt, buf, p, c); + + } + if (ev_is_long(&rec)) { + /* extended code */ + if (rec.s.code == SEQ_EXTENDED && + dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + ev_size = LONG_EVENT_SIZE; + if (c < ev_size) + break; + /* copy the reset 4 bytes */ + if (copy_from_user(rec.c + SHORT_EVENT_SIZE, buf + p, + LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) + break; + p += LONG_EVENT_SIZE - SHORT_EVENT_SIZE; + + } else { + /* old-type code */ + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) + return -EINVAL; + ev_size = SHORT_EVENT_SIZE; + } + + /* insert queue */ + if ((rc = insert_queue(dp, &rec, opt)) < 0) + break; + + c -= ev_size; + } + + if (count == c && is_nonblock_mode(dp->file_mode)) + return -EAGAIN; + return count - c; +} + + +/* + * insert event record to write queue + * return: 0 = OK, non-zero = NG + */ +static int +insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt) +{ + int rc = 0; + snd_seq_event_t event; + + /* if this is a timing event, process the current time */ + if (snd_seq_oss_process_timer_event(dp->timer, rec)) + return 0; /* no need to insert queue */ + + /* parse this event */ + memset(&event, 0, sizeof(event)); + /* set dummy -- to be sure */ + event.type = SNDRV_SEQ_EVENT_NOTEOFF; + snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client); + + if (snd_seq_oss_process_event(dp, rec, &event)) + return 0; /* invalid event - no need to insert queue */ + + event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer); + if (dp->timer->realtime || !dp->timer->running) { + snd_seq_oss_dispatch(dp, &event, 0, 0); + } else { + if (is_nonblock_mode(dp->file_mode)) + rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0); + else + rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0); + } + return rc; +} + + +/* + * select / poll + */ + +unsigned int +snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + + /* input */ + if (dp->readq && is_read_mode(dp->file_mode)) { + if (snd_seq_oss_readq_poll(dp->readq, file, wait)) + mask |= POLLIN | POLLRDNORM; + } + + /* output */ + if (dp->writeq && is_write_mode(dp->file_mode)) { + if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} diff -Nru linux/sound/core/seq/oss/seq_oss_synth.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.c --- linux/sound/core/seq/oss/seq_oss_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,642 @@ +/* + * OSS compatible sequencer driver + * + * synth device handlers + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_synth.h" +#include "seq_oss_midi.h" +#include "../seq_lock.h" +#include + +/* + * constants + */ +#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30 +#define MAX_SYSEX_BUFLEN 128 + + +/* + * definition of synth info records + */ + +/* sysex buffer */ +struct seq_oss_synth_sysex_t { + int len; + int skip; + unsigned char buf[MAX_SYSEX_BUFLEN]; +}; + +/* synth info */ +struct seq_oss_synth_t { + int seq_device; + + /* for synth_info */ + int synth_type; + int synth_subtype; + int nr_voices; + + char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME]; + snd_seq_oss_callback_t oper; + + int opened; + + void *private_data; + snd_use_lock_t use_lock; +}; + + +/* + * device table + */ +static int max_synth_devs = 0; +static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; +static seq_oss_synth_t midi_synth_dev = { + -1, /* seq_device */ + SYNTH_TYPE_MIDI, /* synth_type */ + 0, /* synth_subtype */ + 16, /* nr_voices */ + "MIDI", /* name */ +}; + +static spinlock_t register_lock = SPIN_LOCK_UNLOCKED; + +/* + * prototypes + */ +static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev); +static void reset_channels(seq_oss_synthinfo_t *info); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * global initialization + */ +void __init +snd_seq_oss_synth_init(void) +{ + snd_use_lock_init(&midi_synth_dev.use_lock); +} + +/* + * registration of the synth device + */ +int +snd_seq_oss_synth_register(snd_seq_device_t *dev) +{ + int i; + seq_oss_synth_t *rec; + snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + unsigned long flags; + + if ((rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { + snd_printk(KERN_ERR "can't malloc synth info\n"); + return -ENOMEM; + } + rec->seq_device = -1; + rec->synth_type = reg->type; + rec->synth_subtype = reg->subtype; + rec->nr_voices = reg->nvoices; + rec->oper = reg->oper; + rec->private_data = reg->private_data; + rec->opened = 0; + snd_use_lock_init(&rec->use_lock); + + /* copy and truncate the name of synth device */ + strncpy(rec->name, dev->name, sizeof(rec->name)); + rec->name[sizeof(rec->name)-1] = 0; + + /* registration */ + spin_lock_irqsave(®ister_lock, flags); + for (i = 0; i < max_synth_devs; i++) { + if (synth_devs[i] == NULL) + break; + } + if (i >= max_synth_devs) { + if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) { + spin_unlock_irqrestore(®ister_lock, flags); + snd_printk(KERN_ERR "no more synth slot\n"); + kfree(rec); + return -ENOMEM; + } + max_synth_devs++; + } + rec->seq_device = i; + synth_devs[i] = rec; + debug_printk(("synth %s registered %d\n", rec->name, i)); + spin_unlock_irqrestore(®ister_lock, flags); + dev->driver_data = rec; + return 0; +} + + +int +snd_seq_oss_synth_unregister(snd_seq_device_t *dev) +{ + int index; + seq_oss_synth_t *rec = dev->driver_data; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + for (index = 0; index < max_synth_devs; index++) { + if (synth_devs[index] == rec) + break; + } + if (index >= max_synth_devs) { + spin_unlock_irqrestore(®ister_lock, flags); + snd_printk(KERN_ERR "can't unregister synth\n"); + return -EINVAL; + } + synth_devs[index] = NULL; + if (index == max_synth_devs - 1) { + for (index--; index >= 0; index--) { + if (synth_devs[index]) + break; + } + max_synth_devs = index + 1; + } + spin_unlock_irqrestore(®ister_lock, flags); + + snd_use_lock_sync(&rec->use_lock); + kfree(rec); + + return 0; +} + + +/* + */ +static seq_oss_synth_t * +get_sdev(int dev) +{ + seq_oss_synth_t *rec; + unsigned long flags; + + spin_lock_irqsave(®ister_lock, flags); + rec = synth_devs[dev]; + if (rec) + snd_use_lock_use(&rec->use_lock); + spin_unlock_irqrestore(®ister_lock, flags); + return rec; +} + + +/* + * set up synth tables + */ + +void +snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp) +{ + int i; + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + dp->max_synthdev = max_synth_devs; + dp->synth_opened = 0; + memset(dp->synths, 0, sizeof(dp->synths)); + for (i = 0; i < dp->max_synthdev; i++) { + rec = get_sdev(i); + if (rec == NULL) + continue; + if (rec->oper.open == NULL || rec->oper.close == NULL) { + snd_use_lock_free(&rec->use_lock); + continue; + } + info = &dp->synths[i]; + info->arg.app_index = dp->port; + info->arg.file_mode = dp->file_mode; + info->arg.seq_mode = dp->seq_mode; + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) + info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; + else + info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; + info->opened = 0; + if (!try_inc_mod_count(rec->oper.owner)) { + snd_use_lock_free(&rec->use_lock); + continue; + } + if (rec->oper.open(&info->arg, rec->private_data) < 0) { + dec_mod_count(rec->oper.owner); + snd_use_lock_free(&rec->use_lock); + continue; + } + info->nr_voices = rec->nr_voices; + if (info->nr_voices > 0) { + info->ch = snd_kcalloc(sizeof(seq_oss_chinfo_t) * info->nr_voices, GFP_KERNEL); + reset_channels(info); + } + debug_printk(("synth %d assigned\n", i)); + info->opened++; + rec->opened++; + dp->synth_opened++; + snd_use_lock_free(&rec->use_lock); + } +} + + +/* + * set up synth tables for MIDI emulation - /dev/music mode only + */ + +void +snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp) +{ + int i; + + if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) + return; + + for (i = 0; i < dp->max_mididev; i++) { + seq_oss_synthinfo_t *info; + info = &dp->synths[dp->max_synthdev]; + if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0) + continue; + info->arg.app_index = dp->port; + info->arg.file_mode = dp->file_mode; + info->arg.seq_mode = dp->seq_mode; + info->arg.private_data = info; + info->is_midi = 1; + info->midi_mapped = i; + info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; + snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr); + info->opened = 1; + midi_synth_dev.opened++; + dp->max_synthdev++; + if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) + break; + } +} + + +/* + * clean up synth tables + */ + +void +snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp) +{ + int i; + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + for (i = 0; i < dp->max_synthdev; i++) { + info = &dp->synths[i]; + if (! info->opened) + continue; + if (info->is_midi) { + snd_seq_oss_midi_close(dp, info->midi_mapped); + midi_synth_dev.opened--; + } else { + rec = get_sdev(i); + if (rec == NULL) + continue; + if (rec->opened) { + debug_printk(("synth %d closed\n", i)); + rec->oper.close(&info->arg); + dec_mod_count(rec->oper.owner); + rec->opened--; + } + snd_use_lock_free(&rec->use_lock); + } + if (info->sysex) + kfree(info->sysex); + if (info->ch) + kfree(info->ch); + } + dp->synth_opened = 0; + dp->max_synthdev = 0; +} + +/* + * check if the specified device is MIDI mapped device + */ +static int +is_midi_dev(seq_oss_devinfo_t *dp, int dev) +{ + if (dev < 0 || dev >= dp->max_synthdev) + return 0; + if (dp->synths[dev].is_midi) + return 1; + return 0; +} + +/* + * return synth device information pointer + */ +static seq_oss_synth_t * +get_synthdev(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + if (dev < 0 || dev >= dp->max_synthdev) + return NULL; + if (! dp->synths[dev].opened) + return NULL; + if (dp->synths[dev].is_midi) + return &midi_synth_dev; + if ((rec = get_sdev(dev)) == NULL) + return NULL; + if (! rec->opened) { + snd_use_lock_free(&rec->use_lock); + return NULL; + } + return rec; +} + + +/* + * reset note and velocity on each channel. + */ +static void +reset_channels(seq_oss_synthinfo_t *info) +{ + int i; + if (info->ch == NULL || ! info->nr_voices) + return; + for (i = 0; i < info->nr_voices; i++) { + info->ch[i].note = -1; + info->ch[i].vel = 0; + } +} + + +/* + * reset synth device: + * call reset callback. if no callback is defined, send a heartbeat + * event to the corresponding port. + */ +void +snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + seq_oss_synthinfo_t *info; + + snd_assert(dev >= 0 && dev < dp->max_synthdev, return); + info = &dp->synths[dev]; + if (! info->opened) + return; + if (info->sysex) + info->sysex->len = 0; /* reset sysex */ + reset_channels(info); + if (info->is_midi) { + snd_seq_oss_midi_reset(dp, info->midi_mapped); + if (snd_seq_oss_midi_open(dp, info->midi_mapped, + dp->file_mode) < 0) { + midi_synth_dev.opened--; + info->opened = 0; + if (info->sysex) + kfree(info->sysex); + if (info->ch) + kfree(info->ch); + } + return; + } + + rec = get_sdev(dev); + if (rec == NULL) + return; + if (rec->oper.reset) { + rec->oper.reset(&info->arg); + } else { + snd_seq_event_t ev; + memset(&ev, 0, sizeof(ev)); + snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client, + info->arg.addr.port); + ev.type = SNDRV_SEQ_EVENT_RESET; + snd_seq_oss_dispatch(dp, &ev, 0, 0); + } + snd_use_lock_free(&rec->use_lock); +} + + +/* + * load a patch record: + * call load_patch callback function + */ +int +snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, + const char *buf, int p, int c) +{ + seq_oss_synth_t *rec; + int rc; + + if (dev < 0 || dev >= dp->max_synthdev) + return -ENXIO; + + if (is_midi_dev(dp, dev)) + return 0; + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + + if (rec->oper.load_patch == NULL) + rc = -ENXIO; + else + rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c); + snd_use_lock_free(&rec->use_lock); + return rc; +} + +/* + * check if the device is valid synth device + */ +int +snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev) +{ + seq_oss_synth_t *rec; + rec = get_synthdev(dp, dev); + if (rec) { + snd_use_lock_free(&rec->use_lock); + return 1; + } + return 0; +} + + +/* + * receive OSS 6 byte sysex packet: + * the full sysex message will be sent if it reaches to the end of data + * (0xff). + */ +int +snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev) +{ + int i, send; + unsigned char *dest; + seq_oss_synth_sysex_t *sysex; + + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -ENXIO; + + sysex = dp->synths[dev].sysex; + if (sysex == NULL) { + sysex = snd_kcalloc(sizeof(*sysex), GFP_KERNEL); + if (sysex == NULL) + return -ENOMEM; + dp->synths[dev].sysex = sysex; + } + + send = 0; + dest = sysex->buf + sysex->len; + /* copy 6 byte packet to the buffer */ + for (i = 0; i < 6; i++) { + if (buf[i] == 0xff) { + send = 1; + break; + } + dest[i] = buf[i]; + sysex->len++; + if (sysex->len >= MAX_SYSEX_BUFLEN) { + sysex->len = 0; + sysex->skip = 1; + break; + } + } + + if (sysex->len && send) { + if (sysex->skip) { + sysex->skip = 0; + sysex->len = 0; + return -EINVAL; /* skip */ + } + /* copy the data to event record and send it */ + ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + if (snd_seq_oss_synth_addr(dp, dev, ev)) + return -EINVAL; + ev->data.ext.len = sysex->len; + ev->data.ext.ptr = sysex->buf; + sysex->len = 0; + return 0; + } + + return -EINVAL; /* skip */ +} + +/* + * fill the event source/destination addresses + */ +int +snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev)) + return -EINVAL; + snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client, + dp->synths[dev].arg.addr.port); + return 0; +} + + +/* + * OSS compatible ioctl + */ +int +snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr) +{ + seq_oss_synth_t *rec; + int rc; + + if (is_midi_dev(dp, dev)) + return -ENXIO; + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + if (rec->oper.ioctl == NULL) + rc = -ENXIO; + else + rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr); + snd_use_lock_free(&rec->use_lock); + return rc; +} + + +/* + * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME + */ +int +snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev) +{ + if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev)) + return -ENXIO; + ev->type = SNDRV_SEQ_EVENT_OSS; + memcpy(ev->data.raw8.d, data, 8); + return snd_seq_oss_synth_addr(dp, dev, ev); +} + + +/* + * create OSS compatible synth_info record + */ +int +snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf) +{ + seq_oss_synth_t *rec; + + if (dp->synths[dev].is_midi) { + struct midi_info minf; + snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf); + inf->synth_type = SYNTH_TYPE_MIDI; + inf->synth_subtype = 0; + inf->nr_voices = 16; + inf->device = dev; + strncpy(inf->name, minf.name, sizeof(inf->name)); + } else { + if ((rec = get_synthdev(dp, dev)) == NULL) + return -ENXIO; + inf->synth_type = rec->synth_type; + inf->synth_subtype = rec->synth_subtype; + inf->nr_voices = rec->nr_voices; + inf->device = dev; + strncpy(inf->name, rec->name, sizeof(inf->name)); + snd_use_lock_free(&rec->use_lock); + } + return 0; +} + + +/* + * proc interface + */ +void +snd_seq_oss_synth_info_read(snd_info_buffer_t *buf) +{ + int i; + seq_oss_synth_t *rec; + + snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs); + for (i = 0; i < max_synth_devs; i++) { + snd_iprintf(buf, "\nsynth %d: ", i); + rec = get_sdev(i); + if (rec == NULL) { + snd_iprintf(buf, "*empty*\n"); + continue; + } + snd_iprintf(buf, "[%s]\n", rec->name); + snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n", + rec->synth_type, rec->synth_subtype, + rec->nr_voices); + snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n", + enabled_str((long)rec->oper.ioctl), + enabled_str((long)rec->oper.load_patch)); + snd_use_lock_free(&rec->use_lock); + } +} + diff -Nru linux/sound/core/seq/oss/seq_oss_synth.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.h --- linux/sound/core/seq/oss/seq_oss_synth.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_synth.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,49 @@ +/* + * OSS compatible sequencer driver + * + * synth device information + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_SYNTH_H +#define __SEQ_OSS_SYNTH_H + +#include "seq_oss_device.h" +#include +#include + +typedef struct seq_oss_synth_t seq_oss_synth_t; + +void snd_seq_oss_synth_init(void); +int snd_seq_oss_synth_register(snd_seq_device_t *dev); +int snd_seq_oss_synth_unregister(snd_seq_device_t *dev); +void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp); +void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp); +void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp); + +void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char *buf, int p, int c); +int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev); +int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev); +int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev); +int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr); +int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev); + +int snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf); + +#endif diff -Nru linux/sound/core/seq/oss/seq_oss_timer.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.c --- linux/sound/core/seq/oss/seq_oss_timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,284 @@ +/* + * OSS compatible sequencer driver + * + * Timer control routines + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_timer.h" +#include "seq_oss_event.h" +#include + +/* + */ +#define MIN_OSS_TEMPO 8 +#define MAX_OSS_TEMPO 360 +#define MIN_OSS_TIMEBASE 1 +#define MAX_OSS_TIMEBASE 1000 + +/* + */ +static void calc_alsa_tempo(seq_oss_timer_t *timer); +static int send_timer_event(seq_oss_devinfo_t *dp, int type, int value); + + +/* + * create and register a new timer. + * if queue is not started yet, start it. + */ +seq_oss_timer_t * +snd_seq_oss_timer_new(seq_oss_devinfo_t *dp) +{ + seq_oss_timer_t *rec; + + rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL); + if (rec == NULL) + return NULL; + + rec->dp = dp; + rec->cur_tick = 0; + rec->realtime = 0; + rec->running = 0; + rec->oss_tempo = 60; + rec->oss_timebase = 100; + calc_alsa_tempo(rec); + + return rec; +} + + +/* + * delete timer. + * if no more timer exists, stop the queue. + */ +void +snd_seq_oss_timer_delete(seq_oss_timer_t *rec) +{ + if (rec) { + snd_seq_oss_timer_stop(rec); + kfree(rec); + } +} + + +/* + * process one timing event + * return 1 : event proceseed -- skip this event + * 0 : not a timer event -- enqueue this event + */ +int +snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *ev) +{ + abstime_t parm = ev->t.time; + + if (ev->t.code == EV_TIMING) { + switch (ev->t.cmd) { + case TMR_WAIT_REL: + parm += rec->cur_tick; + rec->realtime = 0; + /* continue to next */ + case TMR_WAIT_ABS: + if (parm == 0) { + rec->realtime = 1; + } else if (parm >= rec->cur_tick) { + rec->realtime = 0; + rec->cur_tick = parm; + } + return 1; /* skip this event */ + + case TMR_START: + snd_seq_oss_timer_start(rec); + return 1; + + } + } else if (ev->s.code == SEQ_WAIT) { + /* time = from 1 to 3 bytes */ + parm = (ev->echo >> 8) & 0xffffff; + if (parm > rec->cur_tick) { + /* set next event time */ + rec->cur_tick = parm; + rec->realtime = 0; + } + return 1; + } + + return 0; +} + + +/* + * convert tempo units + */ +static void +calc_alsa_tempo(seq_oss_timer_t *timer) +{ + timer->tempo = (60 * 1000000) / timer->oss_tempo; + timer->ppq = timer->oss_timebase; +} + + +/* + * dispatch a timer event + */ +static int +send_timer_event(seq_oss_devinfo_t *dp, int type, int value) +{ + snd_seq_event_t ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = type; + ev.source.client = dp->cseq; + ev.source.port = 0; + ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM; + ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + ev.queue = dp->queue; + ev.data.queue.queue = dp->queue; + ev.data.queue.param.value = value; + return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 0, 0); +} + +/* + * set queue tempo and start queue + */ +int +snd_seq_oss_timer_start(seq_oss_timer_t *timer) +{ + seq_oss_devinfo_t *dp = timer->dp; + snd_seq_queue_tempo_t tmprec; + + if (timer->running) + snd_seq_oss_timer_stop(timer); + + memset(&tmprec, 0, sizeof(tmprec)); + tmprec.queue = dp->queue; + tmprec.ppq = timer->ppq; + tmprec.tempo = timer->tempo; + snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, &tmprec); + + send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0); + timer->running = 1; + timer->cur_tick = 0; + return 0; +} + + +/* + * stop queue + */ +int +snd_seq_oss_timer_stop(seq_oss_timer_t *timer) +{ + if (! timer->running) + return 0; + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0); + timer->running = 0; + return 0; +} + + +/* + * continue queue + */ +int +snd_seq_oss_timer_continue(seq_oss_timer_t *timer) +{ + if (timer->running) + return 0; + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0); + timer->running = 1; + return 0; +} + + +/* + * change queue tempo + */ +int +snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value) +{ + if (value < MIN_OSS_TEMPO) + value = MIN_OSS_TEMPO; + else if (value > MAX_OSS_TEMPO) + value = MAX_OSS_TEMPO; + timer->oss_tempo = value; + calc_alsa_tempo(timer); + if (timer->running) + send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo); + return 0; +} + + +/* + * ioctls + */ +int +snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg) +{ + int value; + + if (cmd == SNDCTL_SEQ_CTRLRATE) { + debug_printk(("ctrl rate\n")); + /* if *arg == 0, just return the current rate */ + if (get_user(value, (int *)arg)) + return -EFAULT; + if (value) + return -EINVAL; + value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60; + return put_user(value, (int *)arg) ? -EFAULT : 0; + } + + if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) + return 0; + + switch (cmd) { + case SNDCTL_TMR_START: + debug_printk(("timer start\n")); + return snd_seq_oss_timer_start(timer); + case SNDCTL_TMR_STOP: + debug_printk(("timer stop\n")); + return snd_seq_oss_timer_stop(timer); + case SNDCTL_TMR_CONTINUE: + debug_printk(("timer continue\n")); + return snd_seq_oss_timer_continue(timer); + case SNDCTL_TMR_TEMPO: + debug_printk(("timer tempo\n")); + if (get_user(value, (int *)arg)) + return -EFAULT; + return snd_seq_oss_timer_tempo(timer, value); + case SNDCTL_TMR_TIMEBASE: + debug_printk(("timer timebase\n")); + if (get_user(value, (int *)arg)) + return -EFAULT; + if (value < MIN_OSS_TIMEBASE) + value = MIN_OSS_TIMEBASE; + else if (value > MAX_OSS_TIMEBASE) + value = MAX_OSS_TIMEBASE; + timer->oss_timebase = value; + calc_alsa_tempo(timer); + return 0; + + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SELECT: + case SNDCTL_TMR_SOURCE: + debug_printk(("timer XXX\n")); + /* not supported */ + return 0; + } + return 0; +} diff -Nru linux/sound/core/seq/oss/seq_oss_timer.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.h --- linux/sound/core/seq/oss/seq_oss_timer.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_timer.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,70 @@ +/* + * OSS compatible sequencer driver + * timer handling routines + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_TIMER_H +#define __SEQ_OSS_TIMER_H + +#include "seq_oss_device.h" + +/* + * timer information definition + */ +struct seq_oss_timer_t { + seq_oss_devinfo_t *dp; + reltime_t cur_tick; + int realtime; + int running; + int tempo, ppq; /* ALSA queue */ + int oss_tempo, oss_timebase; +}; + + +seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp); +void snd_seq_oss_timer_delete(seq_oss_timer_t *dp); + +int snd_seq_oss_timer_start(seq_oss_timer_t *timer); +int snd_seq_oss_timer_stop(seq_oss_timer_t *timer); +int snd_seq_oss_timer_continue(seq_oss_timer_t *timer); +int snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value); +#define snd_seq_oss_timer_reset snd_seq_oss_timer_start + +int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, void *arg); + +/* + * get current processed time + */ +static inline abstime_t +snd_seq_oss_timer_cur_tick(seq_oss_timer_t *timer) +{ + return timer->cur_tick; +} + + +/* + * is realtime event? + */ +static inline int +snd_seq_oss_timer_is_realtime(seq_oss_timer_t *timer) +{ + return timer->realtime; +} + +#endif diff -Nru linux/sound/core/seq/oss/seq_oss_writeq.c linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.c --- linux/sound/core/seq/oss/seq_oss_writeq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,183 @@ +/* + * OSS compatible sequencer driver + * + * seq_oss_writeq.c - write queue and sync + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "seq_oss_writeq.h" +#include "seq_oss_event.h" +#include "seq_oss_timer.h" +#include +#include "../seq_lock.h" +#include "../seq_clientmgr.h" + + +/* + * create a write queue record + */ +seq_oss_writeq_t * +snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen) +{ + seq_oss_writeq_t *q; + snd_seq_client_pool_t pool; + + if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) + return NULL; + q->dp = dp; + q->maxlen = maxlen; + spin_lock_init(&q->sync_lock); + q->sync_event_put = 0; + q->sync_time = 0; + init_waitqueue_head(&q->sync_sleep); + + memset(&pool, 0, sizeof(pool)); + pool.client = dp->cseq; + pool.output_pool = maxlen; + pool.output_room = maxlen / 2; + + snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool); + + return q; +} + +/* + * delete the write queue + */ +void +snd_seq_oss_writeq_delete(seq_oss_writeq_t *q) +{ + snd_seq_oss_writeq_clear(q); /* to be sure */ + kfree(q); +} + + +/* + * reset the write queue + */ +void +snd_seq_oss_writeq_clear(seq_oss_writeq_t *q) +{ + snd_seq_remove_events_t reset; + + memset(&reset, 0, sizeof(reset)); + reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */ + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset); + + /* wake up sleepers if any */ + snd_seq_oss_writeq_wakeup(q, 0); +} + +/* + * wait until the write buffer has enough room + */ +int +snd_seq_oss_writeq_sync(seq_oss_writeq_t *q) +{ + seq_oss_devinfo_t *dp = q->dp; + abstime_t time; + unsigned long flags; + + time = snd_seq_oss_timer_cur_tick(dp->timer); + if (q->sync_time >= time) + return 0; /* already finished */ + + if (! q->sync_event_put) { + snd_seq_event_t ev; + evrec_t *rec; + + /* put echoback event */ + memset(&ev, 0, sizeof(ev)); + ev.flags = 0; + ev.type = SNDRV_SEQ_EVENT_ECHO; + ev.time.tick = time; + /* echo back to itself */ + snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port); + rec = (evrec_t*)&ev.data; + rec->t.code = SEQ_SYNCTIMER; + rec->t.time = time; + q->sync_event_put = 1; + snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0); + } + + spin_lock_irqsave(&q->sync_lock, flags); + if (! q->sync_event_put) { /* echoback event has been received */ + spin_unlock_irqrestore(&q->sync_lock, flags); + return 0; + } + + /* wait for echo event */ + snd_seq_sleep_timeout_in_lock(&q->sync_sleep, &q->sync_lock, HZ); + if (signal_pending(current)) { + /* interrupted - return 0 to finish sync */ + q->sync_event_put = 0; + spin_unlock_irqrestore(&q->sync_lock, flags); + return 0; + } + spin_unlock_irqrestore(&q->sync_lock, flags); + if (q->sync_time >= time) + return 0; + else + return 1; +} + +/* + * wake up sync - echo event was catched + */ +void +snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time) +{ + unsigned long flags; + + spin_lock_irqsave(&q->sync_lock, flags); + q->sync_time = time; + q->sync_event_put = 0; + if (waitqueue_active(&q->sync_sleep)) { + wake_up(&q->sync_sleep); + } + spin_unlock_irqrestore(&q->sync_lock, flags); +} + + +/* + * return the unused pool size + */ +int +snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q) +{ + snd_seq_client_pool_t pool; + pool.client = q->dp->cseq; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool); + return pool.output_free; +} + + +/* + * set output threshold size from ioctl + */ +void +snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int val) +{ + snd_seq_client_pool_t pool; + pool.client = q->dp->cseq; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool); + pool.output_room = val; + snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool); +} + diff -Nru linux/sound/core/seq/oss/seq_oss_writeq.h linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.h --- linux/sound/core/seq/oss/seq_oss_writeq.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/oss/seq_oss_writeq.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,50 @@ +/* + * OSS compatible sequencer driver + * write priority queue + * + * Copyright (C) 1998,99 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SEQ_OSS_WRITEQ_H +#define __SEQ_OSS_WRITEQ_H + +#include "seq_oss_device.h" + + +struct seq_oss_writeq_t { + seq_oss_devinfo_t *dp; + int maxlen; + abstime_t sync_time; + int sync_event_put; + wait_queue_head_t sync_sleep; + spinlock_t sync_lock; +}; + + +/* + * seq_oss_writeq.c + */ +seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen); +void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q); +int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time); +int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q); +void snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int size); + + +#endif diff -Nru linux/sound/core/seq/seq.c linux-2.4.19-pre5-mjc/sound/core/seq/seq.c --- linux/sound/core/seq/seq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,141 @@ +/* + * ALSA sequencer main module + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include +#include "seq_clientmgr.h" +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_lock.h" +#include "seq_timer.h" +#include "seq_system.h" +#include "seq_info.h" +#include + +int snd_seq_client_load[64] = {[0 ... 63] = -1}; +int snd_seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL; +int snd_seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE; +int snd_seq_default_timer_card = -1; +int snd_seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM; +int snd_seq_default_timer_subdevice = 0; +int snd_seq_default_timer_resolution = 0; /* Hz */ + +MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +MODULE_PARM(snd_seq_client_load, "i"); +MODULE_PARM_DESC(snd_seq_client_load, "The numbers of global (system) clients to load through kmod."); +MODULE_PARM(snd_seq_default_timer_class, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_class, "The default timer class."); +MODULE_PARM(snd_seq_default_timer_sclass, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_sclass, "The default timer slave class."); +MODULE_PARM(snd_seq_default_timer_card, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_card, "The default timer card number."); +MODULE_PARM(snd_seq_default_timer_device, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_device, "The default timer device number."); +MODULE_PARM(snd_seq_default_timer_subdevice, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_subdevice, "The default timer subdevice number."); +MODULE_PARM(snd_seq_default_timer_resolution, "i"); +MODULE_PARM_DESC(snd_seq_default_timer_resolution, "The default timer resolution in Hz."); + +/* + * INIT PART + */ + + +static int __init alsa_seq_init(void) +{ + int err; + + if ((err = client_init_data()) < 0) + return err; + + /* init memory, room for selected events */ + if ((err = snd_sequencer_memory_init()) < 0) + return err; + + /* init event queues */ + if ((err = snd_seq_queues_init()) < 0) + return err; + + /* register sequencer device */ + if ((err = snd_sequencer_device_init()) < 0) + return err; + + /* register proc interface */ + if ((err = snd_seq_info_init()) < 0) + return err; + + /* register our internal client */ + if ((err = snd_seq_system_client_init()) < 0) + return err; + + return 0; +} + +static void __exit alsa_seq_exit(void) +{ + /* unregister our internal client */ + snd_seq_system_client_done(); + + /* unregister proc interface */ + snd_seq_info_done(); + + /* delete timing queues */ + snd_seq_queues_delete(); + + /* unregister sequencer device */ + snd_sequencer_device_done(); + + /* release event memory */ + snd_sequencer_memory_done(); +} + +module_init(alsa_seq_init) +module_exit(alsa_seq_exit) + + /* seq_clientmgr.c */ +EXPORT_SYMBOL(snd_seq_create_kernel_client); +EXPORT_SYMBOL(snd_seq_delete_kernel_client); +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); +EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); +EXPORT_SYMBOL(snd_seq_kernel_client_ctl); +EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); + /* seq_memory.c */ +EXPORT_SYMBOL(snd_seq_expand_var_event); +EXPORT_SYMBOL(snd_seq_dump_var_event); + /* seq_ports.c */ +EXPORT_SYMBOL(snd_seq_event_port_attach); +EXPORT_SYMBOL(snd_seq_event_port_detach); + /* seq_lock.c */ +#if defined(__SMP__) || defined(CONFIG_SND_DEBUG) +EXPORT_SYMBOL(snd_seq_sleep_in_lock); +EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock); +EXPORT_SYMBOL(snd_use_lock_sync_helper); +#endif diff -Nru linux/sound/core/seq/seq_clientmgr.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.c --- linux/sound/core/seq/seq_clientmgr.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2470 @@ +/* + * ALSA sequencer Client Manager + * Copyright (c) 1998-2001 by Frank van de Pol + * Jaroslav Kysela + * Takashi Iwai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#include +#include "seq_clientmgr.h" +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_timer.h" +#include "seq_info.h" +#include "seq_system.h" +#include + +/* Client Manager + + * this module handles the connections of userland and kernel clients + * + */ + +#define SNDRV_SEQ_LFLG_INPUT 0x0001 +#define SNDRV_SEQ_LFLG_OUTPUT 0x0002 +#define SNDRV_SEQ_LFLG_OPEN (SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT) + +static spinlock_t clients_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_MUTEX(register_mutex); + +/* + * client table + */ +static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; +static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS]; +static usage_t client_usage = {0, 0}; + +/* + * prototypes + */ +static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop); +static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop); + +/* + */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* + */ +static inline unsigned short snd_seq_file_flags(struct file *file) +{ + switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { + case FMODE_WRITE: + return SNDRV_SEQ_LFLG_OUTPUT; + case FMODE_READ: + return SNDRV_SEQ_LFLG_INPUT; + default: + return SNDRV_SEQ_LFLG_OPEN; + } +} + +static inline int snd_seq_write_pool_allocated(client_t *client) +{ + return snd_seq_total_cells(client->pool) > 0; +} + +/* return pointer to client structure for specified id */ +static client_t *clientptr(int clientid) +{ + if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { + snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid); + return NULL; + } + return clienttab[clientid]; +} + +extern int snd_seq_client_load[]; + +client_t *snd_seq_client_use_ptr(int clientid) +{ + unsigned long flags; + client_t *client; + + if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { + snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid); + return NULL; + } + spin_lock_irqsave(&clients_lock, flags); + client = clientptr(clientid); + if (client) + goto __lock; + if (clienttablock[clientid]) { + spin_unlock_irqrestore(&clients_lock, flags); + return NULL; + } + spin_unlock_irqrestore(&clients_lock, flags); +#ifdef CONFIG_KMOD + if (!in_interrupt()) { + if (clientid < 64) { + int idx; + char name[32]; + + for (idx = 0; idx < 64; idx++) { + if (snd_seq_client_load[idx] < 0) + break; + if (snd_seq_client_load[idx] == clientid) { + sprintf(name, "snd-seq-client-%i", clientid); + request_module(name); + break; + } + } + } else if (clientid >= 64 && clientid < 128) { + int card = (clientid - 64) / 8; + if (card < snd_ecards_limit) { +#ifndef MODULE + if (current->fs->root) { +#endif + snd_request_card(card); + snd_seq_device_load_drivers(); +#ifndef MODULE + } +#endif + } + } + spin_lock_irqsave(&clients_lock, flags); + client = clientptr(clientid); + if (client) + goto __lock; + spin_unlock_irqrestore(&clients_lock, flags); + } +#endif + return NULL; + + __lock: + snd_use_lock_use(&client->use_lock); + spin_unlock_irqrestore(&clients_lock, flags); + return client; +} + +static void usage_alloc(usage_t * res, int num) +{ + res->cur += num; + if (res->cur > res->peak) + res->peak = res->cur; +} + +static void usage_free(usage_t * res, int num) +{ + res->cur -= num; +} + +/* initialise data structures */ +int __init client_init_data(void) +{ + /* zap out the client table */ + memset(&clienttablock, 0, sizeof(clienttablock)); + memset(&clienttab, 0, sizeof(clienttab)); + return 0; +} + + +static client_t *seq_create_client1(int client_index, int poolsize) +{ + unsigned long flags; + int c; + client_t *client; + + /* init client data */ + client = snd_kcalloc(sizeof(client_t), GFP_KERNEL); + if (client == NULL) + return NULL; + client->pool = snd_seq_pool_new(poolsize); + if (client->pool == NULL) { + kfree(client); + return NULL; + } + client->type = NO_CLIENT; + snd_use_lock_init(&client->use_lock); + rwlock_init(&client->ports_lock); + init_MUTEX(&client->ports_mutex); + INIT_LIST_HEAD(&client->ports_list_head); + + /* find free slot in the client table */ + spin_lock_irqsave(&clients_lock, flags); + if (client_index < 0) { + for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) { + if (clienttab[c] || clienttablock[c]) + continue; + clienttab[client->number = c] = client; + spin_unlock_irqrestore(&clients_lock, flags); + return client; + } + } else { + if (clienttab[client_index] == NULL && !clienttablock[client_index]) { + clienttab[client->number = client_index] = client; + spin_unlock_irqrestore(&clients_lock, flags); + return client; + } + } + spin_unlock_irqrestore(&clients_lock, flags); + snd_seq_pool_delete(&client->pool); + kfree(client); + return NULL; /* no free slot found or busy, return failure code */ +} + + +static int seq_free_client1(client_t *client) +{ + unsigned long flags; + + snd_assert(client != NULL, return -EINVAL); + snd_seq_delete_all_ports(client); + snd_seq_queue_client_leave(client->number); + spin_lock_irqsave(&clients_lock, flags); + clienttablock[client->number] = 1; + clienttab[client->number] = NULL; + spin_unlock_irqrestore(&clients_lock, flags); + snd_use_lock_sync(&client->use_lock); + snd_seq_queue_client_termination(client->number); + if (client->pool) + snd_seq_pool_delete(&client->pool); + spin_lock_irqsave(&clients_lock, flags); + clienttablock[client->number] = 0; + spin_unlock_irqrestore(&clients_lock, flags); + return 0; +} + + +static void seq_free_client(client_t * client) +{ + down(®ister_mutex); + switch (client->type) { + case NO_CLIENT: + snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", client->number); + break; + case USER_CLIENT: + case KERNEL_CLIENT: + seq_free_client1(client); + usage_free(&client_usage, 1); + break; + + default: + snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type); + } + up(®ister_mutex); + + snd_seq_system_client_ev_client_exit(client->number); +} + + + +/* -------------------------------------------------------- */ + +/* create a user client */ +static int snd_seq_open(struct inode *inode, struct file *file) +{ + int c, mode; /* client id */ + client_t *client; + user_client_t *user; + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS); + if (client == NULL) { + up(®ister_mutex); + return -ENOMEM; /* failure code */ + } + + mode = snd_seq_file_flags(file); + if (mode & SNDRV_SEQ_LFLG_INPUT) + client->accept_input = 1; + if (mode & SNDRV_SEQ_LFLG_OUTPUT) + client->accept_output = 1; + + user = &client->data.user; + user->fifo = NULL; + user->fifo_pool_size = 0; + + if (mode & SNDRV_SEQ_LFLG_INPUT) { + user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS; + user->fifo = snd_seq_fifo_new(user->fifo_pool_size); + if (user->fifo == NULL) { + seq_free_client1(client); + kfree(client); + up(®ister_mutex); + return -ENOMEM; + } + } + + usage_alloc(&client_usage, 1); + client->type = USER_CLIENT; + up(®ister_mutex); + + c = client->number; + (user_client_t *) file->private_data = client; + + /* fill client data */ + user->file = file; + sprintf(client->name, "Client-%d", c); + + /* make others aware this new client */ + snd_seq_system_client_ev_client_start(c); + +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + + return 0; +} + +/* delete a user client */ +static int snd_seq_release(struct inode *inode, struct file *file) +{ + client_t *client = (client_t *) file->private_data; + + if (client) { + seq_free_client(client); + if (client->data.user.fifo) + snd_seq_fifo_delete(&client->data.user.fifo); + kfree(client); + } + +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + + +/* handle client read() */ +/* possible error values: + * -ENXIO invalid client or file open mode + * -ENOSPC FIFO overflow (the flag is cleared after this error report) + * -EINVAL no enough user-space buffer to write the whole event + * -EFAULT seg. fault during copy to user space + */ +static ssize_t snd_seq_read(struct file *file, char *buf, size_t count, loff_t *offset) +{ + client_t *client = (client_t *) file->private_data; + fifo_t *fifo; + int err; + long result = 0; + snd_seq_event_cell_t *cell; + + if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT)) + return -ENXIO; + + if (verify_area(VERIFY_WRITE, buf, count)) + return -EFAULT; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if (!client->accept_input || (fifo = client->data.user.fifo) == NULL) + return -ENXIO; + + if (atomic_read(&fifo->overflow) > 0) { + /* buffer overflow is detected */ + snd_seq_fifo_clear(fifo); + /* return error code */ + return -ENOSPC; + } + + cell = NULL; + err = 0; + snd_seq_fifo_lock(fifo); + + /* while data available in queue */ + while (count >= sizeof(snd_seq_event_t)) { + int nonblock; + + nonblock = (file->f_flags & O_NONBLOCK) || result > 0; + if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) { + break; + } + if (snd_seq_ev_is_variable(&cell->event)) { + snd_seq_event_t tmpev; + tmpev = cell->event; + tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; + if (copy_to_user(buf, &tmpev, sizeof(snd_seq_event_t))) { + err = -EFAULT; + break; + } + count -= sizeof(snd_seq_event_t); + buf += sizeof(snd_seq_event_t); + err = snd_seq_expand_var_event(&cell->event, count, buf, 0, sizeof(snd_seq_event_t)); + if (err < 0) + break; + result += err; + count -= err; + buf += err; + } else { + copy_to_user(buf, &cell->event, sizeof(snd_seq_event_t)); + count -= sizeof(snd_seq_event_t); + buf += sizeof(snd_seq_event_t); + } + snd_seq_cell_free(cell); + cell = NULL; /* to be sure */ + result += sizeof(snd_seq_event_t); + } + + if (err < 0) { + if (cell) + snd_seq_fifo_cell_putback(fifo, cell); + if (err == -EAGAIN && result > 0) + err = 0; + } + snd_seq_fifo_unlock(fifo); + + return (err < 0) ? err : result; +} + + +/* + * check access permission to the port + */ +static int check_port_perm(client_port_t *port, unsigned int flags) +{ + if ((port->capability & flags) != flags) + return 0; + return flags; +} + +/* + * check if the destination client is available, and return the pointer + * if filter is non-zero, client filter bitmap is tested. + */ +static client_t *get_event_dest_client(snd_seq_event_t *event, int filter) +{ + client_t *dest; + + dest = snd_seq_client_use_ptr(event->dest.client); + if (dest == NULL) + return NULL; + if (! dest->accept_input) + goto __not_avail; + if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) && + ! test_bit(event->type, &dest->event_filter)) + goto __not_avail; + if (filter && !(dest->filter & filter)) + goto __not_avail; + + return dest; /* ok - accessible */ +__not_avail: + snd_seq_client_unlock(dest); + return NULL; +} + + +/* + * Return the error event. + * + * If the receiver client is a user client, the original event is + * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If + * the original event is also variable length, the external data is + * copied after the event record. + * If the receiver client is a kernel client, the original event is + * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra + * kmalloc. + */ +static int bounce_error_event(client_t *client, snd_seq_event_t *event, + int err, int atomic, int hop) +{ + snd_seq_event_t bounce_ev; + int result; + + if (client == NULL || + ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) || + ! client->accept_input) + return 0; /* ignored */ + + /* set up quoted error */ + memset(&bounce_ev, 0, sizeof(bounce_ev)); + bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; + bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT; + bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; + bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + bounce_ev.dest.client = client->number; + bounce_ev.dest.port = event->source.port; + bounce_ev.data.quote.origin = event->dest; + bounce_ev.data.quote.event = event; + bounce_ev.data.quote.value = -err; /* use positive value */ + result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1); + if (result < 0) { + client->event_lost++; + return result; + } + + return result; +} + + +/* + * deliver an event to the specified destination. + * if filter is non-zero, client filter bitmap is tested. + * + * RETURN VALUE: 0 : if succeeded + * <0 : error + */ +static int snd_seq_deliver_single_event(client_t *client, + snd_seq_event_t *event, + int filter, int atomic, int hop) +{ + client_t *dest = NULL; + client_port_t *dest_port = NULL; + int result = -ENOENT; + int direct, quoted = 0; + + direct = snd_seq_ev_is_direct(event); + + dest = get_event_dest_client(event, filter); + if (dest == NULL) + goto __skip; + dest_port = snd_seq_port_use_ptr(dest, event->dest.port); + if (dest_port == NULL) + goto __skip; + + /* check permission */ + if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) { + result = -EPERM; + goto __skip; + } + + /* expand the quoted event */ + if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) { + quoted = 1; + event = event->data.quote.event; + if (event == NULL) { + snd_printd("seq: quoted event is NULL\n"); + result = 0; /* do not send bounce error */ + goto __skip; + } + } + + switch (dest->type) { + case USER_CLIENT: + if (dest->data.user.fifo) + result = snd_seq_fifo_event_in(dest->data.user.fifo, event); + break; + + case KERNEL_CLIENT: + if (dest_port->event_input == NULL) + break; + result = dest_port->event_input(event, direct, dest_port->private_data, atomic, hop); + break; + default: + break; + } + + __skip: + if (dest_port) + snd_seq_port_unlock(dest_port); + if (dest) + snd_seq_client_unlock(dest); + + if (result < 0 && !direct) { + if (quoted) { + /* return directly to the original source */ + dest = snd_seq_client_use_ptr(event->source.client); + result = bounce_error_event(dest, event, result, atomic, hop); + snd_seq_client_unlock(dest); + } else { + result = bounce_error_event(client, event, result, atomic, hop); + } + } + return result; +} + + +static void snd_seq_subs_update_event_header(subscribers_t *subs, snd_seq_event_t *event) +{ + if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) { + /* convert time according to flag with subscription */ + queue_t *q; + q = queueptr(subs->info.queue); + if (q) { + event->queue = subs->info.queue; + event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK; + if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) { + event->time.time = snd_seq_timer_get_cur_time(q->timer); + event->flags |= SNDRV_SEQ_TIME_STAMP_REAL; + } else { + event->time.tick = snd_seq_timer_get_cur_tick(q->timer); + event->flags |= SNDRV_SEQ_TIME_STAMP_TICK; + } + queuefree(q); + } + } +} + +/* + * send the event to all subscribers: + */ +static int deliver_to_subscribers(client_t *client, + snd_seq_event_t *event, + int atomic, int hop) +{ + subscribers_t *subs; + int err = 0, num_ev = 0; + snd_seq_event_t event_saved; + client_port_t *src_port; + struct list_head *p; + port_subs_info_t *grp; + + src_port = snd_seq_port_use_ptr(client, event->source.port); + if (src_port == NULL) + return -EINVAL; /* invalid source port */ + /* save original event record */ + event_saved = *event; + grp = &src_port->c_src; + + /* lock list */ + if (atomic) + read_lock(&grp->list_lock); + else + down_read(&grp->list_mutex); + list_for_each(p, &grp->list_head) { + subs = list_entry(p, subscribers_t, src_list); + event->dest = subs->info.dest; + snd_seq_subs_update_event_header(subs, event); + err = snd_seq_deliver_single_event(client, event, + 0, atomic, hop); + if (err < 0) + break; + num_ev++; + /* restore original event record */ + *event = event_saved; + } + if (atomic) + read_unlock(&grp->list_lock); + else + up_read(&grp->list_mutex); + *event = event_saved; /* restore */ + snd_seq_port_unlock(src_port); + return (err < 0) ? err : num_ev; +} + + +#ifdef SUPPORT_BROADCAST +/* + * broadcast to all ports: + */ +static int port_broadcast_event(client_t *client, + snd_seq_event_t *event, + int atomic, int hop) +{ + int num_ev = 0, err = 0; + client_t *dest_client; + struct list_head *p; + + dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST); + if (dest_client == NULL) + return 0; /* no matching destination */ + + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + client_port_t *port = list_entry(p, client_port_t, list); + event->dest.port = port->addr.port; + /* pass NULL as source client to avoid error bounce */ + err = snd_seq_deliver_single_event(NULL, event, + SNDRV_SEQ_FILTER_BROADCAST, + atomic, hop); + if (err < 0) + break; + num_ev++; + } + read_unlock(&client->ports_lock); + snd_seq_client_unlock(dest_client); + event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ + return (err < 0) ? err : num_ev; +} + +/* + * send the event to all clients: + * if destination port is also ADDRESS_BROADCAST, deliver to all ports. + */ +static int broadcast_event(client_t *client, + snd_seq_event_t *event, int atomic, int hop) +{ + int err = 0, num_ev = 0; + int dest; + snd_seq_addr_t addr; + + addr = event->dest; /* save */ + + for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) { + /* don't send to itself */ + if (dest == client->number) + continue; + event->dest.client = dest; + event->dest.port = addr.port; + if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST) + err = port_broadcast_event(client, event, atomic, hop); + else + /* pass NULL as source client to avoid error bounce */ + err = snd_seq_deliver_single_event(NULL, event, + SNDRV_SEQ_FILTER_BROADCAST, + atomic, hop); + if (err < 0) + break; + num_ev += err; + } + event->dest = addr; /* restore */ + return (err < 0) ? err : num_ev; +} + + +/* multicast - not supported yet */ +static int multicast_event(client_t *client, snd_seq_event_t *event, + int atomic, int hop) +{ + snd_printd("seq: multicast not supported yet.\n"); + return 0; /* ignored */ +} +#endif /* SUPPORT_BROADCAST */ + + +/* deliver an event to the destination port(s). + * if the event is to subscribers or broadcast, the event is dispatched + * to multiple targets. + * + * RETURN VALUE: n > 0 : the number of delivered events. + * n == 0 : the event was not passed to any client. + * n < 0 : error - event was not processed. + */ +int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event, + int atomic, int hop) +{ + int result; + + hop++; + if (hop >= SNDRV_SEQ_MAX_HOPS) { + snd_printd("too long delivery path (%d:%d->%d:%d)\n", + event->source.client, event->source.port, + event->dest.client, event->dest.port); + return -EMLINK; + } + + if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS || + event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) + result = deliver_to_subscribers(client, event, atomic, hop); +#ifdef SUPPORT_BROADCAST + else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST || + event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST) + result = broadcast_event(client, event, atomic, hop); + else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS) + result = multicast_event(client, event, atomic, hop); + else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST) + result = port_broadcast_event(client, event, atomic, hop); +#endif + else + result = snd_seq_deliver_single_event(client, event, 0, atomic, hop); + + return result; +} + +/* + * dispatch an event cell: + * This function is called only from queue check routines in timer + * interrupts or after enqueued. + * The event cell shall be released or re-queued in this function. + * + * RETURN VALUE: n > 0 : the number of delivered events. + * n == 0 : the event was not passed to any client. + * n < 0 : error - event was not processed. + */ +int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop) +{ + client_t *client; + int result; + + snd_assert(cell != NULL, return -EINVAL); + + client = snd_seq_client_use_ptr(cell->event.source.client); + if (client == NULL) { + snd_seq_cell_free(cell); /* release this cell */ + return -EINVAL; + } + + if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) { + /* NOTE event: + * the event cell is re-used as a NOTE-OFF event and + * enqueued again. + */ + snd_seq_event_t tmpev, *ev; + + /* reserve this event to enqueue note-off later */ + tmpev = cell->event; + tmpev.type = SNDRV_SEQ_EVENT_NOTEON; + result = snd_seq_deliver_event(client, &tmpev, atomic, hop); + + /* + * This was originally a note event. We now re-use the + * cell for the note-off event. + */ + + ev = &cell->event; + ev->type = SNDRV_SEQ_EVENT_NOTEOFF; + ev->flags |= SNDRV_SEQ_PRIORITY_HIGH; + + /* add the duration time */ + switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + ev->time.tick += ev->data.note.duration; + break; + case SNDRV_SEQ_TIME_STAMP_REAL: + /* unit for duration is ms */ + ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000); + ev->time.time.tv_sec += ev->data.note.duration / 1000 + + ev->time.time.tv_nsec / 1000000000; + ev->time.time.tv_nsec %= 1000000000; + break; + } + ev->data.note.velocity = ev->data.note.off_velocity; + + /* Now queue this cell as the note off event */ + if (snd_seq_enqueue_event(cell, atomic, hop) < 0) + snd_seq_cell_free(cell); /* release this cell */ + + } else { + /* Normal events: + * event cell is freed after processing the event + */ + + result = snd_seq_deliver_event(client, &cell->event, atomic, hop); + snd_seq_cell_free(cell); + } + + snd_seq_client_unlock(client); + return result; +} + + +/* Allocate a cell from client pool and enqueue it to queue: + * if pool is empty and blocking is TRUE, sleep until a new cell is + * available. + */ +static int snd_seq_client_enqueue_event(client_t *client, + snd_seq_event_t *event, + struct file *file, int blocking, + int atomic, int hop) +{ + snd_seq_event_cell_t *cell; + int err; + + /* special queue values - force direct passing */ + if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { + event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + event->queue = SNDRV_SEQ_QUEUE_DIRECT; + } else +#ifdef SUPPORT_BROADCAST + if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) { + event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST; + event->queue = SNDRV_SEQ_QUEUE_DIRECT; + } +#endif + if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { + /* check presence of source port */ + client_port_t *src_port = snd_seq_port_use_ptr(client, event->source.port); + if (src_port == NULL) + return -EINVAL; + snd_seq_port_unlock(src_port); + } + + /* direct event processing without enqueued */ + if (snd_seq_ev_is_direct(event)) { + if (event->type == SNDRV_SEQ_EVENT_NOTE) + return -EINVAL; /* this event must be enqueued! */ + return snd_seq_deliver_event(client, event, atomic, hop); + } + + /* Not direct, normal queuing */ + if (snd_seq_queue_is_used(event->queue, client->number) <= 0) + return -EINVAL; /* invalid queue */ + if (! snd_seq_write_pool_allocated(client)) + return -ENXIO; /* queue is not allocated */ + + /* allocate an event cell */ + err = snd_seq_event_dup(client->pool, event, &cell, !blocking && !atomic, file); + if (err < 0) + return err; + + /* we got a cell. enqueue it. */ + if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) { + snd_seq_cell_free(cell); + return err; + } + + return 0; +} + + +/* + * check validity of event type and data length. + * return non-zero if invalid. + */ +static int check_event_type_and_length(snd_seq_event_t *ev) +{ + switch (snd_seq_ev_length_type(ev)) { + case SNDRV_SEQ_EVENT_LENGTH_FIXED: + if (snd_seq_ev_is_variable_type(ev) || + snd_seq_ev_is_varipc_type(ev)) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARIABLE: + if (! snd_seq_ev_is_variable_type(ev) || + (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARUSR: + if (! snd_seq_ev_is_instr_type(ev) || + ! snd_seq_ev_is_direct(ev)) + return -EINVAL; + break; + case SNDRV_SEQ_EVENT_LENGTH_VARIPC: + if (! snd_seq_ev_is_varipc_type(ev)) + return -EINVAL; + break; + } + return 0; +} + + +/* handle write() */ +/* possible error values: + * -ENXIO invalid client or file open mode + * -ENOMEM malloc failed + * -EFAULT seg. fault during copy from user space + * -EINVAL invalid event + * -EAGAIN no space in output pool + * -EINTR interrupts while sleep + * -EMLINK too many hops + * others depends on return value from driver callback + */ +static ssize_t snd_seq_write(struct file *file, const char *buf, size_t count, loff_t *offset) +{ + client_t *client = (client_t *) file->private_data; + int written = 0, len; + int err = -EINVAL; + snd_seq_event_t event; + + if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) + return -ENXIO; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if (!client->accept_output || client->pool == NULL) + return -ENXIO; + + /* allocate the pool now if the pool is not allocated yet */ + if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { + if (snd_seq_pool_init(client->pool) < 0) + return -ENOMEM; + } + + /* only process whole events */ + while (count >= sizeof(snd_seq_event_t)) { + /* Read in the event header from the user */ + len = sizeof(event); + if (copy_from_user(&event, buf, len)) { + err = -EFAULT; + break; + } + event.source.client = client->number; /* fill in client number */ + /* Check for extension data length */ + if (check_event_type_and_length(&event)) { + err = -EINVAL; + break; + } + + /* check for special events */ + if (event.type == SNDRV_SEQ_EVENT_NONE) + goto __skip_event; + else if (snd_seq_ev_is_reserved(&event)) { + err = -EINVAL; + break; + } + + if (snd_seq_ev_is_variable(&event)) { + int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK; + if (extlen + len > count) { + /* back out, will get an error this time or next */ + err = -EINVAL; + break; + } + /* set user space pointer */ + event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR; + event.data.ext.ptr = (char*)buf + sizeof(snd_seq_event_t); + len += extlen; /* increment data length */ + } + + /* ok, enqueue it */ + err = snd_seq_client_enqueue_event(client, &event, file, + !(file->f_flags & O_NONBLOCK), + 0, 0); + if (err < 0) + break; + + __skip_event: + /* Update pointers and counts */ + count -= len; + buf += len; + written += len; + } + + return written ? written : err; +} + + +/* + * handle polling + */ +static unsigned int snd_seq_poll(struct file *file, poll_table * wait) +{ + client_t *client = (client_t *) file->private_data; + unsigned int mask = 0; + + /* check client structures are in place */ + snd_assert(client != NULL, return -ENXIO); + + if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) && + client->data.user.fifo) { + + /* check if data is available in the outqueue */ + if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait)) + mask |= POLLIN | POLLRDNORM; + } + + if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) { + + /* check if data is available in the pool */ + if (!snd_seq_write_pool_allocated(client) || + snd_seq_pool_poll_wait(client->pool, file, wait)) + mask |= POLLOUT | POLLWRNORM; + } + + return mask; +} + + +/*-----------------------------------------------------*/ + + +/* SYSTEM_INFO ioctl() */ +static int snd_seq_ioctl_system_info(client_t *client, unsigned long arg) +{ + snd_seq_system_info_t info; + + memset(&info, 0, sizeof(info)); + /* fill the info fields */ + info.queues = SNDRV_SEQ_MAX_QUEUES; + info.clients = SNDRV_SEQ_MAX_CLIENTS; + info.ports = 256; /* fixed limit */ + info.channels = 256; /* fixed limit */ + info.cur_clients = client_usage.cur; + info.cur_queues = snd_seq_queue_get_cur_queues(); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* CLIENT_INFO ioctl() */ +static void get_client_info(client_t *cptr, snd_seq_client_info_t *info) +{ + info->client = cptr->number; + + /* fill the info fields */ + info->type = cptr->type; + strcpy(info->name, cptr->name); + info->filter = cptr->filter; + info->event_lost = cptr->event_lost; + *info->event_filter = *cptr->event_filter; + info->num_ports = cptr->num_ports; + memset(info->reserved, 0, sizeof(info->reserved)); +} + +static int snd_seq_ioctl_get_client_info(client_t * client, unsigned long arg) +{ + client_t *cptr; + snd_seq_client_info_t client_info; + + if (copy_from_user(&client_info, (void*)arg, sizeof(client_info))) + return -EFAULT; + + /* requested client number */ + cptr = snd_seq_client_use_ptr(client_info.client); + if (cptr == NULL) + return -ENOENT; /* don't change !!! */ + + get_client_info(cptr, &client_info); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &client_info, sizeof(client_info))) + return -EFAULT; + return 0; +} + + +/* CLIENT_INFO ioctl() */ +static int snd_seq_ioctl_set_client_info(client_t * client, unsigned long arg) +{ + snd_seq_client_info_t client_info; + + if (copy_from_user(&client_info, (void*)arg, sizeof(client_info))) + return -EFAULT; + + /* it is not allowed to set the info fields for an another client */ + if (client->number != client_info.client) + return -EPERM; + /* also client type must be set now */ + if (client->type != client_info.type) + return -EINVAL; + + /* fill the info fields */ + if (client_info.name[0]) { + strncpy(client->name, client_info.name, sizeof(client->name)-1); + client->name[sizeof(client->name)-1] = '\0'; + } + client->filter = client_info.filter; + client->event_lost = client_info.event_lost; + *client->event_filter = *client_info.event_filter; + + return 0; +} + + +/* + * CREATE PORT ioctl() + */ +static int snd_seq_ioctl_create_port(client_t * client, unsigned long arg) +{ + client_port_t *port; + snd_seq_port_info_t info; + snd_seq_port_callback_t *callback; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* it is not allowed to create the port for an another client */ + if (info.addr.client != client->number) + return -EPERM; + + port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1); + if (port == NULL) + return -ENOMEM; + + if (client->type == USER_CLIENT && info.kernel) { + snd_seq_delete_port(client, port->addr.port); + return -EINVAL; + } + if (client->type == KERNEL_CLIENT) { + if ((callback = info.kernel) != NULL) { + if (callback->owner) + port->owner = callback->owner; + port->private_data = callback->private_data; + port->private_free = callback->private_free; + port->callback_all = callback->callback_all; + port->event_input = callback->event_input; + port->c_src.open = callback->subscribe; + port->c_src.close = callback->unsubscribe; + port->c_dest.open = callback->use; + port->c_dest.close = callback->unuse; + } + } + + info.addr = port->addr; + + snd_seq_set_port_info(port, &info); + snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* + * DELETE PORT ioctl() + */ +static int snd_seq_ioctl_delete_port(client_t * client, unsigned long arg) +{ + snd_seq_port_info_t info; + int err; + + /* set passed parameters */ + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* it is not allowed to remove the port for an another client */ + if (info.addr.client != client->number) + return -EPERM; + + err = snd_seq_delete_port(client, info.addr.port); + if (err >= 0) + snd_seq_system_client_ev_port_exit(client->number, info.addr.port); + return err; +} + + +/* + * GET_PORT_INFO ioctl() (on any client) + */ +static int snd_seq_ioctl_get_port_info(client_t *client, unsigned long arg) +{ + client_t *cptr; + client_port_t *port; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + cptr = snd_seq_client_use_ptr(info.addr.client); + if (cptr == NULL) + return -ENXIO; + + port = snd_seq_port_use_ptr(cptr, info.addr.port); + if (port == NULL) { + snd_seq_client_unlock(cptr); + return -ENOENT; /* don't change */ + } + + /* get port info */ + snd_seq_get_port_info(port, &info); + snd_seq_port_unlock(port); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* + * SET_PORT_INFO ioctl() (only ports on this/own client) + */ +static int snd_seq_ioctl_set_port_info(client_t * client, unsigned long arg) +{ + client_port_t *port; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.addr.client != client->number) /* only set our own ports ! */ + return -EPERM; + port = snd_seq_port_use_ptr(client, info.addr.port); + if (port) { + snd_seq_set_port_info(port, &info); + snd_seq_port_unlock(port); + } + return 0; +} + + +/* + * port subscription (connection) + */ +#define PERM_RD (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) +#define PERM_WR (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) + +static int check_subscription_permission(client_t *client, client_port_t *sport, + client_port_t *dport, + snd_seq_port_subscribe_t *subs) +{ + if (client->number != subs->sender.client && + client->number != subs->dest.client) { + /* connection by third client - check export permission */ + if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) + return -EPERM; + if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) + return -EPERM; + } + + /* check read permission */ + /* if sender or receiver is the subscribing client itself, + * no permission check is necessary + */ + if (client->number != subs->sender.client) { + if (! check_port_perm(sport, PERM_RD)) + return -EPERM; + } + /* check write permission */ + if (client->number != subs->dest.client) { + if (! check_port_perm(dport, PERM_WR)) + return -EPERM; + } + return 0; +} + +/* + * send an subscription notify event to user client: + * client must be user client. + */ +int snd_seq_client_notify_subscription(int client, int port, + snd_seq_port_subscribe_t *info, int evtype) +{ + snd_seq_event_t event; + + memset(&event, 0, sizeof(event)); + event.type = evtype; + event.data.connect.dest = info->dest; + event.data.connect.sender = info->sender; + + return snd_seq_system_notify(client, port, &event); /* non-atomic */ +} + + +/* + * add to port's subscription list IOCTL interface + */ +static int snd_seq_ioctl_subscribe_port(client_t * client, unsigned long arg) +{ + int result = -EINVAL; + client_t *receiver = NULL, *sender = NULL; + client_port_t *sport = NULL, *dport = NULL; + snd_seq_port_subscribe_t subs; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + goto __end; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + goto __end; + + result = check_subscription_permission(client, sport, dport, &subs); + if (result < 0) + goto __end; + + /* connect them */ + result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs); + if (! result) /* broadcast announce */ + snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, + &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); + __end: + if (sport) + snd_seq_port_unlock(sport); + if (dport) + snd_seq_port_unlock(dport); + if (sender) + snd_seq_client_unlock(sender); + if (receiver) + snd_seq_client_unlock(receiver); + return result; +} + + +/* + * remove from port's subscription list + */ +static int snd_seq_ioctl_unsubscribe_port(client_t * client, unsigned long arg) +{ + int result = -ENXIO; + client_t *receiver = NULL, *sender = NULL; + client_port_t *sport = NULL, *dport = NULL; + snd_seq_port_subscribe_t subs; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL) + goto __end; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL) + goto __end; + + result = check_subscription_permission(client, sport, dport, &subs); + if (result < 0) + goto __end; + + result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs); + if (! result) /* broadcast announce */ + snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0, + &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); + __end: + if (sport) + snd_seq_port_unlock(sport); + if (dport) + snd_seq_port_unlock(dport); + if (sender) + snd_seq_client_unlock(sender); + if (receiver) + snd_seq_client_unlock(receiver); + return result; +} + + +/* CREATE_QUEUE ioctl() */ +static int snd_seq_ioctl_create_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + int result; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + result = snd_seq_queue_alloc(client->number, info.locked, info.flags); + if (result < 0) + return result; + + q = queueptr(result); + if (q == NULL) + return -EINVAL; + + info.queue = q->queue; + info.locked = q->locked; + info.owner = q->owner; + + /* set queue name */ + if (! info.name[0]) + sprintf(info.name, "Queue-%d", q->queue); + strncpy(q->name, info.name, sizeof(q->name)-1); + q->name[sizeof(q->name)-1] = 0; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* DELETE_QUEUE ioctl() */ +static int snd_seq_ioctl_delete_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + return snd_seq_queue_delete(client->number, info.queue); +} + +/* GET_QUEUE_INFO ioctl() */ +static int snd_seq_ioctl_get_queue_info(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + q = queueptr(info.queue); + if (q == NULL) + return -EINVAL; + + memset(&info, 0, sizeof(info)); + info.queue = q->queue; + info.owner = q->owner; + info.locked = q->locked; + strncpy(info.name, q->name, sizeof(info.name) - 1); + info.name[sizeof(info.name)-1] = 0; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* SET_QUEUE_INFO ioctl() */ +static int snd_seq_ioctl_set_queue_info(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.owner != client->number) + return -EINVAL; + + /* change owner/locked permission */ + if (snd_seq_queue_check_access(info.queue, client->number)) { + if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0) + return -EPERM; + if (info.locked) + snd_seq_queue_use(info.queue, client->number, 1); + } else { + return -EPERM; + } + + q = queueptr(info.queue); + if (! q) + return -EINVAL; + if (q->owner != client->number) { + queuefree(q); + return -EPERM; + } + strncpy(q->name, info.name, sizeof(q->name) - 1); + q->name[sizeof(q->name)-1] = 0; + queuefree(q); + + return 0; +} + +/* GET_NAMED_QUEUE ioctl() */ +static int snd_seq_ioctl_get_named_queue(client_t *client, unsigned long arg) +{ + snd_seq_queue_info_t info; + queue_t *q; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + q = snd_seq_queue_find_name(info.name); + if (q == NULL) + return -EINVAL; + info.queue = q->queue; + info.owner = q->owner; + info.locked = q->locked; + queuefree(q); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +/* GET_QUEUE_STATUS ioctl() */ +static int snd_seq_ioctl_get_queue_status(client_t * client, unsigned long arg) +{ + snd_seq_queue_status_t status; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&status, (void*)arg, sizeof(status))) + return -EFAULT; + + queue = queueptr(status.queue); + if (queue == NULL) + return -EINVAL; + memset(&status, 0, sizeof(status)); + status.queue = queue->queue; + + tmr = queue->timer; + status.events = queue->tickq->cells + queue->timeq->cells; + + status.time = snd_seq_timer_get_cur_time(tmr); + status.tick = snd_seq_timer_get_cur_tick(tmr); + + status.running = tmr->running; + + status.flags = queue->flags; + queuefree(queue); + + if (copy_to_user((void*)arg, &status, sizeof(status))) + return -EFAULT; + return 0; +} + + +/* GET_QUEUE_TEMPO ioctl() */ +static int snd_seq_ioctl_get_queue_tempo(client_t * client, unsigned long arg) +{ + snd_seq_queue_tempo_t tempo; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&tempo, (void*)arg, sizeof(tempo))) + return -EFAULT; + + queue = queueptr(tempo.queue); + if (queue == NULL) + return -EINVAL; + memset(&tempo, 0, sizeof(tempo)); + tempo.queue = queue->queue; + + tmr = queue->timer; + + tempo.tempo = tmr->tempo; + tempo.ppq = tmr->ppq; + tempo.skew_value = tmr->skew; + tempo.skew_base = tmr->skew_base; + queuefree(queue); + + if (copy_to_user((void*)arg, &tempo, sizeof(tempo))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_TEMPO ioctl() */ +static int snd_seq_ioctl_set_queue_tempo(client_t * client, unsigned long arg) +{ + int result; + snd_seq_queue_tempo_t tempo; + + if (copy_from_user(&tempo, (void*)arg, sizeof(tempo))) + return -EFAULT; + + if (snd_seq_queue_check_access(tempo.queue, client->number)) { + result = snd_seq_queue_timer_set_tempo(tempo.queue, client->number, &tempo); + if (result < 0) + return result; + } else { + return -EPERM; + } + + return 0; +} + + +/* GET_QUEUE_TIMER ioctl() */ +static int snd_seq_ioctl_get_queue_timer(client_t * client, unsigned long arg) +{ + snd_seq_queue_timer_t timer; + queue_t *queue; + seq_timer_t *tmr; + + if (copy_from_user(&timer, (void*)arg, sizeof(timer))) + return -EFAULT; + + queue = queueptr(timer.queue); + if (queue == NULL) + return -EINVAL; + + if (down_interruptible(&queue->timer_mutex)) { + queuefree(queue); + return -ERESTARTSYS; + } + tmr = queue->timer; + memset(&timer, 0, sizeof(timer)); + timer.queue = queue->queue; + + timer.type = tmr->type; + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { + timer.u.alsa.id = tmr->alsa_id; + timer.u.alsa.resolution = tmr->preferred_resolution; + } + up(&queue->timer_mutex); + queuefree(queue); + + if (copy_to_user((void*)arg, &timer, sizeof(timer))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_TIMER ioctl() */ +static int snd_seq_ioctl_set_queue_timer(client_t * client, unsigned long arg) +{ + int result = 0; + snd_seq_queue_timer_t timer; + + if (copy_from_user(&timer, (void*)arg, sizeof(timer))) + return -EFAULT; + + if (timer.type != SNDRV_SEQ_TIMER_ALSA) + return -EINVAL; + + if (snd_seq_queue_check_access(timer.queue, client->number)) { + queue_t *q; + seq_timer_t *tmr; + + q = queueptr(timer.queue); + if (q == NULL) + return -ENXIO; + if (down_interruptible(&q->timer_mutex)) { + queuefree(q); + return -ERESTARTSYS; + } + tmr = q->timer; + snd_seq_queue_timer_close(timer.queue); + tmr->type = timer.type; + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { + tmr->alsa_id = timer.u.alsa.id; + tmr->preferred_resolution = timer.u.alsa.resolution; + } + result = snd_seq_queue_timer_open(timer.queue); + up(&q->timer_mutex); + queuefree(q); + } else { + return -EPERM; + } + + return result; +} + + +/* GET_QUEUE_CLIENT ioctl() */ +static int snd_seq_ioctl_get_queue_client(client_t * client, unsigned long arg) +{ + snd_seq_queue_client_t info; + int used; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + used = snd_seq_queue_is_used(info.queue, client->number); + if (used < 0) + return -EINVAL; + info.used = used; + info.client = client->number; + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + + +/* SET_QUEUE_CLIENT ioctl() */ +static int snd_seq_ioctl_set_queue_client(client_t * client, unsigned long arg) +{ + int err; + snd_seq_queue_client_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (info.used >= 0) { + err = snd_seq_queue_use(info.queue, client->number, info.used); + if (err < 0) + return err; + } + + return snd_seq_ioctl_get_queue_client(client, arg); +} + + +/* GET_CLIENT_POOL ioctl() */ +static int snd_seq_ioctl_get_client_pool(client_t * client, unsigned long arg) +{ + snd_seq_client_pool_t info; + client_t *cptr; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + cptr = snd_seq_client_use_ptr(info.client); + if (cptr == NULL) + return -ENOENT; + memset(&info, 0, sizeof(info)); + info.output_pool = cptr->pool->size; + info.output_room = cptr->pool->room; + info.output_free = info.output_pool; + if (cptr->pool) + info.output_free = snd_seq_unused_cells(cptr->pool); + if (cptr->type == USER_CLIENT) { + info.input_pool = cptr->data.user.fifo_pool_size; + info.input_free = info.input_pool; + if (cptr->data.user.fifo) + info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool); + } else { + info.input_pool = 0; + info.input_free = 0; + } + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* SET_CLIENT_POOL ioctl() */ +static int snd_seq_ioctl_set_client_pool(client_t * client, unsigned long arg) +{ + snd_seq_client_pool_t info; + int rc; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + if (client->number != info.client) + return -EINVAL; /* can't change other clients */ + + if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS && + (! snd_seq_write_pool_allocated(client) || + info.output_pool != client->pool->size)) { + if (snd_seq_write_pool_allocated(client)) { + /* remove all existing cells */ + snd_seq_queue_client_leave_cells(client->number); + snd_seq_pool_done(client->pool); + } + client->pool->size = info.output_pool; + rc = snd_seq_pool_init(client->pool); + if (rc < 0) + return rc; + } + if (client->type == USER_CLIENT && client->data.user.fifo != NULL && + info.input_pool >= 1 && + info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS && + info.input_pool != client->data.user.fifo_pool_size) { + /* change pool size */ + rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool); + if (rc < 0) + return rc; + client->data.user.fifo_pool_size = info.input_pool; + } + if (info.output_room >= 1 && + info.output_room <= client->pool->size) { + client->pool->room = info.output_room; + } + + return snd_seq_ioctl_get_client_pool(client, arg); +} + + +/* REMOVE_EVENTS ioctl() */ +static int snd_seq_ioctl_remove_events(client_t * client, unsigned long arg) +{ + snd_seq_remove_events_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* + * Input mostly not implemented XXX. + */ + if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) { + /* + * No restrictions so for a user client we can clear + * the whole fifo + */ + if (client->type == USER_CLIENT) + snd_seq_fifo_clear(client->data.user.fifo); + } + + if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT) + snd_seq_queue_remove_cells(client->number, &info); + + return 0; +} + + +/* + * get subscription info + */ +static int snd_seq_ioctl_get_subscription(client_t *client, unsigned long arg) +{ + int result; + client_t *sender = NULL; + client_port_t *sport = NULL; + snd_seq_port_subscribe_t subs; + subscribers_t *p; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + result = -EINVAL; + if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL) + goto __end; + if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL) + goto __end; + p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest); + if (p) { + result = 0; + subs = p->info; + } else + result = -ENOENT; + + __end: + if (sport) + snd_seq_port_unlock(sport); + if (sender) + snd_seq_client_unlock(sender); + if (result >= 0) { + if (copy_to_user((void*)arg, &subs, sizeof(subs))) + return -EFAULT; + } + return result; +} + + +/* + * get subscription info - check only its presence + */ +static int snd_seq_ioctl_query_subs(client_t *client, unsigned long arg) +{ + int result = -ENXIO; + client_t *cptr = NULL; + client_port_t *port = NULL; + snd_seq_query_subs_t subs; + port_subs_info_t *group; + struct list_head *p; + int i; + + if (copy_from_user(&subs, (void*)arg, sizeof(subs))) + return -EFAULT; + + if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL) + goto __end; + if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL) + goto __end; + + switch (subs.type) { + case SNDRV_SEQ_QUERY_SUBS_READ: + group = &port->c_src; + break; + case SNDRV_SEQ_QUERY_SUBS_WRITE: + group = &port->c_dest; + break; + default: + goto __end; + } + + down_read(&group->list_mutex); + /* search for the subscriber */ + subs.num_subs = group->count; + i = 0; + result = -ENOENT; + list_for_each(p, &group->list_head) { + if (i++ == subs.index) { + /* found! */ + subscribers_t *s; + if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) { + s = list_entry(p, subscribers_t, src_list); + subs.addr = s->info.dest; + } else { + s = list_entry(p, subscribers_t, dest_list); + subs.addr = s->info.sender; + } + subs.flags = s->info.flags; + subs.queue = s->info.queue; + result = 0; + break; + } + } + up_read(&group->list_mutex); + + __end: + if (port) + snd_seq_port_unlock(port); + if (cptr) + snd_seq_client_unlock(cptr); + if (result >= 0) { + if (copy_to_user((void*)arg, &subs, sizeof(subs))) + return -EFAULT; + } + return result; +} + + +/* + * query next client + */ +static int snd_seq_ioctl_query_next_client(client_t *client, unsigned long arg) +{ + client_t *cptr = NULL; + snd_seq_client_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + + /* search for next client */ + info.client++; + if (info.client < 0) + info.client = 0; + for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) { + cptr = snd_seq_client_use_ptr(info.client); + if (cptr) + break; /* found */ + } + if (cptr == NULL) + return -ENOENT; + + get_client_info(cptr, &info); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* + * query next port + */ +static int snd_seq_ioctl_query_next_port(client_t *client, unsigned long arg) +{ + client_t *cptr; + client_port_t *port = NULL; + snd_seq_port_info_t info; + + if (copy_from_user(&info, (void*)arg, sizeof(info))) + return -EFAULT; + cptr = snd_seq_client_use_ptr(info.addr.client); + if (cptr == NULL) + return -ENXIO; + + /* search for next port */ + info.addr.port++; + port = snd_seq_port_query_nearest(cptr, &info); + if (port == NULL) { + snd_seq_client_unlock(cptr); + return -ENOENT; + } + + /* get port info */ + info.addr = port->addr; + snd_seq_get_port_info(port, &info); + snd_seq_port_unlock(port); + snd_seq_client_unlock(cptr); + + if (copy_to_user((void*)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +/* -------------------------------------------------------- */ + +static struct seq_ioctl_table { + unsigned int cmd; + int (*func)(client_t *client, unsigned long arg); +} ioctl_tables[] = { + { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info }, + { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info }, + { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info }, + { SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port }, + { SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port }, + { SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info }, + { SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info }, + { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port }, + { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port }, + { SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue }, + { SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info }, + { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer }, + { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client }, + { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client }, + { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool }, + { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool }, + { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription }, + { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client }, + { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port }, + { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events }, + { SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs }, + { 0, NULL }, +}; + +static int snd_seq_do_ioctl(client_t *client, unsigned int cmd, unsigned long arg) +{ + struct seq_ioctl_table *p; + + switch (cmd) { + case SNDRV_SEQ_IOCTL_PVERSION: + /* return sequencer version number */ + return put_user(SNDRV_SEQ_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_SEQ_IOCTL_CLIENT_ID: + /* return the id of this client */ + return put_user(client->number, (int *)arg) ? -EFAULT : 0; + } + + if (! arg) + return -EFAULT; + for (p = ioctl_tables; p->cmd; p++) { + if (p->cmd == cmd) + return p->func(client, arg); + } + snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n", + cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); + return -ENOTTY; +} + + +static int snd_seq_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + client_t *client = (client_t *) file->private_data; + + snd_assert(client != NULL, return -ENXIO); + + return snd_seq_do_ioctl(client, cmd, arg); +} + + +/* -------------------------------------------------------- */ + + +/* exported to kernel modules */ +int snd_seq_create_kernel_client(snd_card_t *card, int client_index, snd_seq_client_callback_t * callback) +{ + client_t *client; + + snd_assert(! in_interrupt(), return -EBUSY); + + if (callback == NULL) + return -EINVAL; + if (card && client_index > 7) + return -EINVAL; + if (card == NULL && client_index > 63) + return -EINVAL; + if (card) + client_index += 64 + (card->number << 3); + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + /* empty write queue as default */ + client = seq_create_client1(client_index, 0); + if (client == NULL) { + up(®ister_mutex); + return -EBUSY; /* failure code */ + } + usage_alloc(&client_usage, 1); + + client->accept_input = callback->allow_output; + client->accept_output = callback->allow_input; + + /* fill client data */ + client->data.kernel.card = card; + client->data.kernel.private_data = callback->private_data; + sprintf(client->name, "Client-%d", client->number); + + client->type = KERNEL_CLIENT; + up(®ister_mutex); + + /* make others aware this new client */ + snd_seq_system_client_ev_client_start(client->number); + + /* return client number to caller */ + return client->number; +} + +/* exported to kernel modules */ +int snd_seq_delete_kernel_client(int client) +{ + client_t *ptr; + + snd_assert(! in_interrupt(), return -EBUSY); + + ptr = clientptr(client); + if (ptr == NULL) + return -EINVAL; + + seq_free_client(ptr); + kfree(ptr); + return 0; +} + + +/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue + * and snd_seq_kernel_client_enqueue_blocking + */ +static int kernel_client_enqueue(int client, snd_seq_event_t *ev, + struct file *file, int blocking, + int atomic, int hop) +{ + client_t *cptr; + int result; + + snd_assert(ev != NULL, return -EINVAL); + + if (ev->type == SNDRV_SEQ_EVENT_NONE) + return 0; /* ignore this */ + if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR || + ev->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) + return -EINVAL; /* quoted events can't be enqueued */ + + /* fill in client number */ + ev->source.client = client; + + if (check_event_type_and_length(ev)) + return -EINVAL; + + cptr = snd_seq_client_use_ptr(client); + if (cptr == NULL) + return -EINVAL; + + if (! cptr->accept_output) + result = -EPERM; + else /* send it */ + result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop); + + snd_seq_client_unlock(cptr); + return result; +} + +/* + * exported, called by kernel clients to enqueue events (w/o blocking) + * + * RETURN VALUE: zero if succeed, negative if error + */ +int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t * ev, + int atomic, int hop) +{ + return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop); +} + +/* + * exported, called by kernel clients to enqueue events (with blocking) + * + * RETURN VALUE: zero if succeed, negative if error + */ +int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, + struct file *file, + int atomic, int hop) +{ + return kernel_client_enqueue(client, ev, file, 1, atomic, hop); +} + + +/* + * exported, called by kernel clients to dispatch events directly to other + * clients, bypassing the queues. Event time-stamp will be updated. + * + * RETURN VALUE: negative = delivery failed, + * zero, or positive: the number of delivered events + */ +int snd_seq_kernel_client_dispatch(int client, snd_seq_event_t * ev, + int atomic, int hop) +{ + client_t *cptr; + int result; + + snd_assert(ev != NULL, return -EINVAL); + + /* fill in client number */ + ev->queue = SNDRV_SEQ_QUEUE_DIRECT; + ev->source.client = client; + + if (check_event_type_and_length(ev)) + return -EINVAL; + + cptr = snd_seq_client_use_ptr(client); + if (cptr == NULL) + return -EINVAL; + + if (!cptr->accept_output) + result = -EPERM; + else + result = snd_seq_deliver_event(cptr, ev, atomic, hop); + + snd_seq_client_unlock(cptr); + return result; +} + + +/* + * exported, called by kernel clients to perform same functions as with + * userland ioctl() + */ +int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) +{ + client_t *client; + mm_segment_t fs; + int result; + + client = clientptr(clientid); + if (client == NULL) + return -ENXIO; + fs = snd_enter_user(); + result = snd_seq_do_ioctl(client, cmd, (unsigned long)arg); + snd_leave_user(fs); + return result; +} + + +/* exported (for OSS emulator) */ +int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait) +{ + client_t *client; + + client = clientptr(clientid); + if (client == NULL) + return -ENXIO; + + if (! snd_seq_write_pool_allocated(client)) + return 1; + if (snd_seq_pool_poll_wait(client->pool, file, wait)) + return 1; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +/* + * /proc interface + */ +static void snd_seq_info_dump_subscribers(snd_info_buffer_t *buffer, port_subs_info_t *group, int is_src, char *msg) +{ + struct list_head *p; + subscribers_t *s; + int count = 0; + + down_read(&group->list_mutex); + if (list_empty(&group->list_head)) { + up_read(&group->list_mutex); + return; + } + snd_iprintf(buffer, msg); + list_for_each(p, &group->list_head) { + if (is_src) + s = list_entry(p, subscribers_t, src_list); + else + s = list_entry(p, subscribers_t, dest_list); + if (count++) + snd_iprintf(buffer, ", "); + snd_iprintf(buffer, "%d:%d", + is_src ? s->info.dest.client : s->info.sender.client, + is_src ? s->info.dest.port : s->info.sender.port); + if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) + snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue); + if (group->exclusive) + snd_iprintf(buffer, "[ex]"); + } + up_read(&group->list_mutex); + snd_iprintf(buffer, "\n"); +} + +#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-') +#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-') +#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e') + +#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-') + +static void snd_seq_info_dump_ports(snd_info_buffer_t *buffer, client_t *client) +{ + struct list_head *l; + + down(&client->ports_mutex); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n", + p->addr.port, p->name, + FLAG_PERM_RD(p->capability), + FLAG_PERM_WR(p->capability), + FLAG_PERM_EX(p->capability), + FLAG_PERM_DUPLEX(p->capability)); + snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: "); + snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: "); + } + up(&client->ports_mutex); +} + + +/* exported to seq_info.c */ +void snd_seq_info_clients_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + extern void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t * pool, char *space); + int c; + client_t *client; + + snd_iprintf(buffer, "Client info\n"); + snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur); + snd_iprintf(buffer, " peak clients : %d\n", client_usage.peak); + snd_iprintf(buffer, " max clients : %d\n", SNDRV_SEQ_MAX_CLIENTS); + snd_iprintf(buffer, "\n"); + + /* list the client table */ + for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) { + client = snd_seq_client_use_ptr(c); + if (client == NULL) + continue; + if (client->type == NO_CLIENT) + continue; + + snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n", + c, client->name, + client->type == USER_CLIENT ? "User" : "Kernel"); + snd_seq_info_dump_ports(buffer, client); + if (snd_seq_write_pool_allocated(client)) { + snd_iprintf(buffer, " Output pool :\n"); + snd_seq_info_pool(buffer, client->pool, " "); + } + if (client->type == USER_CLIENT && client->data.user.fifo && + client->data.user.fifo->pool) { + snd_iprintf(buffer, " Input pool :\n"); + snd_seq_info_pool(buffer, client->data.user.fifo->pool, " "); + } + snd_seq_client_unlock(client); + } +} + + +/*---------------------------------------------------------------------------*/ + + +/* + * REGISTRATION PART + */ + +static struct file_operations snd_seq_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_seq_read, + write: snd_seq_write, + open: snd_seq_open, + release: snd_seq_release, + poll: snd_seq_poll, + ioctl: snd_seq_ioctl, +}; + +static snd_minor_t snd_seq_reg = +{ + comment: "sequencer", + f_ops: &snd_seq_f_ops, +}; + + +/* + * register sequencer device + */ +int __init snd_sequencer_device_init(void) +{ + int err; + + if (down_interruptible(®ister_mutex)) + return -ERESTARTSYS; + + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) { + up(®ister_mutex); + return err; + } + + up(®ister_mutex); + + return 0; +} + + + +/* + * unregister sequencer device + */ +void __exit snd_sequencer_device_done(void) +{ + snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0); +} diff -Nru linux/sound/core/seq/seq_clientmgr.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.h --- linux/sound/core/seq/seq_clientmgr.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_clientmgr.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,104 @@ +/* + * ALSA sequencer Client Manager + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_CLIENTMGR_H +#define __SND_SEQ_CLIENTMGR_H + +#include +#include "seq_fifo.h" +#include "seq_ports.h" +#include "seq_lock.h" + + +/* client manager */ + +struct _snd_seq_user_client { + struct file *file; /* file struct of client */ + /* ... */ + + /* fifo */ + fifo_t *fifo; /* queue for incoming events */ + int fifo_pool_size; +}; + +struct _snd_seq_kernel_client { + snd_card_t *card; + /* pointer to client functions */ + void *private_data; /* private data for client */ + /* ... */ +}; + + +struct _snd_seq_client { + snd_seq_client_type_t type; + unsigned int accept_input: 1, + accept_output: 1; + char name[64]; /* client name */ + int number; /* client number */ + unsigned int filter; /* filter flags */ + unsigned char client_filter[32]; + unsigned char event_filter[32]; + snd_use_lock_t use_lock; + int event_lost; + /* ports */ + int num_ports; /* number of ports */ + struct list_head ports_list_head; + rwlock_t ports_lock; + struct semaphore ports_mutex; + + /* output pool */ + pool_t *pool; /* memory pool for this client */ + + union { + user_client_t user; + kernel_client_t kernel; + } data; +}; + +/* usage statistics */ +typedef struct { + int cur; + int peak; +} usage_t; + + +extern int client_init_data(void); +extern int snd_sequencer_device_init(void); +extern void snd_sequencer_device_done(void); + +/* get locked pointer to client */ +extern client_t *snd_seq_client_use_ptr(int clientid); + +/* unlock pointer to client */ +#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock) + +/* dispatch event to client(s) */ +extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop); + +/* exported to other modules */ +extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data); +extern int snd_seq_unregister_kernel_client(int client); +extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop); +int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop); +int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait); +int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype); +int snd_seq_deliver_event(client_t *client, snd_seq_event_t *event, int atomic, int hop); + +#endif diff -Nru linux/sound/core/seq/seq_device.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_device.c --- linux/sound/core/seq/seq_device.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_device.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,528 @@ +/* + * ALSA sequencer device management + * Copyright (c) 1999 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + *---------------------------------------------------------------- + * + * This device handler seperates the card driver module from sequencer + * stuff (sequencer core, synth drivers, etc), so that user can avoid + * to spend unnecessary resources e.g. if he needs only listening to + * MP3s. + * + * The card (or lowlevel) driver creates a sequencer device entry + * via snd_seq_device_new(). This is an entry pointer to communicate + * with the sequencer device "driver", which is involved with the + * actual part to communicate with the sequencer core. + * Each sequencer device entry has an id string and the corresponding + * driver with the same id is loaded when required. For example, + * lowlevel codes to access emu8000 chip on sbawe card are included in + * emu8000-synth module. To activate this module, the hardware + * resources like i/o port are passed via snd_seq_device argument. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ALSA sequencer device management"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +/* + * driver list + */ +typedef struct ops_list ops_list_t; + +/* driver state */ +#define DRIVER_EMPTY 0 +#define DRIVER_LOADED (1<<0) +#define DRIVER_REQUESTED (1<<1) +#define DRIVER_LOCKED (1<<2) + +struct ops_list { + char id[ID_LEN]; /* driver id */ + int driver; /* driver state */ + int used; /* reference counter */ + int argsize; /* argument size */ + + /* operators */ + snd_seq_dev_ops_t ops; + + /* registred devices */ + struct list_head dev_list; /* list of devices */ + int num_devices; /* number of associated devices */ + int num_init_devices; /* number of initialized devices */ + struct semaphore reg_mutex; + + struct list_head list; /* next driver */ +}; + + +static LIST_HEAD(opslist); +static int num_ops = 0; +static DECLARE_MUTEX(ops_mutex); +static snd_info_entry_t *info_entry = NULL; + +/* + * prototypes + */ +static int snd_seq_device_free(snd_seq_device_t *dev); +static int snd_seq_device_dev_free(snd_device_t *device); +static int snd_seq_device_dev_register(snd_device_t *device); +static int snd_seq_device_dev_unregister(snd_device_t *device); + +static int init_device(snd_seq_device_t *dev, ops_list_t *ops); +static int free_device(snd_seq_device_t *dev, ops_list_t *ops); +static ops_list_t *find_driver(char *id, int create_if_empty); +static ops_list_t *create_driver(char *id); +static void unlock_driver(ops_list_t *ops); +static void remove_drivers(void); + +/* + * show all drivers and their status + */ + +static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + struct list_head *head; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", + ops->id, + ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), + ops->driver & DRIVER_REQUESTED ? ",requested" : "", + ops->driver & DRIVER_LOCKED ? ",locked" : "", + ops->num_devices); + } + up(&ops_mutex); +} + +/* + * load all registered drivers (called from seq_clientmgr.c) + */ + +void snd_seq_device_load_drivers(void) +{ +#ifdef CONFIG_KMOD + struct list_head *head; + char modname[64]; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (! (ops->driver & DRIVER_LOADED) && + ! (ops->driver & DRIVER_REQUESTED)) { + ops->used++; + up(&ops_mutex); + ops->driver |= DRIVER_REQUESTED; + sprintf(modname, "snd-%s", ops->id); + request_module(modname); + down(&ops_mutex); + ops->used--; + } + } + up(&ops_mutex); +#endif +} + +/* + * register a sequencer device + * card = card info (NULL allowed) + * device = device number (if any) + * id = id of driver + * result = return pointer (NULL allowed if unnecessary) + */ +int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize, + snd_seq_device_t **result) +{ + snd_seq_device_t *dev; + ops_list_t *ops; + int err; + static snd_device_ops_t dops = { + dev_free: snd_seq_device_dev_free, + dev_register: snd_seq_device_dev_register, + dev_unregister: snd_seq_device_dev_unregister + }; + + if (result) + *result = NULL; + + snd_assert(id != NULL, return -EINVAL); + + ops = find_driver(id, 1); + if (ops == NULL) + return -ENOMEM; + + dev = snd_magic_kcalloc(snd_seq_device_t, sizeof(*dev) + argsize, GFP_KERNEL); + if (dev == NULL) { + unlock_driver(ops); + return -ENOMEM; + } + + /* set up device info */ + dev->card = card; + dev->device = device; + strncpy(dev->id, id, sizeof(dev->id) - 1); + dev->id[sizeof(dev->id) - 1] = 0; + dev->argsize = argsize; + dev->status = SNDRV_SEQ_DEVICE_FREE; + + /* add this device to the list */ + down(&ops->reg_mutex); + list_add_tail(&dev->list, &ops->dev_list); + ops->num_devices++; + up(&ops->reg_mutex); + + unlock_driver(ops); + + if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { + snd_seq_device_free(dev); + return err; + } + + if (result) + *result = dev; + + return 0; +} + +/* + * free the existing device + */ +static int snd_seq_device_free(snd_seq_device_t *dev) +{ + ops_list_t *ops; + + snd_assert(dev != NULL, return -EINVAL); + + ops = find_driver(dev->id, 0); + if (ops == NULL) + return -ENXIO; + + /* remove the device from the list */ + down(&ops->reg_mutex); + list_del(&dev->list); + ops->num_devices--; + up(&ops->reg_mutex); + + free_device(dev, ops); + if (dev->private_free) + dev->private_free(dev); + snd_magic_kfree(dev); + + unlock_driver(ops); + + return 0; +} + +static int snd_seq_device_dev_free(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + return snd_seq_device_free(dev); +} + +/* + * register the device + */ +static int snd_seq_device_dev_register(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + ops_list_t *ops; + + ops = find_driver(dev->id, 0); + if (ops == NULL) + return -ENOENT; + + /* initialize this device if the corresponding driver was + * already loaded + */ + if (ops->driver & DRIVER_LOADED) + init_device(dev, ops); + + unlock_driver(ops); + return 0; +} + +/* + * unregister the existing device + */ +static int snd_seq_device_dev_unregister(snd_device_t *device) +{ + snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + return snd_seq_device_free(dev); +} + +/* + * register device driver + * id = driver id + * entry = driver operators - duplicated to each instance + */ +int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize) +{ + struct list_head *head; + ops_list_t *ops; + + if (id == NULL || entry == NULL || + entry->init_device == NULL || entry->free_device == NULL) + return -EINVAL; + + ops = find_driver(id, 1); + if (ops == NULL) + return -ENOMEM; + if (ops->driver & DRIVER_LOADED) { + snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id); + unlock_driver(ops); + return -EBUSY; + } + + down(&ops->reg_mutex); + /* copy driver operators */ + ops->ops = *entry; + ops->driver |= DRIVER_LOADED; + ops->argsize = argsize; + + /* initialize existing devices if necessary */ + list_for_each(head, &ops->dev_list) { + snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); + init_device(dev, ops); + } + up(&ops->reg_mutex); + + unlock_driver(ops); + + return 0; +} + + +/* + * create driver record + */ +static ops_list_t * create_driver(char *id) +{ + ops_list_t *ops; + + ops = kmalloc(sizeof(*ops), GFP_KERNEL); + if (ops == NULL) + return ops; + memset(ops, 0, sizeof(*ops)); + + /* set up driver entry */ + strncpy(ops->id, id, sizeof(ops->id) - 1); + ops->id[sizeof(ops->id) - 1] = 0; + init_MUTEX(&ops->reg_mutex); + ops->driver = DRIVER_EMPTY; + INIT_LIST_HEAD(&ops->dev_list); + /* lock this instance */ + ops->used = 1; + + /* register driver entry */ + down(&ops_mutex); + list_add_tail(&ops->list, &opslist); + num_ops++; + up(&ops_mutex); + + return ops; +} + + +/* + * unregister the specified driver + */ +int snd_seq_device_unregister_driver(char *id) +{ + struct list_head *head; + ops_list_t *ops; + + ops = find_driver(id, 0); + if (ops == NULL) + return -ENXIO; + if (! (ops->driver & DRIVER_LOADED) || + (ops->driver & DRIVER_LOCKED)) { + snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver); + unlock_driver(ops); + return -EBUSY; + } + + /* close and release all devices associated with this driver */ + down(&ops->reg_mutex); + ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ + list_for_each(head, &ops->dev_list) { + snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list); + free_device(dev, ops); + } + + ops->driver = 0; + if (ops->num_init_devices > 0) + snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices); + up(&ops->reg_mutex); + + unlock_driver(ops); + + /* remove empty driver entries */ + remove_drivers(); + + return 0; +} + + +/* + * remove empty driver entries + */ +static void remove_drivers(void) +{ + struct list_head *head; + + down(&ops_mutex); + head = opslist.next; + while (head != &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (! (ops->driver & DRIVER_LOADED) && + ops->used == 0 && ops->num_devices == 0) { + head = head->next; + list_del(&ops->list); + kfree(ops); + num_ops--; + } else + head = head->next; + } + up(&ops_mutex); +} + +/* + * initialize the device - call init_device operator + */ +static int init_device(snd_seq_device_t *dev, ops_list_t *ops) +{ + if (! (ops->driver & DRIVER_LOADED)) + return 0; /* driver is not loaded yet */ + if (dev->status != SNDRV_SEQ_DEVICE_FREE) + return 0; /* already initialized */ + if (ops->argsize != dev->argsize) { + snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); + return -EINVAL; + } + if (ops->ops.init_device(dev) >= 0) { + dev->status = SNDRV_SEQ_DEVICE_REGISTERED; + ops->num_init_devices++; + } else { + snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id); + } + + return 0; +} + +/* + * release the device - call free_device operator + */ +static int free_device(snd_seq_device_t *dev, ops_list_t *ops) +{ + int result; + + if (! (ops->driver & DRIVER_LOADED)) + return 0; /* driver is not loaded yet */ + if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) + return 0; /* not registered */ + if (ops->argsize != dev->argsize) { + snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize); + return -EINVAL; + } + if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { + dev->status = SNDRV_SEQ_DEVICE_FREE; + dev->driver_data = NULL; + ops->num_init_devices--; + } else { + snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id); + } + + return 0; +} + +/* + * find the matching driver with given id + */ +static ops_list_t * find_driver(char *id, int create_if_empty) +{ + struct list_head *head; + + down(&ops_mutex); + list_for_each(head, &opslist) { + ops_list_t *ops = list_entry(head, ops_list_t, list); + if (strcmp(ops->id, id) == 0) { + ops->used++; + up(&ops_mutex); + return ops; + } + } + up(&ops_mutex); + if (create_if_empty) + return create_driver(id); + return NULL; +} + +static void unlock_driver(ops_list_t *ops) +{ + down(&ops_mutex); + ops->used--; + up(&ops_mutex); +} + + +/* + * module part + */ + +static int __init alsa_seq_device_init(void) +{ + info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root); + if (info_entry == NULL) + return -ENOMEM; + info_entry->content = SNDRV_INFO_CONTENT_TEXT; + info_entry->c.text.read_size = 2048; + info_entry->c.text.read = snd_seq_device_info; + if (snd_info_register(info_entry) < 0) { + snd_info_free_entry(info_entry); + return -ENOMEM; + } + return 0; +} + +static void __exit alsa_seq_device_exit(void) +{ + remove_drivers(); + snd_info_unregister(info_entry); + if (num_ops) + snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops); +} + +module_init(alsa_seq_device_init) +module_exit(alsa_seq_device_exit) + +EXPORT_SYMBOL(snd_seq_device_load_drivers); +EXPORT_SYMBOL(snd_seq_device_new); +EXPORT_SYMBOL(snd_seq_device_register_driver); +EXPORT_SYMBOL(snd_seq_device_unregister_driver); diff -Nru linux/sound/core/seq/seq_dtl.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_dtl.c --- linux/sound/core/seq/seq_dtl.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_dtl.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,193 @@ +/* + * DTL(e) event converter + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_queue.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +typedef struct dtl_out { + int out_mtp_network; + unsigned int time_format; + unsigned int full_frame_count; + unsigned char sysex[11]; +} dtl_out_t; + +typedef struct dtl_in { + unsigned int time_format; + unsigned int cur_pos; +} dtl_in_t; + + +static int dtl_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + dtl_out_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->out_mtp_network = sync_info->opt_info[0]; + arg->full_frame_count = sync_info->opt_info[1]; + arg->time_format = sync_info->time_format; + sync_info->param.time.subframes = 1; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + *retp = arg; + return 0; +} + +static int dtl_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + dtl_in_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->time_format = sync_info->time_format; + arg->cur_pos = 0; + sync_info->param.time.subframes = 1; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + *retp = arg; + return 0; +} + + +/* decode sync position */ +static int sync_pos_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + sndrv_seq_time_frame_t cur_out; + unsigned char *buf = arg->sysex; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + + cur_out = snd_seq_position_to_time_frame(arg->time_format, 1, src->data.queue.param.position); + buf[0] = 0xf0; /* SYSEX */ + buf[1] = 0x00; /* MOTU */ + buf[2] = 0x33; /* MOTU */ + buf[3] = 0x7f; + buf[4] = 0x0c; /* DTL full frame */ + buf[5] = arg->out_mtp_network; /* 0x00 or 0x08 */ + buf[6] = cur_out.hour | (arg->time_format << 5); + buf[7] = cur_out.min; + buf[8] = cur_out.sec; + buf[9] = cur_out.frame; + buf[10] = 0xf7; + + ev->type = SNDRV_SEQ_EVENT_SYSEX; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->data.ext.len = 11; + ev->data.ext.ptr = buf; + + return 1; +} + +/* decode sync signal */ +static int sync_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned int pos; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + pos = src->data.queue.param.position; + if (arg->full_frame_count && + (pos % arg->full_frame_count) == 0) + /* send full frame */ + return sync_pos_out(arg, src, ev); + ev->type = SNDRV_SEQ_EVENT_CLOCK; + return 1; +} + +static int dtl_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + dtl_out_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_SYNC: + return sync_out(arg, src, ev); + case SNDRV_SEQ_EVENT_SYNC_POS: + return sync_pos_out(arg, src, ev); + } + return 0; +} + +/* decode sync position */ +static int sync_pos_in(dtl_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned time_format; + static unsigned char id[] = { + 0xf0, 0x00, 0x33, 0x7f, 0x0c, + }; + sndrv_seq_time_frame_t cur_in; + char buf[11]; + + if (snd_seq_expand_var_event(src, 11, buf, 1, 0) != 11) + return 0; + if (memcmp(buf, id, sizeof(id)) != 0) + return 0; + time_format = (buf[6] >> 5) & 3; + if (time_format != arg->time_format) + return -EINVAL; + cur_in.hour = buf[6] & 0x1f; + cur_in.min = buf[7]; + cur_in.sec = buf[8]; + cur_in.frame = buf[9]; + arg->cur_pos = snd_seq_time_frame_to_position(time_format, 1, &cur_in); + + ev->type = SNDRV_SEQ_EVENT_SYNC_POS; + ev->data.queue.sync_time_format = time_format; + ev->data.queue.param.position = arg->cur_pos; + + return 1; +} + +static int dtl_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + dtl_in_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_CLOCK: + arg->cur_pos++; + ev->type = SNDRV_SEQ_EVENT_SYNC; + ev->data.queue.param.position = arg->cur_pos; + return 1; + case SNDRV_SEQ_EVENT_SYSEX: + return sync_pos_in(arg, src, ev); + } + return 0; +} + +/* exported */ +seq_sync_parser_t snd_seq_dtl_parser = { + format: SNDRV_SEQ_SYNC_FMT_DTL, + in: { + open: dtl_open_in, + sync: dtl_sync_in, + }, + out: { + open: dtl_open_out, + sync: dtl_sync_out, + }, +}; + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru linux/sound/core/seq/seq_dummy.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_dummy.c --- linux/sound/core/seq/seq_dummy.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_dummy.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,274 @@ +/* + * ALSA sequencer MIDI-through client + * Copyright (c) 1999-2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "seq_clientmgr.h" +#include +#include + +/* + + Sequencer MIDI-through client + + This gives a simple midi-through client. All the normal input events + are redirected to output port immediately. + The routing can be done via aconnect program in alsa-utils. + + Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY). + If you want to auto-load this module, you may add the following alias + in your /etc/conf.modules file. + + alias snd-seq-client-62 snd-seq-dummy + + The module is loaded on demand for client 62, or /proc/asound/seq/ + is accessed. If you don't need this module to be loaded, alias + snd-seq-client-62 as "off". This will help modprobe. + + The number of ports to be created can be specified via the module + paramter "ports". For example, to create four ports, add the + following option in /etc/modules.conf: + + option snd-seq-dummy ports=4 + + The modle option "duplex=1" enables duplex operation to the port. + In duplex mode, a pair of ports are created instead of single port, + and events are tunneled between pair-ports. For example, input to + port A is sent to output port of another port B and vice versa. + In duplex mode, each port has DUPLEX capability. + + */ + + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ALSA sequencer MIDI-through client"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +MODULE_PARM(ports, "i"); +MODULE_PARM_DESC(ports, "number of ports to be created"); +MODULE_PARM(duplex, "i"); +MODULE_PARM_DESC(duplex, "create DUPLEX ports"); +int ports = 1; +int duplex = 0; + +typedef struct snd_seq_dummy_port { + int client; + int port; + int duplex; + int connect; +} snd_seq_dummy_port_t; + +static int my_client = -1; + +/* + * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events + * to subscribers. + * Note: this callback is called only after all subscribers are removed. + */ +static int +dummy_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_seq_dummy_port_t *p; + int i; + snd_seq_event_t ev; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + memset(&ev, 0, sizeof(ev)); + if (p->duplex) + ev.source.port = p->connect; + else + ev.source.port = p->port; + ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + for (i = 0; i < 16; i++) { + ev.data.control.channel = i; + ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF; + snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); + ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS; + snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0); + } + return 0; +} + +/* + * event input callback - just redirect events to subscribers + */ +static int +dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) +{ + snd_seq_dummy_port_t *p; + snd_seq_event_t tmpev; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM || + ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) + return 0; /* ignore system messages */ + /* save the original sender */ + tmpev.type = SNDRV_SEQ_EVENT_KERNEL_QUOTE; + tmpev.flags = (ev->flags & ~SNDRV_SEQ_EVENT_LENGTH_MASK) + | SNDRV_SEQ_EVENT_LENGTH_FIXED; + tmpev.tag = ev->tag; + tmpev.time = ev->time; + tmpev.data.quote.origin = ev->source; + tmpev.data.quote.event = ev; + if (p->duplex) + tmpev.source.port = p->connect; + else + tmpev.source.port = p->port; + tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop); +} + +/* + * free_private callback + */ +static void +dummy_free(void *private_data) +{ + snd_seq_dummy_port_t *p; + + p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return); + snd_magic_kfree(p); +} + +/* + * create a port + */ +static snd_seq_dummy_port_t __init * +create_port(int idx, int type) +{ + snd_seq_port_info_t pinfo; + snd_seq_port_callback_t pcb; + snd_seq_dummy_port_t *rec; + + if ((rec = snd_magic_kcalloc(snd_seq_dummy_port_t, 0, GFP_KERNEL)) == NULL) + return NULL; + + rec->client = my_client; + rec->duplex = duplex; + rec->connect = 0; + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr.client = my_client; + if (duplex) + sprintf(pinfo.name, "Midi Through Port-%d:%c", idx, + (type ? 'B' : 'A')); + else + sprintf(pinfo.name, "Midi Through Port-%d", idx); + pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + if (duplex) + pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + memset(&pcb, 0, sizeof(pcb)); + pcb.owner = THIS_MODULE; + pcb.unuse = dummy_unuse; + pcb.event_input = dummy_input; + pcb.private_free = dummy_free; + pcb.private_data = rec; + pinfo.kernel = &pcb; + if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) { + snd_magic_kfree(rec); + return NULL; + } + rec->port = pinfo.addr.port; + return rec; +} + +/* + * register client and create ports + */ +static int __init +register_client(void) +{ + snd_seq_client_callback_t cb; + snd_seq_client_info_t cinfo; + snd_seq_dummy_port_t *rec1, *rec2; + int i; + + if (ports < 1) { + snd_printk(KERN_ERR "invalid number of ports %d\n", ports); + return -EINVAL; + } + + /* create client */ + memset(&cb, 0, sizeof(cb)); + cb.allow_input = 1; + cb.allow_output = 1; + my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb); + if (my_client < 0) + return my_client; + + /* set client name */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = my_client; + cinfo.type = KERNEL_CLIENT; + strcpy(cinfo.name, "Midi Through"); + snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + /* create ports */ + for (i = 0; i < ports; i++) { + rec1 = create_port(i, 0); + if (rec1 == NULL) { + snd_seq_delete_kernel_client(my_client); + return -ENOMEM; + } + if (duplex) { + rec2 = create_port(i, 1); + if (rec2 == NULL) { + snd_seq_delete_kernel_client(my_client); + return -ENOMEM; + } + rec1->connect = rec2->port; + rec2->connect = rec1->port; + } + } + + return 0; +} + +/* + * delete client if exists + */ +static void __exit +delete_client(void) +{ + if (my_client >= 0) + snd_seq_delete_kernel_client(my_client); +} + +/* + * Init part + */ + +static int __init alsa_seq_dummy_init(void) +{ + return register_client(); +} + +static void __exit alsa_seq_dummy_exit(void) +{ + delete_client(); +} + +module_init(alsa_seq_dummy_init) +module_exit(alsa_seq_dummy_exit) diff -Nru linux/sound/core/seq/seq_fifo.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.c --- linux/sound/core/seq/seq_fifo.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,259 @@ +/* + * ALSA sequencer FIFO + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_fifo.h" +#include "seq_lock.h" + + +/* FIFO */ + +/* create new fifo */ +fifo_t *snd_seq_fifo_new(int poolsize) +{ + fifo_t *f; + + f = snd_kcalloc(sizeof(fifo_t), GFP_KERNEL); + if (f == NULL) { + snd_printd("malloc failed for snd_seq_fifo_new() \n"); + return NULL; + } + + f->pool = snd_seq_pool_new(poolsize); + if (f->pool == NULL) { + kfree(f); + return NULL; + } + if (snd_seq_pool_init(f->pool) < 0) { + snd_seq_pool_delete(&f->pool); + kfree(f); + return NULL; + } + + spin_lock_init(&f->lock); + snd_use_lock_init(&f->use_lock); + init_waitqueue_head(&f->input_sleep); + atomic_set(&f->overflow, 0); + + f->head = NULL; + f->tail = NULL; + f->cells = 0; + + return f; +} + +void snd_seq_fifo_delete(fifo_t **fifo) +{ + fifo_t *f; + + snd_assert(fifo != NULL, return); + f = *fifo; + snd_assert(f != NULL, return); + *fifo = NULL; + + snd_seq_fifo_clear(f); + + /* wake up clients if any */ + if (waitqueue_active(&f->input_sleep)) + wake_up(&f->input_sleep); + + /* release resources...*/ + /*....................*/ + + if (f->pool) { + snd_seq_pool_done(f->pool); + snd_seq_pool_delete(&f->pool); + } + + kfree(f); +} + +static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f); + +/* clear queue */ +void snd_seq_fifo_clear(fifo_t *f) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + /* clear overflow flag */ + atomic_set(&f->overflow, 0); + + snd_use_lock_sync(&f->use_lock); + spin_lock_irqsave(&f->lock, flags); + /* drain the fifo */ + while ((cell = fifo_cell_out(f)) != NULL) { + snd_seq_cell_free(cell); + } + spin_unlock_irqrestore(&f->lock, flags); +} + + +/* enqueue event to fifo */ +int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + int err; + + snd_assert(f != NULL, return -EINVAL); + + snd_use_lock_use(&f->use_lock); + err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ + if (err < 0) { + if (err == -ENOMEM) + atomic_inc(&f->overflow); + snd_use_lock_free(&f->use_lock); + return err; + } + + /* append new cells to fifo */ + spin_lock_irqsave(&f->lock, flags); + if (f->tail != NULL) + f->tail->next = cell; + f->tail = cell; + if (f->head == NULL) + f->head = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + + /* wakeup client */ + if (waitqueue_active(&f->input_sleep)) + wake_up(&f->input_sleep); + + snd_use_lock_free(&f->use_lock); + + return 0; /* success */ + +} + +/* dequeue cell from fifo */ +static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f) +{ + snd_seq_event_cell_t *cell; + + if ((cell = f->head) != NULL) { + f->head = cell->next; + + /* reset tail if this was the last element */ + if (f->tail == cell) + f->tail = NULL; + + cell->next = NULL; + f->cells--; + } + + return cell; +} + +/* dequeue cell from fifo and copy on user space */ +int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + snd_assert(f != NULL, return -EINVAL); + + *cellp = NULL; + spin_lock_irqsave(&f->lock, flags); + while ((cell = fifo_cell_out(f)) == NULL) { + if (nonblock) { + /* non-blocking - return immediately */ + spin_unlock_irqrestore(&f->lock, flags); + return -EAGAIN; + } + snd_seq_sleep_in_lock(&f->input_sleep, &f->lock); + + if (signal_pending(current)) { + spin_unlock_irqrestore(&f->lock, flags); + return -ERESTARTSYS; + } + } + *cellp = cell; + spin_unlock_irqrestore(&f->lock, flags); + + return 0; +} + + +void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell) +{ + unsigned long flags; + + if (cell) { + spin_lock_irqsave(&f->lock, flags); + cell->next = f->head; + f->head = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + } +} + + +/* polling; return non-zero if queue is available */ +int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait) +{ + poll_wait(file, &f->input_sleep, wait); + return (f->cells > 0); +} + +/* change the size of pool; all old events are removed */ +int snd_seq_fifo_resize(fifo_t *f, int poolsize) +{ + unsigned long flags; + pool_t *newpool, *oldpool; + snd_seq_event_cell_t *cell, *next, *oldhead; + + snd_assert(f != NULL && f->pool != NULL, return -EINVAL); + + /* allocate new pool */ + newpool = snd_seq_pool_new(poolsize); + if (newpool == NULL) + return -ENOMEM; + if (snd_seq_pool_init(newpool) < 0) { + snd_seq_pool_delete(&newpool); + return -ENOMEM; + } + + spin_lock_irqsave(&f->lock, flags); + /* remember old pool */ + oldpool = f->pool; + oldhead = f->head; + /* exchange pools */ + f->pool = newpool; + f->head = NULL; + f->tail = NULL; + f->cells = 0; + /* NOTE: overflow flag is not cleared */ + spin_unlock_irqrestore(&f->lock, flags); + + /* release cells in old pool */ + for (cell = oldhead; cell; cell = next) { + next = cell->next; + snd_seq_cell_free(cell); + } + snd_seq_pool_delete(&oldpool); + + return 0; +} diff -Nru linux/sound/core/seq/seq_fifo.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.h --- linux/sound/core/seq/seq_fifo.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_fifo.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,72 @@ +/* + * ALSA sequencer FIFO + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_FIFO_H +#define __SND_SEQ_FIFO_H + +#include "seq_memory.h" +#include "seq_lock.h" + + +/* === FIFO === */ + +typedef struct { + pool_t *pool; /* FIFO pool */ + snd_seq_event_cell_t* head; /* pointer to head of fifo */ + snd_seq_event_cell_t* tail; /* pointer to tail of fifo */ + int cells; + spinlock_t lock; + snd_use_lock_t use_lock; + wait_queue_head_t input_sleep; + atomic_t overflow; + +} fifo_t; + +/* create new fifo (constructor) */ +extern fifo_t *snd_seq_fifo_new(int poolsize); + +/* delete fifo (destructor) */ +extern void snd_seq_fifo_delete(fifo_t **f); + + +/* enqueue event to fifo */ +extern int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event); + +/* lock fifo from release */ +#define snd_seq_fifo_lock(fifo) snd_use_lock_use(&(fifo)->use_lock) +#define snd_seq_fifo_unlock(fifo) snd_use_lock_free(&(fifo)->use_lock) + +/* get a cell from fifo - fifo should be locked */ +int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock); + +/* free dequeued cell - fifo should be locked */ +extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell); + +/* clean up queue */ +extern void snd_seq_fifo_clear(fifo_t *f); + +/* polling */ +extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait); + +/* resize pool in fifo */ +int snd_seq_fifo_resize(fifo_t *f, int poolsize); + + +#endif diff -Nru linux/sound/core/seq/seq_info.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.c --- linux/sound/core/seq/seq_info.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,76 @@ +/* + * ALSA sequencer /proc interface + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include + +#include "seq_info.h" +#include "seq_clientmgr.h" +#include "seq_timer.h" + + +static snd_info_entry_t *queues_entry; +static snd_info_entry_t *clients_entry; +static snd_info_entry_t *timer_entry; + + +static snd_info_entry_t * __init +create_info_entry(char *name, int size, void (*read)(snd_info_entry_t *, snd_info_buffer_t *)) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root); + if (entry == NULL) + return NULL; + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = size; + entry->c.text.read = read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return NULL; + } + return entry; +} + + +/* create all our /proc entries */ +int __init snd_seq_info_init(void) +{ + queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES), + snd_seq_info_queues_read); + clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS), + snd_seq_info_clients_read); + timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read); + return 0; +} + +int __exit snd_seq_info_done(void) +{ + if (queues_entry) + snd_info_unregister(queues_entry); + if (clients_entry) + snd_info_unregister(clients_entry); + if (timer_entry) + snd_info_unregister(timer_entry); + return 0; +} diff -Nru linux/sound/core/seq/seq_info.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.h --- linux/sound/core/seq/seq_info.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_info.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,36 @@ +/* + * ALSA sequencer /proc info + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_INFO_H +#define __SND_SEQ_INFO_H + +#include +#include + +void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); +void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); +void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); + + +int snd_seq_info_init( void ); +int snd_seq_info_done( void ); + + +#endif diff -Nru linux/sound/core/seq/seq_instr.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_instr.c --- linux/sound/core/seq/seq_instr.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_instr.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,671 @@ +/* + * Generic Instrument routines for ALSA sequencer + * Copyright (c) 1999 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "seq_clientmgr.h" +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + + +static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list) +{ + if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { + spin_lock_irqsave(&list->ops_lock, list->ops_flags); + } else { + down(&list->ops_mutex); + } +} + +static void snd_instr_unlock_ops(snd_seq_kinstr_list_t *list) +{ + if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { + spin_unlock_irqrestore(&list->ops_lock, list->ops_flags); + } else { + up(&list->ops_mutex); + } +} + +snd_seq_kcluster_t *snd_seq_cluster_new(int atomic) +{ + snd_seq_kcluster_t *cluster; + + cluster = (snd_seq_kcluster_t *) snd_kcalloc(sizeof(snd_seq_kcluster_t), atomic ? GFP_ATOMIC : GFP_KERNEL); + return cluster; +} + +void snd_seq_cluster_free(snd_seq_kcluster_t *cluster, int atomic) +{ + if (cluster == NULL) + return; + kfree(cluster); +} + +snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic) +{ + snd_seq_kinstr_t *instr; + + instr = (snd_seq_kinstr_t *) snd_kcalloc(sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); + if (instr == NULL) + return NULL; + instr->add_len = add_len; + return instr; +} + +int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic) +{ + int result = 0; + + if (instr == NULL) + return -EINVAL; + if (instr->ops && instr->ops->remove) + result = instr->ops->remove(instr->ops->private_data, instr, 1); + if (!result) + kfree(instr); + return result; +} + +snd_seq_kinstr_list_t *snd_seq_instr_list_new(void) +{ + snd_seq_kinstr_list_t *list; + + list = (snd_seq_kinstr_list_t *) snd_kcalloc(sizeof(snd_seq_kinstr_list_t), GFP_KERNEL); + if (list == NULL) + return NULL; + spin_lock_init(&list->lock); + spin_lock_init(&list->ops_lock); + init_MUTEX(&list->ops_mutex); + list->owner = -1; + return list; +} + +void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr) +{ + snd_seq_kinstr_list_t *list; + snd_seq_kinstr_t *instr; + snd_seq_kcluster_t *cluster; + int idx; + unsigned long flags; + + if (list_ptr == NULL) + return; + list = *list_ptr; + *list_ptr = NULL; + if (list == NULL) + return; + + for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { + while ((instr = list->hash[idx]) != NULL) { + list->hash[idx] = instr->next; + list->count--; + spin_lock_irqsave(&list->lock, flags); + while (instr->use) { + spin_unlock_irqrestore(&list->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&list->lock, flags); + } + spin_unlock_irqrestore(&list->lock, flags); + if (snd_seq_instr_free(instr, 0)<0) + snd_printk(KERN_WARNING "instrument free problem\n"); + } + while ((cluster = list->chash[idx]) != NULL) { + list->chash[idx] = cluster->next; + list->ccount--; + snd_seq_cluster_free(cluster, 0); + } + } + kfree(list); +} + +static int instr_free_compare(snd_seq_kinstr_t *instr, + snd_seq_instr_header_t *ifree, + int client) +{ + switch (ifree->cmd) { + case SNDRV_SEQ_INSTR_FREE_CMD_ALL: + /* all, except private for other clients */ + if ((instr->instr.std & 0xff000000) == 0) + return 0; + if (((instr->instr.std >> 24) & 0xff) == client) + return 0; + return 1; + case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE: + /* all my private instruments */ + if ((instr->instr.std & 0xff000000) == 0) + return 1; + if (((instr->instr.std >> 24) & 0xff) == client) + return 0; + return 1; + case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER: + /* all my private instruments */ + if ((instr->instr.std & 0xff000000) == 0) { + if (instr->instr.cluster == ifree->id.cluster) + return 0; + return 1; + } + if (((instr->instr.std >> 24) & 0xff) == client) { + if (instr->instr.cluster == ifree->id.cluster) + return 0; + } + return 1; + } + return 1; +} + +int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list, + snd_seq_instr_header_t *ifree, + int client, + int atomic) +{ + snd_seq_kinstr_t *instr, *prev, *next, *flist; + int idx; + unsigned long flags; + + snd_instr_lock_ops(list); + for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { + spin_lock_irqsave(&list->lock, flags); + instr = list->hash[idx]; + prev = flist = NULL; + while (instr) { + while (instr && instr_free_compare(instr, ifree, client)) { + prev = instr; + instr = instr->next; + } + if (instr == NULL) + continue; + if (instr->ops && instr->ops->notify) + instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); + next = instr->next; + if (prev == NULL) { + list->hash[idx] = next; + } else { + prev->next = next; + } + list->count--; + instr->next = flist; + flist = instr; + instr = next; + } + spin_unlock_irqrestore(&list->lock, flags); + while (flist) { + instr = flist; + flist = instr->next; + while (instr->use) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + if (snd_seq_instr_free(instr, atomic)<0) + snd_printk(KERN_WARNING "instrument free problem\n"); + instr = next; + } + } + snd_instr_unlock_ops(list); + return 0; +} + +static int compute_hash_instr_key(snd_seq_instr_t *instr) +{ + int result; + + result = instr->bank | (instr->prg << 16); + result += result >> 24; + result += result >> 16; + result += result >> 8; + return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); +} + +#if 0 +static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster) +{ + int result; + + result = cluster; + result += result >> 24; + result += result >> 16; + result += result >> 8; + return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); +} +#endif + +static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact) +{ + if (exact) { + if (i1->cluster != i2->cluster || + i1->bank != i2->bank || + i1->prg != i2->prg) + return 1; + if ((i1->std & 0xff000000) != (i2->std & 0xff000000)) + return 1; + if (!(i1->std & i2->std)) + return 1; + return 0; + } else { + unsigned int client_check; + + if (i2->cluster && i1->cluster != i2->cluster) + return 1; + client_check = i2->std & 0xff000000; + if (client_check) { + if ((i1->std & 0xff000000) != client_check) + return 1; + } else { + if ((i1->std & i2->std) != i2->std) + return 1; + } + return i1->bank != i2->bank || i1->prg != i2->prg; + } +} + +snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list, + snd_seq_instr_t *instr, + int exact, + int follow_alias) +{ + unsigned long flags; + int depth = 0; + snd_seq_kinstr_t *result; + + if (list == NULL || instr == NULL) + return NULL; + spin_lock_irqsave(&list->lock, flags); + __again: + result = list->hash[compute_hash_instr_key(instr)]; + while (result) { + if (!compare_instr(&result->instr, instr, exact)) { + if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) { + instr = (snd_seq_instr_t *)KINSTR_DATA(result); + if (++depth > 10) + goto __not_found; + goto __again; + } + result->use++; + spin_unlock_irqrestore(&list->lock, flags); + return result; + } + result = result->next; + } + __not_found: + spin_unlock_irqrestore(&list->lock, flags); + return NULL; +} + +void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list, + snd_seq_kinstr_t *instr) +{ + unsigned long flags; + + if (list == NULL || instr == NULL) + return; + spin_lock_irqsave(&list->lock, flags); + if (instr->use <= 0) { + snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name); + } else { + instr->use--; + } + spin_unlock_irqrestore(&list->lock, flags); +} + +static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type) +{ + while (ops) { + if (!strcmp(ops->instr_type, instr_type)) + return ops; + ops = ops->next; + } + return NULL; +} + +static int instr_result(snd_seq_event_t *ev, + int type, int result, + int atomic) +{ + snd_seq_event_t sev; + + memset(&sev, 0, sizeof(sev)); + sev.type = SNDRV_SEQ_EVENT_RESULT; + sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED | + SNDRV_SEQ_PRIORITY_NORMAL; + sev.source = ev->dest; + sev.dest = ev->source; + sev.data.result.event = type; + sev.data.result.result = result; +#if 0 + printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n", + type, result, + sev.queue, + sev.source.client, sev.source.port, + sev.dest.client, sev.dest.port); +#endif + return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0); +} + +static int instr_begin(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + + spin_lock_irqsave(&list->lock, flags); + if (list->owner >= 0 && list->owner != ev->source.client) { + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic); + } + list->owner = ev->source.client; + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic); +} + +static int instr_end(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + + /* TODO: timeout handling */ + spin_lock_irqsave(&list->lock, flags); + if (list->owner == ev->source.client) { + list->owner = -1; + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic); + } + spin_unlock_irqrestore(&list->lock, flags); + return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic); +} + +static int instr_info(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_format_info(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_reset(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_status(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_put(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + unsigned long flags; + snd_seq_instr_header_t put; + snd_seq_kinstr_t *instr; + int result = -EINVAL, len, key; + + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) + goto __return; + + if (ev->data.ext.len < sizeof(snd_seq_instr_header_t)) + goto __return; + if (copy_from_user(&put, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) { + result = -EFAULT; + goto __return; + } + snd_instr_lock_ops(list); + if (put.id.instr.std & 0xff000000) { /* private instrument */ + put.id.instr.std &= 0x00ffffff; + put.id.instr.std |= (unsigned int)ev->source.client << 24; + } + if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) { + snd_seq_instr_free_use(list, instr); + snd_instr_unlock_ops(list); + result = -EBUSY; + goto __return; + } + ops = instr_ops(ops, put.data.data.format); + if (ops == NULL) { + snd_instr_unlock_ops(list); + goto __return; + } + len = ops->add_len; + if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS) + len = sizeof(snd_seq_instr_t); + instr = snd_seq_instr_new(len, atomic); + if (instr == NULL) { + snd_instr_unlock_ops(list); + result = -ENOMEM; + goto __return; + } + instr->ops = ops; + instr->instr = put.id.instr; + strncpy(instr->name, put.data.name, sizeof(instr->name)-1); + instr->name[sizeof(instr->name)-1] = '\0'; + instr->type = put.data.type; + if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) { + result = ops->put(ops->private_data, + instr, + ev->data.ext.ptr + sizeof(snd_seq_instr_header_t), + ev->data.ext.len - sizeof(snd_seq_instr_header_t), + atomic, + put.cmd); + if (result < 0) { + snd_seq_instr_free(instr, atomic); + snd_instr_unlock_ops(list); + goto __return; + } + } + key = compute_hash_instr_key(&instr->instr); + spin_lock_irqsave(&list->lock, flags); + instr->next = list->hash[key]; + list->hash[key] = instr; + list->count++; + spin_unlock_irqrestore(&list->lock, flags); + snd_instr_unlock_ops(list); + result = 0; + __return: + instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic); + return result; +} + +static int instr_get(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_free(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + snd_seq_instr_header_t ifree; + snd_seq_kinstr_t *instr, *prev; + int result = -EINVAL; + unsigned long flags; + unsigned int hash; + + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) + goto __return; + + if (ev->data.ext.len < sizeof(snd_seq_instr_header_t)) + goto __return; + if (copy_from_user(&ifree, ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) { + result = -EFAULT; + goto __return; + } + if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL || + ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE || + ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) { + result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic); + goto __return; + } + if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) { + if (ifree.id.instr.std & 0xff000000) { + ifree.id.instr.std &= 0x00ffffff; + ifree.id.instr.std |= (unsigned int)ev->source.client << 24; + } + hash = compute_hash_instr_key(&ifree.id.instr); + snd_instr_lock_ops(list); + spin_lock_irqsave(&list->lock, flags); + instr = list->hash[hash]; + prev = NULL; + while (instr) { + if (!compare_instr(&instr->instr, &ifree.id.instr, 1)) + goto __free_single; + prev = instr; + instr = instr->next; + } + result = -ENOENT; + spin_unlock_irqrestore(&list->lock, flags); + snd_instr_unlock_ops(list); + goto __return; + + __free_single: + if (prev) { + prev->next = instr->next; + } else { + list->hash[hash] = instr->next; + } + if (instr->ops && instr->ops->notify) + instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); + while (instr->use) { + spin_unlock_irqrestore(&list->lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&list->lock, flags); + } + spin_unlock_irqrestore(&list->lock, flags); + result = snd_seq_instr_free(instr, atomic); + snd_instr_unlock_ops(list); + goto __return; + } + + __return: + instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic); + return result; +} + +static int instr_list(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +static int instr_cluster(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int atomic, int hop) +{ + return -ENXIO; +} + +int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops, + snd_seq_kinstr_list_t *list, + snd_seq_event_t *ev, + int client, + int atomic, + int hop) +{ + int direct = 0; + + snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL); + if (snd_seq_ev_is_direct(ev)) { + direct = 1; + switch (ev->type) { + case SNDRV_SEQ_EVENT_INSTR_BEGIN: + return instr_begin(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_END: + return instr_end(ops, list, ev, atomic, hop); + } + } + if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct) + return -EINVAL; + switch (ev->type) { + case SNDRV_SEQ_EVENT_INSTR_INFO: + return instr_info(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_FINFO: + return instr_format_info(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_RESET: + return instr_reset(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_STATUS: + return instr_status(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_PUT: + return instr_put(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_GET: + return instr_get(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_FREE: + return instr_free(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_LIST: + return instr_list(ops, list, ev, atomic, hop); + case SNDRV_SEQ_EVENT_INSTR_CLUSTER: + return instr_cluster(ops, list, ev, atomic, hop); + } + return -EINVAL; +} + +/* + * Init part + */ + +static int __init alsa_seq_instr_init(void) +{ + return 0; +} + +static void __exit alsa_seq_instr_exit(void) +{ +} + +module_init(alsa_seq_instr_init) +module_exit(alsa_seq_instr_exit) + +EXPORT_SYMBOL(snd_seq_instr_list_new); +EXPORT_SYMBOL(snd_seq_instr_list_free); +EXPORT_SYMBOL(snd_seq_instr_list_free_cond); +EXPORT_SYMBOL(snd_seq_instr_find); +EXPORT_SYMBOL(snd_seq_instr_free_use); +EXPORT_SYMBOL(snd_seq_instr_event); diff -Nru linux/sound/core/seq/seq_lock.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.c --- linux/sound/core/seq/seq_lock.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,85 @@ +/* + * Do sleep inside a spin-lock + * Copyright (c) 1999 by Takashi Iwai + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_lock.h" + +#if defined(__SMP__) || defined(CONFIG_SND_DEBUG) + +/* (interruptible) sleep_on during the specified spinlock */ +void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock) +{ + wait_queue_t wait; + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + + add_wait_queue(p, &wait); + + spin_unlock(lock); + schedule(); + spin_lock_irq(lock); + + remove_wait_queue(p, &wait); +} + +/* (interruptible) sleep_on with timeout during the specified spinlock */ +long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout) +{ + wait_queue_t wait; + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + + add_wait_queue(p, &wait); + + spin_unlock(lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(lock); + + remove_wait_queue(p, &wait); + + return timeout; +} + +/* wait until all locks are released */ +void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) +{ + int max_count = 5 * HZ; + + if (atomic_read(lockp) < 0) { + printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); + return; + } + while (atomic_read(lockp) > 0) { + if (max_count == 0) { + snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + max_count--; + } +} + +#endif diff -Nru linux/sound/core/seq/seq_lock.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.h --- linux/sound/core/seq/seq_lock.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_lock.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,42 @@ +#ifndef __SND_SEQ_LOCK_H +#define __SND_SEQ_LOCK_H + +#include + +#if defined(__SMP__) || defined(CONFIG_SND_DEBUG) + +typedef atomic_t snd_use_lock_t; + +/* initialize lock */ +#define snd_use_lock_init(lockp) atomic_set(lockp, 0) + +/* increment lock */ +#define snd_use_lock_use(lockp) atomic_inc(lockp) + +/* release lock */ +#define snd_use_lock_free(lockp) atomic_dec(lockp) + +/* wait until all locks are released */ +void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line); +#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__) + +/* (interruptible) sleep_on during the specified spinlock */ +void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock); + +/* (interruptible) sleep_on with timeout during the specified spinlock */ +long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout); + +#else /* SMP || CONFIG_SND_DEBUG */ + +typedef spinlock_t snd_use_lock_t; /* dummy */ +#define snd_use_lock_init(lockp) /**/ +#define snd_use_lock_use(lockp) /**/ +#define snd_use_lock_free(lockp) /**/ +#define snd_use_lock_sync(lockp) /**/ + +#define snd_seq_sleep_in_lock(p,lock) interruptible_sleep_on(p) +#define snd_seq_sleep_timeout_in_lock(p,lock,timeout) interruptible_sleep_on_timeout(p,timeout) + +#endif /* SMP || CONFIG_SND_DEBUG */ + +#endif /* __SND_SEQ_LOCK_H */ diff -Nru linux/sound/core/seq/seq_memory.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.c --- linux/sound/core/seq/seq_memory.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,514 @@ +/* + * ALSA sequencer Memory Manager + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#include +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_info.h" +#include "seq_lock.h" + +/* semaphore in struct file record */ +#define semaphore_of(fp) ((fp)->f_dentry->d_inode->i_sem) + + +inline static int snd_seq_pool_available(pool_t *pool) +{ + return pool->total_elements - atomic_read(&pool->counter); +} + +inline static int snd_seq_output_ok(pool_t *pool) +{ + return snd_seq_pool_available(pool) >= pool->room; +} + +/* + * Variable length event: + * The event like sysex uses variable length type. + * The external data may be stored in three different formats. + * 1) kernel space + * This is the normal case. + * ext.data.len = length + * ext.data.ptr = buffer pointer + * 2) user space + * When an event is generated via read(), the external data is + * kept in user space until expanded. + * ext.data.len = length | SNDRV_SEQ_EXT_USRPTR + * ext.data.ptr = userspace pointer + * 3) chained cells + * When the variable length event is enqueued (in prioq or fifo), + * the external data is decomposed to several cells. + * ext.data.len = length | SNDRV_SEQ_EXT_CHAINED + * ext.data.ptr = the additiona cell head + * -> cell.next -> cell.next -> .. + */ + +/* + * exported: + * call dump function to expand external data. + */ + +static int get_var_len(const snd_seq_event_t *event) +{ + if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + return -EINVAL; + + return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; +} + +int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data) +{ + int len, err; + snd_seq_event_cell_t *cell; + + if ((len = get_var_len(event)) <= 0) + return len; + + if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { + char buf[32]; + char *curptr = event->data.ext.ptr; + while (len > 0) { + int size = sizeof(buf); + if (len < size) + size = len; + if (copy_from_user(buf, curptr, size) < 0) + return -EFAULT; + err = func(private_data, buf, size); + if (err < 0) + return err; + curptr += size; + len -= size; + } + return 0; + } if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) { + return func(private_data, event->data.ext.ptr, len); + } + + cell = (snd_seq_event_cell_t*)event->data.ext.ptr; + for (; len > 0 && cell; cell = cell->next) { + int size = sizeof(snd_seq_event_t); + if (len < size) + size = len; + err = func(private_data, &cell->event, size); + if (err < 0) + return err; + len -= size; + } + return 0; +} + + +/* + * exported: + * expand the variable length event to linear buffer space. + */ + +static int copy_in_kernel(char **bufptr, const void *src, int size) +{ + memcpy(*bufptr, src, size); + *bufptr += size; + return 0; +} + +static int copy_in_user(char **bufptr, const void *src, int size) +{ + if (copy_to_user(*bufptr, src, size)) + return -EFAULT; + *bufptr += size; + return 0; +} + +int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned) +{ + int len, newlen; + int err; + + if ((len = get_var_len(event)) < 0) + return len; + newlen = len; + if (size_aligned > 0) + newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned; + if (count < newlen) + return -EAGAIN; + + if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { + if (! in_kernel) + return -EINVAL; + if (copy_from_user(buf, event->data.ext.ptr, len) < 0) + return -EFAULT; + return newlen; + } + err = snd_seq_dump_var_event(event, + in_kernel ? (snd_seq_dump_func_t)copy_in_kernel : + (snd_seq_dump_func_t)copy_in_user, + &buf); + return err < 0 ? err : newlen; +} + + +/* + * release this cell, free extended data if available + */ + +static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell) +{ + cell->next = pool->free; + pool->free = cell; + atomic_dec(&pool->counter); +} + +void snd_seq_cell_free(snd_seq_event_cell_t * cell) +{ + unsigned long flags; + pool_t *pool; + + snd_assert(cell != NULL, return); + pool = cell->pool; + snd_assert(pool != NULL, return); + + spin_lock_irqsave(&pool->lock, flags); + free_cell(pool, cell); + if (snd_seq_ev_is_variable(&cell->event)) { + if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) { + snd_seq_event_cell_t *curp, *nextptr; + curp = cell->event.data.ext.ptr; + for (; curp; curp = nextptr) { + nextptr = curp->next; + curp->next = pool->free; + free_cell(pool, curp); + } + } + } + if (waitqueue_active(&pool->output_sleep)) { + /* has enough space now? */ + if (snd_seq_output_ok(pool)) + wake_up(&pool->output_sleep); + } + spin_unlock_irqrestore(&pool->lock, flags); +} + + +/* + * allocate an event cell. + */ +int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + int err = -EAGAIN; + + if (pool == NULL) + return -EINVAL; + + *cellp = NULL; + + spin_lock_irqsave(&pool->lock, flags); + if (pool->ptr == NULL) { /* not initialized */ + snd_printd("seq: pool is not initialized\n"); + err = -EINVAL; + goto __error; + } + while (pool->free == NULL && ! nonblock && ! pool->closing) { + /* change semaphore to allow other clients + to access device file */ + if (file) + up(&semaphore_of(file)); + + snd_seq_sleep_in_lock(&pool->output_sleep, &pool->lock); + + /* restore semaphore again */ + if (file) + down(&semaphore_of(file)); + + /* interrupted? */ + if (signal_pending(current)) { + err = -ERESTARTSYS; + goto __error; + } + } + if (pool->closing) { /* closing.. */ + err = -ENOMEM; + goto __error; + } + + cell = pool->free; + if (cell) { + int used; + pool->free = cell->next; + atomic_inc(&pool->counter); + used = atomic_read(&pool->counter); + if (pool->max_used < used) + pool->max_used = used; + pool->event_alloc_success++; + /* clear cell pointers */ + cell->next = NULL; + err = 0; + } else + pool->event_alloc_failures++; + *cellp = cell; + +__error: + spin_unlock_irqrestore(&pool->lock, flags); + return err; +} + + +/* + * duplicate the event to a cell. + * if the event has external data, the data is decomposed to additional + * cells. + */ +int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) +{ + int ncells, err; + unsigned int extlen; + snd_seq_event_cell_t *cell; + + *cellp = NULL; + + ncells = 0; + extlen = 0; + if (snd_seq_ev_is_variable(event)) { + extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; + ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); + } + if (ncells >= pool->total_elements) + return -ENOMEM; + + err = snd_seq_cell_alloc(pool, &cell, nonblock, file); + if (err < 0) + return err; + + /* copy the event */ + cell->event = *event; + + /* decompose */ + if (snd_seq_ev_is_variable(event)) { + int len = extlen; + int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED; + int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR; + snd_seq_event_cell_t *src, *tmp, *tail; + char *buf; + + cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED; + cell->event.data.ext.ptr = NULL; + + src = (snd_seq_event_cell_t*)event->data.ext.ptr; + buf = (char *)event->data.ext.ptr; + tail = NULL; + + while (ncells-- > 0) { + int size = sizeof(snd_seq_event_t); + if (len < size) + size = len; + err = snd_seq_cell_alloc(pool, &tmp, nonblock, file); + if (err < 0) + goto __error; + if (cell->event.data.ext.ptr == NULL) + cell->event.data.ext.ptr = tmp; + if (tail) + tail->next = tmp; + tail = tmp; + /* copy chunk */ + if (is_chained && src) { + tmp->event = src->event; + src = src->next; + } else if (is_usrptr) { + if (copy_from_user(&tmp->event, buf, size)) { + err = -EFAULT; + goto __error; + } + } else { + memcpy(&tmp->event, buf, size); + } + buf += size; + len -= size; + } + } + + *cellp = cell; + return 0; + +__error: + snd_seq_cell_free(cell); + return err; +} + + +/* poll wait */ +int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait) +{ + poll_wait(file, &pool->output_sleep, wait); + return snd_seq_output_ok(pool); +} + + +/* allocate room specified number of events */ +int snd_seq_pool_init(pool_t *pool) +{ + int cell; + snd_seq_event_cell_t *cellptr; + unsigned long flags; + + snd_assert(pool != NULL, return -EINVAL); + if (pool->ptr) /* should be atomic? */ + return 0; + + pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size); + if (pool->ptr == NULL) { + snd_printd("seq: malloc for sequencer events failed\n"); + return -ENOMEM; + } + + /* add new cells to the free cell list */ + spin_lock_irqsave(&pool->lock, flags); + pool->free = NULL; + + for (cell = 0; cell < pool->size; cell++) { + cellptr = pool->ptr + cell; + cellptr->pool = pool; + cellptr->next = pool->free; + pool->free = cellptr; + } + pool->room = (pool->size + 1) / 2; + + /* init statistics */ + pool->max_used = 0; + pool->total_elements = pool->size; + spin_unlock_irqrestore(&pool->lock, flags); + return 0; +} + +/* remove events */ +int snd_seq_pool_done(pool_t *pool) +{ + unsigned long flags; + snd_seq_event_cell_t *ptr; + int max_count = 5 * HZ; + + snd_assert(pool != NULL, return -EINVAL); + + /* wait for closing all threads */ + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 1; + spin_unlock_irqrestore(&pool->lock, flags); + + if (waitqueue_active(&pool->output_sleep)) + wake_up(&pool->output_sleep); + + while (atomic_read(&pool->counter) > 0) { + if (max_count == 0) { + snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + max_count--; + } + + /* release all resources */ + spin_lock_irqsave(&pool->lock, flags); + ptr = pool->ptr; + pool->ptr = NULL; + pool->free = NULL; + pool->total_elements = 0; + spin_unlock_irqrestore(&pool->lock, flags); + + if (ptr) + vfree(ptr); + + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 0; + spin_unlock_irqrestore(&pool->lock, flags); + + return 0; +} + + +/* init new memory pool */ +pool_t *snd_seq_pool_new(int poolsize) +{ + pool_t *pool; + + /* create pool block */ + pool = snd_kcalloc(sizeof(pool_t), GFP_KERNEL); + if (pool == NULL) { + snd_printd("seq: malloc failed for pool\n"); + return NULL; + } + spin_lock_init(&pool->lock); + pool->ptr = NULL; + pool->free = NULL; + pool->total_elements = 0; + atomic_set(&pool->counter, 0); + pool->closing = 0; + init_waitqueue_head(&pool->output_sleep); + + pool->size = poolsize; + + /* init statistics */ + pool->max_used = 0; + return pool; +} + +/* remove memory pool */ +int snd_seq_pool_delete(pool_t **ppool) +{ + pool_t *pool = *ppool; + + *ppool = NULL; + if (pool == NULL) + return 0; + snd_seq_pool_done(pool); + kfree(pool); + return 0; +} + +/* initialize sequencer memory */ +int __init snd_sequencer_memory_init(void) +{ + return 0; +} + +/* release sequencer memory */ +void __exit snd_sequencer_memory_done(void) +{ +} + + +/* exported to seq_clientmgr.c */ +void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space) +{ + if (pool == NULL) + return; + snd_iprintf(buffer, "%sPool size : %d\n", space, pool->total_elements); + snd_iprintf(buffer, "%sCells in use : %d\n", space, atomic_read(&pool->counter)); + snd_iprintf(buffer, "%sPeak cells in use : %d\n", space, pool->max_used); + snd_iprintf(buffer, "%sAlloc success : %d\n", space, pool->event_alloc_success); + snd_iprintf(buffer, "%sAlloc failures : %d\n", space, pool->event_alloc_failures); +} diff -Nru linux/sound/core/seq/seq_memory.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.h --- linux/sound/core/seq/seq_memory.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_memory.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,105 @@ +/* + * ALSA sequencer Memory Manager + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_MEMORYMGR_H +#define __SND_SEQ_MEMORYMGR_H + +#include +#include + +typedef struct pool pool_t; + +/* container for sequencer event (internal use) */ +typedef struct snd_seq_event_cell_t { + snd_seq_event_t event; + pool_t *pool; /* used pool */ + struct snd_seq_event_cell_t *next; /* next cell */ +} snd_seq_event_cell_t; + +/* design note: the pool is a contigious block of memory, if we dynamicly + want to add additional cells to the pool be better store this in another + pool as we need to know the base address of the pool when releasing + memory. */ + +struct pool { + snd_seq_event_cell_t *ptr; /* pointer to first event chunk */ + snd_seq_event_cell_t *free; /* pointer to the head of the free list */ + + int total_elements; /* pool size actually allocated */ + atomic_t counter; /* cells free */ + + int size; /* pool size to be allocated */ + int room; /* watermark for sleep/wakeup */ + + int closing; + + /* statistics */ + int max_used; + int event_alloc_nopool; + int event_alloc_failures; + int event_alloc_success; + + /* Write locking */ + wait_queue_head_t output_sleep; + + /* Pool lock */ + spinlock_t lock; +}; + +extern void snd_seq_cell_free(snd_seq_event_cell_t* cell); +int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file); + +int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file); + +/* return number of unused (free) cells */ +static inline int snd_seq_unused_cells(pool_t *pool) +{ + return pool ? pool->total_elements - atomic_read(&pool->counter) : 0; +} + +/* return total number of allocated cells */ +static inline int snd_seq_total_cells(pool_t *pool) +{ + return pool ? pool->total_elements : 0; +} + +/* init pool - allocate events */ +int snd_seq_pool_init(pool_t *pool); + +/* done pool - free events */ +int snd_seq_pool_done(pool_t *pool); + +/* create pool */ +pool_t *snd_seq_pool_new(int poolsize); + +/* remove pool */ +int snd_seq_pool_delete(pool_t **pool); + +/* init memory */ +int snd_sequencer_memory_init(void); + +/* release event memory */ +void snd_sequencer_memory_done(void); + +/* polling */ +int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait); + + +#endif diff -Nru linux/sound/core/seq/seq_midi.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi.c --- linux/sound/core/seq/seq_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,461 @@ +/* + * Generic MIDI synth driver for ALSA sequencer + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* +Possible options for midisynth module: + - automatic opening of midi ports on first received event or subscription + (close will be performed when client leaves) +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +int output_buffer_size = PAGE_SIZE; +MODULE_PARM(output_buffer_size, "i"); +MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes."); +int input_buffer_size = PAGE_SIZE; +MODULE_PARM(input_buffer_size, "i"); +MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes."); + +/* data for this midi synth driver */ +typedef struct { + snd_card_t *card; + int device; + int subdevice; + snd_rawmidi_file_t input_rfile; + snd_rawmidi_file_t output_rfile; + int seq_client; + int seq_port; + snd_midi_event_t *parser; +} seq_midisynth_t; + +typedef struct { + int seq_client; + int num_ports; + int ports_per_device[SNDRV_RAWMIDI_DEVICES]; + seq_midisynth_t *ports[SNDRV_RAWMIDI_DEVICES]; +} seq_midisynth_client_t; + +static seq_midisynth_client_t *synths[SNDRV_CARDS]; +static DECLARE_MUTEX(register_mutex); + +/* handle rawmidi input event (MIDI v1.0 stream) */ +static void snd_midi_input_event(snd_rawmidi_substream_t * substream) +{ + snd_rawmidi_runtime_t *runtime; + seq_midisynth_t *msynth; + snd_seq_event_t ev; + char buf[16], *pbuf; + long res, count; + + if (substream == NULL) + return; + runtime = substream->runtime; + msynth = (seq_midisynth_t *) runtime->private_data; + if (msynth == NULL) + return; + memset(&ev, 0, sizeof(ev)); + while (runtime->avail > 0) { + res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); + if (res <= 0) + continue; + if (msynth->parser == NULL) + continue; + pbuf = buf; + while (res > 0) { + count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev); + if (count < 0) + break; + pbuf += count; + res -= count; + if (ev.type != SNDRV_SEQ_EVENT_NONE) { + ev.source.port = msynth->seq_port; + ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0); + /* clear event and reset header */ + memset(&ev, 0, sizeof(ev)); + } + } + } +} + +static int dump_midi(snd_rawmidi_substream_t *substream, const char *buf, int count) +{ + snd_rawmidi_runtime_t *runtime; + int tmp; + + snd_assert(substream != NULL || buf != NULL, return -EINVAL); + runtime = substream->runtime; + if ((tmp = runtime->avail) < count) { + snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp); + return -ENOMEM; + } + if (snd_rawmidi_kernel_write(substream, buf, count) < count) + return -EINVAL; + return 0; +} + +static int event_process_midi(snd_seq_event_t * ev, int direct, + void *private_data, int atomic, int hop) +{ + seq_midisynth_t *msynth = (seq_midisynth_t *) private_data; + unsigned char msg[10]; /* buffer for constructing midi messages */ + snd_rawmidi_substream_t *substream; + int res; + + snd_assert(msynth != NULL, return -EINVAL); + substream = msynth->output_rfile.output; + if (substream == NULL) + return -ENODEV; + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */ + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { + /* invalid event */ + snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); + return 0; + } + res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); + snd_midi_event_reset_decode(msynth->parser); + if (res < 0) + return res; + } else { + if (msynth->parser == NULL) + return -EIO; + res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); + if (res < 0) + return res; + if ((res = dump_midi(substream, msg, res)) < 0) { + snd_midi_event_reset_decode(msynth->parser); + return res; + } + } + return 0; +} + + +static int snd_seq_midisynth_new(seq_midisynth_t *msynth, + snd_card_t *card, + int device, + int subdevice) +{ + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0) + return -ENOMEM; + msynth->card = card; + msynth->device = device; + msynth->subdevice = subdevice; + return 0; +} + +/* open associated midi device for input */ +static int midisynth_subscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + snd_rawmidi_runtime_t *runtime; + snd_rawmidi_params_t params; + + /* open midi port */ + if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) { + snd_printd("midi input open failed!!!\n"); + return err; + } + runtime = msynth->input_rfile.input->runtime; + memset(¶ms, 0, sizeof(params)); + params.avail_min = 1; + params.buffer_size = input_buffer_size; + if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms)) < 0) { + snd_rawmidi_kernel_release(&msynth->input_rfile); + return err; + } + runtime->event = snd_midi_input_event; + runtime->private_data = msynth; + snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); + return 0; +} + +/* close associated midi device for input */ +static int midisynth_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + + snd_assert(msynth->input_rfile.input != NULL, return -EINVAL); + err = snd_rawmidi_kernel_release(&msynth->input_rfile); + return err; +} + +/* open associated midi device for output */ +static int midisynth_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + int err; + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + snd_rawmidi_params_t params; + + /* open midi port */ + if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) { + snd_printd("midi output open failed!!!\n"); + return err; + } + memset(¶ms, 0, sizeof(params)); + params.avail_min = 1; + params.buffer_size = output_buffer_size; + if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) { + snd_rawmidi_kernel_release(&msynth->output_rfile); + return err; + } + return 0; +} + +/* close associated midi device for output */ +static int midisynth_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + seq_midisynth_t *msynth = (seq_midisynth_t *)private_data; + unsigned char buf = 0xff; /* MIDI reset */ + + snd_assert(msynth->output_rfile.output != NULL, return -EINVAL); + /* sending single MIDI reset message to shut the device up */ + snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1); + snd_rawmidi_drain_output(msynth->output_rfile.output); + return snd_rawmidi_kernel_release(&msynth->output_rfile); +} + +/* delete given midi synth port */ +static void snd_seq_midisynth_delete(seq_midisynth_t *msynth) +{ + snd_seq_port_info_t port; + + if (msynth == NULL) + return; + + if (msynth->seq_client > 0) { + /* delete port */ + memset(&port, 0, sizeof(port)); + port.addr.client = msynth->seq_client; + port.addr.port = msynth->seq_port; + snd_seq_kernel_client_ctl(port.addr.client, SNDRV_SEQ_IOCTL_DELETE_PORT, &port); + } + + if (msynth->parser) + snd_midi_event_free(msynth->parser); +} + +/* register new midi synth port */ +int +snd_seq_midisynth_register_port(snd_seq_device_t *dev) +{ + seq_midisynth_client_t *client; + seq_midisynth_t *msynth, *ms; + snd_seq_port_info_t port; + snd_rawmidi_info_t info; + int newclient = 0, p, ports; + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t inf; + snd_card_t *card = dev->card; + int device = dev->device; + unsigned int input_count = 0, output_count = 0; + + snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL); + info.device = device; + info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + info.subdevice = 0; + if (snd_rawmidi_info_select(card, &info) >= 0) + output_count = info.subdevices_count; + info.stream = SNDRV_RAWMIDI_STREAM_INPUT; + if (snd_rawmidi_info_select(card, &info) >= 0) { + input_count = info.subdevices_count; + } + ports = output_count; + if (ports < input_count) + ports = input_count; + if (ports == 0) + return -ENODEV; + if (ports > (256 / SNDRV_RAWMIDI_DEVICES)) + ports = 256 / SNDRV_RAWMIDI_DEVICES; + + down(®ister_mutex); + client = synths[card->number]; + if (client == NULL) { + newclient = 1; + client = snd_kcalloc(sizeof(seq_midisynth_client_t), GFP_KERNEL); + if (client == NULL) { + up(®ister_mutex); + return -ENOMEM; + } + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = client; + callbacks.allow_input = callbacks.allow_output = 1; + client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks); + if (client->seq_client < 0) { + kfree(client); + up(®ister_mutex); + return -ENOMEM; + } + /* set our client name */ + memset(&inf,0,sizeof(snd_seq_client_info_t)); + inf.client = client->seq_client; + inf.type = KERNEL_CLIENT; + sprintf(inf.name, "External MIDI %i", card->number); + snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf); + } + + msynth = snd_kcalloc(sizeof(seq_midisynth_t) * ports, GFP_KERNEL); + if (msynth == NULL) + goto __nomem; + + for (p = 0; p < ports; p++) { + ms = &msynth[p]; + + if (snd_seq_midisynth_new(ms, card, device, p) < 0) + goto __nomem; + + /* declare port */ + memset(&port, 0, sizeof(port)); + port.addr.client = client->seq_client; + port.addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + memset(&info, 0, sizeof(info)); + info.device = device; + if (p < output_count) + info.stream = SNDRV_RAWMIDI_STREAM_OUTPUT; + else + info.stream = SNDRV_RAWMIDI_STREAM_INPUT; + info.subdevice = p; + if (snd_rawmidi_info_select(card, &info) >= 0) + strcpy(port.name, info.subname); + if (! port.name[0]) { + if (ports > 1) + sprintf(port.name, "MIDI %d-%d-%d", card->number, device, p); + else + sprintf(port.name, "MIDI %d-%d", card->number, device); + } + if ((info.flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count) + port.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + if ((info.flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count) + port.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + if ((port.capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) && + info.flags & SNDRV_RAWMIDI_INFO_DUPLEX) + port.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + port.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + port.midi_channels = 16; + memset(&pcallbacks, 0, sizeof(pcallbacks)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.private_data = ms; + pcallbacks.subscribe = midisynth_subscribe; + pcallbacks.unsubscribe = midisynth_unsubscribe; + pcallbacks.use = midisynth_use; + pcallbacks.unuse = midisynth_unuse; + pcallbacks.event_input = event_process_midi; + port.kernel = &pcallbacks; + if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &port)<0) + goto __nomem; + ms->seq_client = client->seq_client; + ms->seq_port = port.addr.port; + } + client->ports_per_device[device] = ports; + client->ports[device] = msynth; + client->num_ports++; + if (newclient) + synths[card->number] = client; + up(®ister_mutex); + return 0; /* success */ + + __nomem: + if (msynth != NULL) { + for (p = 0; p < ports; p++) + snd_seq_midisynth_delete(&msynth[p]); + kfree(msynth); + } + if (newclient) { + snd_seq_delete_kernel_client(client->seq_client); + kfree(client); + } + up(®ister_mutex); + return -ENOMEM; +} + +/* release midi synth port */ +int +snd_seq_midisynth_unregister_port(snd_seq_device_t *dev) +{ + seq_midisynth_client_t *client; + seq_midisynth_t *msynth; + snd_card_t *card = dev->card; + int device = dev->device, p, ports; + + down(®ister_mutex); + client = synths[card->number]; + if (client == NULL || client->ports[device] == NULL) { + up(®ister_mutex); + return -ENODEV; + } + snd_seq_event_port_detach(client->seq_client, client->ports[device]->seq_port); + ports = client->ports_per_device[device]; + client->ports_per_device[device] = 0; + msynth = client->ports[device]; + client->ports[device] = NULL; + snd_runtime_check(msynth != NULL || ports <= 0, goto __skip); + for (p = 0; p < ports; p++) + snd_seq_midisynth_delete(&msynth[p]); + kfree(msynth); + __skip: + client->num_ports--; + if (client->num_ports <= 0) { + snd_seq_delete_kernel_client(client->seq_client); + synths[card->number] = NULL; + kfree(client); + } + up(®ister_mutex); + return 0; +} + + +static int __init alsa_seq_midi_init(void) +{ + static snd_seq_dev_ops_t ops = { + snd_seq_midisynth_register_port, + snd_seq_midisynth_unregister_port, + }; + memset(&synths, 0, sizeof(synths)); + snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0); + return 0; +} + +static void __exit alsa_seq_midi_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); +} + +module_init(alsa_seq_midi_init) +module_exit(alsa_seq_midi_exit) diff -Nru linux/sound/core/seq/seq_midi_clock.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_clock.c --- linux/sound/core/seq/seq_midi_clock.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_clock.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,91 @@ +/* + * MIDI clock event converter + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_queue.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +typedef struct midi_clock { + unsigned int cur_pos; +} midi_clock_t; + +static int midi_open(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + midi_clock_t *arg; + + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + sync_info->param.tick.ppq = 24; + sync_info->param.tick.ticks = 1; + arg->cur_pos = 0; + *retp = arg; + return 0; +} + +static int midi_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + switch (src->type) { + case SNDRV_SEQ_EVENT_SYNC: + ev->type = SNDRV_SEQ_EVENT_CLOCK; + return 1; + case SNDRV_SEQ_EVENT_SYNC_POS: + ev->type = SNDRV_SEQ_EVENT_SONGPOS; + ev->data.control.value = src->data.queue.param.position / 6; + return 1; + } + return 0; +} + +static int midi_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + midi_clock_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_CLOCK: + ev->type = SNDRV_SEQ_EVENT_SYNC; + ev->data.queue.param.position = arg->cur_pos; + arg->cur_pos++; + return 1; + case SNDRV_SEQ_EVENT_SONGPOS: + ev->type = SNDRV_SEQ_EVENT_SYNC_POS; + arg->cur_pos = src->data.control.value * 6; + ev->data.queue.param.position = arg->cur_pos; + return 1; + } + return 0; +} + +/* exported */ +seq_sync_parser_t snd_seq_midi_clock_parser = { + format: SNDRV_SEQ_SYNC_FMT_MIDI_CLOCK, + in: { + open: midi_open, + sync: midi_sync_in, + }, + out: { + open: midi_open, + sync: midi_sync_out, + }, +}; + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru linux/sound/core/seq/seq_midi_emul.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_emul.c --- linux/sound/core/seq/seq_midi_emul.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_emul.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,739 @@ +/* + * GM/GS/XG midi module. + * + * Copyright (C) 1999 Steve Ratcliffe + * + * Based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * This module is used to keep track of the current midi state. + * It can be used for drivers that are required to emulate midi when + * the hardware doesn't. + * + * It was written for a AWE64 driver, but there should be no AWE specific + * code in here. If there is it should be reported as a bug. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe"); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); + +/* Prototypes for static functions */ +static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel); +static void do_control(snd_midi_op_t *ops, void *private, + snd_midi_channel_set_t *chset, snd_midi_channel_t *chan, + int control, int value); +static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset); +static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); +static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan); +void snd_midi_reset_controllers(snd_midi_channel_t *chan); +static void reset_all_channels(snd_midi_channel_set_t *chset); + + +/* + * Process an event in a driver independant way. This means dealing + * with RPN, NRPN, SysEx etc that are defined for common midi applications + * such as GM, GS and XG. + * There modes that this module will run in are: + * Generic MIDI - no interpretation at all, it will just save current values + * of controlers etc. + * GM - You can use all gm_ prefixed elements of chan. Controls, RPN, NRPN, + * SysEx will be interpreded as defined in General Midi. + * GS - You can use all gs_ prefixed elements of chan. Codes for GS will be + * interpreted. + * XG - You can use all xg_ prefixed elements of chan. Codes for XG will + * be interpreted. + */ +void +snd_midi_process_event(snd_midi_op_t *ops, + snd_seq_event_t *ev, snd_midi_channel_set_t *chanset) +{ + snd_midi_channel_t *chan; + void *drv; + int dest_channel = 0; + + if (ev == NULL || chanset == NULL) { + snd_printd("ev or chanbase NULL (snd_midi_process_event)\n"); + return; + } + if (chanset->channels == NULL) + return; + + if (snd_seq_ev_is_channel_type(ev)) { + dest_channel = ev->data.note.channel; + if (dest_channel >= chanset->max_channels) { + snd_printd("dest channel is %d, max is %d\n", dest_channel, chanset->max_channels); + return; + } + } + + chan = chanset->channels + dest_channel; + drv = chanset->private_data; + + /* EVENT_NOTE should be processed before queued */ + if (ev->type == SNDRV_SEQ_EVENT_NOTE) + return; + + /* Make sure that we don't have a note on that should really be + * a note off */ + if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0) + ev->type = SNDRV_SEQ_EVENT_NOTEOFF; + + /* Make sure the note is within array range */ + if (ev->type == SNDRV_SEQ_EVENT_NOTEON || + ev->type == SNDRV_SEQ_EVENT_NOTEOFF || + ev->type == SNDRV_SEQ_EVENT_KEYPRESS) { + if (ev->data.note.note >= 128) + return; + } + + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEON: + if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) { + if (ops->note_off) + ops->note_off(drv, ev->data.note.note, 0, chan); + } + chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON; + if (ops->note_on) + ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan); + break; + case SNDRV_SEQ_EVENT_NOTEOFF: + if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON)) + break; + if (ops->note_off) + note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity); + break; + case SNDRV_SEQ_EVENT_KEYPRESS: + if (ops->key_press) + ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan); + break; + case SNDRV_SEQ_EVENT_CONTROLLER: + do_control(ops, drv, chanset, chan, + ev->data.control.param, ev->data.control.value); + break; + case SNDRV_SEQ_EVENT_PGMCHANGE: + chan->midi_program = ev->data.control.value; + break; + case SNDRV_SEQ_EVENT_PITCHBEND: + chan->midi_pitchbend = ev->data.control.value; + if (ops->control) + ops->control(drv, MIDI_CTL_PITCHBEND, chan); + break; + case SNDRV_SEQ_EVENT_CHANPRESS: + chan->midi_pressure = ev->data.control.value; + if (ops->control) + ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan); + break; + case SNDRV_SEQ_EVENT_CONTROL14: + /* Best guess is that this is any of the 14 bit controller values */ + if (ev->data.control.param < 32) { + /* set low part first */ + chan->control[ev->data.control.param + 32] = + ev->data.control.value & 0x7f; + do_control(ops, drv, chanset, chan, + ev->data.control.param, + ((ev->data.control.value>>7) & 0x7f)); + } else + do_control(ops, drv, chanset, chan, + ev->data.control.param, + ev->data.control.value); + break; + case SNDRV_SEQ_EVENT_NONREGPARAM: + /* Break it back into its controler values */ + chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED; + chan->control[MIDI_CTL_MSB_DATA_ENTRY] + = (ev->data.control.value >> 7) & 0x7f; + chan->control[MIDI_CTL_LSB_DATA_ENTRY] + = ev->data.control.value & 0x7f; + chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] + = (ev->data.control.param >> 7) & 0x7f; + chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] + = ev->data.control.param & 0x7f; + nrpn(ops, drv, chan, chanset); + break; + case SNDRV_SEQ_EVENT_REGPARAM: + /* Break it back into its controler values */ + chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED; + chan->control[MIDI_CTL_MSB_DATA_ENTRY] + = (ev->data.control.value >> 7) & 0x7f; + chan->control[MIDI_CTL_LSB_DATA_ENTRY] + = ev->data.control.value & 0x7f; + chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] + = (ev->data.control.param >> 7) & 0x7f; + chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB] + = ev->data.control.param & 0x7f; + rpn(ops, drv, chan, chanset); + break; + case SNDRV_SEQ_EVENT_SYSEX: + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { + unsigned char sysexbuf[64]; + int len; + len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0); + if (len > 0) + sysex(ops, drv, sysexbuf, len, chanset); + } + break; + case SNDRV_SEQ_EVENT_SONGPOS: + case SNDRV_SEQ_EVENT_SONGSEL: + case SNDRV_SEQ_EVENT_CLOCK: + case SNDRV_SEQ_EVENT_START: + case SNDRV_SEQ_EVENT_CONTINUE: + case SNDRV_SEQ_EVENT_STOP: + case SNDRV_SEQ_EVENT_QFRAME: + case SNDRV_SEQ_EVENT_TEMPO: + case SNDRV_SEQ_EVENT_TIMESIGN: + case SNDRV_SEQ_EVENT_KEYSIGN: + goto not_yet; + case SNDRV_SEQ_EVENT_SENSING: + break; + case SNDRV_SEQ_EVENT_CLIENT_START: + case SNDRV_SEQ_EVENT_CLIENT_EXIT: + case SNDRV_SEQ_EVENT_CLIENT_CHANGE: + case SNDRV_SEQ_EVENT_PORT_START: + case SNDRV_SEQ_EVENT_PORT_EXIT: + case SNDRV_SEQ_EVENT_PORT_CHANGE: + case SNDRV_SEQ_EVENT_SAMPLE: + case SNDRV_SEQ_EVENT_SAMPLE_START: + case SNDRV_SEQ_EVENT_SAMPLE_STOP: + case SNDRV_SEQ_EVENT_SAMPLE_FREQ: + case SNDRV_SEQ_EVENT_SAMPLE_VOLUME: + case SNDRV_SEQ_EVENT_SAMPLE_LOOP: + case SNDRV_SEQ_EVENT_SAMPLE_POSITION: + case SNDRV_SEQ_EVENT_ECHO: + not_yet: + default: + /*snd_printd("Unimplemented event %d\n", ev->type);*/ + break; + } +} + + +/* + * release note + */ +static void +note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel) +{ + if (chan->gm_hold) { + /* Hold this note until pedal is turned off */ + chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED; + } else if (chan->note[note] & SNDRV_MIDI_NOTE_SUSTENUTO) { + /* Mark this note as release; it will be turned off when sustenuto + * is turned off */ + chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED; + } else { + chan->note[note] = 0; + if (ops->note_off) + ops->note_off(drv, note, vel, chan); + } +} + +/* + * Do all driver independant operations for this controler and pass + * events that need to take place immediately to the driver. + */ +static void +do_control(snd_midi_op_t *ops, void *drv, snd_midi_channel_set_t *chset, + snd_midi_channel_t *chan, int control, int value) +{ + int i; + + /* Switches */ + if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) { + /* These are all switches; either off or on so set to 0 or 127 */ + value = (value >= 64)? 127: 0; + } + chan->control[control] = value; + + switch (control) { + case MIDI_CTL_SUSTAIN: + if (value == 0) { + /* Sustain has been released, turn off held notes */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) { + chan->note[i] = SNDRV_MIDI_NOTE_OFF; + if (ops->note_off) + ops->note_off(drv, i, 0, chan); + } + } + } + break; + case MIDI_CTL_PORTAMENTO: + break; + case MIDI_CTL_SUSTENUTO: + if (value) { + /* Mark each note that is currently held down */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_ON) + chan->note[i] |= SNDRV_MIDI_NOTE_SUSTENUTO; + } + } else { + /* release all notes that were held */ + for (i = 0; i < 128; i++) { + if (chan->note[i] & SNDRV_MIDI_NOTE_SUSTENUTO) { + chan->note[i] &= ~SNDRV_MIDI_NOTE_SUSTENUTO; + if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) { + chan->note[i] = SNDRV_MIDI_NOTE_OFF; + if (ops->note_off) + ops->note_off(drv, i, 0, chan); + } + } + } + } + break; + case MIDI_CTL_MSB_DATA_ENTRY: + chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0; + /* go through here */ + case MIDI_CTL_LSB_DATA_ENTRY: + if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED) + rpn(ops, drv, chan, chset); + else + nrpn(ops, drv, chan, chset); + break; + case MIDI_CTL_REGIST_PARM_NUM_LSB: + case MIDI_CTL_REGIST_PARM_NUM_MSB: + chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED; + break; + case MIDI_CTL_NONREG_PARM_NUM_LSB: + case MIDI_CTL_NONREG_PARM_NUM_MSB: + chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED; + break; + + case MIDI_CTL_ALL_SOUNDS_OFF: + all_sounds_off(ops, drv, chan); + break; + + case MIDI_CTL_ALL_NOTES_OFF: + all_notes_off(ops, drv, chan); + break; + + case MIDI_CTL_MSB_BANK: + if (chset->midi_mode == SNDRV_MIDI_MODE_XG) { + if (value == 127) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } + break; + case MIDI_CTL_LSB_BANK: + break; + + case MIDI_CTL_RESET_CONTROLLERS: + snd_midi_reset_controllers(chan); + break; + + case MIDI_CTL_SOFT_PEDAL: + case MIDI_CTL_LEGATO_FOOTSWITCH: + case MIDI_CTL_HOLD2: + case MIDI_CTL_SC1_SOUND_VARIATION: + case MIDI_CTL_SC2_TIMBRE: + case MIDI_CTL_SC3_RELEASE_TIME: + case MIDI_CTL_SC4_ATTACK_TIME: + case MIDI_CTL_SC5_BRIGHTNESS: + case MIDI_CTL_E1_REVERB_DEPTH: + case MIDI_CTL_E2_TREMOLO_DEPTH: + case MIDI_CTL_E3_CHORUS_DEPTH: + case MIDI_CTL_E4_DETUNE_DEPTH: + case MIDI_CTL_E5_PHASER_DEPTH: + goto notyet; + notyet: + default: + if (ops->control) + ops->control(drv, control, chan); + break; + } +} + + +/* + * intialize the MIDI status + */ +void +snd_midi_channel_set_clear(snd_midi_channel_set_t *chset) +{ + int i; + + chset->midi_mode = SNDRV_MIDI_MODE_GM; + chset->gs_master_volume = 127; + + for (i = 0; i < chset->max_channels; i++) { + snd_midi_channel_t *chan = chset->channels + i; + memset(chan->note, 0, sizeof(chan->note)); + + chan->midi_aftertouch = 0; + chan->midi_pressure = 0; + chan->midi_program = 0; + chan->midi_pitchbend = 0; + snd_midi_reset_controllers(chan); + chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + chan->gm_rpn_fine_tuning = 0; + chan->gm_rpn_coarse_tuning = 0; + + if (i == 9) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } +} + +/* + * Process a rpn message. + */ +static void +rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + int type; + int val; + + if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) { + type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) | + chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]; + val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) | + chan->control[MIDI_CTL_LSB_DATA_ENTRY]; + + switch (type) { + case 0x0000: /* Pitch bend sensitivity */ + /* MSB only / 1 semitone per 128 */ + chan->gm_rpn_pitch_bend_range = val; + break; + + case 0x0001: /* fine tuning: */ + /* MSB/LSB, 8192=center, 100/8192 cent step */ + chan->gm_rpn_fine_tuning = val - 8192; + break; + + case 0x0002: /* coarse tuning */ + /* MSB only / 8192=center, 1 semitone per 128 */ + chan->gm_rpn_coarse_tuning = val - 8192; + break; + + case 0x7F7F: /* "lock-in" RPN */ + /* ignored */ + break; + } + } + /* should call nrpn or rpn callback here.. */ +} + +/* + * Process an nrpn message. + */ +static void +nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + /* parse XG NRPNs here if possible */ + if (ops->nrpn) + ops->nrpn(drv, chan, chset); +} + + +/* + * convert channel parameter in GS sysex + */ +static int +get_channel(unsigned char cmd) +{ + int p = cmd & 0x0f; + if (p == 0) + p = 9; + else if (p < 10) + p--; + return p; +} + + +/* + * Process a sysex message. + */ +static void +sysex(snd_midi_op_t *ops, void *private, unsigned char *buf, int len, snd_midi_channel_set_t *chset) +{ + /* GM on */ + static unsigned char gm_on_macro[] = { + 0x7e,0x7f,0x09,0x01, + }; + /* XG on */ + static unsigned char xg_on_macro[] = { + 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00, + }; + /* GS prefix + * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off + * reverb mode: XX=0x01, YY=0x30, ZZ=0-7 + * chorus mode: XX=0x01, YY=0x38, ZZ=0-7 + * master vol: XX=0x00, YY=0x04, ZZ=0-127 + */ + static unsigned char gs_pfx_macro[] = { + 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/ + }; + + int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED; + + if (len <= 0 || buf[0] != 0xf0) + return; + /* skip first byte */ + buf++; + len--; + + /* GM on */ + if (len >= sizeof(gm_on_macro) && + memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) { + if (chset->midi_mode != SNDRV_MIDI_MODE_GS && + chset->midi_mode != SNDRV_MIDI_MODE_XG) { + chset->midi_mode = SNDRV_MIDI_MODE_GM; + reset_all_channels(chset); + parsed = SNDRV_MIDI_SYSEX_GM_ON; + } + } + + /* GS macros */ + else if (len >= 8 && + memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) { + if (chset->midi_mode != SNDRV_MIDI_MODE_GS && + chset->midi_mode != SNDRV_MIDI_MODE_XG) + chset->midi_mode = SNDRV_MIDI_MODE_GS; + + if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) { + /* GS reset */ + parsed = SNDRV_MIDI_SYSEX_GS_RESET; + reset_all_channels(chset); + } + + else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) { + /* drum pattern */ + int p = get_channel(buf[5]); + if (p < chset->max_channels) { + parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL; + if (buf[7]) + chset->channels[p].drum_channel = 1; + else + chset->channels[p].drum_channel = 0; + } + + } else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) { + /* program */ + int p = get_channel(buf[5]); + if (p < chset->max_channels && + ! chset->channels[p].drum_channel) { + parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL; + chset->channels[p].midi_program = buf[7]; + } + + } else if (buf[5] == 0x01 && buf[6] == 0x30) { + /* reverb mode */ + parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE; + chset->gs_reverb_mode = buf[7]; + + } else if (buf[5] == 0x01 && buf[6] == 0x38) { + /* chorus mode */ + parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE; + chset->gs_chorus_mode = buf[7]; + + } else if (buf[5] == 0x00 && buf[6] == 0x04) { + /* master volume */ + parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE; + chset->gs_master_volume = buf[7]; + + } + } + + /* XG on */ + else if (len >= sizeof(xg_on_macro) && + memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) { + int i; + chset->midi_mode = SNDRV_MIDI_MODE_XG; + parsed = SNDRV_MIDI_SYSEX_XG_ON; + /* reset CC#0 for drums */ + for (i = 0; i < chset->max_channels; i++) { + if (chset->channels[i].drum_channel) + chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127; + else + chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0; + } + } + + if (ops->sysex) + ops->sysex(private, buf - 1, len + 1, parsed, chset); +} + +/* + * all sound off + */ +static void +all_sounds_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan) +{ + int n; + + if (! ops->note_terminate) + return; + for (n = 0; n < 128; n++) { + if (chan->note[n]) { + ops->note_terminate(drv, n, chan); + chan->note[n] = 0; + } + } +} + +/* + * all notes off + */ +static void +all_notes_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan) +{ + int n; + + if (! ops->note_off) + return; + for (n = 0; n < 128; n++) { + if (chan->note[n] == SNDRV_MIDI_NOTE_ON) + note_off(ops, drv, chan, n, 0); + } +} + +/* + * Initialise a single midi channel control block. + */ +void snd_midi_channel_init(snd_midi_channel_t *p, int n) +{ + if (p == NULL) + return; + + memset(p, 0, sizeof(snd_midi_channel_t)); + p->private = NULL; + p->number = n; + + snd_midi_reset_controllers(p); + p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + p->gm_rpn_fine_tuning = 0; + p->gm_rpn_coarse_tuning = 0; + + if (n == 9) + p->drum_channel = 1; /* Default ch 10 as drums */ +} + +/* + * Allocate and initialise a set of midi channel control blocks. + */ +snd_midi_channel_t *snd_midi_channel_init_set(int n) +{ + snd_midi_channel_t *chan; + int i; + + chan = kmalloc(n * sizeof(snd_midi_channel_t), GFP_KERNEL); + if (chan) { + for (i = 0; i < n; i++) + snd_midi_channel_init(chan+i, i); + } + + return chan; +} + +/* + * reset all midi channels + */ +static void +reset_all_channels(snd_midi_channel_set_t *chset) +{ + int ch; + for (ch = 0; ch < chset->max_channels; ch++) { + snd_midi_channel_t *chan = chset->channels + ch; + snd_midi_reset_controllers(chan); + chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */ + chan->gm_rpn_fine_tuning = 0; + chan->gm_rpn_coarse_tuning = 0; + + if (ch == 9) + chan->drum_channel = 1; + else + chan->drum_channel = 0; + } +} + + +/* + * Allocate and initialise a midi channel set. + */ +snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n) +{ + snd_midi_channel_set_t *chset; + + chset = kmalloc(sizeof(*chset), GFP_KERNEL); + if (chset) { + chset->channels = snd_midi_channel_init_set(n); + chset->private_data = NULL; + chset->max_channels = n; + } + return chset; +} + +/* + * Reset the midi controllers on a particular channel to default values. + */ +void snd_midi_reset_controllers(snd_midi_channel_t *chan) +{ + memset(chan->control, 0, sizeof(chan->control)); + chan->gm_volume = 127; + chan->gm_expression = 127; + chan->gm_pan = 64; +} + + +/* + * Free a midi channel set. + */ +void snd_midi_channel_free_set(snd_midi_channel_set_t *chset) +{ + if (chset == NULL) + return; + if (chset->channels != NULL) + kfree(chset->channels); + kfree(chset); +} + +static int __init alsa_seq_midi_emul_init(void) +{ + return 0; +} + +static void __exit alsa_seq_midi_emul_exit(void) +{ +} + +module_init(alsa_seq_midi_emul_init) +module_exit(alsa_seq_midi_emul_exit) + +EXPORT_SYMBOL(snd_midi_process_event); +EXPORT_SYMBOL(snd_midi_channel_set_clear); +EXPORT_SYMBOL(snd_midi_channel_init); +EXPORT_SYMBOL(snd_midi_channel_init_set); +EXPORT_SYMBOL(snd_midi_channel_alloc_set); +EXPORT_SYMBOL(snd_midi_channel_free_set); diff -Nru linux/sound/core/seq/seq_midi_event.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_event.c --- linux/sound/core/seq/seq_midi_event.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_midi_event.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,469 @@ +/* + * MIDI byte <-> sequencer event coder + * + * Copyright (C) 1998,99 Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai , Jaroslav Kysela "); +MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder"); +MODULE_LICENSE("GPL"); + +/* queue type */ +/* from 0 to 7 are normal commands (note off, on, etc.) */ +#define ST_NOTEOFF 0 +#define ST_NOTEON 1 +#define ST_SPECIAL 8 +#define ST_SYSEX ST_SPECIAL +/* from 8 to 15 are events for 0xf0-0xf7 */ + + +/* status event types */ +typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev); +typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf); + +/* + * prototypes + */ +static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev); +static void note_decode(snd_seq_event_t *ev, unsigned char *buf); +static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf); +static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf); +static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf); +static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf); + +/* + * event list + */ +static struct status_event_list_t { + int event; + int qlen; + event_encode_t encode; + event_decode_t decode; +} status_event[] = { + /* 0x80 - 0xf0 */ + {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode}, + {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode}, + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */ + /* 0xf0 - 0xff */ + {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */ + {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ + {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ + {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */ + {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf7 */ + {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf9 */ + {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */ + {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */ + {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */ + {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */ + {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */ + {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ +}; + +static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); + +static struct extra_event_list_t { + int event; + int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev); +} extra_event[] = { + {SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14}, + /*{SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_nrpn},*/ + /*{SNDRV_SEQ_EVENT_REGPARAM, extra_decode_rpn},*/ +}; + +#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) + +/* + * new/delete record + */ + +int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev) +{ + snd_midi_event_t *dev; + + *rdev = NULL; + dev = (snd_midi_event_t *)snd_kcalloc(sizeof(snd_midi_event_t), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + if (bufsize > 0) { + dev->buf = kmalloc(bufsize, GFP_KERNEL); + if (dev->buf == NULL) { + kfree(dev); + return -ENOMEM; + } + } + dev->bufsize = bufsize; + dev->lastcmd = 0xff; + spin_lock_init(&dev->lock); + *rdev = dev; + return 0; +} + +void snd_midi_event_free(snd_midi_event_t *dev) +{ + if (dev != NULL) { + if (dev->buf) + kfree(dev->buf); + kfree(dev); + } +} + +/* + * initialize record + */ +inline static void reset_encode(snd_midi_event_t *dev) +{ + dev->read = 0; + dev->qlen = 0; + dev->type = 0; +} + +void snd_midi_event_reset_encode(snd_midi_event_t *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + reset_encode(dev); + spin_unlock_irqrestore(&dev->lock, flags); +} + +void snd_midi_event_reset_decode(snd_midi_event_t *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + dev->lastcmd = 0xff; + spin_unlock_irqrestore(&dev->lock, flags); +} + +void snd_midi_event_init(snd_midi_event_t *dev) +{ + snd_midi_event_reset_encode(dev); + snd_midi_event_reset_decode(dev); +} + +/* + * resize buffer + */ +int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize) +{ + unsigned char *new_buf, *old_buf; + unsigned long flags; + + if (bufsize == dev->bufsize) + return 0; + new_buf = kmalloc(bufsize, GFP_KERNEL); + if (new_buf == NULL) + return -ENOMEM; + spin_lock_irqsave(&dev->lock, flags); + old_buf = dev->buf; + dev->buf = new_buf; + dev->bufsize = bufsize; + reset_encode(dev); + spin_unlock_irqrestore(&dev->lock, flags); + if (old_buf) + kfree(old_buf); + return 0; +} + +/* + * read bytes and encode to sequencer event if finished + * return the size of encoded bytes + */ +long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev) +{ + long result = 0; + int rc; + + ev->type = SNDRV_SEQ_EVENT_NONE; + + while (count-- > 0) { + rc = snd_midi_event_encode_byte(dev, *buf++, ev); + result++; + if (rc < 0) + return rc; + else if (rc > 0) + return result; + } + + return result; +} + +/* + * read one byte and encode to sequencer event: + * return 1 if MIDI bytes are encoded to an event + * 0 data is not finished + * negative for error + */ +int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev) +{ + int rc = 0; + unsigned long flags; + + c &= 0xff; + + if (c >= MIDI_CMD_COMMON_CLOCK) { + /* real-time event */ + ev->type = status_event[ST_SPECIAL + c - 0xf0].event; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + return 1; + } + + spin_lock_irqsave(&dev->lock, flags); + if (dev->qlen > 0) { + /* rest of command */ + dev->buf[dev->read++] = c; + if (dev->type != ST_SYSEX) + dev->qlen--; + } else { + /* new command */ + dev->read = 1; + if (c & 0x80) { + dev->buf[0] = c; + if ((c & 0xf0) == 0xf0) /* special events */ + dev->type = (c & 0x0f) + ST_SPECIAL; + else + dev->type = (c >> 4) & 0x07; + dev->qlen = status_event[dev->type].qlen; + } else { + /* process this byte as argument */ + dev->buf[dev->read++] = c; + dev->qlen = status_event[dev->type].qlen - 1; + } + } + if (dev->qlen == 0) { + ev->type = status_event[dev->type].event; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + if (status_event[dev->type].encode) /* set data values */ + status_event[dev->type].encode(dev, ev); + rc = 1; + } else if (dev->type == ST_SYSEX) { + if (c == MIDI_CMD_COMMON_SYSEX_END || + dev->read >= dev->bufsize) { + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->type = SNDRV_SEQ_EVENT_SYSEX; + ev->data.ext.len = dev->read; + ev->data.ext.ptr = dev->buf; + if (c != MIDI_CMD_COMMON_SYSEX_END) + dev->read = 0; /* continue to parse */ + else + reset_encode(dev); /* all parsed */ + rc = 1; + } + } + + spin_unlock_irqrestore(&dev->lock, flags); + return rc; +} + +/* encode note event */ +static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.note.channel = dev->buf[0] & 0x0f; + ev->data.note.note = dev->buf[1]; + ev->data.note.velocity = dev->buf[2]; +} + +/* encode one parameter controls */ +static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.value = dev->buf[1]; +} + +/* encode pitch wheel change */ +static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192; +} + +/* encode midi control change */ +static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.channel = dev->buf[0] & 0x0f; + ev->data.control.param = dev->buf[1]; + ev->data.control.value = dev->buf[2]; +} + +/* encode one parameter value*/ +static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.value = dev->buf[1]; +} + +/* encode song position */ +static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev) +{ + ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1]; +} + +/* + * decode from a sequencer event to midi bytes + * return the size of decoded midi events + */ +long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev) +{ + int cmd, type; + + if (ev->type == SNDRV_SEQ_EVENT_NONE) + return -ENOENT; + + for (type = 0; type < numberof(status_event); type++) { + if (ev->type == status_event[type].event) + goto __found; + } + for (type = 0; type < numberof(extra_event); type++) { + if (ev->type == extra_event[type].event) + return extra_event[type].decode(dev, buf, count, ev); + } + return -ENOENT; + + __found: + if (type >= ST_SPECIAL) + cmd = 0xf0 + (type - ST_SPECIAL); + else + /* data.note.channel and data.control.channel is identical */ + cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f); + + + if (cmd == MIDI_CMD_COMMON_SYSEX) { + snd_midi_event_reset_decode(dev); + return snd_seq_expand_var_event(ev, count, buf, 1, 0); + } else { + int qlen; + unsigned char xbuf[4]; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd) { + dev->lastcmd = cmd; + spin_unlock_irqrestore(&dev->lock, flags); + xbuf[0] = cmd; + if (status_event[type].decode) + status_event[type].decode(ev, xbuf + 1); + qlen = status_event[type].qlen + 1; + } else { + spin_unlock_irqrestore(&dev->lock, flags); + if (status_event[type].decode) + status_event[type].decode(ev, xbuf + 0); + qlen = status_event[type].qlen; + } + if (count < qlen) + return -ENOMEM; + memcpy(buf, xbuf, qlen); + return qlen; + } +} + + +/* decode note event */ +static void note_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.note.note & 0x7f; + buf[1] = ev->data.note.velocity & 0x7f; +} + +/* decode one parameter controls */ +static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.value & 0x7f; +} + +/* decode pitch wheel change */ +static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + int value = ev->data.control.value + 8192; + buf[0] = value & 0x7f; + buf[1] = (value >> 7) & 0x7f; +} + +/* decode midi control change */ +static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.param & 0x7f; + buf[1] = ev->data.control.value & 0x7f; +} + +/* decode song position */ +static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf) +{ + buf[0] = ev->data.control.value & 0x7f; + buf[1] = (ev->data.control.value >> 7) & 0x7f; +} + +/* decode 14bit control */ +static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev) +{ + if (ev->data.control.param < 32) { + if (count < 5) + return -ENOMEM; + buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); + buf[1] = ev->data.control.param; + buf[2] = (ev->data.control.value >> 7) & 0x7f; + buf[3] = ev->data.control.param + 32; + buf[4] = ev->data.control.value & 0x7f; + dev->lastcmd = buf[0]; + return 5; + } else { + if (count < 3) + return -ENOMEM; + buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); + buf[1] = ev->data.control.param & 0x7f; + buf[4] = ev->data.control.value & 0x7f; + dev->lastcmd = buf[0]; + return 3; + } +} + +/* + * exports + */ + +EXPORT_SYMBOL(snd_midi_event_new); +EXPORT_SYMBOL(snd_midi_event_free); +EXPORT_SYMBOL(snd_midi_event_resize_buffer); +EXPORT_SYMBOL(snd_midi_event_init); +EXPORT_SYMBOL(snd_midi_event_reset_encode); +EXPORT_SYMBOL(snd_midi_event_reset_decode); +EXPORT_SYMBOL(snd_midi_event_encode); +EXPORT_SYMBOL(snd_midi_event_encode_byte); +EXPORT_SYMBOL(snd_midi_event_decode); diff -Nru linux/sound/core/seq/seq_mtc.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_mtc.c --- linux/sound/core/seq/seq_mtc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_mtc.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,252 @@ +/* + * MTC event converter + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include "seq_queue.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +typedef struct mtc_out { + int out_channel; + unsigned int time_format; + sndrv_seq_time_frame_t cur_time; + unsigned int decode_offset; + unsigned char sysex[10]; +} mtc_out_t; + +typedef struct mtc_in { + unsigned int time_format; + sndrv_seq_time_frame_t cur_time; + unsigned int cur_pos; + int prev_in_offset; +} mtc_in_t; + + +static int mtc_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + mtc_out_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->out_channel = sync_info->opt_info[0]; + if (arg->out_channel == 0) + arg->out_channel = 127; + arg->time_format = sync_info->time_format; + sync_info->param.time.subframes = 4; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + memset(&arg->cur_time, 0, sizeof(arg->cur_time)); + *retp = arg; + return 0; +} + +static int mtc_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp) +{ + mtc_in_t *arg; + + if (sync_info->time_format >= 4) + return -EINVAL; + if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL) + return -ENOMEM; + arg->time_format = sync_info->time_format; + memset(&arg->cur_time, 0, sizeof(arg->cur_time)); + arg->cur_pos = 0; + arg->prev_in_offset = -1; + sync_info->param.time.subframes = 4; /* MTC uses quarter frame */ + sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format); + *retp = arg; + return 0; +} + + +/* decode sync signal */ +static int sync_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + int val, offset; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + offset = (src->data.queue.param.position + arg->decode_offset) % 8; + if (offset == 0) { + /* convert and remember the current time + for the following 7 MTC quarter frames */ + arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, src->data.queue.param.position); + } + switch (offset) { + case 0: val = arg->cur_time.frame & 0x0f; break; + case 1: val = (arg->cur_time.frame >> 4) & 0x0f; break; + case 2: val = arg->cur_time.sec & 0x0f; break; + case 3: val = (arg->cur_time.sec >> 4) & 0x0f; break; + case 4: val = arg->cur_time.min & 0x0f; break; + case 5: val = (arg->cur_time.min >> 4) & 0x0f; break; + case 6: val = arg->cur_time.hour & 0x0f; break; + case 7: + default: + val = ((arg->cur_time.hour >> 4) & 0x01) | (arg->time_format << 1); + break; + } + val |= (offset << 4); + ev->type = SNDRV_SEQ_EVENT_QFRAME; + ev->data.control.value = val; + return 1; +} + +/* decode sync position */ +static int sync_pos_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned int pos; + unsigned char *buf = arg->sysex; + + if (arg->time_format != src->data.queue.sync_time_format) + return -EINVAL; + + pos = src->data.queue.param.position; /* quarter frames */ + arg->decode_offset = pos & 4; + pos /= 4; + arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, pos); + + buf[0] = 0xf0; /* SYSEX */ + buf[1] = 0x7f; + buf[2] = arg->out_channel; + buf[3] = 0x01; + buf[4] = 0x01; + buf[5] = arg->cur_time.hour | (arg->time_format << 5); + buf[6] = arg->cur_time.min; + buf[7] = arg->cur_time.sec; + buf[8] = arg->cur_time.frame; + buf[9] = 0xf7; + + ev->type = SNDRV_SEQ_EVENT_SYSEX; + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->data.ext.len = 10; + ev->data.ext.ptr = buf; + + return 1; +} + +static int mtc_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + mtc_out_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_SYNC: + return sync_out(arg, src, ev); + case SNDRV_SEQ_EVENT_SYNC_POS: + return sync_pos_out(arg, src, ev); + } + return 0; +} + +/* decode sync signal */ +static int sync_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + int val, offset; + unsigned int time_format; + + offset = (src->data.control.value & 0x70) >> 4; + val = src->data.control.value & 0x0f; + if (offset > 0 && offset != arg->prev_in_offset + 1) { + /* bad quarter frame message - something missing.. */ + arg->prev_in_offset = -1; /* wait for next 0 */ + return -EINVAL; + } + switch (offset) { + case 0: arg->cur_time.frame = val; break; + case 1: arg->cur_time.frame |= (val << 4); break; + case 2: arg->cur_time.sec = val; break; + case 3: arg->cur_time.sec |= (val << 4); break; + case 4: arg->cur_time.min = val; break; + case 5: arg->cur_time.min |= (val << 4); break; + case 6: arg->cur_time.hour = val; break; + case 7: + default: + arg->cur_time.hour |= (val & 1) << 4; + time_format = (val >> 1) & 3; + if (time_format != arg->time_format) + return -EINVAL; + arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time); + arg->cur_pos += 7; /* correct the receive time */ + break; + } + + ev->type = SNDRV_SEQ_EVENT_SYNC; + ev->data.queue.sync_time_format = arg->time_format; + ev->data.queue.param.position = arg->cur_pos; + arg->cur_pos++; + + return 1; /* composed */ +} + +/* decode sync position */ +static int sync_pos_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + unsigned time_format; + char buf[10]; + + if (snd_seq_expand_var_event(src, 10, buf, 1, 0) != 10) + return 0; + if (buf[1] != 0x7f || buf[3] != 0x01 || buf[4] != 0x01) + return 0; + time_format = (buf[5] >> 5) & 3; + if (time_format != arg->time_format) + return -EINVAL; + arg->cur_time.hour = buf[5] & 0x1f; + arg->cur_time.min = buf[6]; + arg->cur_time.sec = buf[7]; + arg->cur_time.frame = buf[8]; + arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time); + + ev->type = SNDRV_SEQ_EVENT_SYNC_POS; + ev->data.queue.sync_time_format = time_format; + ev->data.queue.param.position = arg->cur_pos; + + return 1; /* composed */ +} + +static int mtc_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev) +{ + mtc_in_t *arg = _arg; + switch (src->type) { + case SNDRV_SEQ_EVENT_QFRAME: + return sync_in(arg, src, ev); + case SNDRV_SEQ_EVENT_SYSEX: + return sync_pos_in(arg, src, ev); + } + return 0; +} + +/* exported */ +seq_sync_parser_t snd_seq_mtc_parser = { + format: SNDRV_SEQ_SYNC_FMT_MTC, + in: { + open: mtc_open_in, + sync: mtc_sync_in, + }, + out: { + open: mtc_open_out, + sync: mtc_sync_out, + }, +}; + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru linux/sound/core/seq/seq_ports.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.c --- linux/sound/core/seq/seq_ports.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,669 @@ +/* + * ALSA sequencer Ports + * Copyright (c) 1998 by Frank van de Pol + * Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_system.h" +#include "seq_ports.h" +#include "seq_clientmgr.h" + +/* + + registration of client ports + + */ + + +/* + +NOTE: the current implementation of the port structure as a linked list is +not optimal for clients that have many ports. For sending messages to all +subscribers of a port we first need to find the address of the port +structure, which means we have to traverse the list. A direct access table +(array) would be better, but big preallocated arrays waste memory. + +Possible actions: + +1) leave it this way, a client does normaly does not have more than a few +ports + +2) replace the linked list of ports by a array of pointers which is +dynamicly kmalloced. When a port is added or deleted we can simply allocate +a new array, copy the corresponding pointers, and delete the old one. We +then only need a pointer to this array, and an integer that tells us how +much elements are in array. + +*/ + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* return pointer to port structure - port is locked if found */ +client_port_t *snd_seq_port_use_ptr(client_t *client, int num) +{ + struct list_head *p; + client_port_t *port; + + if (client == NULL) + return NULL; + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + port = list_entry(p, client_port_t, list); + if (port->addr.port == num) { + if (port->closing) + break; /* deleting now */ + snd_use_lock_use(&port->use_lock); + read_unlock(&client->ports_lock); + return port; + } + } + read_unlock(&client->ports_lock); + return NULL; /* not found */ +} + + +/* search for the next port - port is locked if found */ +client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo) +{ + int num; + struct list_head *p; + client_port_t *port, *found; + + num = pinfo->addr.port; + found = NULL; + read_lock(&client->ports_lock); + list_for_each(p, &client->ports_list_head) { + port = list_entry(p, client_port_t, list); + if (port->addr.port < num) + continue; + if (port->addr.port == num) { + found = port; + break; + } + if (found == NULL || port->addr.port < found->addr.port) + found = port; + } + if (found) { + if (found->closing) + found = NULL; + else + snd_use_lock_use(&found->use_lock); + } + read_unlock(&client->ports_lock); + return found; +} + + +/* initialize port_subs_info_t */ +static void port_subs_info_init(port_subs_info_t *grp) +{ + INIT_LIST_HEAD(&grp->list_head); + grp->count = 0; + grp->exclusive = 0; + rwlock_init(&grp->list_lock); + init_rwsem(&grp->list_mutex); + grp->open = NULL; + grp->close = NULL; +} + + +/* create a port, port number is returned (-1 on failure) */ +client_port_t *snd_seq_create_port(client_t *client, int port) +{ + unsigned long flags; + client_port_t *new_port; + struct list_head *l; + int num = -1; + + /* sanity check */ + snd_assert(client, return NULL); + + if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) { + snd_printk(KERN_WARNING "too many ports for client %d\n", client->number); + return NULL; + } + + /* create a new port */ + new_port = snd_kcalloc(sizeof(client_port_t), GFP_KERNEL); + if (! new_port) { + snd_printd("malloc failed for registering client port\n"); + return NULL; /* failure, out of memory */ + } + /* init port data */ + new_port->addr.client = client->number; + new_port->addr.port = -1; + new_port->owner = THIS_MODULE; + sprintf(new_port->name, "port-%d", num); + snd_use_lock_init(&new_port->use_lock); + port_subs_info_init(&new_port->c_src); + port_subs_info_init(&new_port->c_dest); + + num = port >= 0 ? port : 0; + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + if (p->addr.port > num) + break; + if (port < 0) /* auto-probe mode */ + num = p->addr.port + 1; + } + /* insert the new port */ + list_add_tail(&new_port->list, l); + client->num_ports++; + new_port->addr.port = num; /* store the port number in the port */ + write_unlock_irqrestore(&client->ports_lock, flags); + up(&client->ports_mutex); + sprintf(new_port->name, "port-%d", num); + + return new_port; +} + +/* */ +enum group_type_t { + SRC_LIST, DEST_LIST +}; + +static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack); +static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack); + + +static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp) +{ + client_port_t *p; + *cp = snd_seq_client_use_ptr(addr->client); + if (*cp) { + p = snd_seq_port_use_ptr(*cp, addr->port); + if (! p) { + snd_seq_client_unlock(*cp); + *cp = NULL; + } + return p; + } + return NULL; +} + +/* + * remove all subscribers on the list + * this is called from port_delete, for each src and dest list. + */ +static void clear_subscriber_list(client_t *client, client_port_t *port, + port_subs_info_t *grp, int grptype) +{ + struct list_head *p, *n; + + down_write(&grp->list_mutex); + list_for_each_safe(p, n, &grp->list_head) { + subscribers_t *subs; + client_t *c; + client_port_t *aport; + + if (grptype == SRC_LIST) { + subs = list_entry(p, subscribers_t, src_list); + aport = get_client_port(&subs->info.dest, &c); + } else { + subs = list_entry(p, subscribers_t, dest_list); + aport = get_client_port(&subs->info.sender, &c); + } + list_del(p); + unsubscribe_port(client, port, grp, &subs->info, 0); + if (!aport) { + /* looks like the connected port is being deleted. + * we decrease the counter, and when both ports are deleted + * remove the subscriber info + */ + if (atomic_dec_and_test(&subs->ref_count)) + kfree(subs); + } else { + /* ok we got the connected port */ + port_subs_info_t *agrp; + agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src; + down_write(&agrp->list_mutex); + if (grptype == SRC_LIST) + list_del(&subs->dest_list); + else + list_del(&subs->src_list); + unsubscribe_port(c, aport, agrp, &subs->info, 1); + kfree(subs); + up_write(&agrp->list_mutex); + snd_seq_port_unlock(aport); + snd_seq_client_unlock(c); + } + } + up_write(&grp->list_mutex); +} + +/* delete port data */ +static int port_delete(client_t *client, client_port_t *port) +{ + /* set closing flag and wait for all port access are gone */ + port->closing = 1; + snd_use_lock_sync(&port->use_lock); + + /* clear subscribers info */ + clear_subscriber_list(client, port, &port->c_src, SRC_LIST); + clear_subscriber_list(client, port, &port->c_dest, DEST_LIST); + + if (port->private_free) + port->private_free(port->private_data); + + snd_assert(port->c_src.count == 0,); + snd_assert(port->c_dest.count == 0,); + + kfree(port); + return 0; +} + + +/* delete a port with the given port id */ +int snd_seq_delete_port(client_t *client, int port) +{ + unsigned long flags; + struct list_head *l; + client_port_t *found = NULL; + + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + list_for_each(l, &client->ports_list_head) { + client_port_t *p = list_entry(l, client_port_t, list); + if (p->addr.port == port) { + /* ok found. delete from the list at first */ + list_del(l); + client->num_ports--; + found = p; + break; + } + } + write_unlock_irqrestore(&client->ports_lock, flags); + up(&client->ports_mutex); + if (found) + return port_delete(client, found); + else + return -ENOENT; +} + +/* delete the all ports belonging to the given client */ +int snd_seq_delete_all_ports(client_t *client) +{ + unsigned long flags; + struct list_head deleted_list, *p, *n; + + /* move the port list to deleted_list, and + * clear the port list in the client data. + */ + down(&client->ports_mutex); + write_lock_irqsave(&client->ports_lock, flags); + if (! list_empty(&client->ports_list_head)) { + __list_add(&deleted_list, + client->ports_list_head.prev, + client->ports_list_head.next); + INIT_LIST_HEAD(&client->ports_list_head); + } else { + INIT_LIST_HEAD(&deleted_list); + } + client->num_ports = 0; + write_unlock_irqrestore(&client->ports_lock, flags); + + /* remove each port in deleted_list */ + list_for_each_safe(p, n, &deleted_list) { + client_port_t *port = list_entry(p, client_port_t, list); + list_del(p); + snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); + port_delete(client, port); + } + up(&client->ports_mutex); + return 0; +} + +/* set port info fields */ +int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info) +{ + snd_assert(port && info, return -EINVAL); + + /* set port name */ + if (info->name[0]) { + strncpy(port->name, info->name, sizeof(port->name)-1); + port->name[sizeof(port->name)-1] = '\0'; + } + + /* set capabilities */ + port->capability = info->capability; + + /* get port type */ + port->type = info->type; + + /* information about supported channels/voices */ + port->midi_channels = info->midi_channels; + port->synth_voices = info->synth_voices; + + return 0; +} + +/* get port info fields */ +int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info) +{ + snd_assert(port && info, return -EINVAL); + + /* get port name */ + strncpy(info->name, port->name, sizeof(info->name)); + + /* get capabilities */ + info->capability = port->capability; + + /* get port type */ + info->type = port->type; + + /* information about supported channels/voices */ + info->midi_channels = port->midi_channels; + info->synth_voices = port->synth_voices; + + /* get subscriber counts */ + info->read_use = port->c_src.count; + info->write_use = port->c_dest.count; + + return 0; +} + + + +/* + * call callback functions (if any): + * the callbacks are invoked only when the first (for connection) or + * the last subscription (for disconnection) is done. Second or later + * subscription results in increment of counter, but no callback is + * invoked. + * This feature is useful if these callbacks are associated with + * initialization or termination of devices (see seq_midi.c). + * + * If callback_all option is set, the callback function is invoked + * at each connnection/disconnection. + */ + +static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, + snd_seq_port_subscribe_t *info, int send_ack) +{ + int err = 0; + + if (!try_inc_mod_count(port->owner)) + return -EFAULT; + grp->count++; + if (grp->open && (port->callback_all || grp->count == 1)) { + err = grp->open(port->private_data, info); + if (err < 0) { + dec_mod_count(port->owner); + grp->count--; + } + } + if (err >= 0 && send_ack && client->type == USER_CLIENT) + snd_seq_client_notify_subscription(port->addr.client, port->addr.port, + info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); + + return err; +} + +static int unsubscribe_port(client_t *client, client_port_t *port, + port_subs_info_t *grp, + snd_seq_port_subscribe_t *info, int send_ack) +{ + int err = 0; + + snd_assert(port->owner, return -EFAULT); + if (! grp->count) + return -EINVAL; + grp->count--; + if (grp->close && (port->callback_all || grp->count == 0)) + err = grp->close(port->private_data, info); + if (send_ack && client->type == USER_CLIENT) + snd_seq_client_notify_subscription(port->addr.client, port->addr.port, + info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); + dec_mod_count(port->owner); + return err; +} + + + +/* check if both addresses are identical */ +static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s) +{ + return (r->client == s->client) && (r->port == s->port); +} + +/* check the two subscribe info match */ +/* if flags is zero, checks only sender and destination addresses */ +static int match_subs_info(snd_seq_port_subscribe_t *r, + snd_seq_port_subscribe_t *s) +{ + if (addr_match(&r->sender, &s->sender) && + addr_match(&r->dest, &s->dest)) { + if (r->flags && r->flags == s->flags) + return r->queue == s->queue; + else if (! r->flags) + return 1; + } + return 0; +} + + +/* connect two ports */ +int snd_seq_port_connect(client_t *connector, + client_t *src_client, client_port_t *src_port, + client_t *dest_client, client_port_t *dest_port, + snd_seq_port_subscribe_t *info) +{ + port_subs_info_t *src = &src_port->c_src; + port_subs_info_t *dest = &dest_port->c_dest; + subscribers_t *subs; + struct list_head *p; + int err, src_called = 0; + unsigned long flags; + int exclusive; + + subs = snd_kcalloc(sizeof(*subs), GFP_KERNEL); + if (! subs) + return -ENOMEM; + + subs->info = *info; + atomic_set(&subs->ref_count, 2); + + down_write(&src->list_mutex); + down_write(&dest->list_mutex); + + exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; + err = -EBUSY; + if (exclusive) { + if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head)) + goto __error; + } else { + if (src->exclusive || dest->exclusive) + goto __error; + /* check whether already exists */ + list_for_each(p, &src->list_head) { + subscribers_t *s = list_entry(p, subscribers_t, src_list); + if (match_subs_info(info, &s->info)) + goto __error; + } + list_for_each(p, &dest->list_head) { + subscribers_t *s = list_entry(p, subscribers_t, dest_list); + if (match_subs_info(info, &s->info)) + goto __error; + } + } + + if ((err = subscribe_port(src_client, src_port, src, info, + connector->number != src_client->number)) < 0) + goto __error; + src_called = 1; + + if ((err = subscribe_port(dest_client, dest_port, dest, info, + connector->number != dest_client->number)) < 0) + goto __error; + + /* add to list */ + write_lock_irqsave(&src->list_lock, flags); + // write_lock(&dest->list_lock); // no other lock yet + list_add_tail(&subs->src_list, &src->list_head); + list_add_tail(&subs->dest_list, &dest->list_head); + // write_unlock(&dest->list_lock); // no other lock yet + write_unlock_irqrestore(&src->list_lock, flags); + + src->exclusive = dest->exclusive = exclusive; + + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return 0; + + __error: + if (src_called) + unsubscribe_port(src_client, src_port, src, info, + connector->number != src_client->number); + kfree(subs); + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return err; +} + + +/* remove the connection */ +int snd_seq_port_disconnect(client_t *connector, + client_t *src_client, client_port_t *src_port, + client_t *dest_client, client_port_t *dest_port, + snd_seq_port_subscribe_t *info) +{ + port_subs_info_t *src = &src_port->c_src; + port_subs_info_t *dest = &dest_port->c_dest; + subscribers_t *subs; + struct list_head *p; + int err = -ENOENT; + unsigned long flags; + + down_write(&src->list_mutex); + down_write(&dest->list_mutex); + + /* look for the connection */ + list_for_each(p, &src->list_head) { + subs = list_entry(p, subscribers_t, src_list); + if (match_subs_info(info, &subs->info)) { + write_lock_irqsave(&src->list_lock, flags); + // write_lock(&dest->list_lock); // no lock yet + list_del(&subs->src_list); + list_del(&subs->dest_list); + // write_unlock(&dest->list_lock); + write_unlock_irqrestore(&src->list_lock, flags); + src->exclusive = dest->exclusive = 0; + unsubscribe_port(src_client, src_port, src, info, + connector->number != src_client->number); + unsubscribe_port(dest_client, dest_port, dest, info, + connector->number != dest_client->number); + kfree(subs); + err = 0; + break; + } + } + + up_write(&dest->list_mutex); + up_write(&src->list_mutex); + return err; +} + + +/* get matched subscriber */ +subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, + snd_seq_addr_t *dest_addr) +{ + struct list_head *p; + subscribers_t *s, *found = NULL; + + down_read(&src_grp->list_mutex); + list_for_each(p, &src_grp->list_head) { + s = list_entry(p, subscribers_t, src_list); + if (addr_match(dest_addr, &s->info.dest)) { + found = s; + break; + } + } + up_read(&src_grp->list_mutex); + return found; +} + +/* + * Attach a device driver that wants to receive events from the + * sequencer. Returns the new port number on success. + * A driver that wants to receive the events converted to midi, will + * use snd_seq_midisynth_register_port(). + */ +/* exported */ +int snd_seq_event_port_attach(int client, + snd_seq_port_callback_t *pcbp, + int cap, + int type, + char *portname) +{ + snd_seq_port_info_t portinfo; + int ret; + + /* Set up the port */ + memset(&portinfo, 0, sizeof(portinfo)); + portinfo.addr.client = client; + if (portname) + strncpy(portinfo.name, portname, sizeof(portinfo.name)); + else + sprintf(portinfo.name, "Unamed port"); + + portinfo.capability = cap; + portinfo.type = type; + portinfo.kernel = pcbp; + + /* Create it */ + ret = snd_seq_kernel_client_ctl(client, + SNDRV_SEQ_IOCTL_CREATE_PORT, + &portinfo); + + if (ret >= 0) + ret = portinfo.addr.port; + + return ret; +} + + +/* + * Detach the driver from a port. + */ +/* exported */ +int snd_seq_event_port_detach(int client, int port) +{ + snd_seq_port_info_t portinfo; + int err; + + memset(&portinfo, 0, sizeof(portinfo)); + portinfo.addr.client = client; + portinfo.addr.port = port; + err = snd_seq_kernel_client_ctl(client, + SNDRV_SEQ_IOCTL_DELETE_PORT, + &portinfo); + + return err; +} diff -Nru linux/sound/core/seq/seq_ports.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.h --- linux/sound/core/seq/seq_ports.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_ports.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,124 @@ +/* + * ALSA sequencer Ports + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_PORTS_H +#define __SND_SEQ_PORTS_H + +#include +#include "seq_lock.h" + +/* list of 'exported' ports */ + +/* Client ports that are not exported are still accessible, but are + anonymous ports. + + If a port supports SUBSCRIPTION, that port can send events to all + subscribersto a special address, with address + (queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all + recipients that are registered in the subscription list. A typical + application for these SUBSCRIPTION events is handling of incoming MIDI + data. The port doesn't 'know' what other clients are interested in this + message. If for instance a MIDI recording application would like to receive + the events from that port, it will first have to subscribe with that port. + +*/ + +typedef struct subscribers_t { + snd_seq_port_subscribe_t info; /* additional info */ + struct list_head src_list; /* link of sources */ + struct list_head dest_list; /* link of destinations */ + atomic_t ref_count; +} subscribers_t; + +typedef struct port_subs_info_t { + struct list_head list_head; /* list of subscribed ports */ + unsigned int count; /* count of subscribers */ + unsigned int exclusive: 1; /* exclusive mode */ + struct rw_semaphore list_mutex; + rwlock_t list_lock; + snd_seq_kernel_port_open_t *open; + snd_seq_kernel_port_close_t *close; +} port_subs_info_t; + +typedef struct client_port_t { + + snd_seq_addr_t addr; /* client/port number */ + struct module *owner; /* owner of this port */ + char name[64]; /* port name */ + struct list_head list; /* port list */ + snd_use_lock_t use_lock; + + /* subscribers */ + port_subs_info_t c_src; /* read (sender) list */ + port_subs_info_t c_dest; /* write (dest) list */ + + snd_seq_kernel_port_input_t *event_input; + snd_seq_kernel_port_private_free_t *private_free; + void *private_data; + unsigned int callback_all : 1; + unsigned int closing : 1; + + /* capability, inport, output, sync */ + unsigned int capability; /* port capability bits */ + unsigned int type; /* port type bits */ + + /* supported channels */ + int midi_channels; + int synth_voices; + +} client_port_t; + +/* return pointer to port structure and lock port */ +client_port_t *snd_seq_port_use_ptr(client_t *client, int num); + +/* search for next port - port is locked if found */ +client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo); + +/* unlock the port */ +#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock) + +/* create a port, port number is returned (-1 on failure) */ +client_port_t *snd_seq_create_port(client_t *client, int port_index); + +/* delete a port */ +int snd_seq_delete_port(client_t *client, int port); + +/* delete all ports */ +int snd_seq_delete_all_ports(client_t *client); + +/* set port info fields */ +int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info); + +/* get port info fields */ +int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info); + +/* add subscriber to subscription list */ +int snd_seq_port_connect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info); + +/* remove subscriber from subscription list */ +int snd_seq_port_disconnect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info); + +/* subscribe port */ +int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info); + +/* get matched subscriber */ +subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr); + +#endif diff -Nru linux/sound/core/seq/seq_prioq.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.c --- linux/sound/core/seq/seq_prioq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,449 @@ +/* + * ALSA sequencer Priority Queue + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "seq_timer.h" +#include "seq_prioq.h" +#include "seq_timer.h" + + +/* Implementation is a simple linked list for now... + + This priority queue orders the events on timestamp. For events with an + equeal timestamp the queue behaves as a FIFO. + + * + * +-------+ + * Head --> | first | + * +-------+ + * |next + * +-----v-+ + * | | + * +-------+ + * | + * +-----v-+ + * | | + * +-------+ + * | + * +-----v-+ + * Tail --> | last | + * +-------+ + * + + */ + + + +/* create new prioq (constructor) */ +prioq_t *snd_seq_prioq_new(void) +{ + prioq_t *f; + + f = snd_kcalloc(sizeof(prioq_t), GFP_KERNEL); + if (f == NULL) { + snd_printd("oops: malloc failed for snd_seq_prioq_new()\n"); + return NULL; + } + + spin_lock_init(&f->lock); + f->head = NULL; + f->tail = NULL; + f->cells = 0; + + return f; +} + +/* delete prioq (destructor) */ +void snd_seq_prioq_delete(prioq_t **fifo) +{ + prioq_t *f = *fifo; + *fifo = NULL; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n"); + return; + } + + /* release resources...*/ + /*....................*/ + + if (f->cells > 0) { + /* drain prioQ */ + while (f->cells > 0) + snd_seq_cell_free(snd_seq_prioq_cell_out(f)); + } + + kfree(f); +} + + + + +/* compare timestamp between events */ +/* return 1 if a >= b; 0 */ +static inline int compare_timestamp(snd_seq_event_t * a, snd_seq_event_t * b) +{ + if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) { + /* compare ticks */ + return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick)); + } else { + /* compare real time */ + return (snd_seq_compare_real_time(&a->time.time, &b->time.time)); + } +} + +/* compare timestamp between events */ +/* return negative if a < b; + * zero if a = b; + * positive if a > b; + */ +static inline int compare_timestamp_rel(snd_seq_event_t *a, snd_seq_event_t *b) +{ + if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) { + /* compare ticks */ + if (a->time.tick > b->time.tick) + return 1; + else if (a->time.tick == b->time.tick) + return 0; + else + return -1; + } else { + /* compare real time */ + if (a->time.time.tv_sec > b->time.time.tv_sec) + return 1; + else if (a->time.time.tv_sec == b->time.time.tv_sec) { + if (a->time.time.tv_nsec > b->time.time.tv_nsec) + return 1; + else if (a->time.time.tv_nsec == b->time.time.tv_nsec) + return 0; + else + return -1; + } else + return -1; + } +} + +/* enqueue cell to prioq */ +void snd_seq_prioq_cell_in(prioq_t * f, snd_seq_event_cell_t * cell) +{ + snd_seq_event_cell_t *cur, *prev; + unsigned long flags; + int prior; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return; + } + if (cell == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL cell\n"); + return; + } + + /* check flags */ + prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK); + + spin_lock_irqsave(&f->lock, flags); + + /* check if this element needs to inserted at the end (ie. ordered + data is inserted) This will be very likeley if a sequencer + application or midi file player is feeding us (sequential) data */ + if (f->tail && !prior) { + if (compare_timestamp(&cell->event, &f->tail->event)) { + /* add new cell to tail of the fifo */ + f->tail->next = cell; + f->tail = cell; + cell->next = NULL; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); + return; + } + } + /* traverse list of elements to find the place where the new cell is + to be inserted... Note that this is a order n process ! */ + + prev = NULL; /* previous cell */ + cur = f->head; /* cursor */ + + while (cur != NULL) { + /* compare timestamps */ + int rel = compare_timestamp_rel(&cell->event, &cur->event); + if (rel < 0) + /* new cell has earlier schedule time, */ + break; + else if (rel == 0 && prior) + /* equal schedule time and prior to others */ + break; + /* new cell has equal or larger schedule time, */ + /* move cursor to next cell */ + prev = cur; + cur = cur->next; + } + + /* insert it before cursor */ + if (prev != NULL) + prev->next = cell; + cell->next = cur; + + if (f->head == cur) /* this is the first cell, set head to it */ + f->head = cell; + if (cur == NULL) /* reached end of the list */ + f->tail = cell; + f->cells++; + spin_unlock_irqrestore(&f->lock, flags); +} + +/* dequeue cell from prioq */ +snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t * f) +{ + snd_seq_event_cell_t *cell; + unsigned long flags; + + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return NULL; + } + spin_lock_irqsave(&f->lock, flags); + + cell = f->head; + if (cell) { + f->head = cell->next; + + /* reset tail if this was the last element */ + if (f->tail == cell) + f->tail = NULL; + + cell->next = NULL; + f->cells--; + } + + spin_unlock_irqrestore(&f->lock, flags); + return cell; +} + +/* return number of events available in prioq */ +int snd_seq_prioq_avail(prioq_t * f) +{ + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return 0; + } + return f->cells; +} + + +/* peek at cell at the head of the prioq */ +snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t * f) +{ + if (f == NULL) { + snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n"); + return NULL; + } + return f->head; +} + + +static inline int prioq_match(snd_seq_event_cell_t *cell, int client, int timestamp) +{ + if (cell->event.source.client == client || + cell->event.dest.client == client) + return 1; + if (!timestamp) + return 0; + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + if (cell->event.time.tick) + return 1; + break; + case SNDRV_SEQ_TIME_STAMP_REAL: + if (cell->event.time.time.tv_sec || + cell->event.time.time.tv_nsec) + return 1; + break; + } + return 0; +} + +/* remove cells for left client */ +void snd_seq_prioq_leave(prioq_t * f, int client, int timestamp) +{ + register snd_seq_event_cell_t *cell, *next; + unsigned long flags; + snd_seq_event_cell_t *prev = NULL; + snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext; + + /* collect all removed cells */ + spin_lock_irqsave(&f->lock, flags); + cell = f->head; + while (cell) { + next = cell->next; + if (prioq_match(cell, client, timestamp)) { + /* remove cell from prioq */ + if (cell == f->head) { + f->head = cell->next; + } else { + prev->next = cell->next; + } + if (cell == f->tail) + f->tail = cell->next; + f->cells--; + /* add cell to free list */ + cell->next = NULL; + if (freefirst == NULL) { + freefirst = cell; + } else { + freeprev->next = cell; + } + freeprev = cell; + } else { +#if 0 + printk("type = %i, source = %i, dest = %i, client = %i\n", + cell->event.type, + cell->event.source.client, + cell->event.dest.client, + client); +#endif + prev = cell; + } + cell = next; + } + spin_unlock_irqrestore(&f->lock, flags); + + /* remove selected cells */ + while (freefirst) { + freenext = freefirst->next; + snd_seq_cell_free(freefirst); + freefirst = freenext; + } +} + +static int prioq_remove_match(snd_seq_remove_events_t *info, + snd_seq_event_t *ev) +{ + int res; + + if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) { + if (ev->dest.client != info->dest.client || + ev->dest.port != info->dest.port) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) { + if (! snd_seq_ev_is_channel_type(ev)) + return 0; + /* data.note.channel and data.control.channel are identical */ + if (ev->data.note.channel != info->channel) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) { + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) + res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick); + else + res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); + if (!res) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) { + if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) + res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick); + else + res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); + if (res) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) { + if (ev->type != info->type) + return 0; + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) { + /* Do not remove off events */ + switch (ev->type) { + case SNDRV_SEQ_EVENT_NOTEOFF: + /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */ + return 0; + default: + break; + } + } + if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) { + if (info->tag != ev->tag) + return 0; + } + + return 1; +} + +/* remove cells matching remove criteria */ +void snd_seq_prioq_remove_events(prioq_t * f, int client, + snd_seq_remove_events_t *info) +{ + register snd_seq_event_cell_t *cell, *next; + unsigned long flags; + snd_seq_event_cell_t *prev = NULL; + snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext; + + /* collect all removed cells */ + spin_lock_irqsave(&f->lock, flags); + cell = f->head; + + while (cell) { + next = cell->next; + if (cell->event.source.client == client && + prioq_remove_match(info, &cell->event)) { + + /* remove cell from prioq */ + if (cell == f->head) { + f->head = cell->next; + } else { + prev->next = cell->next; + } + + if (cell == f->tail) + f->tail = cell->next; + f->cells--; + + /* add cell to free list */ + cell->next = NULL; + if (freefirst == NULL) { + freefirst = cell; + } else { + freeprev->next = cell; + } + + freeprev = cell; + } else { + prev = cell; + } + cell = next; + } + spin_unlock_irqrestore(&f->lock, flags); + + /* remove selected cells */ + while (freefirst) { + freenext = freefirst->next; + snd_seq_cell_free(freefirst); + freefirst = freenext; + } +} + + diff -Nru linux/sound/core/seq/seq_prioq.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.h --- linux/sound/core/seq/seq_prioq.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_prioq.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,62 @@ +/* + * ALSA sequencer Priority Queue + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_PRIOQ_H +#define __SND_SEQ_PRIOQ_H + +#include "seq_memory.h" + + +/* === PRIOQ === */ + +typedef struct { + snd_seq_event_cell_t* head; /* pointer to head of prioq */ + snd_seq_event_cell_t* tail; /* pointer to tail of prioq */ + int cells; + spinlock_t lock; +} prioq_t; + + +/* create new prioq (constructor) */ +extern prioq_t *snd_seq_prioq_new(void); + +/* delete prioq (destructor) */ +extern void snd_seq_prioq_delete(prioq_t **fifo); + +/* enqueue cell to prioq */ +extern void snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell); + +/* dequeue cell from prioq */ +extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f); + +/* return number of events available in prioq */ +extern int snd_seq_prioq_avail(prioq_t *f); + +/* peek at cell at the head of the prioq */ +extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f); + +/* client left queue */ +extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp); + +/* Remove events */ +void snd_seq_prioq_remove_events(prioq_t * f, int client, + snd_seq_remove_events_t *info); + +#endif diff -Nru linux/sound/core/seq/seq_queue.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.c --- linux/sound/core/seq/seq_queue.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,872 @@ +/* + * ALSA sequencer Timing queue handling + * Copyright (c) 1998-1999 by Frank van de Pol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * MAJOR CHANGES + * Nov. 13, 1999 Takashi Iwai + * - Queues are allocated dynamically via ioctl. + * - When owner client is deleted, all owned queues are deleted, too. + * - Owner of unlocked queue is kept unmodified even if it is + * manipulated by other clients. + * - Owner field in SET_QUEUE_OWNER ioctl must be identical with the + * caller client. i.e. Changing owner to a third client is not + * allowed. + * + * Aug. 30, 2000 Takashi Iwai + * - Queues are managed in static array again, but with better way. + * The API itself is identical. + * - The queue is locked when queue_t pinter is returned via + * queueptr(). This pointer *MUST* be released afterward by + * queuefree(ptr). + * - Addition of experimental sync support. + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_clientmgr.h" +#include "seq_fifo.h" +#include "seq_timer.h" +#include "seq_info.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT +/* FIXME: this should be in a header file */ +void snd_seq_sync_info_read(queue_t *q, snd_info_buffer_t *buffer); +#endif + +static void snd_seq_check_queue_in_tasklet(unsigned long private_data); + +/* list of allocated queues */ +static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES]; +static spinlock_t queue_list_lock = SPIN_LOCK_UNLOCKED; +/* number of queues allocated */ +static int num_queues = 0; + +int snd_seq_queue_get_cur_queues(void) +{ + return num_queues; +} + +/*----------------------------------------------------------------*/ + +/* assign queue id and insert to list */ +static int queue_list_add(queue_t *q) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&queue_list_lock, flags); + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if (! queue_list[i]) { + queue_list[i] = q; + q->queue = i; + num_queues++; + spin_unlock_irqrestore(&queue_list_lock, flags); + return i; + } + } + spin_unlock_irqrestore(&queue_list_lock, flags); + return -1; +} + +static queue_t *queue_list_remove(int id, int client) +{ + queue_t *q; + unsigned long flags; + + spin_lock_irqsave(&queue_list_lock, flags); + q = queue_list[id]; + if (q) { + spin_lock(&q->owner_lock); + if (q->owner == client) { + /* found */ + q->klocked = 1; + spin_unlock(&q->owner_lock); + queue_list[id] = NULL; + num_queues--; + spin_unlock_irqrestore(&queue_list_lock, flags); + return q; + } + spin_unlock(&q->owner_lock); + } + spin_unlock_irqrestore(&queue_list_lock, flags); + return NULL; +} + +/*----------------------------------------------------------------*/ + +/* create new queue (constructor) */ +static queue_t *queue_new(int owner, int locked) +{ + queue_t *q; + + q = snd_kcalloc(sizeof(queue_t), GFP_KERNEL); + if (q == NULL) { + snd_printd("malloc failed for snd_seq_queue_new()\n"); + return NULL; + } + + spin_lock_init(&q->owner_lock); + spin_lock_init(&q->check_lock); + init_MUTEX(&q->timer_mutex); + snd_use_lock_init(&q->use_lock); + tasklet_init(&q->taskq, snd_seq_check_queue_in_tasklet, (unsigned long)q); + q->queue = -1; + + q->tickq = snd_seq_prioq_new(); + q->timeq = snd_seq_prioq_new(); + q->timer = snd_seq_timer_new(); + if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) { + snd_seq_prioq_delete(&q->tickq); + snd_seq_prioq_delete(&q->timeq); + snd_seq_timer_delete(&q->timer); + kfree(q); + return NULL; + } + + q->owner = owner; + q->locked = locked; + q->klocked = 0; + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + q->master_lock = RW_LOCK_UNLOCKED; + q->slave_lock = RW_LOCK_UNLOCKED; + INIT_LIST_HEAD(&q->master_head); + q->slave.format = 0; +#endif + + return q; +} + +/* delete queue (destructor) */ +static void queue_delete(queue_t *q) +{ +#ifdef SNDRV_SEQ_SYNC_SUPPORT + if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) + snd_seq_sync_delete_port(q); +#endif + /* stop and release the timer */ + snd_seq_timer_stop(q->timer); + snd_seq_timer_close(q); + /* wait until access free */ + snd_use_lock_sync(&q->use_lock); + /* release resources... */ + snd_seq_prioq_delete(&q->tickq); + snd_seq_prioq_delete(&q->timeq); + snd_seq_timer_delete(&q->timer); + + kfree(q); +} + + +/*----------------------------------------------------------------*/ + +/* setup queues */ +int __init snd_seq_queues_init(void) +{ + /* + memset(queue_list, 0, sizeof(queue_list)); + num_queues = 0; + */ + return 0; +} + +/* delete all existing queues */ +void __exit snd_seq_queues_delete(void) +{ + int i; + + /* clear list */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if (queue_list[i]) + queue_delete(queue_list[i]); + } +} + +/* allocate a new queue - + * return queue index value or negative value for error + */ +int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags) +{ + queue_t *q; + + q = queue_new(client, locked); + if (q == NULL) + return -ENOMEM; + q->info_flags = info_flags; + if (queue_list_add(q) < 0) { + queue_delete(q); + return -ENOMEM; + } + snd_seq_queue_use(q->queue, client, 1); /* use this queue */ +#ifdef SNDRV_SEQ_SYNC_SUPPORT + if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) { + if (snd_seq_sync_create_port(q) < 0) + q->info_flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC; + } +#endif + return q->queue; +} + +/* delete a queue - queue must be owned by the client */ +int snd_seq_queue_delete(int client, int queueid) +{ + queue_t *q; + + if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES) + return -EINVAL; + q = queue_list_remove(queueid, client); + if (q == NULL) + return -EINVAL; + queue_delete(q); + + return 0; +} + + +/* return pointer to queue structure for specified id */ +queue_t *queueptr(int queueid) +{ + queue_t *q; + unsigned long flags; + + if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES) + return NULL; + spin_lock_irqsave(&queue_list_lock, flags); + q = queue_list[queueid]; + if (q) + snd_use_lock_use(&q->use_lock); + spin_unlock_irqrestore(&queue_list_lock, flags); + return q; +} + +/* return the (first) queue matching with the specified name */ +queue_t *snd_seq_queue_find_name(char *name) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) != NULL) { + if (strncpy(q->name, name, sizeof(q->name)) == 0) + return q; + queuefree(q); + } + } + return NULL; +} + + +/* -------------------------------------------------------- */ + +void snd_seq_check_queue(queue_t *q, int atomic, int hop) +{ + unsigned long flags; + int dequeue; + int rc; + snd_seq_event_cell_t *cell; + + if (q == NULL) + return; + + /* make this function non-reentrant */ + spin_lock_irqsave(&q->check_lock, flags); + if (q->check_blocked) { + q->check_again = 1; + spin_unlock_irqrestore(&q->check_lock, flags); + return; /* other thread is already checking queues */ + } + q->check_blocked = 1; + spin_unlock_irqrestore(&q->check_lock, flags); + + if (atomic) + dequeue = SNDRV_SEQ_MAX_DEQUEUE; + else + dequeue = 0x7fffffff; /* XXX */ + + __again: + /* Process tick queue... */ + + /* limit the number of elements dequeued per pass to save the machine from lockups */ + while (dequeue > 0) { + + cell = snd_seq_prioq_cell_peek(q->tickq); + if (cell == NULL) + break; + if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) { + cell = snd_seq_prioq_cell_out(q->tickq); + if (cell != NULL) { + rc = snd_seq_dispatch_event(cell, atomic, hop); + if (rc > 0) + dequeue -= rc; + } + } else { + /* event remains in the queue */ + break; + } + } + + + /* Process time queue... */ + + /* limit the number of elements dequeued per pass to save the machine from lockups */ + while (dequeue > 0) { + cell = snd_seq_prioq_cell_peek(q->timeq); + if (cell == NULL) + break; + if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) { + cell = snd_seq_prioq_cell_out(q->timeq); + if (cell != NULL) { + rc = snd_seq_dispatch_event(cell, atomic, hop); + if (rc > 0) + dequeue -= rc; + } + + } else { + /* event remains in the queue */ + break; + } + } + + /* free lock */ + spin_lock_irqsave(&q->check_lock, flags); + if (q->check_again && dequeue > 0) { + q->check_again = 0; + spin_unlock_irqrestore(&q->check_lock, flags); + goto __again; + } + q->check_blocked = 0; + if (dequeue <= 0 && atomic) + tasklet_hi_schedule(&q->taskq); + spin_unlock_irqrestore(&q->check_lock, flags); +} + +/* tasklet */ +static void snd_seq_check_queue_in_tasklet(unsigned long private_data) +{ + queue_t *q = (queue_t *)private_data; + snd_seq_check_queue(q, 0, 0); +} + +/* enqueue a event to singe queue */ +int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop) +{ + int dest; + queue_t *q; + + snd_assert(cell != NULL, return -EINVAL); + dest = cell->event.queue; /* destination queue */ + q = queueptr(dest); + if (q == NULL) + return -EINVAL; + /* handle relative time stamps, convert them into absolute */ + if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) { + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + cell->event.time.tick += q->timer->tick.cur_tick; + break; + + case SNDRV_SEQ_TIME_STAMP_REAL: + snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time); + break; + } + cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK; + cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS; + } + /* enqueue event in the real-time or midi queue */ + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + snd_seq_prioq_cell_in(q->tickq, cell); + break; + + case SNDRV_SEQ_TIME_STAMP_REAL: + snd_seq_prioq_cell_in(q->timeq, cell); + break; + } + + /* trigger dispatching */ + snd_seq_check_queue(q, atomic, hop); + + queuefree(q); /* unlock */ + + return 0; +} + + +/*----------------------------------------------------------------*/ + +static inline int check_access(queue_t *q, int client) +{ + return (q->owner == client) || (!q->locked && !q->klocked); +} + +/* check if the client has permission to modify queue parameters. + * if it does, lock the queue + */ +static int queue_access_lock(queue_t *q, int client) +{ + unsigned long flags; + int access_ok; + + spin_lock_irqsave(&q->owner_lock, flags); + access_ok = check_access(q, client); + if (access_ok) + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + return access_ok; +} + +/* unlock the queue */ +static inline void queue_access_unlock(queue_t *q) +{ + unsigned long flags; + + spin_lock_irqsave(&q->owner_lock, flags); + q->klocked = 0; + spin_unlock_irqrestore(&q->owner_lock, flags); +} + +/* exported - only checking permission */ +int snd_seq_queue_check_access(int queueid, int client) +{ + queue_t *q = queueptr(queueid); + int access_ok; + unsigned long flags; + + if (! q) + return 0; + spin_lock_irqsave(&q->owner_lock, flags); + access_ok = check_access(q, client); + spin_unlock_irqrestore(&q->owner_lock, flags); + queuefree(q); + return access_ok; +} + +/*----------------------------------------------------------------*/ + +/* + * change queue's owner and permission + */ +int snd_seq_queue_set_owner(int queueid, int client, int locked) +{ + queue_t *q = queueptr(queueid); + + if (q == NULL) + return -EINVAL; + + if (! queue_access_lock(q, client)) { + queuefree(q); + return -EPERM; + } + + q->locked = locked ? 1 : 0; + q->owner = client; + queue_access_unlock(q); + queuefree(q); + + return 0; +} + + +/*----------------------------------------------------------------*/ + +/* open timer - + * q->use mutex should be down before calling this function to avoid + * confliction with snd_seq_queue_use() + */ +int snd_seq_queue_timer_open(int queueid) +{ + int result = 0; + queue_t *queue; + seq_timer_t *tmr; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + tmr = queue->timer; + if ((result = snd_seq_timer_open(queue)) < 0) { + snd_seq_timer_defaults(tmr); + result = snd_seq_timer_open(queue); + } + queuefree(queue); + return result; +} + +/* close timer - + * q->use mutex should be down before calling this function + */ +int snd_seq_queue_timer_close(int queueid) +{ + queue_t *queue; + seq_timer_t *tmr; + int result = 0; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + tmr = queue->timer; + snd_seq_timer_close(queue); + queuefree(queue); + return result; +} + +/* change queue tempo and ppq */ +int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info) +{ + queue_t *q = queueptr(queueid); + int result; + + if (q == NULL) + return -EINVAL; + if (! queue_access_lock(q, client)) { + queuefree(q); + return -EPERM; + } + + result = snd_seq_timer_set_tempo(q->timer, info->tempo); + if (result >= 0) + result = snd_seq_timer_set_ppq(q->timer, info->ppq); + if (result >= 0 && info->skew_base > 0) + result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_tempo(q); +#endif + queue_access_unlock(q); + queuefree(q); + return result; +} + + +/* use or unuse this queue - + * if it is the first client, starts the timer. + * if it is not longer used by any clients, stop the timer. + */ +int snd_seq_queue_use(int queueid, int client, int use) +{ + queue_t *queue; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + down(&queue->timer_mutex); + if (use) { + if (!test_and_set_bit(client, &queue->clients_bitmap)) + queue->clients++; + } else { + if (test_and_clear_bit(client, &queue->clients_bitmap)) + queue->clients--; + } + if (queue->clients) { + if (use && queue->clients == 1) + snd_seq_timer_defaults(queue->timer); + snd_seq_timer_open(queue); + } else { + snd_seq_timer_close(queue); + } + up(&queue->timer_mutex); + queuefree(queue); + return 0; +} + +/* + * check if queue is used by the client + * return negative value if the queue is invalid. + * return 0 if not used, 1 if used. + */ +int snd_seq_queue_is_used(int queueid, int client) +{ + queue_t *q; + int result; + + q = queueptr(queueid); + if (q == NULL) + return -EINVAL; /* invalid queue */ + result = test_bit(client, &q->clients_bitmap) ? 1 : 0; + queuefree(q); + return result; +} + + +/*----------------------------------------------------------------*/ + +/* notification that client has left the system - + * stop the timer on all queues owned by this client + */ +void snd_seq_queue_client_termination(int client) +{ + unsigned long flags; + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + spin_lock_irqsave(&q->owner_lock, flags); + if (q->owner == client) + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + if (q->owner == client) { + if (q->timer->running) + snd_seq_timer_stop(q->timer); + snd_seq_timer_reset(q->timer); + } + queuefree(q); + } +} + +/* final stage notification - + * remove cells for no longer exist client (for non-owned queue) + * or delete this queue (for owned queue) + */ +void snd_seq_queue_client_leave(int client) +{ + int i; + queue_t *q; + + /* delete own queues from queue list */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queue_list_remove(i, client)) != NULL) + queue_delete(q); + } + + /* remove cells from existing queues - + * they are not owned by this client + */ + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + if (test_bit(client, q->clients_bitmap)) { + snd_seq_prioq_leave(q->tickq, client, 0); + snd_seq_prioq_leave(q->timeq, client, 0); + snd_seq_queue_use(q->queue, client, 0); + } + queuefree(q); + } +} + + + +/*----------------------------------------------------------------*/ + +/* remove cells from all queues */ +void snd_seq_queue_client_leave_cells(int client) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + snd_seq_prioq_leave(q->tickq, client, 0); + snd_seq_prioq_leave(q->timeq, client, 0); + queuefree(q); + } +} + +/* remove cells based on flush criteria */ +void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info) +{ + int i; + queue_t *q; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + if (test_bit(client, q->clients_bitmap) && + (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) || + q->queue == info->queue)) { + snd_seq_prioq_remove_events(q->tickq, client, info); + snd_seq_prioq_remove_events(q->timeq, client, info); + } + queuefree(q); + } +} + +/*----------------------------------------------------------------*/ + +/* + * send events to all subscribed ports + */ +static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop) +{ + snd_seq_event_t sev; + + sev = *ev; + + sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS; + sev.time.tick = q->timer->tick.cur_tick; + sev.queue = q->queue; + sev.data.queue.queue = q->queue; + + if (from_timer_port) { + /* broadcast events from Timer port */ + sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; + sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop); + } +#ifdef SNDRV_SEQ_SYNC_SUPPORT + if (q->info_flags & SNDRV_SEQ_QUEUE_FLG_SYNC) { + /* broadcast events also to slave clients */ + sev.source = q->sync_port; + sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop); + } +#endif +} + +/* + * process a received queue-control event. + * this function is exported for seq_sync.c. + */ +void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop) +{ + switch (ev->type) { + case SNDRV_SEQ_EVENT_START: + snd_seq_prioq_leave(q->tickq, ev->source.client, 1); + snd_seq_prioq_leave(q->timeq, ev->source.client, 1); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_clear(q); +#endif + snd_seq_timer_start(q->timer); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + snd_seq_sync_check(q, 0, atomic, hop); /* trigger the first signal */ +#endif + break; + + case SNDRV_SEQ_EVENT_CONTINUE: + snd_seq_timer_continue(q->timer); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + snd_seq_sync_check(q, 0, atomic, hop); +#endif + break; + + case SNDRV_SEQ_EVENT_STOP: + snd_seq_timer_stop(q->timer); + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_TEMPO: + snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_tempo(q); +#endif + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + break; + + case SNDRV_SEQ_EVENT_SETPOS_TICK: + if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) { +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_tick(q, 0, atomic, hop); +#endif + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + + case SNDRV_SEQ_EVENT_SETPOS_TIME: + if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) { +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_update_time(q, 0, atomic, hop); +#endif + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + case SNDRV_SEQ_EVENT_QUEUE_SKEW: + if (snd_seq_timer_set_skew(q->timer, + ev->data.queue.param.skew.value, + ev->data.queue.param.skew.base) == 0) { + queue_broadcast_event(q, ev, from_timer_port, atomic, hop); + } + break; + } +} + + +/* + * Queue control via timer control port: + * this function is exported as a callback of timer port. + */ +int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop) +{ + queue_t *q; + + snd_assert(ev != NULL, return -EINVAL); + q = queueptr(ev->data.queue.queue); + + if (q == NULL) + return -EINVAL; + + if (! queue_access_lock(q, ev->source.client)) { + queuefree(q); + return -EPERM; + } + + snd_seq_queue_process_event(q, ev, 1, atomic, hop); + + queue_access_unlock(q); + queuefree(q); + return 0; +} + + +/*----------------------------------------------------------------*/ + +/* exported to seq_info.c */ +void snd_seq_info_queues_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + int i, bpm; + queue_t *q; + seq_timer_t *tmr; + + for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { + if ((q = queueptr(i)) == NULL) + continue; + + tmr = q->timer; + if (tmr->tempo) + bpm = 60000000 / tmr->tempo; + else + bpm = 0; + + snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name); + snd_iprintf(buffer, "owned by client : %d\n", q->owner); + snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free"); + snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq)); + snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq)); + snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped"); + snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq); + snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo); + snd_iprintf(buffer, "current BPM : %d\n", bpm); + snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec); + snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick); + snd_iprintf(buffer, "\n"); +#ifdef SNDRV_SEQ_SYNC_SUPPORT + snd_seq_sync_info_read(q, buffer); +#endif + queuefree(q); + } +} diff -Nru linux/sound/core/seq/seq_queue.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.h --- linux/sound/core/seq/seq_queue.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_queue.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,154 @@ +/* + * ALSA sequencer Queue handling + * Copyright (c) 1998-1999 by Frank van de Pol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_QUEUE_H +#define __SND_SEQ_QUEUE_H + +#include "seq_memory.h" +#include "seq_prioq.h" +#include "seq_timer.h" +#include "seq_lock.h" +#ifdef SNDRV_SEQ_SYNC_SUPPORT +#include "seq_sync.h" +#endif +#include +#include + +#define SEQ_QUEUE_NO_OWNER (-1) + +struct _snd_seq_queue { + int queue; /* queue number */ + + char name[64]; /* name of this queue */ + + prioq_t *tickq; /* midi tick event queue */ + prioq_t *timeq; /* real-time event queue */ + + seq_timer_t *timer; /* time keeper for this queue */ + int owner; /* client that 'owns' the timer */ + unsigned int locked:1, /* timer is only accesibble by owner if set */ + klocked:1, /* kernel lock (after START) */ + check_again:1, + check_blocked:1; + + unsigned int flags; /* status flags */ + unsigned int info_flags; /* info for sync */ + + spinlock_t owner_lock; + spinlock_t check_lock; + + /* clients which uses this queue (bitmap) */ + unsigned int clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned int)]; + unsigned int clients; /* users of this queue */ + struct semaphore timer_mutex; + + snd_use_lock_t use_lock; + + struct tasklet_struct taskq; + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + struct list_head master_head; /* list of masters */ + queue_sync_t slave; /* slave (exclusive) */ + + rwlock_t master_lock; + rwlock_t slave_lock; + + snd_seq_addr_t sync_port; /* address of the attached sync port */ +#endif +}; + + +/* get the number of current queues */ +int snd_seq_queue_get_cur_queues(void); + +/* init queues structure */ +int snd_seq_queues_init(void); + +/* delete queues */ +void snd_seq_queues_delete(void); + + +/* create new queue (constructor) */ +int snd_seq_queue_alloc(int client, int locked, unsigned int flags); + +/* delete queue (destructor) */ +int snd_seq_queue_delete(int client, int queueid); + +/* notification that client has left the system */ +void snd_seq_queue_client_termination(int client); + +/* final stage */ +void snd_seq_queue_client_leave(int client); + +/* enqueue a event received from one the clients */ +int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop); + +/* Remove events */ +void snd_seq_queue_client_leave_cells(int client); +void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info); + +/* return pointer to queue structure for specified id */ +queue_t *queueptr(int queueid); +/* unlock */ +#define queuefree(q) snd_use_lock_free(&(q)->use_lock) + +/* return the (first) queue matching with the specified name */ +queue_t *snd_seq_queue_find_name(char *name); + +/* check single queue and dispatch events */ +void snd_seq_check_queue(queue_t *q, int atomic, int hop); + +/* access to queue's parameters */ +int snd_seq_queue_check_access(int queueid, int client); +int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info); +int snd_seq_queue_set_owner(int queueid, int client, int locked); +int snd_seq_queue_set_locked(int queueid, int client, int locked); +int snd_seq_queue_timer_open(int queueid); +int snd_seq_queue_timer_close(int queueid); +int snd_seq_queue_use(int queueid, int client, int use); +int snd_seq_queue_is_used(int queueid, int client); + +int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop); +void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int from_timer_port, int atomic, int hop); + +/* + * 64bit division - for sync stuff.. + */ +#if defined(i386) || defined(i486) + +#define udiv_qrnnd(q, r, n1, n0, d) \ + __asm__ ("divl %4" \ + : "=a" ((u32)(q)), \ + "=d" ((u32)(r)) \ + : "0" ((u32)(n0)), \ + "1" ((u32)(n1)), \ + "rm" ((u32)(d))) + +#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0) +#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0) +#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y) + +#else +#define u64_div(x,y,q) ((q) = (u32)((u64)(x) / (u64)(y))) +#define u64_mod(x,y,r) ((r) = (u32)((u64)(x) % (u64)(y))) +#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r)) +#endif + + +#endif diff -Nru linux/sound/core/seq/seq_sync.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.c --- linux/sound/core/seq/seq_sync.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1006 @@ +/* + * ALSA sequencer queue synchronization routine + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include + +#include "seq_memory.h" +#include "seq_queue.h" +#include "seq_clientmgr.h" +#include "seq_fifo.h" +#include "seq_timer.h" +#include "seq_info.h" + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + +#define FOR_EACH_LIST(var,list) \ +for (var = (list)->next; var != list; var = var->next) + +/* + * callbacks + */ +static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop); +static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs); +static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs); +static void queue_delete_all_masters(queue_t *q); +static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs); +static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs); +static void queue_sync_close_parser(queue_sync_t *sync, int slave); + +/* + * pre-defined event parsers + */ + +extern seq_sync_parser_t snd_seq_midi_clock_parser; /* seq_midi_clock.c */ +extern seq_sync_parser_t snd_seq_mtc_parser; /* seq_mtc.c */ +extern seq_sync_parser_t snd_seq_dtl_parser; /* seq_dtl.c */ + +static seq_sync_parser_t *event_parsers[] = { + &snd_seq_midi_clock_parser, + &snd_seq_mtc_parser, + &snd_seq_dtl_parser, + NULL +}; + +/* + * create a sync port corresponding to the specified queue + */ +int snd_seq_sync_create_port(queue_t *queue) +{ + snd_seq_port_info_t port; + snd_seq_port_callback_t pcallbacks; + + memset(&pcallbacks, 0, sizeof(pcallbacks)); + memset(&port, 0, sizeof(port)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.event_input = event_input_sync; + pcallbacks.subscribe = queue_master_add; + pcallbacks.unsubscribe = queue_master_remove; + pcallbacks.use = queue_slave_set; + pcallbacks.unuse = queue_slave_reset; + pcallbacks.private_data = queue; + pcallbacks.callback_all = 1; /* call callbacks at each subscription */ + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ| + SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE| + SNDRV_SEQ_PORT_CAP_DUPLEX| + SNDRV_SEQ_PORT_CAP_SYNC_READ|SNDRV_SEQ_PORT_CAP_SYNC_WRITE; + port.type = 0; + sprintf(port.name, "Sync Queue %d", queue->queue); + port.kernel = &pcallbacks; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.port = snd_seq_queue_sync_port(queue->queue); + if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_CREATE_PORT, &port) < 0) + return -ENOMEM; + queue->sync_port.client = SNDRV_SEQ_CLIENT_SYSTEM; + queue->sync_port.port = port.port; + return 0; +} + +/* + * delete attached sync port to the queue + */ +int snd_seq_sync_delete_port(queue_t *queue) +{ + snd_seq_port_info_t port; + + memset(&port, 0, sizeof(port)); + port.client = queue->sync_port.client; + port.port = queue->sync_port.port; + if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_DELETE_PORT, &port) < 0) + return -ENOMEM; + queue_delete_all_masters(queue); + queue_sync_close_parser(&queue->slave, 1); + return 0; +} + + +/* + * send a sync signal to the sync slave client + */ +static void queue_send_sync_event(queue_t *q, queue_sync_t *master, int type, int atomic, int hop) +{ + snd_seq_event_t event; + + memset(&event, 0, sizeof(event)); + + event.flags = SNDRV_SEQ_TIME_MODE_ABS; + /* since we use direct delivery, we have to convert time stamp here.. */ + switch (master->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + event.flags |= SNDRV_SEQ_TIME_STAMP_TICK; + event.time.tick = q->timer->tick.cur_tick; + break; + case SNDRV_SEQ_SYNC_TIME: + event.flags |= SNDRV_SEQ_TIME_STAMP_REAL; + event.time.time = q->timer->cur_time; + break; + } + event.type = type; + event.data.queue.queue = q->queue; + event.data.queue.sync_format = master->format; + event.data.queue.sync_time_format = master->time_format; + event.data.queue.param.position = master->counter; + event.source = q->sync_port; + event.dest = master->addr; + if (master->parser) { + snd_seq_event_t newev; + newev = event; + if (master->parser->out.sync(master->parser_arg, &event, &newev) > 0) { + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &newev, atomic, hop); + return; + } + } + snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &event, atomic, hop); +} + +/* + * initialize the sync position + */ +static void queue_sync_clear(queue_sync_t *sync) +{ + memset(&sync->cur_time, 0, sizeof(sync->cur_time)); + sync->counter = 0; + sync->sync_tick.cur_tick = 0; + sync->sync_tick.fraction = 0; +} + +/* + * initialize all sync positions + */ +void snd_seq_sync_clear(queue_t *q) +{ + struct list_head *head; + + /* clear master positions */ + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + queue_sync_clear(master); + } + read_unlock(&q->master_lock); + read_lock(&q->slave_lock); + queue_sync_clear(&q->slave); + read_unlock(&q->slave_lock); +} + + +/* + * change tick resolution of sync master/slave + */ +static void queue_sync_set_tick_resolution(queue_t *q, queue_sync_t *sync) +{ + unsigned int tempo, ppq; + tempo = q->timer->tempo; + if (sync->param.tick.ppq == 0) + ppq = q->timer->ppq; + else + ppq = sync->param.tick.ppq; + snd_seq_timer_set_tick_resolution(&sync->sync_tick, tempo, ppq, sync->param.tick.ticks); +} + + +/* + * update sync-master resolutions + */ +void snd_seq_sync_update_tempo(queue_t *q) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format & SNDRV_SEQ_SYNC_TICK) + queue_sync_set_tick_resolution(q, master); + } + read_unlock(&q->master_lock); + read_lock(&q->slave_lock); + if (q->slave.format & SNDRV_SEQ_SYNC_TICK) + queue_sync_set_tick_resolution(q, &q->slave); + read_unlock(&q->slave_lock); +} + + +/* + * change the tick position from the current tick of the queue + */ +static void queue_sync_change_tick(queue_t *q, queue_sync_t *sync) +{ + if (sync->param.tick.ppq == 0) + sync->counter = q->timer->tick.cur_tick; + else + sync->counter = (q->timer->tick.cur_tick * sync->param.tick.ppq) / q->timer->ppq; + sync->counter /= sync->param.tick.ticks; + sync->sync_tick.cur_tick = sync->counter; + sync->sync_tick.fraction = 0; +} + +/* + * change the time position from the current time of the queue + */ +static void queue_sync_change_time(queue_t *q, queue_sync_t *sync) +{ + /* we need 64bit calculation here.. */ + u64 nsec; + + nsec = q->timer->cur_time.tv_sec; + nsec *= 1000000000UL; + nsec += q->timer->cur_time.tv_nsec; + u64_div(nsec, sync->param.time.resolution, sync->counter); + sync->counter *= sync->param.time.subframes; + sync->cur_time = q->timer->cur_time; +} + + +/* + * update the tick position of all sync + */ +void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format & SNDRV_SEQ_SYNC_TICK) { + queue_sync_change_tick(q, master); + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop); /* broadcast to client */ + } + } + read_unlock(&q->master_lock); + if (master_only) + return; + read_lock(&q->slave_lock); + if (q->slave.format & SNDRV_SEQ_SYNC_TICK) + queue_sync_change_tick(q, &q->slave); + read_unlock(&q->slave_lock); +} + +/* + * update the time position of all sync + */ +void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format & SNDRV_SEQ_SYNC_TIME) { + queue_sync_change_time(q, master); + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop); + } + } + read_unlock(&q->master_lock); + if (master_only) + return; + read_lock(&q->slave_lock); + if (q->slave.format & SNDRV_SEQ_SYNC_TIME) + queue_sync_change_time(q, &q->slave); + read_unlock(&q->slave_lock); +} + + +/* + * check the current timer value and send sync messages if the sync + * time is elapsed + */ +static void queue_master_check(queue_t *q, unsigned long ticks, int atomic, int hop) +{ + struct list_head *head; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + switch (master->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + snd_seq_timer_update_tick(&master->sync_tick, ticks); + while (master->sync_tick.cur_tick >= master->counter) { + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop); + master->counter++; + } + break; + case SNDRV_SEQ_SYNC_TIME: + while (snd_seq_compare_real_time(&q->timer->cur_time, &master->cur_time)) { + queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop); + snd_seq_inc_time_nsec(&master->cur_time, master->resolution); + master->counter++; + } + break; + } + } + read_unlock(&q->master_lock); +} + + +/* + * slave stuff + */ + +/* + * update tick + */ +static void queue_slave_check(queue_t *q, unsigned long ticks) +{ + switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + snd_seq_timer_update_tick(&q->slave.sync_tick, ticks); + break; + case SNDRV_SEQ_SYNC_TIME: + /* nothing */ + break; + } +} + +/* + * slave synchronization in real-time unit + */ +static int queue_slave_sync_time(queue_t *q, unsigned int position) +{ + struct timeval tm; + long diff_time, new_period; + queue_sync_t *sync = &q->slave; + sndrv_seq_queue_time_sync_t *p = &sync->param.time; + seq_timer_t *tmr = q->timer; + u64 external_counter, tmp; + + do_gettimeofday(&tm); + if (tmr->sync_start) { + /* XXX: we should use 64bit for diff_time, too. */ + diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 + + ((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec); + diff_time = (p->x0 * tmr->sync_time_diff + p->x1 * (diff_time * 1000)) / (p->x0 + p->x1); +#define MIN_DIFF_TIME 1000 /* 1ms minimum */ + if (diff_time < MIN_DIFF_TIME) + diff_time = MIN_DIFF_TIME; + tmr->sync_time_diff = diff_time; + tmp = (u64)tmr->base_period * (u64)sync->resolution; + u64_div(tmp, diff_time, new_period); + + /* phase adjustment */ + external_counter = position; + external_counter *= sync->resolution; + + /* calculate current time */ + tmp = snd_seq_timer_get_cur_nsec(tmr, &tm); + + if (external_counter > tmp) { + tmp = external_counter - tmp; + if (tmp < p->max_time_diff) { + /* locked */ + int hz = p->phase_correct_time / tmr->base_period; + diff_time = (u32)tmp; + diff_time /= hz; + new_period += diff_time; + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } + } else { + tmp = tmp - external_counter; + if (tmp == 0) + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + else if (tmp < p->max_time_diff) { + /* locked */ + int hz = p->phase_correct_time / tmr->base_period; + diff_time = (u32)tmp; + diff_time /= hz; + if (new_period - diff_time > MIN_DIFF_TIME) { + new_period -= diff_time; + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } else + q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } + } + tmr->period = new_period; + } else { + tmr->sync_start = 1; + tmr->sync_time_diff = sync->resolution; + } + tmr->sync_last_tm = tm; + sync->counter = position; + + return 0; +} + +/* + * slave synchronization in tick unit + */ +static int queue_slave_sync_tick(queue_t *q, unsigned int position) +{ + struct timeval tm; + long diff_time, tick_diff; + unsigned int tick_time; + queue_sync_t *sync = &q->slave; + seq_timer_t *tmr = q->timer; + sndrv_seq_queue_tick_sync_t *p = &sync->param.tick; + + do_gettimeofday(&tm); + if (tmr->sync_start) { + /* XXX: diff_time should be 64bit for enough long sync period.. */ + diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 + + ((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec); + diff_time *= 1000; /* in nsec */ + tick_time = (p->x0 * sync->sync_tick.resolution + + p->x1 * diff_time) / (p->x0 + p->x1); + /* phase adjustment */ + tick_diff = (long)position - (long)sync->sync_tick.cur_tick; + if (tick_diff != 0) { + if (tick_diff >= -p->max_tick_diff && + tick_diff <= p->max_tick_diff) { + /* locked */ + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + tick_time = (tick_time * p->max_tick_diff2) / + (p->max_tick_diff2 + tick_diff); + } else { + /* sync lost.. freewheeling */ + q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + } + } else + q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST; + +#define MIN_TICK_TIME 1000 /* 1ms */ + if (tick_time < MIN_TICK_TIME) + tick_time = MIN_TICK_TIME; + + sync->sync_tick.resolution = tick_time; + snd_seq_timer_update_tick(&sync->sync_tick, 0); + if (p->ppq) + tmr->tick.resolution = (tick_time * p->ppq) / tmr->ppq; + else + tmr->tick.resolution = tick_time; + snd_seq_timer_update_tick(&tmr->tick, 0); + tmr->tempo = (tmr->tick.resolution * tmr->ppq) / 1000; + + } else + tmr->sync_start = 1; + tmr->sync_last_tm = tm; + + sync->counter = position; + + return 0; +} + + +/* + */ +static void queue_slave_jump_to_time(queue_t *q, unsigned int position, int atomic, int hop) +{ + u64 nsec; + queue_sync_t *sync = &q->slave; + + q->slave.counter = position; + nsec = sync->counter; + nsec *= sync->resolution; + u64_divmod(nsec, 1000000000, sync->cur_time.tv_sec, sync->cur_time.tv_nsec); + q->timer->cur_time = sync->cur_time; + + /* update master */ + snd_seq_sync_update_time(q, 1, atomic, hop); +} + +static void queue_slave_jump_to_tick(queue_t *q, unsigned int position, int atomic, int hop) +{ + unsigned int tick; + queue_sync_t *sync = &q->slave; + + sync->counter = position; + sync->sync_tick.cur_tick = sync->counter; + sync->sync_tick.fraction = 0; + + /* update queue timer */ + if (sync->param.tick.ppq == 0) + tick = sync->counter; + else + tick = sync->counter * q->timer->ppq / sync->param.tick.ppq; + q->timer->tick.cur_tick = tick * sync->param.tick.ticks; + q->timer->tick.fraction = 0; + + /* update master */ + snd_seq_sync_update_tick(q, 1, atomic, hop); +} + + +/* + * event input callback + */ +static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + queue_t *q = private_data; + unsigned long flags; + snd_seq_event_t newev; + + snd_assert(q != NULL, return -ENXIO); + + /* lock the queue owner access.. */ + spin_lock_irqsave(&q->owner_lock, flags); + q->klocked = 1; + spin_unlock_irqrestore(&q->owner_lock, flags); + + read_lock(&q->slave_lock); + if (q->slave.format) { + if (q->slave.parser) { + memset(&newev, 0, sizeof(newev)); + if (q->slave.parser->in.sync(q->slave.parser_arg, ev, &newev) > 0) + ev = &newev; + } + } + if (ev->type == SNDRV_SEQ_EVENT_SYNC) { + /* slave signal received */ + switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + queue_slave_sync_tick(q, ev->data.queue.param.position); + break; + case SNDRV_SEQ_SYNC_TIME: + queue_slave_sync_time(q, ev->data.queue.param.position); + break; + } + } else if (ev->type == SNDRV_SEQ_EVENT_SYNC_POS) { + /* jump to position */ + switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + if (q->timer->running) + queue_slave_sync_tick(q, ev->data.queue.param.position); + else + queue_slave_jump_to_tick(q, ev->data.queue.param.position, atomic, hop); + break; + case SNDRV_SEQ_SYNC_TIME: + if (q->timer->running) + queue_slave_sync_time(q, ev->data.queue.param.position); + else + queue_slave_jump_to_time(q, ev->data.queue.param.position, atomic, hop); + break; + } + } else { + /* control queue */ + snd_seq_queue_process_event(q, ev, 0, atomic, hop); + } + read_unlock(&q->slave_lock); + + /* unlock */ + spin_lock_irqsave(&q->owner_lock, flags); + q->klocked = 0; + spin_unlock_irqrestore(&q->owner_lock, flags); + + return 0; +} + + +/* + * initialize sync parameters + */ +static int queue_param_init(queue_t *q, queue_sync_t *sync, + snd_seq_addr_t *addr, sndrv_seq_queue_sync_t *info, + int slave) +{ + seq_sync_parser_t *parser, **list; + + sync->format = info->format; + sync->time_format = info->time_format; + *sync->opt_info = *info->info; + sync->addr = *addr; + /* copy params */ + if (info->format&SNDRV_SEQ_SYNC_TICK) + sync->param.tick=info->param.tick; + else + sync->param.time=info->param.time; + + sync->parser = NULL; + sync->parser_arg = NULL; + for (list = event_parsers; (parser = *list) != NULL; list++) { + if (parser->format == sync->format) { + int err; + if (slave) + err = parser->in.open(sync, &sync->parser_arg); + else + err = parser->out.open(sync, &sync->parser_arg); + if (err < 0) + return err; + sync->parser = parser; + break; + } + } + + switch (sync->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + if (sync->param.tick.ppq > 200) + goto __error; + if (sync->param.tick.ticks == 0) + sync->param.tick.ticks = 1; + queue_sync_set_tick_resolution(q, sync); + /* sync slave parameters -- will be configurable */ + sync->param.tick.x0 = 4; + sync->param.tick.x1 = 1; + sync->param.tick.max_tick_diff = 50; + sync->param.tick.max_tick_diff2 = sync->param.tick.max_tick_diff * 2; + break; + case SNDRV_SEQ_SYNC_TIME: + sync->resolution = sync->param.time.resolution; + if (sync->param.time.subframes == 0) + goto __error; + sync->resolution /= sync->param.time.subframes; + if (sync->resolution < 1000000) /* minimum = 1ms */ + goto __error; + /* sync slave parameters -- will be configurable */ + sync->param.time.x0 = 2; + sync->param.time.x1 = 1; + sync->param.time.max_time_diff = 1000000000UL; /* 1s */ + sync->param.time.phase_correct_time = 100000000UL; /* 0.1s */ + break; + default: + snd_printd("seq_sync: invalid format %x\n", sync->format); + goto __error; + } + return 0; + +__error: + queue_sync_close_parser(sync, slave); + return -EINVAL; +} + + +/* + * close event parser if exists + */ +static void queue_sync_close_parser(queue_sync_t *sync, int slave) +{ + if (sync->parser == NULL) + return; + if (slave) { + if (sync->parser->in.close) + sync->parser->in.close(sync->parser_arg); + else if (sync->parser_arg) + kfree(sync->parser_arg); + } else { + if (sync->parser->out.close) + sync->parser->out.close(sync->parser_arg); + else if (sync->parser_arg) + kfree(sync->parser_arg); + } + sync->parser = NULL; + sync->parser_arg = NULL; +} + + +/* + * add to master list + */ +static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + queue_sync_t *master; + unsigned long flags; + int err; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + master = snd_kcalloc(sizeof(*master), GFP_KERNEL); + if (master == NULL) + return -ENOMEM; + err = queue_param_init(q, master, &subs->dest, &subs->opt.sync_info, 0); + if (err < 0) { + kfree(master); + return err; + } + write_lock_irqsave(&q->master_lock, flags); + list_add(&master->list, &q->master_head); + write_unlock_irqrestore(&q->master_lock, flags); + + return 0; +} + +/* + * remove master + */ +static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + sndrv_seq_queue_sync_t *info; + snd_seq_addr_t *addr; + struct list_head *head; + unsigned long flags; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + info = &subs->opt.sync_info; + addr = &subs->dest; + + write_lock_irqsave(&q->master_lock, flags); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + if (master->format == info->format && + master->addr.client == addr->client && + master->addr.port == addr->port) { + list_del(&master->list); + write_unlock_irqrestore(&q->master_lock, flags); + queue_sync_close_parser(master, 0); + kfree(master); + return 0; + } + } + write_unlock_irqrestore(&q->master_lock, flags); + snd_printd("seq_queue: can't find master from %d.%d format %0x\n", addr->client, addr->port, info->format); + return -ENXIO; +} + +/* remove all master connections if any exist */ +static void queue_delete_all_masters(queue_t *q) +{ + struct list_head *head; + unsigned long flags; + + write_lock_irqsave(&q->master_lock, flags); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + list_del(&master->list); + queue_sync_close_parser(master, 0); + kfree(master); + } + write_unlock_irqrestore(&q->master_lock, flags); +} + +/* + * set slave mode + */ +static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + unsigned long flags; + int err; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + write_lock_irqsave(&q->slave_lock, flags); + if (q->slave.format) { + write_unlock_irqrestore(&q->slave_lock, flags); + return -EBUSY; + } + err = queue_param_init(q, &q->slave, &subs->sender, + &subs->opt.sync_info, 1); + if (err < 0) { + q->slave.format = 0; + write_unlock_irqrestore(&q->slave_lock, flags); + return err; + } + write_unlock_irqrestore(&q->slave_lock, flags); + return 0; +} + +/* + * remove slave mode + */ +static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs) +{ + queue_t *q = private_data; + unsigned long flags; + + snd_assert(q != NULL, return -EINVAL); + if (! subs->sync) + return -EINVAL; + write_lock_irqsave(&q->slave_lock, flags); + if (q->slave.addr.client == subs->sender.client && + q->slave.addr.port == subs->sender.port) { + q->slave.format = 0; + queue_sync_close_parser(&q->slave, 1); + write_unlock_irqrestore(&q->slave_lock, flags); + return 0; + } + write_unlock_irqrestore(&q->slave_lock, flags); + snd_printd("seq_queue: can't match slave condition\n"); + return -ENXIO; +} + + +/* + * sync check + * this function is called at each timer interrupt. + */ + +void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop) +{ + queue_master_check(q, resolution, atomic, hop); + queue_slave_check(q, resolution); +} + + +/* + * support functions for SMPTE time frame + */ +static unsigned int linear_time_to_position(sndrv_seq_time_frame_t time, + int nframes, int nsubs) +{ + unsigned int count; + count = time.hour * 60 + time.min; + count = count * 60 + time.sec; + count = count * nframes + time.frame; + count = count * nsubs + time.subframe; + return count; +} + +static sndrv_seq_time_frame_t linear_position_to_time(unsigned int count, + int nframes, int nsubs) +{ + sndrv_seq_time_frame_t time; + time.subframe = count % nsubs; + count /= nsubs; + time.hour = count / (3600 * nframes); + count %= 3600 * nframes; + time.min = count / (60 * nframes); + count %= 60 * nframes; + time.sec = count / nframes; + time.frame = count % nframes; + return time; +} + +/* drop frame - only 30fps */ +#define NFRAMES 30 +#define FRAMES_PER_MIN (NFRAMES * 60 - 2) +#define FRAMES_PER_10MIN (FRAMES_PER_MIN * 10 + 2) +#define FRAMES_PER_HOUR (FRAMES_PER_10MIN * 6) + +static unsigned int drop_time_to_position(sndrv_seq_time_frame_t time, int nsubs) +{ + unsigned int count, min; + + min = time.min % 10; + count = time.frame; + if (min > 0) { + if (time.sec == 0 && time.frame < 2) + count = 2; + } + count += time.sec * NFRAMES; + count += min * FRAMES_PER_MIN; + count += (time.min / 10) * FRAMES_PER_10MIN; + count += time.hour * (FRAMES_PER_HOUR); + count *= nsubs; + count += time.subframe; + + return count; +} + +static sndrv_seq_time_frame_t drop_position_to_time(int count, int nsubs) +{ + unsigned int min10; + sndrv_seq_time_frame_t time; + + time.subframe = count % nsubs; + count /= nsubs; + min10 = count / FRAMES_PER_10MIN; + time.hour = min10 / 6; + min10 %= 6; + count %= FRAMES_PER_10MIN; + if (count < 2) { + time.min = min10 * 10; + time.sec = 0; + } else { + count -= 2; + time.min = count / FRAMES_PER_MIN; + time.min += min10 * 10; + count %= FRAMES_PER_MIN; + count += 2; + time.sec = count / NFRAMES; + count %= NFRAMES; + } + time.frame = count; + + return time; +} + +/* convert from position counter to time frame */ +sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos) +{ + switch (format) { + case SNDRV_SEQ_SYNC_FPS_24: + return linear_position_to_time(pos, 24, nsubs); + case SNDRV_SEQ_SYNC_FPS_25: + return linear_position_to_time(pos, 25, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_NDP: + return linear_position_to_time(pos, 30, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_DP: + default: + return drop_position_to_time(pos, nsubs); + } +} + +/* convert from position counter to time frame */ +unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime) +{ + switch (format) { + case SNDRV_SEQ_SYNC_FPS_24: + return linear_time_to_position(*rtime, 24, nsubs); + case SNDRV_SEQ_SYNC_FPS_25: + return linear_time_to_position(*rtime, 25, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_NDP: + return linear_time_to_position(*rtime, 30, nsubs); + case SNDRV_SEQ_SYNC_FPS_30_DP: + default: + return drop_time_to_position(*rtime, nsubs); + } +} + +/* resolution in nsec */ +unsigned long snd_seq_get_smpte_resolution(int time_format) +{ + switch (time_format) { + case SNDRV_SEQ_SYNC_FPS_24: + return 1000000000UL / 24; + case SNDRV_SEQ_SYNC_FPS_25: + return 1000000000UL / 25; + case SNDRV_SEQ_SYNC_FPS_30_DP: + case SNDRV_SEQ_SYNC_FPS_30_NDP: + return (unsigned long)(1000000000.0/29.97); + } + return 0; +} + + +/* + * proc interface + */ + +static void print_sync_info(snd_info_buffer_t *buffer, queue_sync_t *sync) +{ + snd_iprintf(buffer, " [%s] ==> %d:%d\n", + (sync->format & SNDRV_SEQ_SYNC_TICK ? "tick" : "time"), + sync->addr.client, sync->addr.port); + snd_iprintf(buffer, " format 0x%0x / time_format %d\n", + sync->format, sync->time_format); + switch (sync->format & SNDRV_SEQ_SYNC_MODE) { + case SNDRV_SEQ_SYNC_TICK: + snd_iprintf(buffer, " ppq: %d, ticks: %d\n", + sync->param.tick.ppq, sync->param.tick.ticks); + snd_iprintf(buffer, " resolution: %ld ns, position: %d\n", + sync->sync_tick.resolution, + sync->counter); + break; + case SNDRV_SEQ_SYNC_TIME: + snd_iprintf(buffer, " subframes %d, resolution: %ld ns, position: %d\n", + sync->param.time.subframes, + sync->resolution, + sync->counter); + break; + } +} + +void snd_seq_sync_info_read(queue_t *q, snd_info_buffer_t *buffer) +{ + struct list_head *head; + int count = 0; + + read_lock(&q->master_lock); + FOR_EACH_LIST(head, &q->master_head) { + queue_sync_t *master = list_entry(head, queue_sync_t, list); + snd_iprintf(buffer, "master %d", count); + print_sync_info(buffer, master); + count++; + } + read_unlock(&q->master_lock); + if (q->slave.format) { + snd_iprintf(buffer, "slave"); + print_sync_info(buffer, &q->slave); + count++; + } + if (count) + snd_iprintf(buffer, "\n"); +} + +#endif /* SNDRV_SEQ_SYNC_SUPPORT */ diff -Nru linux/sound/core/seq/seq_sync.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.h --- linux/sound/core/seq/seq_sync.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_sync.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,79 @@ +/* + * Synchronization of ALSA sequencer queues + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_SYNC_H +#define __SND_SEQ_SYNC_H + +typedef struct snd_queue_sync queue_sync_t; +typedef struct snd_seq_sync_parser seq_sync_parser_t; +typedef void *seq_sync_arg_t; + +struct snd_queue_sync { + unsigned char format; + unsigned char time_format; + unsigned char opt_info[6]; /* optional info */ + snd_seq_addr_t addr; /* master/slave address */ + + unsigned int counter; /* current position */ + unsigned long resolution; /* resolution for time */ + snd_seq_real_time_t cur_time; /* current time */ + seq_timer_tick_t sync_tick; /* tick info */ + + union { + struct sndrv_seq_queue_tick_sync tick; + struct sndrv_seq_queue_time_sync time; + } param; + + seq_sync_parser_t *parser; + seq_sync_arg_t parser_arg; + + struct list_head list; +}; + + +struct seq_sync_parser_ops { + int (*open)(queue_sync_t *sync_info, seq_sync_arg_t *retp); + int (*sync)(seq_sync_arg_t arg, const snd_seq_event_t *src, snd_seq_event_t *ev); + int (*close)(seq_sync_arg_t arg); +}; + +struct snd_seq_sync_parser { + unsigned int format; /* supported format */ + struct seq_sync_parser_ops in; /* sync-in (slave) */ + struct seq_sync_parser_ops out; /* sync-out (mastering) */ +}; + +/* + * prototypes + */ +int snd_seq_sync_create_port(queue_t *queue); +int snd_seq_sync_delete_port(queue_t *queue); +void snd_seq_sync_clear(queue_t *q); +void snd_seq_sync_update_tempo(queue_t *q); +void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop); +void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop); +void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop); + +sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos); +unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime); +unsigned long snd_seq_get_smpte_resolution(int time_format); + + +#endif diff -Nru linux/sound/core/seq/seq_system.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.c --- linux/sound/core/seq/seq_system.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,183 @@ +/* + * ALSA sequencer System services Client + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_system.h" +#include "seq_timer.h" +#include "seq_queue.h" + +/* internal client that provide system services, access to timer etc. */ + +/* + * Port "Timer" + * - send tempo /start/stop etc. events to this port to manipulate the + * queue's timer. The queue address is specified in + * data.queue.queue. + * - this port supports subscription. The received timer events are + * broadcasted to all subscribed clients. The modified tempo + * value is stored on data.queue.value. + * The modifier client/port is not send. + * + * Port "Announce" + * - does not receive message + * - supports supscription. For each client or port attaching to or + * detaching from the system an announcement is send to the subscribed + * clients. + * + * Idea: the subscription mechanism might also work handy for distributing + * synchronisation and timing information. In this case we would ideally have + * a list of subscribers for each type of sync (time, tick), for each timing + * queue. + * + * NOTE: the queue to be started, stopped, etc. must be specified + * in data.queue.addr.queue field. queue is used only for + * scheduling, and no longer referred as affected queue. + * They are used only for timer broadcast (see above). + * -- iwai + */ + + +/* client id of our system client */ +static int sysclient = -1; + +/* port id numbers for this client */ +static int announce_port = -1; + + + +/* fill standard header data, source port & channel are filled in */ +static int setheader(snd_seq_event_t * ev, int client, int port) +{ + if (announce_port < 0) + return -ENODEV; + + memset(ev, 0, sizeof(snd_seq_event_t)); + + ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; + ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; + + ev->source.client = sysclient; + ev->source.port = announce_port; + ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + + /* fill data */ + /*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/ + ev->data.addr.client = client; + ev->data.addr.port = port; + + return 0; +} + + +/* entry points for broadcasting system events */ +void snd_seq_system_broadcast(int client, int port, int type) +{ + snd_seq_event_t ev; + + if (setheader(&ev, client, port) < 0) + return; + ev.type = type; + snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0); +} + +/* entry points for broadcasting system events */ +int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev) +{ + ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; + ev->source.client = sysclient; + ev->source.port = announce_port; + ev->dest.client = client; + ev->dest.port = port; + return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0); +} + +/* call-back handler for timer events */ +static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + return snd_seq_control_queue(ev, atomic, hop); +} + +/* register our internal client */ +int __init snd_seq_system_client_init(void) +{ + + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t inf; + snd_seq_port_info_t port; + + memset(&callbacks, 0, sizeof(callbacks)); + memset(&pcallbacks, 0, sizeof(pcallbacks)); + memset(&inf, 0, sizeof(inf)); + memset(&port, 0, sizeof(port)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.event_input = event_input_timer; + + /* register client */ + callbacks.allow_input = callbacks.allow_output = 1; + sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks); + + /* set our name */ + inf.client = 0; + inf.type = KERNEL_CLIENT; + strcpy(inf.name, "System"); + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf); + + /* register timer */ + strcpy(port.name, "Timer"); + port.capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */ + port.capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */ + port.kernel = &pcallbacks; + port.type = 0; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.addr.client = sysclient; + port.addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + + /* register announcement port */ + strcpy(port.name, "Announce"); + port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ + port.kernel = NULL; + port.type = 0; + port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; + port.addr.client = sysclient; + port.addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, &port); + announce_port = port.addr.port; + + return 0; +} + + +/* unregister our internal client */ +void __exit snd_seq_system_client_done(void) +{ + int oldsysclient = sysclient; + + if (oldsysclient >= 0) { + sysclient = -1; + announce_port = -1; + snd_seq_delete_kernel_client(oldsysclient); + } +} diff -Nru linux/sound/core/seq/seq_system.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.h --- linux/sound/core/seq/seq_system.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_system.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,46 @@ +/* + * ALSA sequencer System Client + * Copyright (c) 1998 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_SYSTEM_H +#define __SND_SEQ_SYSTEM_H + +#include + + +/* entry points for broadcasting system events */ +void snd_seq_system_broadcast(int client, int port, int type); + +#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START) +#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT) +#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE) +#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START) +#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT) +#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE) + +int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev); + +/* register our internal client */ +int snd_seq_system_client_init(void); + +/* unregister our internal client */ +void snd_seq_system_client_done(void); + + +#endif diff -Nru linux/sound/core/seq/seq_timer.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.c --- linux/sound/core/seq/seq_timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,432 @@ +/* + * ALSA sequencer Timer + * Copyright (c) 1998-1999 by Frank van de Pol + * Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include "seq_timer.h" +#include "seq_queue.h" +#include "seq_info.h" + +extern int snd_seq_default_timer_class; +extern int snd_seq_default_timer_sclass; +extern int snd_seq_default_timer_card; +extern int snd_seq_default_timer_device; +extern int snd_seq_default_timer_subdevice; +extern int snd_seq_default_timer_resolution; + +#define SKEW_BASE 0x10000 /* 16bit shift */ + +void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks) +{ + if (tempo < 1000000) + tick->resolution = (tempo * 1000) / ppq; + else { + /* might overflow.. */ + unsigned int s; + s = tempo % ppq; + s = (s * 1000) / ppq; + tick->resolution = (tempo / ppq) * 1000; + tick->resolution += s; + } + if (tick->resolution <= 0) + tick->resolution = 1; + tick->resolution *= nticks; + snd_seq_timer_update_tick(tick, 0); +} + +/* create new timer (constructor) */ +seq_timer_t *snd_seq_timer_new(void) +{ + seq_timer_t *tmr; + + tmr = snd_kcalloc(sizeof(seq_timer_t), GFP_KERNEL); + if (tmr == NULL) { + snd_printd("malloc failed for snd_seq_timer_new() \n"); + return NULL; + } + spin_lock_init(&tmr->lock); + + /* reset setup to defaults */ + snd_seq_timer_defaults(tmr); + + /* reset time */ + snd_seq_timer_reset(tmr); + + return tmr; +} + +/* delete timer (destructor) */ +void snd_seq_timer_delete(seq_timer_t **tmr) +{ + seq_timer_t *t = *tmr; + *tmr = NULL; + + if (t == NULL) { + snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n"); + return; + } + t->running = 0; + + /* reset time */ + snd_seq_timer_stop(t); + snd_seq_timer_reset(t); + + kfree(t); +} + +void snd_seq_timer_defaults(seq_timer_t * tmr) +{ + /* setup defaults */ + tmr->ppq = 96; /* 96 PPQ */ + tmr->tempo = 500000; /* 120 BPM */ + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + tmr->running = 0; + + tmr->type = SNDRV_SEQ_TIMER_ALSA; + tmr->alsa_id.dev_class = snd_seq_default_timer_class; + tmr->alsa_id.dev_sclass = snd_seq_default_timer_sclass; + tmr->alsa_id.card = snd_seq_default_timer_card; + tmr->alsa_id.device = snd_seq_default_timer_device; + tmr->alsa_id.subdevice = snd_seq_default_timer_subdevice; + tmr->preferred_resolution = snd_seq_default_timer_resolution; + + tmr->skew = tmr->skew_base = SKEW_BASE; +} + +void snd_seq_timer_reset(seq_timer_t * tmr) +{ + unsigned long flags; + + spin_lock_irqsave(&tmr->lock, flags); + + /* reset time & songposition */ + tmr->cur_time.tv_sec = 0; + tmr->cur_time.tv_nsec = 0; + + tmr->tick.cur_tick = 0; + tmr->tick.fraction = 0; + + spin_unlock_irqrestore(&tmr->lock, flags); +} + + +/* called by timer interrupt routine. the period time since previous invocation is passed */ +static void snd_seq_timer_interrupt(snd_timer_instance_t *timeri, + unsigned long resolution, + unsigned long ticks, void *data) +{ + unsigned long flags; + queue_t *q = (queue_t *)data; + seq_timer_t *tmr; + + if (q == NULL) + return; + tmr = q->timer; + if (tmr == NULL) + return; + if (!tmr->running) + return; + + resolution *= ticks; + if (tmr->skew != tmr->skew_base) { + /* FIXME: assuming skew_base = 0x10000 */ + resolution = (resolution >> 16) * tmr->skew + + (((resolution & 0xffff) * tmr->skew) >> 16); + } + + spin_lock_irqsave(&tmr->lock, flags); + + /* update timer */ + snd_seq_inc_time_nsec(&tmr->cur_time, resolution); + + /* calculate current tick */ + snd_seq_timer_update_tick(&tmr->tick, resolution); + + /* register actual time of this timer update */ + do_gettimeofday(&tmr->last_update); + + spin_unlock_irqrestore(&tmr->lock, flags); + + /* check queues and dispatch events */ + snd_seq_check_queue(q, 1, 0); +} + +/* set current tempo */ +int snd_seq_timer_set_tempo(seq_timer_t * tmr, int tempo) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + if (tempo <= 0) + return -EINVAL; + spin_lock_irqsave(&tmr->lock, flags); + if (tempo != tmr->tempo) { + tmr->tempo = tempo; + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + } + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current ppq */ +int snd_seq_timer_set_ppq(seq_timer_t * tmr, int ppq) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + if (ppq <= 0) + return -EINVAL; + spin_lock_irqsave(&tmr->lock, flags); + if (tmr->running && (ppq != tmr->ppq)) { + /* refuse to change ppq on running timers */ + /* because it will upset the song position (ticks) */ + spin_unlock_irqrestore(&tmr->lock, flags); + snd_printd("seq: cannot change ppq of a running timer\n"); + return -EBUSY; + } + + tmr->ppq = ppq; + snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1); + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current tick position */ +int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + spin_lock_irqsave(&tmr->lock, flags); + tmr->tick.cur_tick = position; + tmr->tick.fraction = 0; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set current real-time position */ +int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + snd_seq_sanity_real_time(&position); + spin_lock_irqsave(&tmr->lock, flags); + tmr->cur_time = position; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +/* set timer skew */ +int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base) +{ + unsigned long flags; + + snd_assert(tmr, return -EINVAL); + + /* FIXME */ + if (base != SKEW_BASE) { + snd_printd("invalid skew base 0x%x\n", base); + return -EINVAL; + } + spin_lock_irqsave(&tmr->lock, flags); + tmr->skew = skew; + spin_unlock_irqrestore(&tmr->lock, flags); + return 0; +} + +int snd_seq_timer_open(queue_t *q) +{ + snd_timer_instance_t *t; + seq_timer_t *tmr; + char str[32]; + + tmr = q->timer; + snd_assert(tmr != NULL, return -EINVAL); + if (tmr->timeri) + return -EBUSY; + sprintf(str, "sequencer queue %i", q->queue); + if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { /* standard ALSA timer */ + if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) + tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; + t = snd_timer_open(str, &tmr->alsa_id, q->queue); + if (t == NULL && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) { + if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL || + tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) { + snd_timer_id_t tid; + memset(&tid, 0, sizeof(tid)); + tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; + tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; + tid.card = -1; + tid.device = SNDRV_TIMER_GLOBAL_SYSTEM; + t = snd_timer_open(str, &tid, q->queue); + } + if (t == NULL) { + snd_printk(KERN_ERR "fatal error: cannot create timer\n"); + return -ENODEV; + } + } + } else { + return -EINVAL; + } + t->callback = snd_seq_timer_interrupt; + t->callback_data = q; + t->flags |= SNDRV_TIMER_IFLG_AUTO; + tmr->timeri = t; + return 0; +} + +int snd_seq_timer_close(queue_t *q) +{ + seq_timer_t *tmr; + + tmr = q->timer; + snd_assert(tmr != NULL, return -EINVAL); + if (tmr->timeri) { + snd_timer_stop(tmr->timeri); + snd_timer_close(tmr->timeri); + tmr->timeri = NULL; + } + return 0; +} + +void snd_seq_timer_stop(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return; + if (!tmr->running) + return; + tmr->running = 0; + snd_timer_stop(tmr->timeri); +} + +static int initialize_timer(seq_timer_t *tmr) +{ + snd_timer_t *t; + t = tmr->timeri->timer; + snd_assert(t, return -EINVAL); + + tmr->ticks = 1; + if (tmr->preferred_resolution && + ! (t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { + unsigned long r = t->hw.resolution; + if (! r && t->hw.c_resolution) + r = t->hw.c_resolution(t); + if (r) { + tmr->ticks = (unsigned int)(tmr->preferred_resolution / r); + if (! tmr->ticks) + tmr->ticks = 1; + } + } + tmr->initialized = 1; + return 0; +} + +void snd_seq_timer_start(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return; + if (tmr->running) + snd_seq_timer_stop(tmr); + snd_seq_timer_reset(tmr); + if (initialize_timer(tmr) < 0) + return; + snd_timer_start(tmr->timeri, tmr->ticks); + tmr->running = 1; + do_gettimeofday(&tmr->last_update); +} + +void snd_seq_timer_continue(seq_timer_t * tmr) +{ + if (! tmr->timeri) + return; + if (tmr->running) + return; + if (! tmr->initialized) + if (initialize_timer(tmr) < 0) + return; + snd_timer_start(tmr->timeri, tmr->ticks); + tmr->running = 1; + do_gettimeofday(&tmr->last_update); +} + +/* return current 'real' time. use timeofday() to get better granularity. */ +snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr) +{ + snd_seq_real_time_t cur_time; + + cur_time = tmr->cur_time; + if (tmr->running) { + struct timeval tm; + int usec; + do_gettimeofday(&tm); + usec = (int)(tm.tv_usec - tmr->last_update.tv_usec); + if (usec < 0) { + cur_time.tv_nsec += (1000000 + usec) * 1000; + cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1; + } else { + cur_time.tv_nsec += usec * 1000; + cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec; + } + snd_seq_sanity_real_time(&cur_time); + } + + return cur_time; +} + +/* TODO: use interpolation on tick queue (will only be usefull for very + high PPQ values) */ +snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr) +{ + return tmr->tick.cur_tick; +} + + +/* exported to seq_info.c */ +void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int idx; + queue_t *q; + seq_timer_t *tmr; + snd_timer_instance_t *ti; + unsigned long resolution; + + for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) { + q = queueptr(idx); + if (q == NULL) + continue; + if ((tmr = q->timer) == NULL || + (ti = tmr->timeri) == NULL) { + queuefree(q); + continue; + } + snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name); + resolution = snd_timer_resolution(ti) * tmr->ticks; + snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000); + snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base); + queuefree(q); + } +} diff -Nru linux/sound/core/seq/seq_timer.h linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.h --- linux/sound/core/seq/seq_timer.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_timer.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,151 @@ +/* + * ALSA sequencer Timer + * Copyright (c) 1998-1999 by Frank van de Pol + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __SND_SEQ_TIMER_H +#define __SND_SEQ_TIMER_H + +#include +#include + +typedef struct { + snd_seq_tick_time_t cur_tick; /* current tick */ + unsigned long resolution; /* time per tick in nsec */ + unsigned long fraction; /* current time per tick in nsec */ +} seq_timer_tick_t; + +typedef struct { + /* ... tempo / offset / running state */ + + unsigned int running:1, /* running state of queue */ + initialized:1; /* timer is initialized */ + + unsigned int tempo; /* current tempo, us/tick */ + int ppq; /* time resolution, ticks/quarter */ + + snd_seq_real_time_t cur_time; /* current time */ + seq_timer_tick_t tick; /* current tick */ + int tick_updated; + + int type; /* timer type */ + snd_timer_id_t alsa_id; /* ALSA's timer ID */ + snd_timer_instance_t *timeri; /* timer instance */ + unsigned int ticks; + unsigned long preferred_resolution; /* timer resolution */ + + unsigned int skew; + unsigned int skew_base; + +#ifdef SNDRV_SEQ_SYNC_SUPPORT + int sync_start; + struct timeval sync_last_tm; + unsigned int sync_time_diff; +#endif + + struct timeval last_update; /* time of last clock update, used for interpolation */ + + spinlock_t lock; +} seq_timer_t; + + +/* create new timer (constructor) */ +extern seq_timer_t *snd_seq_timer_new(void); + +/* delete timer (destructor) */ +extern void snd_seq_timer_delete(seq_timer_t **tmr); + +void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks); + +/* */ +static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution) +{ + if (tick->resolution > 0) { + tick->fraction += resolution; + tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution); + tick->fraction %= tick->resolution; + } +} + + +/* compare timestamp between events */ +/* return 1 if a >= b; otherwise return 0 */ +static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b) +{ + /* compare ticks */ + return (*a >= *b); +} + +static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b) +{ + /* compare real time */ + if (a->tv_sec > b->tv_sec) + return 1; + if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec)) + return 1; + return 0; +} + + +static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm) +{ + while (tm->tv_nsec >= 1000000000) { + /* roll-over */ + tm->tv_nsec -= 1000000000; + tm->tv_sec++; + } +} + + +/* increment timestamp */ +static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc) +{ + tm->tv_sec += inc->tv_sec; + tm->tv_nsec += inc->tv_nsec; + snd_seq_sanity_real_time(tm); +} + +static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec) +{ + tm->tv_nsec += nsec; + snd_seq_sanity_real_time(tm); +} + +/* called by timer isr */ +int snd_seq_timer_open(queue_t *q); +int snd_seq_timer_close(queue_t *q); +int snd_seq_timer_midi_open(queue_t *q); +int snd_seq_timer_midi_close(queue_t *q); +void snd_seq_timer_defaults(seq_timer_t *tmr); +void snd_seq_timer_reset(seq_timer_t *tmr); +void snd_seq_timer_stop(seq_timer_t *tmr); +void snd_seq_timer_start(seq_timer_t *tmr); +void snd_seq_timer_continue(seq_timer_t *tmr); +int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo); +int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq); +int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position); +int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position); +int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base); +snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr); +snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr); + +#ifdef SNDRV_SEQ_SYNC_SUPPORT +u64 snd_seq_timer_get_cur_nsec(seq_timer_t *tmr, struct timeval *tm); +#endif + +#endif diff -Nru linux/sound/core/seq/seq_virmidi.c linux-2.4.19-pre5-mjc/sound/core/seq/seq_virmidi.c --- linux/sound/core/seq/seq_virmidi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/seq/seq_virmidi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,525 @@ +/* + * Virtual Raw MIDI client on Sequencer + * + * Copyright (c) 2000 by Takashi Iwai , + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Virtual Raw MIDI client + * + * The virtual rawmidi client is a sequencer client which associate + * a rawmidi device file. The created rawmidi device file can be + * accessed as a normal raw midi, but its MIDI source and destination + * are arbitrary. For example, a user-client software synth connected + * to this port can be used as a normal midi device as well. + * + * The virtual rawmidi device accepts also multiple opens. Each file + * has its own input buffer, so that no conflict would occur. The drain + * of input/output buffer acts only to the local buffer. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer"); +MODULE_LICENSE("GPL"); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * initialize an event record + */ +static void snd_virmidi_init_event(snd_virmidi_t *vmidi, snd_seq_event_t *ev) +{ + memset(ev, 0, sizeof(*ev)); + ev->source.port = vmidi->port; + switch (vmidi->seq_mode) { + case SNDRV_VIRMIDI_SEQ_DISPATCH: + ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; + break; + case SNDRV_VIRMIDI_SEQ_ATTACH: + /* FIXME: source and destination are same - not good.. */ + ev->dest.client = vmidi->client; + ev->dest.port = vmidi->port; + break; + } +} + +/* + * decode input event and put to read buffer of each opened file + */ +static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_t *ev) +{ + snd_virmidi_t *vmidi; + struct list_head *list; + unsigned char msg[4]; + int len; + + snd_assert(rdev != NULL, return -EINVAL); + + if (!(rdev->flags & SNDRV_VIRMIDI_USE)) + return 0; /* ignored */ + + read_lock(&rdev->filelist_lock); + list_for_each(list, &rdev->filelist) { + vmidi = list_entry(list, snd_virmidi_t, list); + if (!vmidi->trigger) + continue; + if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + continue; + snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream); + } else { + len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev); + if (len > 0) + snd_rawmidi_receive(vmidi->substream, msg, len); + } + } + read_unlock(&rdev->filelist_lock); + + return 0; +} + +/* + * event_input callback from sequencer + */ +int snd_virmidi_receive(snd_seq_event_t *ev, int direct, + void *private_data, int atomic, int hop) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + return snd_virmidi_dev_receive_event(rdev, ev); +} + +/* + * trigger rawmidi stream for input + */ +static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + + if (up) { + vmidi->trigger = 1; + } else { + vmidi->trigger = 0; + } +} + +/* + * trigger rawmidi stream for output + */ +static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + int count, res; + unsigned char buf[32], *pbuf; + + if (up) { + vmidi->trigger = 1; + if (!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) { + snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail); + return; /* ignored */ + } + if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { + if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0) + return; + vmidi->event.type = SNDRV_SEQ_EVENT_NONE; + } + while (1) { + count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); + if (count <= 0) + break; + pbuf = buf; + while (count > 0) { + res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event); + if (res < 0) { + snd_midi_event_reset_encode(vmidi->parser); + continue; + } + snd_rawmidi_transmit_ack(substream, res); + pbuf += res; + count -= res; + if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { + if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0) + return; + vmidi->event.type = SNDRV_SEQ_EVENT_NONE; + } + } + } + } else { + vmidi->trigger = 0; + } +} + +/* + * open rawmidi handle for input + */ +static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_rawmidi_runtime_t *runtime = substream->runtime; + snd_virmidi_t *vmidi; + unsigned long flags; + + vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + if (vmidi == NULL) + return -ENOMEM; + vmidi->substream = substream; + if (snd_midi_event_new(0, &vmidi->parser) < 0) { + snd_magic_kfree(vmidi); + return -ENOMEM; + } + vmidi->seq_mode = rdev->seq_mode; + vmidi->client = rdev->client; + vmidi->port = rdev->port; + runtime->private_data = vmidi; + write_lock_irqsave(&rdev->filelist_lock, flags); + list_add_tail(&vmidi->list, &rdev->filelist); + write_unlock_irqrestore(&rdev->filelist_lock, flags); + vmidi->rdev = rdev; + return 0; +} + +/* + * open rawmidi handle for output + */ +static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_rawmidi_runtime_t *runtime = substream->runtime; + snd_virmidi_t *vmidi; + + vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + if (vmidi == NULL) + return -ENOMEM; + vmidi->substream = substream; + if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) { + snd_magic_kfree(vmidi); + return -ENOMEM; + } + vmidi->seq_mode = rdev->seq_mode; + vmidi->client = rdev->client; + vmidi->port = rdev->port; + snd_virmidi_init_event(vmidi, &vmidi->event); + vmidi->rdev = rdev; + runtime->private_data = vmidi; + return 0; +} + +/* + * close rawmidi handle for input + */ +static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_midi_event_free(vmidi->parser); + list_del(&vmidi->list); + substream->runtime->private_data = NULL; + snd_magic_kfree(vmidi); + return 0; +} + +/* + * close rawmidi handle for output + */ +static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream) +{ + snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_midi_event_free(vmidi->parser); + substream->runtime->private_data = NULL; + snd_magic_kfree(vmidi); + return 0; +} + +/* + * subscribe callback - allow output to rawmidi device + */ +static int snd_virmidi_subscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + if (!try_inc_mod_count(rdev->card->module)) + return -EFAULT; + rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE; + return 0; +} + +/* + * unsubscribe callback - disallow output to rawmidi device + */ +static int snd_virmidi_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE; + dec_mod_count(rdev->card->module); + return 0; +} + + +/* + * use callback - allow input to rawmidi device + */ +static int snd_virmidi_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + if (!try_inc_mod_count(rdev->card->module)) + return -EFAULT; + rdev->flags |= SNDRV_VIRMIDI_USE; + return 0; +} + +/* + * unuse callback - disallow input to rawmidi device + */ +static int snd_virmidi_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_virmidi_dev_t *rdev; + + rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev->flags &= ~SNDRV_VIRMIDI_USE; + dec_mod_count(rdev->card->module); + return 0; +} + + +/* + * Register functions + */ + +static snd_rawmidi_ops_t snd_virmidi_input_ops = { + open: snd_virmidi_input_open, + close: snd_virmidi_input_close, + trigger: snd_virmidi_input_trigger, +}; + +static snd_rawmidi_ops_t snd_virmidi_output_ops = { + open: snd_virmidi_output_open, + close: snd_virmidi_output_close, + trigger: snd_virmidi_output_trigger, +}; + +/* + * create a sequencer client and a port + */ +static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev) +{ + int client; + snd_seq_client_callback_t callbacks; + snd_seq_port_callback_t pcallbacks; + snd_seq_client_info_t info; + snd_seq_port_info_t pinfo; + int err; + + if (rdev->client >= 0) + return 0; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = rdev; + callbacks.allow_input = 1; + callbacks.allow_output = 1; + client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks); + if (client < 0) + return client; + rdev->client = client; + + /* set client name */ + memset(&info, 0, sizeof(info)); + info.client = client; + info.type = KERNEL_CLIENT; + sprintf(info.name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info); + + /* create a port */ + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.addr.client = client; + sprintf(pinfo.name, "VirMIDI %d-%d", rdev->card->number, rdev->device); + /* set all capabilities */ + pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; + pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + pinfo.midi_channels = 16; + memset(&pcallbacks, 0, sizeof(pcallbacks)); + pcallbacks.owner = THIS_MODULE; + pcallbacks.private_data = rdev; + pcallbacks.subscribe = snd_virmidi_subscribe; + pcallbacks.unsubscribe = snd_virmidi_unsubscribe; + pcallbacks.use = snd_virmidi_use; + pcallbacks.unuse = snd_virmidi_unuse; + pcallbacks.event_input = snd_virmidi_receive; + pinfo.kernel = &pcallbacks; + err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo); + if (err < 0) { + snd_seq_delete_kernel_client(client); + rdev->client = -1; + return err; + } + + rdev->port = pinfo.addr.port; + return 0; /* success */ +} + + +/* + * release the sequencer client + */ +static void snd_virmidi_dev_detach_seq(snd_virmidi_dev_t *rdev) +{ + if (rdev->client >= 0) { + snd_seq_delete_kernel_client(rdev->client); + rdev->client = -1; + } +} + +/* + * register the device + */ +static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + int err; + + switch (rdev->seq_mode) { + case SNDRV_VIRMIDI_SEQ_DISPATCH: + err = snd_virmidi_dev_attach_seq(rdev); + if (err < 0) + return err; + break; + case SNDRV_VIRMIDI_SEQ_ATTACH: + if (rdev->client == 0) + return -EINVAL; + /* should check presence of port more strictly.. */ + break; + default: + snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode); + return -EINVAL; + } + return 0; +} + + +/* + * unregister the device + */ +static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + + if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH) + snd_virmidi_dev_detach_seq(rdev); + return 0; +} + +/* + * + */ +static snd_rawmidi_global_ops_t snd_virmidi_global_ops = { + dev_register: snd_virmidi_dev_register, + dev_unregister: snd_virmidi_dev_unregister, +}; + +/* + * free device + */ +static void snd_virmidi_free(snd_rawmidi_t *rmidi) +{ + snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return); + snd_magic_kfree(rdev); +} + +/* + * create a new device + */ +int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi) +{ + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + int err; + + *rrmidi = NULL; + if ((err = snd_rawmidi_new(card, "VirMidi", device, + 16, /* may be configurable */ + 16, /* may be configurable */ + &rmidi)) < 0) + return err; + strcpy(rmidi->name, rmidi->id); + rdev = snd_magic_kcalloc(snd_virmidi_dev_t, 0, GFP_KERNEL); + if (rdev == NULL) { + snd_device_free(card, rmidi); + return -ENOMEM; + } + rdev->card = card; + rdev->rmidi = rmidi; + rdev->device = device; + rdev->client = -1; + rwlock_init(&rdev->filelist_lock); + INIT_LIST_HEAD(&rdev->filelist); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; + rmidi->private_data = rdev; + rmidi->private_free = snd_virmidi_free; + rmidi->ops = &snd_virmidi_global_ops; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + *rrmidi = rmidi; + return 0; +} + +/* + * ENTRY functions + */ + +static int __init alsa_virmidi_init(void) +{ + return 0; +} + +static void __exit alsa_virmidi_exit(void) +{ +} + +module_init(alsa_virmidi_init) +module_exit(alsa_virmidi_exit) + +EXPORT_SYMBOL(snd_virmidi_new); diff -Nru linux/sound/core/sound.c linux-2.4.19-pre5-mjc/sound/core/sound.c --- linux/sound/core/sound.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/sound.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,507 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DEVFS_FS +#include +#endif + +#define SNDRV_OS_MINORS 256 + +int snd_major = CONFIG_SND_MAJOR; +static int snd_cards_limit = SNDRV_CARDS; +int snd_device_mode = S_IFCHR | S_IRUGO | S_IWUGO; +int snd_device_gid = 0; +int snd_device_uid = 0; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_SUPPORTED_DEVICE("sound"); +MODULE_PARM(snd_major, "i"); +MODULE_PARM_DESC(snd_major, "Major # for sound driver."); +MODULE_PARM_SYNTAX(snd_major, "default:116,skill:devel"); +MODULE_PARM(snd_cards_limit, "i"); +MODULE_PARM_DESC(snd_cards_limit, "Count of soundcards installed in the system."); +MODULE_PARM_SYNTAX(snd_cards_limit, "default:8,skill:advanced"); +MODULE_PARM(snd_device_mode, "i"); +MODULE_PARM_DESC(snd_device_mode, "Device file permission mask for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(snd_device_mode, "default:0666,base:8"); +MODULE_PARM(snd_device_gid, "i"); +MODULE_PARM_DESC(snd_device_gid, "Device file GID for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(snd_device_gid, "default:0"); +MODULE_PARM(snd_device_uid, "i"); +MODULE_PARM_DESC(snd_device_uid, "Device file UID for sound dynamic device filesystem."); +MODULE_PARM_SYNTAX(snd_device_uid, "default:0"); + +int snd_ecards_limit; + +static struct list_head snd_minors_hash[SNDRV_CARDS]; + +static DECLARE_MUTEX(sound_mutex); + +#ifdef CONFIG_DEVFS_FS +static devfs_handle_t devfs_handle = NULL; +#endif + +#ifdef CONFIG_KMOD + +void snd_request_card(int card) +{ + char str[32]; + + if (snd_cards[card] != NULL) + return; + if (card < 0 || card >= snd_ecards_limit) + return; + sprintf(str, "snd-card-%i", card); + request_module(str); +} + +static void snd_request_other(int minor) +{ + char *str; + + switch (minor) { + case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; + case SNDRV_MINOR_TIMER: str = "snd-timer"; break; + default: return; + } + request_module(str); +} + +#endif /* request_module support */ + +static snd_minor_t *snd_minor_search(int minor) +{ + struct list_head *list; + snd_minor_t *mptr; + + list_for_each(list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]) { + mptr = list_entry(list, snd_minor_t, list); + if (mptr->number == minor) + return mptr; + } + return NULL; +} + +static int snd_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + int card = SNDRV_MINOR_CARD(minor); + int dev = SNDRV_MINOR_DEVICE(minor); + snd_minor_t *mptr = NULL; + struct file_operations *old_fops; + int err = 0; + + if (dev != SNDRV_MINOR_SEQUENCER) { + if (snd_cards[card] == NULL) { +#ifdef CONFIG_KMOD + snd_request_card(card); + if (snd_cards[card] == NULL) +#endif + return -ENODEV; + } + } else { +#ifdef CONFIG_KMOD + if ((mptr = snd_minor_search(minor)) == NULL) + snd_request_other(minor); +#endif + } + if (mptr == NULL && (mptr = snd_minor_search(minor)) == NULL) + return -ENODEV; + old_fops = file->f_op; + file->f_op = fops_get(mptr->f_ops); + if (file->f_op->open) + err = file->f_op->open(inode, file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; +} + +struct file_operations snd_fops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + open: snd_open +}; + +static int snd_kernel_minor(int type, snd_card_t * card, int dev) +{ + int minor; + + switch (type) { + case SNDRV_DEVICE_TYPE_SEQUENCER: + case SNDRV_DEVICE_TYPE_TIMER: + minor = type; + break; + case SNDRV_DEVICE_TYPE_CONTROL: + snd_assert(card != NULL, return -EINVAL); + minor = SNDRV_MINOR(card->number, type); + break; + case SNDRV_DEVICE_TYPE_HWDEP: + case SNDRV_DEVICE_TYPE_RAWMIDI: + case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: + case SNDRV_DEVICE_TYPE_PCM_CAPTURE: + snd_assert(card != NULL, return -EINVAL); + minor = SNDRV_MINOR(card->number, type + dev); + break; + default: + return -EINVAL; + } + snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); + return minor; +} + +int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) +{ + int minor = snd_kernel_minor(type, card, dev); + snd_minor_t *preg; + + if (minor < 0) + return minor; + preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL); + if (preg == NULL) + return -ENOMEM; + *preg = *reg; + preg->number = minor; + preg->device = dev; + preg->dev = NULL; + down(&sound_mutex); + if (snd_minor_search(minor)) { + up(&sound_mutex); + kfree(preg); + return -EBUSY; + } + if (name) + preg->dev = snd_info_create_device(name, minor, 0); + list_add_tail(&preg->list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]); + up(&sound_mutex); + return 0; +} + +int snd_unregister_device(int type, snd_card_t * card, int dev) +{ + int minor = snd_kernel_minor(type, card, dev); + snd_minor_t *mptr; + + if (minor < 0) + return minor; + down(&sound_mutex); + if ((mptr = snd_minor_search(minor)) == NULL) { + up(&sound_mutex); + return -EINVAL; + } + if (mptr->dev) + snd_info_free_device(mptr->dev); + list_del(&mptr->list); + up(&sound_mutex); + kfree(mptr); + return 0; +} + +/* + * INFO PART + */ + +static snd_info_entry_t *snd_minor_info_entry = NULL; + +static void snd_minor_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int card, device; + struct list_head *list; + snd_minor_t *mptr; + + down(&sound_mutex); + for (card = 0; card < SNDRV_CARDS; card++) { + list_for_each(list, &snd_minors_hash[card]) { + mptr = list_entry(list, snd_minor_t, list); + if (SNDRV_MINOR_DEVICE(mptr->number) != SNDRV_MINOR_SEQUENCER) { + if ((device = mptr->device) >= 0) + snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, device, mptr->comment); + else + snd_iprintf(buffer, "%3i: [%i] : %s\n", mptr->number, card, mptr->comment); + } else { + snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); + } + } + } + up(&sound_mutex); +} + +int __init snd_minor_info_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_minor_info_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_minor_info_entry = entry; + return 0; +} + +int __exit snd_minor_info_done(void) +{ + if (snd_minor_info_entry) + snd_info_unregister(snd_minor_info_entry); + return 0; +} + +/* + * INIT PART + */ + +static int __init alsa_sound_init(void) +{ +#ifdef CONFIG_DEVFS_FS + short controlnum; + char controlname[24]; +#endif +#ifdef CONFIG_SND_OSSEMUL + int err; +#endif + int card; + + snd_ecards_limit = snd_cards_limit; + for (card = 0; card < SNDRV_CARDS; card++) + INIT_LIST_HEAD(&snd_minors_hash[card]); +#ifdef CONFIG_SND_OSSEMUL + if ((err = snd_oss_init_module()) < 0) + return err; +#endif +#ifdef CONFIG_DEVFS_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + devfs_handle = devfs_mk_dir(NULL, "snd", 3, NULL); +#else + devfs_handle = devfs_mk_dir(NULL, "snd", NULL); +#endif + if (devfs_register_chrdev(snd_major, "alsa", &snd_fops)) { +#else + if (register_chrdev(snd_major, "alsa", &snd_fops)) { +#endif + snd_printk(KERN_ERR "unable to register native major device number %d\n", snd_major); +#ifdef CONFIG_SND_OSSEMUL + snd_oss_cleanup_module(); +#endif + return -EIO; + } +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_init(); +#endif + if (snd_info_init() < 0) { +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_done(); +#endif +#ifdef CONFIG_SND_OSSEMUL + snd_oss_cleanup_module(); +#endif + return -ENOMEM; + } +#ifdef CONFIG_SND_OSSEMUL + snd_info_minor_register(); +#endif +#ifdef CONFIG_DEVFS_FS + for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) { + sprintf(controlname, "snd/controlC%d", controlnum); + devfs_register(NULL, controlname, DEVFS_FL_DEFAULT, + snd_major, controlnum<<5, snd_device_mode | S_IFCHR, + &snd_fops, NULL); + } +#endif +#ifndef MODULE + printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) + pm_init(); +#endif + return 0; +} + +static void __exit alsa_sound_exit(void) +{ +#ifdef CONFIG_DEVFS_FS + devfs_handle_t master; + char controlname[24]; + short controlnum; + + for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) { + sprintf(controlname, "snd/controlC%d", controlnum); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + master = devfs_find_handle(NULL, controlname, strlen(controlname), 0, 0, DEVFS_SPECIAL_CHR, 0); +#else + master = devfs_find_handle(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0); +#endif + devfs_unregister(master); + } +#endif + +#ifdef CONFIG_SND_OSSEMUL + snd_info_minor_unregister(); + snd_oss_cleanup_module(); +#endif + snd_info_done(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) + pm_done(); +#endif +#ifdef CONFIG_SND_DEBUG_MEMORY + snd_memory_done(); +#endif +#ifdef CONFIG_DEVFS_FS + if (devfs_unregister_chrdev(snd_major, "alsa") != 0) +#else + if (unregister_chrdev(snd_major, "alsa") != 0) +#endif + snd_printk(KERN_ERR "unable to unregister major device number %d\n", snd_major); +#ifdef CONFIG_DEVFS_FS + devfs_unregister(devfs_handle); +#endif +} + +module_init(alsa_sound_init) +module_exit(alsa_sound_exit) + + /* sound.c */ +EXPORT_SYMBOL(snd_ecards_limit); +#if defined(CONFIG_KMOD) +EXPORT_SYMBOL(snd_request_card); +#endif +EXPORT_SYMBOL(snd_register_device); +EXPORT_SYMBOL(snd_unregister_device); +#if defined(CONFIG_SND_OSSEMUL) +EXPORT_SYMBOL(snd_register_oss_device); +EXPORT_SYMBOL(snd_unregister_oss_device); +#endif + /* memory.c */ +#ifdef CONFIG_SND_DEBUG_MEMORY +EXPORT_SYMBOL(snd_hidden_kmalloc); +EXPORT_SYMBOL(snd_hidden_kfree); +EXPORT_SYMBOL(snd_hidden_vmalloc); +EXPORT_SYMBOL(snd_hidden_vfree); +EXPORT_SYMBOL(_snd_magic_kmalloc); +EXPORT_SYMBOL(_snd_magic_kcalloc); +EXPORT_SYMBOL(snd_magic_kfree); +#endif +EXPORT_SYMBOL(snd_kcalloc); +EXPORT_SYMBOL(snd_kmalloc_strdup); +EXPORT_SYMBOL(snd_malloc_pages); +EXPORT_SYMBOL(snd_malloc_pages_fallback); +EXPORT_SYMBOL(snd_free_pages); +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_malloc_isa_pages); +EXPORT_SYMBOL(snd_malloc_isa_pages_fallback); +#endif +#ifdef CONFIG_PCI +EXPORT_SYMBOL(snd_malloc_pci_pages); +EXPORT_SYMBOL(snd_malloc_pci_pages_fallback); +EXPORT_SYMBOL(snd_free_pci_pages); +#endif +EXPORT_SYMBOL(copy_to_user_fromio); +EXPORT_SYMBOL(copy_from_user_toio); + /* init.c */ +EXPORT_SYMBOL(snd_cards_count); +EXPORT_SYMBOL(snd_cards); +#ifdef CONFIG_SND_OSSEMUL +EXPORT_SYMBOL(snd_mixer_oss_notify_callback); +#endif +EXPORT_SYMBOL(snd_card_new); +EXPORT_SYMBOL(snd_card_free); +EXPORT_SYMBOL(snd_card_register); +EXPORT_SYMBOL(snd_component_add); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_power_wait); +#endif + /* device.c */ +EXPORT_SYMBOL(snd_device_new); +EXPORT_SYMBOL(snd_device_register); +EXPORT_SYMBOL(snd_device_free); +EXPORT_SYMBOL(snd_device_free_all); + /* isadma.c */ +#ifdef CONFIG_ISA +EXPORT_SYMBOL(snd_dma_program); +EXPORT_SYMBOL(snd_dma_disable); +EXPORT_SYMBOL(snd_dma_residue); +#endif + /* info.c */ +EXPORT_SYMBOL(snd_seq_root); +EXPORT_SYMBOL(snd_create_proc_entry); +EXPORT_SYMBOL(snd_remove_proc_entry); +EXPORT_SYMBOL(snd_iprintf); +EXPORT_SYMBOL(snd_info_get_line); +EXPORT_SYMBOL(snd_info_get_str); +EXPORT_SYMBOL(snd_info_create_module_entry); +EXPORT_SYMBOL(snd_info_create_card_entry); +EXPORT_SYMBOL(snd_info_free_entry); +EXPORT_SYMBOL(snd_info_create_device); +EXPORT_SYMBOL(snd_info_free_device); +EXPORT_SYMBOL(snd_info_register); +EXPORT_SYMBOL(snd_info_unregister); + /* info_oss.c */ +#ifdef CONFIG_SND_OSSEMUL +EXPORT_SYMBOL(snd_oss_info_register); +#endif + /* control.c */ +EXPORT_SYMBOL(snd_ctl_new); +EXPORT_SYMBOL(snd_ctl_new1); +EXPORT_SYMBOL(snd_ctl_free_one); +EXPORT_SYMBOL(snd_ctl_add); +EXPORT_SYMBOL(snd_ctl_remove); +EXPORT_SYMBOL(snd_ctl_remove_id); +EXPORT_SYMBOL(snd_ctl_rename_id); +EXPORT_SYMBOL(snd_ctl_find_numid); +EXPORT_SYMBOL(snd_ctl_find_id); +EXPORT_SYMBOL(snd_ctl_notify); +EXPORT_SYMBOL(snd_ctl_register_ioctl); +EXPORT_SYMBOL(snd_ctl_unregister_ioctl); + /* misc.c */ +EXPORT_SYMBOL(snd_task_name); +#ifdef CONFIG_SND_VERBOSE_PRINTK +EXPORT_SYMBOL(snd_verbose_printk); +#endif + /* wrappers */ +#ifdef CONFIG_SND_DEBUG_MEMORY +EXPORT_SYMBOL(snd_wrapper_kmalloc); +EXPORT_SYMBOL(snd_wrapper_kfree); +#endif +#ifdef HACK_PCI_ALLOC_CONSISTENT +EXPORT_SYMBOL(snd_pci_hack_alloc_consistent); +#endif diff -Nru linux/sound/core/sound_oss.c linux-2.4.19-pre5-mjc/sound/core/sound_oss.c --- linux/sound/core/sound_oss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/sound_oss.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,249 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include + +#ifdef CONFIG_SND_OSSEMUL + +#if !defined(CONFIG_SOUND_ALSA) && !defined(CONFIG_SOUND_ALSA_MODULE) +#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND_ALSA) in the kernel." +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define SNDRV_OS_MINORS 256 + +static struct list_head snd_oss_minors_hash[SNDRV_CARDS]; + +static DECLARE_MUTEX(sound_oss_mutex); + +static snd_minor_t *snd_oss_minor_search(int minor) +{ + struct list_head *list; + snd_minor_t *mptr; + + list_for_each(list, &snd_oss_minors_hash[SNDRV_MINOR_OSS_CARD(minor)]) { + mptr = list_entry(list, snd_minor_t, list); + if (mptr->number == minor) + return mptr; + } + return NULL; +} + +static int snd_oss_kernel_minor(int type, snd_card_t * card, int dev) +{ + int minor; + + switch (type) { + case SNDRV_OSS_DEVICE_TYPE_MIXER: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER)); + break; + case SNDRV_OSS_DEVICE_TYPE_SEQUENCER: + minor = SNDRV_MINOR_OSS_SEQUENCER; + break; + case SNDRV_OSS_DEVICE_TYPE_MUSIC: + minor = SNDRV_MINOR_OSS_MUSIC; + break; + case SNDRV_OSS_DEVICE_TYPE_PCM: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM)); + break; + case SNDRV_OSS_DEVICE_TYPE_MIDI: + snd_assert(card != NULL && dev <= 1, return -EINVAL); + minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI)); + break; + case SNDRV_OSS_DEVICE_TYPE_DMFM: + minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM); + break; + case SNDRV_OSS_DEVICE_TYPE_SNDSTAT: + minor = SNDRV_MINOR_OSS_SNDSTAT; + break; + default: + return -EINVAL; + } + snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); + return minor; +} + +int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name) +{ + int minor = snd_oss_kernel_minor(type, card, dev); + int minor_unit; + snd_minor_t *preg; + int cidx = SNDRV_MINOR_OSS_CARD(minor); + int track2 = -1; + int register1 = -1, register2 = -1; + + if (minor < 0) + return minor; + preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL); + if (preg == NULL) + return -ENOMEM; + *preg = *reg; + preg->number = minor; + preg->device = dev; + preg->dev = NULL; + down(&sound_oss_mutex); + list_add_tail(&preg->list, &snd_oss_minors_hash[cidx]); + minor_unit = SNDRV_MINOR_OSS_DEVICE(minor); + switch (minor_unit) { + case SNDRV_MINOR_OSS_PCM: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); + break; + case SNDRV_MINOR_OSS_MIDI: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); + break; + case SNDRV_MINOR_OSS_MIDI1: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); + break; + } + register1 = register_sound_special(reg->f_ops, minor); + if (register1 != minor) + goto __end; + if (track2 >= 0) { + register2 = register_sound_special(reg->f_ops, track2); + if (register2 != track2) + goto __end; + } + up(&sound_oss_mutex); + return 0; + + __end: + if (register2 >= 0) + unregister_sound_special(register2); + if (register1 >= 0) + unregister_sound_special(register1); + list_del(&preg->list); + up(&sound_oss_mutex); + kfree(preg); + return -EBUSY; +} + +int snd_unregister_oss_device(int type, snd_card_t * card, int dev) +{ + int minor = snd_oss_kernel_minor(type, card, dev); + int cidx = SNDRV_MINOR_OSS_CARD(minor); + int track2 = -1; + snd_minor_t *mptr; + + if (minor < 0) + return minor; + down(&sound_oss_mutex); + mptr = snd_oss_minor_search(minor); + if (mptr == NULL) { + up(&sound_oss_mutex); + return -ENOENT; + } + unregister_sound_special(minor); + switch (SNDRV_MINOR_OSS_DEVICE(minor)) { + case SNDRV_MINOR_OSS_PCM: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); + break; + case SNDRV_MINOR_OSS_MIDI: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI); + break; + case SNDRV_MINOR_OSS_MIDI1: + track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); + break; + } + if (track2 >= 0) + unregister_sound_special(track2); + list_del(&mptr->list); + up(&sound_oss_mutex); + kfree(mptr); + return 0; +} + +/* + * INFO PART + */ + +static snd_info_entry_t *snd_minor_info_oss_entry = NULL; + +static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + int card, dev; + struct list_head *list; + snd_minor_t *mptr; + + down(&sound_oss_mutex); + for (card = 0; card < SNDRV_CARDS; card++) { + list_for_each(list, &snd_oss_minors_hash[card]) { + mptr = list_entry(list, snd_minor_t, list); + dev = SNDRV_MINOR_OSS_DEVICE(mptr->number); + if (dev != SNDRV_MINOR_OSS_SNDSTAT && + dev != SNDRV_MINOR_OSS_SEQUENCER && + dev != SNDRV_MINOR_OSS_MUSIC) + snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, dev, mptr->comment); + else + snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment); + } + } + up(&sound_oss_mutex); +} + +int __init snd_minor_info_oss_init(void) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = PAGE_SIZE; + entry->c.text.read = snd_minor_info_oss_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_minor_info_oss_entry = entry; + return 0; +} + +int __exit snd_minor_info_oss_done(void) +{ + if (snd_minor_info_oss_entry) + snd_info_unregister(snd_minor_info_oss_entry); + return 0; +} + +int __init snd_oss_init_module(void) +{ + int card; + + for (card = 0; card < SNDRV_CARDS; card++) + INIT_LIST_HEAD(&snd_oss_minors_hash[card]); + return 0; +} + +void snd_oss_cleanup_module(void) +{ +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -Nru linux/sound/core/timer.c linux-2.4.19-pre5-mjc/sound/core/timer.c --- linux/sound/core/timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/timer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1362 @@ +/* + * Timers abstract layer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +int snd_timer_limit = 1; +MODULE_AUTHOR("Jaroslav Kysela , Takashi Iwai "); +MODULE_DESCRIPTION("ALSA timer interface"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_PARM(snd_timer_limit, "i"); +MODULE_PARM_DESC(snd_timer_limit, "Maximum global timers in system. (1 by default)"); + +typedef struct { + snd_timer_instance_t *timeri; + unsigned long ticks; + unsigned long overrun; + int qhead; + int qtail; + int qused; + int queue_size; + snd_timer_read_t *queue; + spinlock_t qlock; + wait_queue_head_t qchange_sleep; +} snd_timer_user_t; + +/* list of timers */ +static LIST_HEAD(snd_timer_list); + +/* list of slave instances */ +static LIST_HEAD(snd_timer_slave_list); + +/* lock for slave active lists */ +static spinlock_t slave_active_lock = SPIN_LOCK_UNLOCKED; + +static DECLARE_MUTEX(register_mutex); + +static int snd_timer_free(snd_timer_t *timer); +static int snd_timer_dev_free(snd_device_t *device); +static int snd_timer_dev_register(snd_device_t *device); +static int snd_timer_dev_unregister(snd_device_t *device); + +static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * create a timer instance with the given owner string. + * when timer is not NULL, increments the module counter + */ +static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer) +{ + snd_timer_instance_t *timeri; + timeri = snd_kcalloc(sizeof(snd_timer_instance_t), GFP_KERNEL); + if (timeri == NULL) + return NULL; + timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); + if (! timeri->owner) { + kfree(timeri); + return NULL; + } + INIT_LIST_HEAD(&timeri->open_list); + INIT_LIST_HEAD(&timeri->active_list); + INIT_LIST_HEAD(&timeri->slave_list_head); + INIT_LIST_HEAD(&timeri->slave_active_head); + timeri->in_use = (atomic_t)ATOMIC_INIT(0); + + timeri->timer = timer; + if (timer && timer->card && !try_inc_mod_count(timer->card->module)) { + kfree(timeri->owner); + kfree(timeri); + return NULL; + } + + return timeri; +} + +/* + * find a timer instance from the given timer id + */ +static snd_timer_t *snd_timer_find(snd_timer_id_t *tid) +{ + snd_timer_t *timer = NULL; + struct list_head *p; + + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + + if (timer->tmr_class != tid->dev_class) + continue; + if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || + timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && + (timer->card == NULL || + timer->card->number != tid->card)) + continue; + if (timer->tmr_device != tid->device) + continue; + if (timer->tmr_subdevice != tid->subdevice) + continue; + return timer; + } + return NULL; +} + +#ifdef CONFIG_KMOD + +static void snd_timer_request(snd_timer_id_t *tid) +{ + char str[32]; + + switch (tid->dev_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + sprintf(str, "snd-timer-%i", tid->device); + break; + case SNDRV_TIMER_CLASS_CARD: + case SNDRV_TIMER_CLASS_PCM: + sprintf(str, "snd-card-%i", tid->card); + break; + default: + return; + } + request_module(str); +} + +#endif + +/* + * look for a master instance matching with the slave id of the given slave. + * when found, relink the open_link of the slave. + * + * call this with register_mutex down. + */ +static void snd_timer_check_slave(snd_timer_instance_t *slave) +{ + snd_timer_t *timer; + snd_timer_instance_t *master; + struct list_head *p, *q; + + /* FIXME: it's really dumb to look up all entries.. */ + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + list_for_each(q, &timer->open_list_head) { + master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); + if (slave->slave_class == master->slave_class && + slave->slave_id == master->slave_id) { + list_del(&slave->open_list); + list_add_tail(&slave->open_list, &master->slave_list_head); + slave->master = master; + return; + } + } + } +} + +/* + * look for slave instances matching with the slave id of the given master. + * when found, relink the open_link of slaves. + * + * call this with register_mutex down. + */ +static void snd_timer_check_master(snd_timer_instance_t *master) +{ + snd_timer_instance_t *slave; + struct list_head *p, *n; + unsigned long flags; + + /* check all pending slaves */ + list_for_each_safe(p, n, &snd_timer_slave_list) { + slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + if (slave->slave_class == master->slave_class && + slave->slave_id == master->slave_id) { + list_del(p); + list_add_tail(p, &master->slave_list_head); + spin_lock_irqsave(&slave_active_lock, flags); + /* protected here so that timer_start() doesn't start + * this slave yet. + */ + slave->master = master; + spin_unlock_irqrestore(&slave_active_lock, flags); + } + } +} + +/* + * open a timer instance + * when opening a master, the slave id must be here given. + */ +snd_timer_instance_t *snd_timer_open(char *owner, snd_timer_id_t *tid, + unsigned int slave_id) +{ + snd_timer_t *timer; + snd_timer_instance_t *timeri = NULL; + + if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { + /* open a slave instance */ + if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || + tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { + snd_printd("invalid slave class %i\n", tid->dev_sclass); + return NULL; + } + down(®ister_mutex); + timeri = snd_timer_instance_new(owner, NULL); + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = tid->device; + timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; + list_add_tail(&timeri->open_list, &snd_timer_slave_list); + snd_timer_check_slave(timeri); + up(®ister_mutex); + return timeri; + } + + /* open a master instance */ + down(®ister_mutex); + timer = snd_timer_find(tid); +#ifdef CONFIG_KMOD + if (timer == NULL) { + up(®ister_mutex); + snd_timer_request(tid); + down(®ister_mutex); + timer = snd_timer_find(tid); + } +#endif + if (timer) { + timeri = snd_timer_instance_new(owner, timer); + if (timeri) { + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = slave_id; + if (list_empty(&timer->open_list_head) && timer->hw.open) + timer->hw.open(timer); + list_add_tail(&timeri->open_list, &timer->open_list_head); + snd_timer_check_master(timeri); + } + } + up(®ister_mutex); + return timeri; +} + + +/* + * close a timer instance + */ +int snd_timer_close(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer = NULL; + unsigned long flags; + struct list_head *p, *n; + snd_timer_instance_t *slave; + + snd_assert(timeri != NULL, return -ENXIO); + + snd_timer_stop(timeri); /* force to stop the timer */ + + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { + down(®ister_mutex); + list_del(&timeri->open_list); + up(®ister_mutex); + } else { + timer = timeri->timer; + down(®ister_mutex); + list_del(&timeri->open_list); + if (timer && list_empty(&timer->open_list_head) && timer->hw.close) + timer->hw.close(timer); + /* remove slave links */ + list_for_each_safe(p, n, &timeri->slave_list_head) { + slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + list_del(p); + list_add_tail(p, &snd_timer_slave_list); + spin_lock_irqsave(&slave_active_lock, flags); + slave->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + list_del_init(&slave->active_list); + slave->master = NULL; + spin_unlock_irqrestore(&slave_active_lock, flags); + } + up(®ister_mutex); + } + if (timeri->private_free) + timeri->private_free(timeri); + if (timeri->owner) + kfree(timeri->owner); + kfree(timeri); + if (timer && timer->card) + dec_mod_count(timer->card->module); + return 0; +} + +unsigned long snd_timer_resolution(snd_timer_instance_t * timeri) +{ + snd_timer_t * timer; + + if (timeri == NULL) + return 0; + if ((timer = timeri->timer) != NULL) { + if (timer->hw.c_resolution) + return timer->hw.c_resolution(timer); + return timer->hw.resolution; + } + return 0; +} + +static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks) +{ + list_del(&timeri->active_list); + list_add_tail(&timeri->active_list, &timer->active_list_head); + if (timer->running) { + timer->flags |= SNDRV_TIMER_FLG_RESCHED; + timeri->flags |= SNDRV_TIMER_IFLG_START; + return 1; /* delayed start */ + } else { + timer->sticks = sticks; + timer->hw.start(timer); + timer->running++; + timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; + return 0; + } +} + +static int snd_timer_start_slave(snd_timer_instance_t *timeri) +{ + unsigned long flags; + + spin_lock_irqsave(&slave_active_lock, flags); + timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; + if (timeri->master) + list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); + spin_unlock_irqrestore(&slave_active_lock, flags); + return 1; /* delayed start */ +} + +int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks) +{ + snd_timer_t *timer; + int result = -EINVAL; + unsigned long flags; + + if (timeri == NULL || ticks < 1) + return -EINVAL; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_start_slave(timeri); + timer = timeri->timer; + if (timer == NULL) + return -EINVAL; + spin_lock_irqsave(&timer->lock, flags); + timeri->ticks = timeri->cticks = ticks; + result = snd_timer_start1(timer, timeri, ticks); + spin_unlock_irqrestore(&timer->lock, flags); + return result; +} + +/* + * stop the timer instance. + * + * FIXME: do not call this from the timer callback! + */ +int snd_timer_stop(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer; + unsigned long flags; + + snd_assert(timeri != NULL, return -ENXIO); + + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { + spin_lock_irqsave(&slave_active_lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { + timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + list_del_init(&timeri->active_list); + } + spin_unlock_irqrestore(&slave_active_lock, flags); + return 0; + } + + timer = timeri->timer; + if (! timer) + return -EINVAL; + while (atomic_read(&timeri->in_use)) + udelay(10); + spin_lock_irqsave(&timer->lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { + timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + list_del_init(&timeri->active_list); + if (!(--timer->running)) { + timer->hw.stop(timer); + if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { + timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; + snd_timer_reschedule(timer, 0); + if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { + timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; + timer->hw.start(timer); + } + } + } + } + spin_unlock_irqrestore(&timer->lock, flags); + return 0; +} + +/* + * start again.. the tick is kept. + */ +int snd_timer_continue(snd_timer_instance_t * timeri) +{ + snd_timer_t *timer; + int result = -EINVAL; + unsigned long flags; + + if (timeri == NULL) + return result; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_start_slave(timeri); + timer = timeri->timer; + if (! timer) + return -EINVAL; + spin_lock_irqsave(&timer->lock, flags); + if (!timeri->cticks) + timeri->cticks = 1; + result = snd_timer_start1(timer, timeri, timer->sticks); + spin_unlock_irqrestore(&timer->lock, flags); + return result; +} + +/* + * reschedule the timer + * + * start pending instances and check the scheduling ticks. + * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. + */ +static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left) +{ + snd_timer_instance_t *ti; + unsigned long ticks = ~0UL; + struct list_head *p; + + list_for_each(p, &timer->active_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + if (ti->flags & SNDRV_TIMER_IFLG_START) { + ti->flags &= ~SNDRV_TIMER_IFLG_START; + ti->flags |= SNDRV_TIMER_IFLG_RUNNING; + timer->running++; + } + if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { + if (ticks > ti->cticks) + ticks = ti->cticks; + } + } + if (ticks == ~0UL) { + timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; + return; + } + if (ticks > timer->hw.ticks) + ticks = timer->hw.ticks; + if (ticks_left != ticks) + timer->flags |= SNDRV_TIMER_FLG_CHANGE; + timer->sticks = ticks; +} + + +/* + * timer interrupt + * + * ticks_left is usually equal to timer->sticks. + * + */ +void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left) +{ + snd_timer_instance_t *ti, *ts; + unsigned long resolution; + LIST_HEAD(done_list_head); + struct list_head *p, *q, *n; + + if (timer == NULL) + return; + spin_lock(&timer->lock); + /* loop for all active instances + * here we cannot use list_for_each because the active_list of a processed + * instance is relinked to done_list_head before callback is called. + */ + list_for_each_safe(p, n, &timer->active_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { + if (ti->cticks < ticks_left) + ti->cticks = 0; + else + ti->cticks -= ticks_left; + if (!ti->cticks) { /* expired */ + if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { + ti->cticks = ti->ticks; + } else { + ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + timer->running--; + } + /* relink to done_list */ + list_del(p); + list_add_tail(p, &done_list_head); + atomic_inc(&ti->in_use); + } + } + } + if (timer->flags & SNDRV_TIMER_FLG_RESCHED) + snd_timer_reschedule(timer, ticks_left); + if (timer->running) { + if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { + timer->hw.stop(timer); + timer->flags |= SNDRV_TIMER_FLG_CHANGE; + } + if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || + (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { + /* restart timer */ + timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; + timer->hw.start(timer); + } + } else { + timer->hw.stop(timer); + } + + /* remember the current resolution */ + if (timer->hw.c_resolution) + resolution = timer->hw.c_resolution(timer); + else + resolution = timer->hw.resolution; + + /* now process all callbacks */ + list_for_each_safe(p, n, &done_list_head) { + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); + /* append to active_list */ + list_del(p); + list_add_tail(p, &timer->active_list_head); + spin_unlock(&timer->lock); + if (ti->callback) + ti->callback(ti, resolution, ti->ticks, ti->callback_data); + spin_lock(&slave_active_lock); + /* call callbacks of slaves */ + list_for_each(q, &ti->slave_active_head) { + ts = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, active_list); + if (ts->callback) + ts->callback(ts, resolution, ti->ticks, ts->callback_data); + } + spin_unlock(&slave_active_lock); + atomic_dec(&ti->in_use); + spin_lock(&timer->lock); + } + spin_unlock(&timer->lock); +} + + +/* + + */ + +int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer) +{ + snd_timer_t *timer; + int err; + static snd_device_ops_t ops = { + dev_free: snd_timer_dev_free, + dev_register: snd_timer_dev_register, + dev_unregister: snd_timer_dev_unregister + }; + + snd_assert(tid != NULL, return -EINVAL); + snd_assert(rtimer != NULL, return -EINVAL); + *rtimer = NULL; + timer = snd_magic_kcalloc(snd_timer_t, 0, GFP_KERNEL); + if (timer == NULL) + return -ENOMEM; + timer->tmr_class = tid->dev_class; + timer->card = card; + timer->tmr_device = tid->device; + timer->tmr_subdevice = tid->subdevice; + if (id) + strncpy(timer->id, id, sizeof(timer->id) - 1); + INIT_LIST_HEAD(&timer->device_list); + INIT_LIST_HEAD(&timer->open_list_head); + INIT_LIST_HEAD(&timer->active_list_head); + spin_lock_init(&timer->lock); + if (card != NULL) { + if ((err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)) < 0) { + snd_timer_free(timer); + return err; + } + } + *rtimer = timer; + return 0; +} + +static int snd_timer_free(snd_timer_t *timer) +{ + snd_assert(timer != NULL, return -ENXIO); + if (timer->private_free) + timer->private_free(timer); + snd_magic_kfree(timer); + return 0; +} + +int snd_timer_dev_free(snd_device_t *device) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + return snd_timer_free(timer); +} + +int snd_timer_dev_register(snd_device_t *dev) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, dev->device_data, return -ENXIO); + snd_timer_t *timer1; + struct list_head *p; + + snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO); + if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && + !timer->hw.resolution && timer->hw.c_resolution == NULL) + return -EINVAL; + + down(®ister_mutex); + list_for_each(p, &snd_timer_list) { + timer1 = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer1->tmr_class > timer->tmr_class) + break; + if (timer1->tmr_class < timer->tmr_class) + continue; + if (timer1->card && timer->card) { + if (timer1->card->number > timer->card->number) + break; + if (timer1->card->number < timer->card->number) + continue; + } + if (timer1->tmr_device > timer->tmr_device) + break; + if (timer1->tmr_device < timer->tmr_device) + continue; + if (timer1->tmr_subdevice > timer->tmr_subdevice) + break; + if (timer1->tmr_subdevice < timer->tmr_subdevice) + continue; + /* conflicts.. */ + up(®ister_mutex); + return -EBUSY; + } + list_add_tail(&timer->device_list, p); + up(®ister_mutex); + return 0; +} + +int snd_timer_unregister(snd_timer_t *timer) +{ + struct list_head *p, *n; + snd_timer_instance_t *ti; + + snd_assert(timer != NULL, return -ENXIO); + down(®ister_mutex); + if (! list_empty(&timer->open_list_head)) { + snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer); + list_for_each_safe(p, n, &timer->open_list_head) { + list_del_init(p); + ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); + ti->timer = NULL; + } + } + list_del(&timer->device_list); + up(®ister_mutex); + return snd_timer_free(timer); +} + +static int snd_timer_dev_unregister(snd_device_t *device) +{ + snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + return snd_timer_unregister(timer); +} + +/* + * exported functions for global timers + */ +int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer) +{ + snd_timer_id_t tid; + + tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = -1; + tid.device = device; + tid.subdevice = 0; + return snd_timer_new(NULL, id, &tid, rtimer); +} + +int snd_timer_global_free(snd_timer_t *timer) +{ + return snd_timer_free(timer); +} + +int snd_timer_global_register(snd_timer_t *timer) +{ + snd_device_t dev; + + memset(&dev, 0, sizeof(dev)); + dev.device_data = timer; + return snd_timer_dev_register(&dev); +} + +int snd_timer_global_unregister(snd_timer_t *timer) +{ + return snd_timer_unregister(timer); +} + +/* + * System timer + */ + +unsigned int snd_timer_system_resolution(void) +{ + return 1000000000L / HZ; +} + +static void snd_timer_s_function(unsigned long data) +{ + snd_timer_t *timer = (snd_timer_t *)data; + snd_timer_interrupt(timer, timer->sticks); +} + +static int snd_timer_s_start(snd_timer_t * timer) +{ + struct timer_list *tlist; + + tlist = (struct timer_list *) timer->private_data; + tlist->expires = jiffies + timer->sticks; + add_timer(tlist); + return 0; +} + +static int snd_timer_s_stop(snd_timer_t * timer) +{ + struct timer_list *tlist; + + tlist = (struct timer_list *) timer->private_data; + del_timer(tlist); + timer->sticks = tlist->expires - jiffies; + return 0; +} + +static struct _snd_timer_hardware snd_timer_system = +{ + flags: SNDRV_TIMER_HW_FIRST, + resolution: 1000000000L / HZ, + ticks: 10000000L, + start: snd_timer_s_start, + stop: snd_timer_s_stop +}; + +static void snd_timer_free_system(snd_timer_t *timer) +{ + if (timer->private_data) + kfree(timer->private_data); +} + +static int snd_timer_register_system(void) +{ + snd_timer_t *timer; + struct timer_list *tlist; + int err; + + if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer)) < 0) + return err; + strcpy(timer->name, "system timer"); + timer->hw = snd_timer_system; + tlist = (struct timer_list *) snd_kcalloc(sizeof(struct timer_list), GFP_KERNEL); + if (tlist == NULL) { + snd_timer_free(timer); + return -ENOMEM; + } + tlist->function = snd_timer_s_function; + tlist->data = (unsigned long) timer; + timer->private_data = tlist; + timer->private_free = snd_timer_free_system; + return snd_timer_global_register(timer); +} + +/* + * Info interface + */ + +static void snd_timer_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + unsigned long flags; + snd_timer_t *timer; + snd_timer_instance_t *ti; + struct list_head *p, *q; + + down(®ister_mutex); + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + switch (timer->tmr_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + snd_iprintf(buffer, "G%i: ", timer->tmr_device); + break; + case SNDRV_TIMER_CLASS_CARD: + snd_iprintf(buffer, "C%i-%i: ", timer->card->number, timer->tmr_device); + break; + case SNDRV_TIMER_CLASS_PCM: + snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, timer->tmr_device, timer->tmr_subdevice); + break; + default: + snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, timer->card ? timer->card->number : -1, timer->tmr_device, timer->tmr_subdevice); + } + snd_iprintf(buffer, "%s :", timer->name); + if (timer->hw.resolution) + snd_iprintf(buffer, " %lu.%luus (%lu ticks)", timer->hw.resolution / 1000, timer->hw.resolution % 1000, timer->hw.ticks); + if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) + snd_iprintf(buffer, " SLAVE"); + snd_iprintf(buffer, "\n"); + spin_lock_irqsave(&timer->lock, flags); + list_for_each(q, &timer->open_list_head) { + ti = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); + snd_iprintf(buffer, " Client %s : %s : lost interrupts %li\n", + ti->owner ? ti->owner : "unknown", + ti->flags & (SNDRV_TIMER_IFLG_START|SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped", + ti->lost); + } + spin_unlock_irqrestore(&timer->lock, flags); + } + up(®ister_mutex); +} + +/* + * USER SPACE interface + */ + +static void snd_timer_user_interrupt(snd_timer_instance_t *timeri, + unsigned long resolution, + unsigned long ticks, + void *data) +{ + unsigned long flags; + snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, data, return); + snd_timer_read_t *r; + + if (tu->qused >= tu->queue_size) { + tu->overrun++; + } else { + spin_lock_irqsave(&tu->qlock, flags); + r = &tu->queue[tu->qtail++]; + tu->qtail %= tu->queue_size; + r->resolution = resolution; + r->ticks = ticks; + tu->qused++; + spin_unlock_irqrestore(&tu->qlock, flags); + wake_up(&tu->qchange_sleep); + } +} + +static int snd_timer_user_open(struct inode *inode, struct file *file) +{ + snd_timer_user_t *tu; + + tu = snd_magic_kcalloc(snd_timer_user_t, 0, GFP_KERNEL); + if (tu == NULL) + return -ENOMEM; + spin_lock_init(&tu->qlock); + init_waitqueue_head(&tu->qchange_sleep); + tu->ticks = 1; + tu->queue_size = 128; + tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); + if (tu->queue == NULL) { + snd_magic_kfree(tu); + return -ENOMEM; + } + file->private_data = tu; +#ifdef LINUX_2_2 + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int snd_timer_user_release(struct inode *inode, struct file *file) +{ + snd_timer_user_t *tu; + + if (file->private_data) { + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + file->private_data = NULL; + if (tu->timeri) + snd_timer_close(tu->timeri); + if (tu->queue) + kfree(tu->queue); + snd_magic_kfree(tu); + } +#ifdef LINUX_2_2 + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +static void snd_timer_user_zero_id(snd_timer_id_t *id) +{ + id->dev_class = SNDRV_TIMER_CLASS_NONE; + id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; + id->card = -1; + id->device = -1; + id->subdevice = -1; +} + +static void snd_timer_user_copy_id(snd_timer_id_t *id, snd_timer_t *timer) +{ + id->dev_class = timer->tmr_class; + id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; + id->card = timer->card ? timer->card->number : -1; + id->device = timer->tmr_device; + id->subdevice = timer->tmr_subdevice; +} + +static int snd_timer_user_next_device(snd_timer_id_t *_tid) +{ + snd_timer_id_t id; + snd_timer_t *timer; + struct list_head *p; + + if (copy_from_user(&id, _tid, sizeof(id))) + return -EFAULT; + down(®ister_mutex); + if (id.dev_class < 0) { /* first item */ + if (list_empty(&snd_timer_list)) + snd_timer_user_zero_id(&id); + else { + timer = (snd_timer_t *)list_entry(snd_timer_list.next, snd_timer_t, device_list); + snd_timer_user_copy_id(&id, timer); + } + } else { + switch (id.dev_class) { + case SNDRV_TIMER_CLASS_GLOBAL: + id.device = id.device < 0 ? 0 : id.device + 1; + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_device >= id.device) { + snd_timer_user_copy_id(&id, timer); + break; + } + } + if (p == &snd_timer_list) + snd_timer_user_zero_id(&id); + break; + case SNDRV_TIMER_CLASS_CARD: + case SNDRV_TIMER_CLASS_PCM: + if (id.card < 0) { + id.card = 0; + } else { + if (id.card < 0) { + id.card = 0; + } else { + if (id.device < 0) { + id.device = 0; + } else { + id.subdevice = id.subdevice < 0 ? 0 : id.subdevice + 1; + } + } + } + list_for_each(p, &snd_timer_list) { + timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + if (timer->tmr_class > id.dev_class) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_class < id.dev_class) + continue; + if (timer->card->number > id.card) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->card->number < id.card) + continue; + if (timer->tmr_device > id.device) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_device < id.device) + continue; + if (timer->tmr_subdevice > id.subdevice) { + snd_timer_user_copy_id(&id, timer); + break; + } + if (timer->tmr_subdevice < id.subdevice) + continue; + snd_timer_user_copy_id(&id, timer); + break; + } + if (p == &snd_timer_list) + snd_timer_user_zero_id(&id); + break; + default: + snd_timer_user_zero_id(&id); + } + } + up(®ister_mutex); + if (copy_to_user(_tid, &id, sizeof(*_tid))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_tselect(struct file *file, snd_timer_select_t *_tselect) +{ + snd_timer_user_t *tu; + snd_timer_select_t tselect; + char str[32]; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + if (tu->timeri) + snd_timer_close(tu->timeri); + if (copy_from_user(&tselect, _tselect, sizeof(tselect))) + return -EFAULT; + sprintf(str, "application %i", current->pid); + if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) + tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; + if ((tu->timeri = snd_timer_open(str, &tselect.id, current->pid)) == NULL) + return -ENODEV; + tu->timeri->callback = snd_timer_user_interrupt; + tu->timeri->callback_data = (void *)tu; + return 0; +} + +static int snd_timer_user_info(struct file *file, snd_timer_info_t *_info) +{ + snd_timer_user_t *tu; + snd_timer_info_t info; + snd_timer_t *t; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + t = tu->timeri->timer; + snd_assert(t != NULL, return -ENXIO); + memset(&info, 0, sizeof(info)); + info.card = t->card ? t->card->number : -1; + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) + info.flags |= SNDRV_TIMER_FLG_SLAVE; + strncpy(info.id, t->id, sizeof(info.id)-1); + strncpy(info.name, t->name, sizeof(info.name)-1); + info.ticks = t->hw.ticks; + info.resolution = t->hw.resolution; + if (copy_to_user(_info, &info, sizeof(*_info))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_params(struct file *file, snd_timer_params_t *_params) +{ + unsigned long flags; + snd_timer_user_t *tu; + snd_timer_params_t params; + snd_timer_t *t; + snd_timer_read_t *tr; + int err; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + t = tu->timeri->timer; + snd_assert(t != NULL, return -ENXIO); + if (copy_from_user(¶ms, _params, sizeof(params))) + return -EFAULT; + if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { + err = -EINVAL; + goto _end; + } + if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) { + err = -EINVAL; + goto _end; + } + snd_timer_stop(tu->timeri); + spin_lock_irqsave(&t->lock, flags); + if (params.flags & SNDRV_TIMER_PSFLG_AUTO) { + tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; + } else { + tu->timeri->flags &= ~SNDRV_TIMER_IFLG_AUTO; + } + spin_unlock_irqrestore(&t->lock, flags); + if (params.queue_size > 0 && tu->queue_size != params.queue_size) { + tr = (snd_timer_read_t *)kmalloc(params.queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); + if (tr) { + kfree(tu->queue); + tu->queue_size = params.queue_size; + tu->queue = tr; + } + } + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) { + tu->ticks = 1; + } else { + tu->ticks = params.ticks; + } + err = 0; + _end: + if (copy_to_user(_params, ¶ms, sizeof(params))) + return -EFAULT; + return err; +} + +static int snd_timer_user_status(struct file *file, snd_timer_status_t *_status) +{ + unsigned long flags; + snd_timer_user_t *tu; + snd_timer_status_t status; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + memset(&status, 0, sizeof(status)); + status.resolution = snd_timer_resolution(tu->timeri); + status.lost = tu->timeri->lost; + status.overrun = tu->overrun; + spin_lock_irqsave(&tu->qlock, flags); + status.queue = tu->qused; + spin_unlock_irqrestore(&tu->qlock, flags); + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_start(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + snd_timer_stop(tu->timeri); + tu->timeri->lost = 0; + return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0; +} + +static int snd_timer_user_stop(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; +} + +static int snd_timer_user_continue(struct file *file) +{ + int err; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + snd_assert(tu->timeri != NULL, return -ENXIO); + tu->timeri->lost = 0; + return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; +} + +static int snd_timer_user_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + switch (cmd) { + case SNDRV_TIMER_IOCTL_PVERSION: + return put_user(SNDRV_TIMER_VERSION, (int *)arg) ? -EFAULT : 0; + case SNDRV_TIMER_IOCTL_NEXT_DEVICE: + return snd_timer_user_next_device((snd_timer_id_t *)arg); + case SNDRV_TIMER_IOCTL_SELECT: + return snd_timer_user_tselect(file, (snd_timer_select_t *)arg); + case SNDRV_TIMER_IOCTL_INFO: + return snd_timer_user_info(file, (snd_timer_info_t *)arg); + case SNDRV_TIMER_IOCTL_PARAMS: + return snd_timer_user_params(file, (snd_timer_params_t *)arg); + case SNDRV_TIMER_IOCTL_STATUS: + return snd_timer_user_status(file, (snd_timer_status_t *)arg); + case SNDRV_TIMER_IOCTL_START: + return snd_timer_user_start(file); + case SNDRV_TIMER_IOCTL_STOP: + return snd_timer_user_stop(file); + case SNDRV_TIMER_IOCTL_CONTINUE: + return snd_timer_user_continue(file); + } + return -ENOTTY; +} + +static ssize_t snd_timer_user_read(struct file *file, char *buffer, size_t count, loff_t *offset) +{ + snd_timer_user_t *tu; + long result = 0; + int err = 0; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + while (count - result >= sizeof(snd_timer_read_t)) { + spin_lock_irq(&tu->qlock); + while (!tu->qused) { + wait_queue_t wait; + + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irq(&tu->qlock); + err = -EAGAIN; + break; + } + + set_current_state(TASK_INTERRUPTIBLE); + init_waitqueue_entry(&wait, current); + add_wait_queue(&tu->qchange_sleep, &wait); + + spin_unlock(&tu->qlock); + schedule(); + spin_lock_irq(&tu->qlock); + + remove_wait_queue(&tu->qchange_sleep, &wait); + + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + } + spin_unlock_irq(&tu->qlock); + if (err < 0) + break; + + if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(snd_timer_read_t))) { + err = -EFAULT; + break; + } + + tu->qhead %= tu->queue_size; + spin_lock_irq(&tu->qlock); + tu->qused--; + spin_unlock_irq(&tu->qlock); + result += sizeof(snd_timer_read_t); + buffer += sizeof(snd_timer_read_t); + } + return err? err: result; +} + +static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) +{ + unsigned int mask; + snd_timer_user_t *tu; + + tu = snd_magic_cast(snd_timer_user_t, file->private_data, return 0); + + poll_wait(file, &tu->qchange_sleep, wait); + + mask = 0; + if (tu->qused) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static struct file_operations snd_timer_f_ops = +{ +#ifndef LINUX_2_2 + owner: THIS_MODULE, +#endif + read: snd_timer_user_read, + open: snd_timer_user_open, + release: snd_timer_user_release, + poll: snd_timer_user_poll, + ioctl: snd_timer_user_ioctl, +}; + +static snd_minor_t snd_timer_reg = +{ + comment: "timer", + f_ops: &snd_timer_f_ops, +}; + +/* + * ENTRY functions + */ + +static snd_info_entry_t *snd_timer_proc_entry = NULL; + +static int __init alsa_timer_init(void) +{ + int err; + snd_info_entry_t *entry; + +#ifdef CONFIG_SND_OSSEMUL + snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer"); +#endif + if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128; + entry->c.text.read = snd_timer_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + snd_timer_proc_entry = entry; + if ((err = snd_timer_register_system()) < 0) + snd_printk(KERN_ERR "unable to register system timer (%i)\n", err); + if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, + NULL, 0, &snd_timer_reg, "timer"))<0) + snd_printk(KERN_ERR "unable to register timer device (%i)\n", err); + return 0; +} + +static void __exit alsa_timer_exit(void) +{ + struct list_head *p, *n; + + snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0); + /* unregister the system timer */ + list_for_each_safe(p, n, &snd_timer_list) { + snd_timer_t *timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); + snd_timer_unregister(timer); + } + if (snd_timer_proc_entry) { + snd_info_unregister(snd_timer_proc_entry); + snd_timer_proc_entry = NULL; + } +#ifdef CONFIG_SND_OSSEMUL + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); +#endif +} + +module_init(alsa_timer_init) +module_exit(alsa_timer_exit) + +EXPORT_SYMBOL(snd_timer_open); +EXPORT_SYMBOL(snd_timer_close); +EXPORT_SYMBOL(snd_timer_resolution); +EXPORT_SYMBOL(snd_timer_start); +EXPORT_SYMBOL(snd_timer_stop); +EXPORT_SYMBOL(snd_timer_continue); +EXPORT_SYMBOL(snd_timer_new); +EXPORT_SYMBOL(snd_timer_global_new); +EXPORT_SYMBOL(snd_timer_global_free); +EXPORT_SYMBOL(snd_timer_global_register); +EXPORT_SYMBOL(snd_timer_global_unregister); +EXPORT_SYMBOL(snd_timer_interrupt); +EXPORT_SYMBOL(snd_timer_system_resolution); diff -Nru linux/sound/core/wrappers.c linux-2.4.19-pre5-mjc/sound/core/wrappers.c --- linux/sound/core/wrappers.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/core/wrappers.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,62 @@ +/* + * Various wrappers + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef ALSA_BUILD +#include "config.h" +#endif + +#define __NO_VERSION__ +#include +#include +#ifdef ALSA_BUILD +#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) && !defined(__DEPEND__) +#define MODVERSIONS +#include +#include "sndversions.h" +#endif +#endif +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG_MEMORY +void *snd_wrapper_kmalloc(size_t size, int flags) +{ + return kmalloc(size, flags); +} + +void snd_wrapper_kfree(const void *obj) +{ + kfree(obj); +} + +void *snd_wrapper_vmalloc(unsigned long size) +{ + return vmalloc(size); +} + +void snd_wrapper_vfree(void *obj) +{ + vfree(obj); +} +#endif diff -Nru linux/sound/drivers/Config.help linux-2.4.19-pre5-mjc/sound/drivers/Config.help --- linux/sound/drivers/Config.help Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/Config.help Mon Apr 8 22:31:23 2002 @@ -0,0 +1,18 @@ +CONFIG_SND_DUMMY + Say 'Y' or 'M' to include dummy driver. This driver does nothing, but + emulates various mixer controls and PCM devices. + +CONFIG_SND_VIRMIDI + Say 'Y' or 'M' to include virtual MIDI driver. This driver allows to + connect applications using raw MIDI devices to sequencer. + +CONFIG_SND_MTPAV + Say 'Y' or 'M' to include support for MOTU MidiTimePiece AV multiport + MIDI adapter. + +CONFIG_SND_SERIAL_U16550 + Say 'Y' or 'M' to include support for MIDI serial port driver. It works + with serial UARTs 16550 and better. + +CONFIG_SND_MPU401 + Say 'Y' or 'M' to include support for MPU401 hardware using UART access. diff -Nru linux/sound/drivers/Config.in linux-2.4.19-pre5-mjc/sound/drivers/Config.in --- linux/sound/drivers/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/Config.in Mon Apr 8 22:31:23 2002 @@ -0,0 +1,12 @@ +# ALSA generic drivers + +mainmenu_option next_comment +comment 'Generic devices' + +dep_tristate 'Dummy (/dev/null) soundcard' CONFIG_SND_DUMMY $CONFIG_SND +dep_tristate 'Virtual MIDI soundcard' CONFIG_SND_VIRMIDI $CONFIG_SND $CONFIG_SND_SEQUENCER +dep_tristate 'MOTU MidiTimePiece AV multiport MIDI' CONFIG_SND_MTPAV $CONFIG_SND +dep_tristate 'UART16550 - MIDI only driver' CONFIG_SND_SERIAL_U16550 $CONFIG_SND +dep_tristate 'Generic MPU-401 UART driver' CONFIG_SND_MPU401 $CONFIG_SND + +endmenu diff -Nru linux/sound/drivers/Makefile linux-2.4.19-pre5-mjc/sound/drivers/Makefile --- linux/sound/drivers/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,36 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := drivers.o + +subdir-y := opl3 mpu401 +subdir-m := $(subdir-y) + +list-multi := snd-dummy.o snd-mtpav.o snd-serial-u16550.o snd-virmidi.o + +snd-dummy-objs := dummy.o +snd-mtpav-objs := mtpav.o +snd-serial-u16550-objs := serial-u16550.o +snd-virmidi-objs := virmidi.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_DUMMY) += snd-dummy.o +obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o +obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o +obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o + +include $(TOPDIR)/Rules.make + +snd-dummy.o: $(snd-dummy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-dummy-objs) + +snd-mtpav.o: $(snd-mtpav-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mtpav-objs) + +snd-serial-u16550.o: $(snd-serial-u16550-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-serial-u16550-objs) + +snd-virmidi.o: $(snd-virmidi-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-virmidi-objs) diff -Nru linux/sound/drivers/dummy.c linux-2.4.19-pre5-mjc/sound/drivers/dummy.c --- linux/sound/drivers/dummy.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/dummy.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,613 @@ +/* + * Dummy soundcard + * Copyright (c) by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Dummy soundcard (/dev/null)"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA,Dummy soundcard}}"); + +#define MAX_PCM_DEVICES 4 +#define MAX_PCM_SUBSTREAMS 16 +#define MAX_MIDI_DEVICES 2 + +#if 0 /* RME9652 emulation */ +#define MAX_BUFFER_SIZE (26 * 64 * 1024) +#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE +#define USE_CHANNELS_MIN 26 +#define USE_CHANNELS_MAX 26 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX 2 +#endif + +/* defaults */ +#ifndef MAX_BUFFER_SIZE +#define MAX_BUFFER_SIZE (64*1024) +#endif +#ifndef USE_FORMATS +#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) +#endif +#ifndef USE_CHANNELS_MIN +#define USE_CHANNELS_MIN 1 +#endif +#ifndef USE_CHANNELS_MAX +#define USE_CHANNELS_MAX 2 +#endif +#ifndef USE_PERIODS_MIN +#define USE_PERIODS_MIN 1 +#endif +#ifndef USE_PERIODS_MAX +#define USE_PERIODS_MAX 1024 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static int snd_pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; +//static int snd_midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for dummy soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for dummy soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this dummy soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_devs, "PCM devices # (0-4) for dummy driver."); +MODULE_PARM_SYNTAX(snd_pcm_devs, SNDRV_ENABLED ",allows:{{0,4}},default:1,dialog:list"); +MODULE_PARM(snd_pcm_substreams, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_substreams, "PCM substreams # (1-16) for dummy driver."); +MODULE_PARM_SYNTAX(snd_pcm_substreams, SNDRV_ENABLED ",allows:{{1,16}},default:8,dialog:list"); +//MODULE_PARM(snd_midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +//MODULE_PARM_DESC(snd_midi_devs, "MIDI devices # (0-2) for dummy driver."); +//MODULE_PARM_SYNTAX(snd_midi_devs, SNDRV_ENABLED ",allows:{{0,2}},default:8,dialog:list"); + +#define MIXER_ADDR_MASTER 0 +#define MIXER_ADDR_LINE 1 +#define MIXER_ADDR_MIC 2 +#define MIXER_ADDR_SYNTH 3 +#define MIXER_ADDR_CD 4 +#define MIXER_ADDR_LAST 4 + +typedef struct snd_card_dummy { + snd_card_t *card; + spinlock_t mixer_lock; + int mixer_volume[MIXER_ADDR_LAST+1][2]; + int capture_source[MIXER_ADDR_LAST+1][2]; +} snd_card_dummy_t; + +typedef struct snd_card_dummy_pcm { + snd_card_dummy_t *dummy; + spinlock_t lock; + struct timer_list timer; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_bps; /* bytes per second */ + unsigned int pcm_jiffie; /* bytes per one jiffie */ + unsigned int pcm_irq_pos; /* IRQ position */ + unsigned int pcm_buf_pos; /* position in buffer */ + snd_pcm_substream_t *substream; +} snd_card_dummy_pcm_t; + +static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int snd_card_dummy_playback_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_card_dummy_capture_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + + dpcm->timer.expires = 1 + jiffies; + add_timer(&dpcm->timer); +} + +static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + + del_timer(&dpcm->timer); +} + +static int snd_card_dummy_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_card_dummy_pcm_timer_start(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_card_dummy_pcm_timer_stop(substream); + } else { + return -EINVAL; + } + return 0; +} + +static int snd_card_dummy_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_card_dummy_pcm_timer_start(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_card_dummy_pcm_timer_stop(substream); + } else { + return -EINVAL; + } + return 0; +} + +static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + unsigned int bps; + + bps = runtime->rate * runtime->channels; + bps *= snd_pcm_format_width(runtime->format); + bps /= 8; + if (bps <= 0) + return -EINVAL; + dpcm->pcm_bps = bps; + dpcm->pcm_jiffie = bps / HZ; + dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream); + dpcm->pcm_count = snd_pcm_lib_period_bytes(substream); + dpcm->pcm_irq_pos = 0; + dpcm->pcm_buf_pos = 0; + return 0; +} + +static int snd_card_dummy_playback_prepare(snd_pcm_substream_t * substream) +{ + return snd_card_dummy_pcm_prepare(substream); +} + +static int snd_card_dummy_capture_prepare(snd_pcm_substream_t * substream) +{ + return snd_card_dummy_pcm_prepare(substream); +} + +static void snd_card_dummy_pcm_timer_function(unsigned long data) +{ + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, (void *)data, return); + + dpcm->timer.expires = 1 + jiffies; + add_timer(&dpcm->timer); + spin_lock_irq(&dpcm->lock); + dpcm->pcm_irq_pos += dpcm->pcm_jiffie; + dpcm->pcm_buf_pos += dpcm->pcm_jiffie; + dpcm->pcm_buf_pos %= dpcm->pcm_size; + while (dpcm->pcm_irq_pos >= dpcm->pcm_count) { + dpcm->pcm_irq_pos -= dpcm->pcm_count; + snd_pcm_period_elapsed(dpcm->substream); + } + spin_unlock_irq(&dpcm->lock); +} + +static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + + return bytes_to_frames(runtime, dpcm->pcm_buf_pos); +} + +static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + + return bytes_to_frames(runtime, dpcm->pcm_buf_pos); +} + +static snd_pcm_hardware_t snd_card_dummy_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: USE_FORMATS, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: USE_CHANNELS_MIN, + channels_max: USE_CHANNELS_MAX, + buffer_bytes_max: MAX_BUFFER_SIZE, + period_bytes_min: 64, + period_bytes_max: MAX_BUFFER_SIZE, + periods_min: USE_PERIODS_MIN, + periods_max: USE_PERIODS_MAX, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_card_dummy_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: USE_FORMATS, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: USE_CHANNELS_MIN, + channels_max: USE_CHANNELS_MAX, + buffer_bytes_max: MAX_BUFFER_SIZE, + period_bytes_min: 64, + period_bytes_max: MAX_BUFFER_SIZE, + periods_min: USE_PERIODS_MIN, + periods_max: USE_PERIODS_MAX, + fifo_size: 0, +}; + +static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime) +{ + snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + snd_magic_kfree(dpcm); +} + +static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm; + + dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { + snd_magic_kfree(dpcm); + return -ENOMEM; + } + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_dummy_pcm_timer_function; + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_playback; + if (substream->pcm->device & 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_card_dummy_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_card_dummy_pcm_t *dpcm; + + dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { + snd_magic_kfree(dpcm); + return -ENOMEM; + } + memset(runtime->dma_area, 0, runtime->dma_bytes); + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_dummy_pcm_timer_function; + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_dummy_runtime_free; + runtime->hw = snd_card_dummy_capture; + if (substream->pcm->device == 1) { + runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; + runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; + } + if (substream->pcm->device & 2) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return 0; +} + +static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return 0; +} + +static snd_pcm_ops_t snd_card_dummy_playback_ops = { + open: snd_card_dummy_playback_open, + close: snd_card_dummy_playback_close, + ioctl: snd_card_dummy_playback_ioctl, + prepare: snd_card_dummy_playback_prepare, + trigger: snd_card_dummy_playback_trigger, + pointer: snd_card_dummy_playback_pointer, +}; + +static snd_pcm_ops_t snd_card_dummy_capture_ops = { + open: snd_card_dummy_capture_open, + close: snd_card_dummy_capture_close, + ioctl: snd_card_dummy_capture_ioctl, + prepare: snd_card_dummy_capture_prepare, + trigger: snd_card_dummy_capture_trigger, + pointer: snd_card_dummy_capture_pointer, +}; + +static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm)) < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); + pcm->private_data = dummy; + pcm->info_flags = 0; + strcpy(pcm->name, "Dummy PCM"); + return 0; +} + +#define DUMMY_VOLUME(xname, xindex, addr) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_dummy_volume_info, \ + get: snd_dummy_volume_get, put: snd_dummy_volume_put, \ + private_value: addr } + +static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value; + + spin_lock_irqsave(&dummy->mixer_lock, flags); + ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0]; + ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1]; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return 0; +} + +static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] % 101; + right = ucontrol->value.integer.value[1] % 101; + spin_lock_irqsave(&dummy->mixer_lock, flags); + change = dummy->mixer_volume[addr][0] != left && + dummy->mixer_volume[addr][1] != right; + dummy->mixer_volume[addr][0] = left; + dummy->mixer_volume[addr][1] = right; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return change; +} + +#define DUMMY_CAPSRC(xname, xindex, addr) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_dummy_capsrc_info, \ + get: snd_dummy_capsrc_get, put: snd_dummy_capsrc_put, \ + private_value: addr } + +static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value; + + spin_lock_irqsave(&dummy->mixer_lock, flags); + ucontrol->value.integer.value[0] = dummy->capture_source[addr][0]; + ucontrol->value.integer.value[1] = dummy->capture_source[addr][1]; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return 0; +} + +static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] & 1; + right = ucontrol->value.integer.value[1] & 1; + spin_lock_irqsave(&dummy->mixer_lock, flags); + change = dummy->capture_source[addr][0] != left && + dummy->capture_source[addr][1] != right; + dummy->capture_source[addr][0] = left; + dummy->capture_source[addr][1] = right; + spin_unlock_irqrestore(&dummy->mixer_lock, flags); + return change; +} + +#define DUMMY_CONTROLS (sizeof(snd_dummy_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_dummy_controls[] = { +DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), +DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH), +DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE), +DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC), +DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER), +DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD), +DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER) +}; + +int __init snd_card_dummy_new_mixer(snd_card_dummy_t * dummy) +{ + snd_card_t *card = dummy->card; + int idx, err; + + snd_assert(dummy != NULL, return -EINVAL); + spin_lock_init(&dummy->mixer_lock); + strcpy(card->mixername, "Dummy Mixer"); + + for (idx = 0; idx < DUMMY_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0) + return err; + } + return 0; +} + +static int __init snd_card_dummy_probe(int dev) +{ + snd_card_t *card; + struct snd_card_dummy *dummy; + int idx, err; + + if (!snd_enable[dev]) + return -ENODEV; + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_dummy)); + if (card == NULL) + return -ENOMEM; + dummy = (struct snd_card_dummy *)card->private_data; + dummy->card = card; + for (idx = 0; idx < MAX_PCM_DEVICES && idx < snd_pcm_devs[dev]; idx++) { + if (snd_pcm_substreams[dev] < 1) + snd_pcm_substreams[dev] = 1; + if (snd_pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) + snd_pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; + if ((err = snd_card_dummy_pcm(dummy, idx, snd_pcm_substreams[dev])) < 0) + goto __nodev; + } + if ((err = snd_card_dummy_new_mixer(dummy)) < 0) + goto __nodev; + strcpy(card->driver, "Dummy"); + strcpy(card->shortname, "Dummy"); + sprintf(card->longname, "Dummy %i", dev + 1); + if ((err = snd_card_register(card)) == 0) { + snd_dummy_cards[dev] = card; + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __init alsa_card_dummy_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_card_dummy_probe(dev) < 0) { +#ifdef MODULE + printk(KERN_ERR "Dummy soundcard #%i not found or device busy\n", dev + 1); +#endif + break; + } + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Dummy soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_dummy_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_dummy_cards[idx]); +} + +module_init(alsa_card_dummy_init) +module_exit(alsa_card_dummy_exit) + +#ifndef MODULE + +/* format is: snd-dummy=snd_enable,snd_index,snd_id, + snd_pcm_devs,snd_pcm_substreams */ + +static int __init alsa_card_dummy_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_pcm_devs[nr_dev]) == 2 && + get_option(&str,&snd_pcm_substreams[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-dummy=", alsa_card_dummy_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/drivers/mpu401/Makefile linux-2.4.19-pre5-mjc/sound/drivers/mpu401/Makefile --- linux/sound/drivers/mpu401/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/mpu401/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,53 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _mpu401.o + +list-multi := snd-mpu401.o snd-mpu401-uart.o + +export-objs := mpu401_uart.o + +snd-mpu401-objs := mpu401.o +snd-mpu401-uart-objs := mpu401_uart.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_MPU401) += snd-mpu401.o snd-mpu401-uart.o +obj-$(CONFIG_SND_ALS100) += snd-mpu401-uart.o +obj-$(CONFIG_SND_AZT2320) += snd-mpu401-uart.o +obj-$(CONFIG_SND_DT0197H) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES18XX) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPL3SA2) += snd-mpu401-uart.o +obj-$(CONFIG_SND_AD1816A) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4231) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4232) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CS4236) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1688) += snd-mpu401-uart.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-mpu401-uart.o +obj-$(CONFIG_SND_OPTI93X) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SB16) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SBAWE) += snd-mpu401-uart.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ALS4000) += snd-mpu401-uart.o +obj-$(CONFIG_SND_CMIPCI) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1938) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o +obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o +obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o +obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o +obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o +obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o +obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o +obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o + +include $(TOPDIR)/Rules.make + +snd-mpu401.o: $(snd-mpu401-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-objs) + +snd-mpu401-uart.o: $(snd-mpu401-uart-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-mpu401-uart-objs) diff -Nru linux/sound/drivers/mpu401/mpu401.c linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401.c --- linux/sound/drivers/mpu401/mpu401.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,155 @@ +/* + * Driver for generic MPU-401 boards (UART mode only) + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("MPU-401 UART"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable MPU-401 device."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for MPU-401 device."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); + +static snd_card_t *snd_mpu401_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static int __init snd_card_mpu401_probe(int dev) +{ + snd_card_t *card; + int err; + + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify or disable IRQ port\n"); + return -EINVAL; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + snd_port[dev], 0, + snd_irq[dev], snd_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) { + printk(KERN_ERR "MPU401 not detected at 0x%lx\n", snd_port[dev]); + snd_card_free(card); + return -ENODEV; + } + strcpy(card->driver, "MPU-401 UART"); + strcpy(card->shortname, card->driver); + sprintf(card->longname, "%s at 0x%lx, ", card->shortname, snd_port[dev]); + if (snd_irq[dev] >= 0) { + sprintf(card->longname + strlen(card->longname), "IRQ %d", snd_irq[dev]); + } else { + strcat(card->longname, "polled"); + } + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_mpu401_cards[dev] = card; + return 0; +} + +static int __init alsa_card_mpu401_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + if (snd_card_mpu401_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "MPU-401 device not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_mpu401_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_mpu401_cards[idx]); +} + +module_init(alsa_card_mpu401_init) +module_exit(alsa_card_mpu401_exit) + +#ifndef MODULE + +/* format is: snd-mpu401=snd_enable,snd_index,snd_id,snd_port,snd_irq */ + +static int __init alsa_card_mpu401_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-mpu401=", alsa_card_mpu401_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/drivers/mpu401/mpu401_uart.c linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401_uart.c --- linux/sound/drivers/mpu401/mpu401_uart.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/mpu401/mpu401_uart.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,445 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of MPU-401 in UART mode + * + * MPU-401 supports UART mode which is not capable generate transmit + * interrupts thus output is done via polling. Also, if irq < 0, then + * input is done also via polling. Do not expect good performance. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); +MODULE_LICENSE("GPL"); + +static void snd_mpu401_uart_input_read(mpu401_t * mpu); +static void snd_mpu401_uart_output_write(mpu401_t * mpu); + +/* + + */ + +#define snd_mpu401_input_avail(mpu) (!(inb(MPU401C(mpu)) & 0x80)) +#define snd_mpu401_output_ready(mpu) (!(inb(MPU401C(mpu)) & 0x40)) + +#define MPU401_RESET 0xff +#define MPU401_ENTER_UART 0x3f +#define MPU401_ACK 0xfe + +static void snd_mpu401_uart_clear_rx(mpu401_t *mpu) +{ + int timeout = 100000; + for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) + inb(MPU401D(mpu)); +#ifdef CONFIG_SND_DEBUG + if (timeout <= 0) + snd_printk("cmd: clear rx timeout (status = 0x%x)\n", inb(MPU401C(mpu))); +#endif +} + +static void _snd_mpu401_uart_interrupt(mpu401_t *mpu) +{ + if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) + snd_mpu401_uart_input_read(mpu); + else + snd_mpu401_uart_clear_rx(mpu); + /* ok. for better Tx performance try do some output when input is done */ + if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + snd_mpu401_uart_output_write(mpu); +} + +void snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + mpu401_t *mpu = snd_magic_cast(mpu401_t, dev_id, return); + + if (mpu == NULL) + return; + _snd_mpu401_uart_interrupt(mpu); +} + +static void snd_mpu401_uart_timer(unsigned long data) +{ + unsigned long flags; + mpu401_t *mpu = snd_magic_cast(mpu401_t, (void *)data, return); + + spin_lock_irqsave(&mpu->timer_lock, flags); + /*mpu->mode |= MPU401_MODE_TIMER;*/ + mpu->timer.expires = 1 + jiffies; + add_timer(&mpu->timer); + spin_unlock_irqrestore(&mpu->timer_lock, flags); + if (mpu->rmidi) + _snd_mpu401_uart_interrupt(mpu); +} + +static void snd_mpu401_uart_add_timer (mpu401_t *mpu, int input) +{ + unsigned long flags; + + spin_lock_irqsave (&mpu->timer_lock, flags); + if (mpu->timer_invoked == 0) { + mpu->timer.data = (unsigned long)mpu; + mpu->timer.function = snd_mpu401_uart_timer; + mpu->timer.expires = 1 + jiffies; + add_timer(&mpu->timer); + } + mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER; + spin_unlock_irqrestore (&mpu->timer_lock, flags); +} + +static void snd_mpu401_uart_remove_timer (mpu401_t *mpu, int input) +{ + unsigned long flags; + + spin_lock_irqsave (&mpu->timer_lock, flags); + if (mpu->timer_invoked) { + mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER; + if (! mpu->timer_invoked) + del_timer(&mpu->timer); + } + spin_unlock_irqrestore (&mpu->timer_lock, flags); +} + +/* + + */ + +static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack) +{ + unsigned long flags; + int timeout, ok; + + spin_lock_irqsave(&mpu->input_lock, flags); + if (mpu->hardware != MPU401_HW_TRID4DWAVE) { + outb(0x00, MPU401D(mpu)); + /*snd_mpu401_uart_clear_rx(mpu);*/ + } + /* ok. standard MPU-401 initialization */ + if (mpu->hardware != MPU401_HW_SB) { + for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--) + udelay(10); +#ifdef CONFIG_SND_DEBUG + if (!timeout) + snd_printk("cmd: tx timeout (status = 0x%x)\n", inb(MPU401C(mpu))); +#endif + } + outb(cmd, MPU401C(mpu)); + if (ack) { + ok = 0; + timeout = 10000; + while (!ok && timeout-- > 0) { + if (snd_mpu401_input_avail(mpu)) { + if (inb(MPU401D(mpu)) == MPU401_ACK) + ok = 1; + } + } + if (!ok && inb(MPU401D(mpu)) == MPU401_ACK) + ok = 1; + } else { + ok = 1; + } + spin_unlock_irqrestore(&mpu->input_lock, flags); + if (! ok) + snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu))); + // snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu))); +} + +/* + * input/output open/close - protected by open_mutex in rawmidi.c + */ +static int snd_mpu401_uart_input_open(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + int err; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) + return err; + if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); + snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_input = substream; + set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + return 0; +} + +static int snd_mpu401_uart_output_open(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + int err; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) + return err; + if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1); + snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_output = substream; + set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); + return 0; +} + +static int snd_mpu401_uart_input_close(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); + mpu->substream_input = NULL; + if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + if (mpu->close_input) + mpu->close_input(mpu); + return 0; +} + +static int snd_mpu401_uart_output_close(snd_rawmidi_substream_t * substream) +{ + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); + mpu->substream_output = NULL; + if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) + snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); + if (mpu->close_output) + mpu->close_output(mpu); + return 0; +} + +/* + * trigger input + */ +static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mpu401_t *mpu; + int max = 64; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&mpu->input_lock, flags); + if (up) { + if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { + /* flush FIFO */ + while (max-- > 0) + inb(MPU401D(mpu)); + } + if (mpu->irq < 0) + snd_mpu401_uart_add_timer(mpu, 1); + } else { + if (mpu->irq < 0) + snd_mpu401_uart_remove_timer(mpu, 1); + clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); + } + spin_unlock_irqrestore(&mpu->input_lock, flags); + if (up) + snd_mpu401_uart_input_read(mpu); +} + +static void snd_mpu401_uart_input_read(mpu401_t * mpu) +{ + int max = 128; + unsigned char byte; + + /* prevent double enter via event callback */ + if (test_and_set_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode)) + return; + spin_lock(&mpu->input_lock); + while (max-- > 0) { + if (snd_mpu401_input_avail(mpu)) { + byte = inb(MPU401D(mpu)); + if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { + spin_unlock(&mpu->input_lock); + snd_rawmidi_receive(mpu->substream_input, &byte, 1); + spin_lock(&mpu->input_lock); + } + } else { + break; /* input not available */ + } + } + spin_unlock(&mpu->input_lock); + clear_bit(MPU401_MODE_BIT_RX_LOOP, &mpu->mode); +} + +/* + * Tx FIFO sizes: + * CS4237B - 16 bytes + * AudioDrive ES1688 - 12 bytes + * S3 SonicVibes - 8 bytes + * SoundBlaster AWE 64 - 2 bytes (ugly hardware) + */ + +static void snd_mpu401_uart_output_write(mpu401_t * mpu) +{ + unsigned char byte; + int max = 256, timeout; + + if (!test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) + return; + /* prevent double enter */ + if (test_and_set_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode)) + return; + do { + spin_lock(&mpu->output_lock); + if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { + for (timeout = 100; timeout > 0; timeout--) { + if (snd_mpu401_output_ready(mpu)) { + outb(byte, MPU401D(mpu)); + snd_rawmidi_transmit_ack(mpu->substream_output, 1); + break; + } + } + } else { + snd_mpu401_uart_remove_timer (mpu, 0); + max = 1; /* no other data - leave the tx loop */ + } + spin_unlock(&mpu->output_lock); + } while (--max > 0); + clear_bit(MPU401_MODE_BIT_TX_LOOP, &mpu->mode); +} + +static void snd_mpu401_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mpu401_t *mpu; + + mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&mpu->output_lock, flags); + if (up) { + set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + snd_mpu401_uart_add_timer(mpu, 0); + } else { + snd_mpu401_uart_remove_timer(mpu, 0); + clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + } + spin_unlock_irqrestore(&mpu->output_lock, flags); + if (up) + snd_mpu401_uart_output_write(mpu); +} + +/* + + */ + +static snd_rawmidi_ops_t snd_mpu401_uart_output = +{ + open: snd_mpu401_uart_output_open, + close: snd_mpu401_uart_output_close, + trigger: snd_mpu401_uart_output_trigger, +}; + +static snd_rawmidi_ops_t snd_mpu401_uart_input = +{ + open: snd_mpu401_uart_input_open, + close: snd_mpu401_uart_input_close, + trigger: snd_mpu401_uart_input_trigger, +}; + +static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi) +{ + mpu401_t *mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return); + if (mpu->irq_flags && mpu->irq >= 0) + free_irq(mpu->irq, (void *) mpu); + if (mpu->res) { + release_resource(mpu->res); + kfree_nocheck(mpu->res); + } + snd_magic_kfree(mpu); +} + +int snd_mpu401_uart_new(snd_card_t * card, int device, + unsigned short hardware, + unsigned long port, int integrated, + int irq, int irq_flags, + snd_rawmidi_t ** rrawmidi) +{ + mpu401_t *mpu; + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0) + return err; + mpu = snd_magic_kcalloc(mpu401_t, 0, GFP_KERNEL); + if (mpu == NULL) { + snd_device_free(card, rmidi); + return -ENOMEM; + } + rmidi->private_data = mpu; + rmidi->private_free = snd_mpu401_uart_free; + spin_lock_init(&mpu->input_lock); + spin_lock_init(&mpu->output_lock); + spin_lock_init(&mpu->timer_lock); + mpu->hardware = hardware; + if (!integrated) { + if ((mpu->res = request_region(port, 2, "MPU401 UART")) == NULL) { + snd_device_free(card, rmidi); + return -EBUSY; + } + } + mpu->port = port; + if (irq >= 0 && irq_flags) { + if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) { + snd_printk("unable to grab IRQ %d\n", irq); + snd_device_free(card, rmidi); + return -EBUSY; + } + mpu->irq = irq; + mpu->irq_flags = irq_flags; + } + strcpy(rmidi->name, "MPU-401 (UART)"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + mpu->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +EXPORT_SYMBOL(snd_mpu401_uart_interrupt); +EXPORT_SYMBOL(snd_mpu401_uart_new); + +/* + * INIT part + */ + +static int __init alsa_mpu401_uart_init(void) +{ + return 0; +} + +static void __exit alsa_mpu401_uart_exit(void) +{ +} + +module_init(alsa_mpu401_uart_init) +module_exit(alsa_mpu401_uart_exit) diff -Nru linux/sound/drivers/mtpav.c linux-2.4.19-pre5-mjc/sound/drivers/mtpav.c --- linux/sound/drivers/mtpav.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/mtpav.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,813 @@ +/* + * MOTU Midi Timepiece ALSA Main routines + * Copyright by Michael T. Mayers (c) Jan 09, 2000 + * mail: tweakoz@pacbell.net + * Thanks to John Galbraith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This driver is for the 'Mark Of The Unicorn' (MOTU) + * MidiTimePiece AV multiport MIDI interface + * + * IOPORTS + * ------- + * 8 MIDI Ins and 8 MIDI outs + * Video Sync In (BNC), Word Sync Out (BNC), + * ADAT Sync Out (DB9) + * SMPTE in/out (1/4") + * 2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs. + * Macintosh RS422 serial port + * RS422 "network" port for ganging multiple MTP's + * PC Parallel Port ( which this driver currently uses ) + * + * MISC FEATURES + * ------------- + * Hardware MIDI routing, merging, and filtering + * MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources + * 128 'scene' memories, recallable from MIDI program change + * + * + * ChangeLog + * Jun 11 2001 Takashi Iwai + * - Recoded & debugged + * - Added timer interrupt for midi outputs + * - snd_hwports is between 1 and 8, which specifies the number of hardware ports. + * The three global ports, computer, adat and broadcast ports, are created + * always after h/w and remote ports. + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include +#include + +/* + * globals + */ +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Michael T. Mayers"); +MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{MOTU,MidiTimePiece AV multiport MIDI}}"); + +// io resources +#define MTPAV_IOBASE 0x378 +#define MTPAV_IRQ 7 +#define MTPAV_MAX_PORTS 8 + +static int snd_index = SNDRV_DEFAULT_IDX1; +static char *snd_id = SNDRV_DEFAULT_STR1; +static long snd_port = MTPAV_IOBASE; /* 0x378, 0x278 */ +static int snd_irq = MTPAV_IRQ; /* 7, 5 */ +static int snd_hwports = MTPAV_MAX_PORTS; /* use hardware ports 1-8 */ + +MODULE_PARM(snd_index, "i"); +MODULE_PARM_DESC(snd_index, "Index value for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "s"); +MODULE_PARM_DESC(snd_id, "ID string for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_port, "l"); +MODULE_PARM_DESC(snd_port, "Parallel port # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x378},{0x278}},dialog:list"); +MODULE_PARM(snd_irq, "i"); +MODULE_PARM_DESC(snd_irq, "Parallel IRQ # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{7},{5}},dialog:list"); +MODULE_PARM(snd_hwports, "i"); +MODULE_PARM_DESC(snd_hwports, "Hardware ports # for MotuMTPAV MIDI."); +MODULE_PARM_SYNTAX(snd_hwports, SNDRV_ENABLED ",allows:{{1,8}},dialog:list"); + +/* + * defines + */ +//#define USE_FAKE_MTP // dont actually read/write to MTP device (for debugging without an actual unit) (does not work yet) + +// parallel port usage masks +#define SIGS_BYTE 0x08 +#define SIGS_RFD 0x80 +#define SIGS_IRQ 0x40 +#define SIGS_IN0 0x10 +#define SIGS_IN1 0x20 + +#define SIGC_WRITE 0x04 +#define SIGC_READ 0x08 +#define SIGC_INTEN 0x10 + +#define DREG 0 +#define SREG 1 +#define CREG 2 + +// +#define MTPAV_MODE_INPUT_OPENED 0x01 +#define MTPAV_MODE_OUTPUT_OPENED 0x02 +#define MTPAV_MODE_INPUT_TRIGGERED 0x04 +#define MTPAV_MODE_OUTPUT_TRIGGERED 0x08 + +#define NUMPORTS (0x12+1) + + +/* + */ + +typedef struct mtpav_port { + u8 number; + u8 hwport; + u8 mode; + snd_rawmidi_substream_t *input; + snd_rawmidi_substream_t *output; +} mtpav_port_t; + +typedef struct mtpav { + snd_card_t *card; + unsigned long port; + struct resource *res_port; + int irq; /* interrupt (for inputs) */ + spinlock_t spinlock; + int share_irq; /* number of accesses to input interrupts */ + int istimer; /* number of accesses to timer interrupts */ + struct timer_list timer; /* timer interrupts for outputs */ + snd_rawmidi_t *rmidi; + int num_ports; /* number of hw ports (1-8) */ + mtpav_port_t ports[NUMPORTS]; /* all ports including computer, adat and bc */ + + u32 inmidiport; /* selected input midi port */ + u32 inmidistate; /* during midi command 0xf5 */ + + u32 outmidihwport; /* selected output midi hw port */ +} mtpav_t; + + +/* + * global instance + * hey, we handle at most only one card.. + */ +static mtpav_t *mtp_card; + +/* + * possible hardware ports (selected by 0xf5 port message) + * 0x00 all ports + * 0x01 .. 0x08 this MTP's ports 1..8 + * 0x09 .. 0x10 networked MTP's ports (9..16) + * 0x11 networked MTP's computer port + * 0x63 to ADAT + * + * mappig: + * subdevice 0 - (X-1) ports + * X - (2*X-1) networked ports + * X computer + * X+1 ADAT + * X+2 all ports + * + * where X = chip->num_ports + */ + +#define MTPAV_PIDX_COMPUTER 0 +#define MTPAV_PIDX_ADAT 1 +#define MTPAV_PIDX_BROADCAST 2 + + +static int translate_subdevice_to_hwport(mtpav_t *chip, int subdev) +{ + if (subdev < 0) + return 0x01; /* invalid - use port 0 as default */ + else if (subdev < chip->num_ports) + return subdev + 1; /* single mtp port */ + else if (subdev < chip->num_ports * 2) + return subdev - chip->num_ports + 0x09; /* remote port */ + else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER) + return 0x11; /* computer port */ + else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT) + return 0x63; /* ADAT */ + return 0; /* all ports */ +} + +static int translate_hwport_to_subdevice(mtpav_t *chip, int hwport) +{ + int port; + if (hwport <= 0x00) /* all ports */ + return chip->num_ports + MTPAV_PIDX_BROADCAST; + else if (hwport <= 0x08) { /* single port */ + port = hwport - 1; + if (port >= chip->num_ports) + port = 0; + return port; + } else if (hwport <= 0x10) { /* remote port */ + port = hwport - 0x09 + chip->num_ports; + if (port >= chip->num_ports * 2) + port = chip->num_ports; + return port; + } else if (hwport == 0x11) /* computer port */ + return chip->num_ports + MTPAV_PIDX_COMPUTER; + else /* ADAT */ + return chip->num_ports + MTPAV_PIDX_ADAT; +} + + +/* + */ + +static u8 snd_mtpav_getreg(mtpav_t *chip, u16 reg) +{ + u8 rval = 0; + + if (reg == SREG) { + rval = inb(chip->port + SREG); + rval = (rval & 0xf8); + } else if (reg == CREG) { + rval = inb(chip->port + CREG); + rval = (rval & 0x1c); + } + + return rval; +} + +/* + */ + +static void snd_mtpav_mputreg(mtpav_t *chip, u16 reg, u8 val) +{ + if (reg == DREG) { + outb(val, chip->port + DREG); + } else if (reg == CREG) { + outb(val, chip->port + CREG); + } +} + +/* + */ + +static void snd_mtpav_wait_rfdhi(mtpav_t *chip) +{ + int counts = 10000; + u8 sbyte; + + sbyte = snd_mtpav_getreg(chip, SREG); + while (!(sbyte & SIGS_RFD) && counts--) { + sbyte = snd_mtpav_getreg(chip, SREG); + udelay(10); + } +} + +static void snd_mtpav_send_byte(mtpav_t *chip, u8 byte) +{ + u8 tcbyt; + u8 clrwrite; + u8 setwrite; + + snd_mtpav_wait_rfdhi(chip); + + ///////////////// + + tcbyt = snd_mtpav_getreg(chip, CREG); + clrwrite = tcbyt & (SIGC_WRITE ^ 0xff); + setwrite = tcbyt | SIGC_WRITE; + + snd_mtpav_mputreg(chip, DREG, byte); + snd_mtpav_mputreg(chip, CREG, clrwrite); // clear write bit + + snd_mtpav_mputreg(chip, CREG, setwrite); // set write bit + +} + + +/* + */ + +/* call this with spin lock held */ +static void snd_mtpav_output_port_write(mtpav_port_t *port, + snd_rawmidi_substream_t *substream) +{ + u8 outbyte; + + // send port change command if necessary + + if (port->hwport != mtp_card->outmidihwport) { + mtp_card->outmidihwport = port->hwport; + + snd_mtpav_send_byte(mtp_card, 0xf5); + snd_mtpav_send_byte(mtp_card, port->hwport); + //snd_printk("new outport: 0x%x\n", (unsigned int) port->hwport); + + } + + // send data + + while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1) + snd_mtpav_send_byte(mtp_card, outbyte); +} + +static void snd_mtpav_output_write(snd_rawmidi_substream_t * substream) +{ + mtpav_port_t *port = &mtp_card->ports[substream->number]; + unsigned long flags; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + snd_mtpav_output_port_write(port, substream); + spin_unlock_irqrestore(&mtp_card->spinlock, flags); +} + + +/* + * mtpav control + */ + +static void snd_mtpav_portscan(mtpav_t *chip) // put mtp into smart routing mode +{ + u8 port; + + for (port = 0; port < 8; port++) { + snd_mtpav_send_byte(chip, 0xf5); + snd_mtpav_send_byte(chip, port); + snd_mtpav_send_byte(chip, 0xfe); + } +} + +/* + */ + +static int snd_mtpav_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + //printk("mtpav port: %d opened\n", (int) substream->number); + spin_lock_irqsave(&mtp_card->spinlock, flags); + port->mode |= MTPAV_MODE_INPUT_OPENED; + port->input = substream; + if (mtp_card->share_irq++ == 0) + snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +} + +/* + */ + +static int snd_mtpav_input_close(snd_rawmidi_substream_t *substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + //printk("mtpav port: %d closed\n", (int) port); + + spin_lock_irqsave(&mtp_card->spinlock, flags); + + port->mode &= (~MTPAV_MODE_INPUT_OPENED); + port->input = NULL; + if (--mtp_card->share_irq == 0) + snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts + + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +} + +/* + */ + +static void snd_mtpav_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + if (up) + port->mode |= MTPAV_MODE_INPUT_TRIGGERED; + else + port->mode &= ~MTPAV_MODE_INPUT_TRIGGERED; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + +} + + +/* + * timer interrupt for outputs + */ + +static void snd_mtpav_output_timer(unsigned long data) +{ + mtpav_t *chip = snd_magic_cast(mtpav_t, (void *)data, return); + int p; + + spin_lock(&chip->spinlock); + /* reprogram timer */ + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); + /* process each port */ + for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) { + mtpav_port_t *port = &mtp_card->ports[p]; + if ((port->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && port->output) + snd_mtpav_output_port_write(port, port->output); + } + spin_unlock(&chip->spinlock); +} + +static void snd_mtpav_add_output_timer(mtpav_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->spinlock, flags); + chip->timer.function = snd_mtpav_output_timer; + chip->timer.data = (unsigned long) mtp_card; + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); + spin_unlock_irqrestore(&chip->spinlock, flags); +} + +static void snd_mtpav_remove_output_timer(mtpav_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->spinlock, flags); + del_timer(&chip->timer); + spin_unlock_irqrestore(&chip->spinlock, flags); +} + +/* + */ + +static int snd_mtpav_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + port->mode |= MTPAV_MODE_OUTPUT_OPENED; + port->output = substream; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +}; + +/* + */ + +static int snd_mtpav_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + port->mode &= (~MTPAV_MODE_OUTPUT_OPENED); + port->output = NULL; + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + return 0; +}; + +/* + */ + +static void snd_mtpav_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + mtpav_port_t *port = &mtp_card->ports[substream->number]; + + spin_lock_irqsave(&mtp_card->spinlock, flags); + if (up) { + if (! (port->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) { + if (mtp_card->istimer++ == 0) + snd_mtpav_add_output_timer(mtp_card); + port->mode |= MTPAV_MODE_OUTPUT_TRIGGERED; + } + } else { + port->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED; + if (--mtp_card->istimer == 0) + snd_mtpav_remove_output_timer(mtp_card); + } + spin_unlock_irqrestore(&mtp_card->spinlock, flags); + + if (up) + snd_mtpav_output_write(substream); +} + +/* + * midi interrupt for inputs + */ + +static void snd_mtpav_inmidi_process(mtpav_t *mcrd, u8 inbyte) +{ + mtpav_port_t *port; + + if (mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST) + return; + + port = &mcrd->ports[mcrd->inmidiport]; + if (port->mode & MTPAV_MODE_INPUT_TRIGGERED) + snd_rawmidi_receive(port->input, &inbyte, 1); +} + +static void snd_mtpav_inmidi_h(mtpav_t * mcrd, u8 inbyte) +{ + snd_assert(mcrd, return); + + if (inbyte >= 0xf8) { + /* real-time midi code */ + snd_mtpav_inmidi_process(mcrd, inbyte); + return; + } + + if (mcrd->inmidistate == 0) { // awaiting command + if (inbyte == 0xf5) // MTP port # + mcrd->inmidistate = 1; + else + snd_mtpav_inmidi_process(mcrd, inbyte); + } else if (mcrd->inmidistate) { + mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte); + mcrd->inmidistate = 0; + } +} + +static void snd_mtpav_read_bytes(mtpav_t * mcrd) +{ + u8 clrread, setread; + u8 mtp_read_byte; + u8 sr, cbyt; + int i; + + u8 sbyt = snd_mtpav_getreg(mcrd, SREG); + + //printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); + + if (!(sbyt & SIGS_BYTE)) + return; + + cbyt = snd_mtpav_getreg(mcrd, CREG); + clrread = cbyt & (SIGC_READ ^ 0xff); + setread = cbyt | SIGC_READ; + + do { + + mtp_read_byte = 0; + for (i = 0; i < 4; i++) { + snd_mtpav_mputreg(mcrd, CREG, setread); + sr = snd_mtpav_getreg(mcrd, SREG); + snd_mtpav_mputreg(mcrd, CREG, clrread); + + sr &= SIGS_IN0 | SIGS_IN1; + sr >>= 4; + mtp_read_byte |= sr << (i * 2); + } + + snd_mtpav_inmidi_h(mcrd, mtp_read_byte); + + sbyt = snd_mtpav_getreg(mcrd, SREG); + + } while (sbyt & SIGS_BYTE); +} + +static void snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs) +{ + mtpav_t *mcard = snd_magic_cast(mtpav_t, dev_id, return); + + //printk("irqh()\n"); + spin_lock(&mcard->spinlock); + snd_mtpav_read_bytes(mcard); + spin_unlock(&mcard->spinlock); +} + +/* + * get ISA resources + */ +static int snd_mtpav_get_ISA(mtpav_t * mcard) +{ + if ((mcard->res_port = request_region(snd_port, 3, "MotuMTPAV MIDI")) == NULL) { + snd_printk("MTVAP port 0x%lx is busy\n", snd_port); + return -EBUSY; + } + mcard->port = snd_port; + if (request_irq(snd_irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", (void *)mcard)) { + snd_printk("MTVAP IRQ %d busy\n", snd_irq); + return -EBUSY; + } + mcard->irq = snd_irq; + return 0; +} + + +/* + */ + +static snd_rawmidi_ops_t snd_mtpav_output = { + open: snd_mtpav_output_open, + close: snd_mtpav_output_close, + trigger: snd_mtpav_output_trigger, +}; + +static snd_rawmidi_ops_t snd_mtpav_input = { + open: snd_mtpav_input_open, + close: snd_mtpav_input_close, + trigger: snd_mtpav_input_trigger, +}; + + +/* + * get RAWMIDI resources + */ + +static void snd_mtpav_set_name(mtpav_t *chip, snd_rawmidi_substream_t *substream) +{ + if (substream->number >= 0 && substream->number < chip->num_ports) + sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1); + else if (substream->number >= 8 && substream->number < chip->num_ports * 2) + sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); + else if (substream->number == chip->num_ports * 2) + strcpy(substream->name, "MTP computer"); + else if (substream->number == chip->num_ports * 2 + 1) + strcpy(substream->name, "MTP ADAT"); + else + strcpy(substream->name, "MTP broadcast"); +} + +static int snd_mtpav_get_RAWMIDI(mtpav_t * mcard) +{ + int rval = 0; + snd_rawmidi_t *rawmidi; + snd_rawmidi_substream_t *substream; + struct list_head *list; + + //printk("entering snd_mtpav_get_RAWMIDI\n"); + + if (snd_hwports < 1) + mcard->num_ports = 1; + else if (snd_hwports > 8) + mcard->num_ports = 8; + else + mcard->num_ports = snd_hwports; + + if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0, + mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, + mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, + &mcard->rmidi)) < 0) + return rval; + rawmidi = mcard->rmidi; + + list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_mtpav_set_name(mcard, substream); + substream->ops = &snd_mtpav_input; + } + list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { + substream = list_entry(list, snd_rawmidi_substream_t, list); + snd_mtpav_set_name(mcard, substream); + substream->ops = &snd_mtpav_output; + mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number); + } + rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + sprintf(rawmidi->name, "MTP AV MIDI"); + //printk("exiting snd_mtpav_get_RAWMIDI() \n"); + return 0; +} + +/* + */ + +static mtpav_t *new_mtpav(void) +{ + mtpav_t *ncrd = (mtpav_t *) snd_kcalloc(sizeof(mtpav_t), GFP_KERNEL); + if (ncrd != NULL) { + spin_lock_init(&ncrd->spinlock); + + ncrd->card = NULL; + ncrd->irq = -1; + ncrd->share_irq = 0; + + ncrd->inmidiport = 0xffffffff; + ncrd->inmidistate = 0; + ncrd->outmidihwport = 0xffffffff; + } + return ncrd; +} + +/* + */ + +static void free_mtpav(mtpav_t * crd) +{ + unsigned long flags; + + spin_lock_irqsave(&crd->spinlock, flags); + if (crd->istimer > 0) + snd_mtpav_remove_output_timer(crd); + spin_unlock_irqrestore(&crd->spinlock, flags); + if (crd->irq >= 0) + free_irq(crd->irq, (void *)crd); + if (crd->res_port) { + release_resource(crd->res_port); + kfree_nocheck(crd->res_port); + } + if (crd != NULL) + kfree(crd); +} + +/* + */ + +static int __init alsa_card_mtpav_init(void) +{ + int err = 0; + char longname_buffer[80]; + + mtp_card = new_mtpav(); + if (mtp_card == NULL) + return -ENOMEM; + + mtp_card->card = snd_card_new(snd_index, snd_id, THIS_MODULE, 0); + if (mtp_card->card == NULL) { + free_mtpav(mtp_card); + return -ENOMEM; + } + + err = snd_mtpav_get_ISA(mtp_card); + //printk("snd_mtpav_get_ISA returned: %d\n", err); + if (err < 0) + goto __error; + + strcpy(mtp_card->card->driver, "MTPAV"); + strcpy(mtp_card->card->shortname, "MTPAV on parallel port"); + memset(longname_buffer, 0, sizeof(longname_buffer)); + sprintf(longname_buffer, "MTPAV on parallel port at"); + + err = snd_mtpav_get_RAWMIDI(mtp_card); + //snd_printk("snd_mtapv_get_RAWMIDI returned: %d\n", err); + if (err < 0) + goto __error; + + err = snd_card_register(mtp_card->card); // dont snd_card_register until AFTER all cards reources done! + + //printk("snd_card_register returned %d\n", err); + if (err < 0) + goto __error; + + + snd_mtpav_portscan(mtp_card); + + printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", snd_irq, snd_port); + + return 0; + + __error: + snd_card_free(mtp_card->card); + free_mtpav(mtp_card); + return err; +} + +/* + */ + +static void __exit alsa_card_mtpav_exit(void) +{ + if (mtp_card == NULL) + return; + if (mtp_card->card) + snd_card_free(mtp_card->card); + free_mtpav(mtp_card); +} + +/* + */ + +module_init(alsa_card_mtpav_init) +module_exit(alsa_card_mtpav_exit) + +#ifndef MODULE + +/* format is: snd-mtpav=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_hwports */ + +static int __init alsa_card_mtpav_setup(char *str) +{ + int __attribute__ ((__unused__)) enable = 1; + + (void)(get_option(&str,&enable) == 2 && + get_option(&str,&snd_index) == 2 && + get_id(&str,&snd_id) == 2 && + get_option(&str,(int *)&snd_port) == 2 && + get_option(&str,&snd_irq) == 2 && + get_option(&str,&snd_hwports) == 2); + return 1; +} + +__setup("snd-mtpav=", alsa_card_mtpav_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/drivers/opl3/Makefile linux-2.4.19-pre5-mjc/sound/drivers/opl3/Makefile --- linux/sound/drivers/opl3/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,76 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _opl3.o + +list-multi := snd-opl3-lib.o snd-opl3-synth.o + +export-objs := opl3_lib.o + +snd-opl3-lib-objs := opl3_lib.o opl3_synth.o +snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) +snd-opl3-synth-objs += opl3_oss.o +endif + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-opl3-lib.o +obj-$(CONFIG_SND_AZT2320) += snd-opl3-lib.o +obj-$(CONFIG_SND_DT0197H) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES18XX) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-lib.o +obj-$(CONFIG_SND_AD1816A) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4232) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4236) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES1688) += snd-opl3-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-lib.o +obj-$(CONFIG_SND_OPTI93X) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB8) += snd-opl3-lib.o +obj-$(CONFIG_SND_SB16) += snd-opl3-lib.o +obj-$(CONFIG_SND_SBAWE) += snd-opl3-lib.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-lib.o +obj-$(CONFIG_SND_ALS4000) += snd-opl3-lib.o +obj-$(CONFIG_SND_CMIPCI) += snd-opl3-lib.o +obj-$(CONFIG_SND_CS4281) += snd-opl3-lib.o +obj-$(CONFIG_SND_ES1938) += snd-opl3-lib.o +obj-$(CONFIG_SND_FM801) += snd-opl3-lib.o +obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-lib.o +obj-$(CONFIG_SND_YMFPCI) += snd-opl3-lib.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_ALS100) += snd-opl3-synth.o + obj-$(CONFIG_SND_AZT2320) += snd-opl3-synth.o + obj-$(CONFIG_SND_DT0197H) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES18XX) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPL3SA2) += snd-opl3-synth.o + obj-$(CONFIG_SND_AD1816A) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4232) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4236) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES1688) += snd-opl3-synth.o + obj-$(CONFIG_SND_GUSEXTREME) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opl3-synth.o + obj-$(CONFIG_SND_OPTI93X) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB8) += snd-opl3-synth.o + obj-$(CONFIG_SND_SB16) += snd-opl3-synth.o + obj-$(CONFIG_SND_SBAWE) += snd-opl3-synth.o + obj-$(CONFIG_SND_WAVEFRONT) += snd-opl3-synth.o + obj-$(CONFIG_SND_ALS4000) += snd-opl3-synth.o + obj-$(CONFIG_SND_CMIPCI) += snd-opl3-synth.o + obj-$(CONFIG_SND_CS4281) += snd-opl3-synth.o + obj-$(CONFIG_SND_ES1938) += snd-opl3-synth.o + obj-$(CONFIG_SND_FM801) += snd-opl3-synth.o + obj-$(CONFIG_SND_SONICVIBES) += snd-opl3-synth.o + obj-$(CONFIG_SND_YMFPCI) += snd-opl3-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-opl3-lib.o: $(snd-opl3-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-lib-objs) + +snd-opl3-synth.o: $(snd-opl3-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3-synth-objs) diff -Nru linux/sound/drivers/opl3/opl3_drums.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_drums.c --- linux/sound/drivers/opl3/opl3_drums.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_drums.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,224 @@ +/* + * Copyright (c) by Uros Bizjak + * + * OPL2/OPL3/OPL4 FM routines for internal percussion channels + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "opl3_voice.h" + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +static char snd_opl3_drum_table[47] = +{ + OPL3_BASSDRUM_ON, OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, /* 35 - 37 */ + OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON, OPL3_SNAREDRUM_ON, /* 38 - 40 */ + OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, OPL3_BASSDRUM_ON, /* 41 - 43 */ + OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_HIHAT_ON, /* 44 - 46 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, /* 47 - 49 */ + + OPL3_TOMTOM_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 50 - 52 */ + OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, OPL3_CYMBAL_ON, /* 53 - 55 */ + OPL3_HIHAT_ON, OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, /* 56 - 58 */ + OPL3_CYMBAL_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 59 - 61 */ + OPL3_HIHAT_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 62 - 64 */ + + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 65 - 67 */ + OPL3_TOMTOM_ON, OPL3_HIHAT_ON, OPL3_HIHAT_ON, /* 68 - 70 */ + OPL3_HIHAT_ON, OPL3_HIHAT_ON, OPL3_TOMTOM_ON, /* 71 - 73 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 74 - 76 */ + OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, OPL3_TOMTOM_ON, /* 77 - 79 */ + OPL3_CYMBAL_ON, OPL3_CYMBAL_ON /* 80 - 81 */ +}; + +typedef struct snd_opl3_drum_voice { + int voice; + int op; + unsigned char am_vib; + unsigned char ksl_level; + unsigned char attack_decay; + unsigned char sustain_release; + unsigned char feedback_connection; + unsigned char wave_select; +} snd_opl3_drum_voice_t; + +typedef struct snd_opl3_drum_note { + int voice; + unsigned char fnum; + unsigned char octave_f; + unsigned char feedback_connection; +} snd_opl3_drum_note_t; + +static snd_opl3_drum_voice_t bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00}; +static snd_opl3_drum_voice_t bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00}; +static snd_opl3_drum_note_t bass_note = {6, 0x90, 0x09}; + +static snd_opl3_drum_voice_t hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00}; + +static snd_opl3_drum_voice_t snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02}; +static snd_opl3_drum_note_t snare_note = {7, 0xf4, 0x0d}; + +static snd_opl3_drum_voice_t tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00}; +static snd_opl3_drum_note_t tomtom_note = {8, 0xf4, 0x09}; + +static snd_opl3_drum_voice_t cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00}; + +/* + * set drum voice characteristics + */ +void snd_opl3_drum_voice_set(opl3_t *opl3, snd_opl3_drum_voice_t *data) +{ + unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; + unsigned char voice_offset = data->voice; + unsigned short opl3_reg; + + /* Set OPL3 AM_VIB register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, data->am_vib); + + /* Set OPL3 KSL_LEVEL register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, data->ksl_level); + + /* Set OPL3 ATTACK_DECAY register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, data->attack_decay); + + /* Set OPL3 SUSTAIN_RELEASE register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, data->sustain_release); + + /* Set OPL3 FEEDBACK_CONNECTION register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, data->feedback_connection); + + /* Select waveform */ + opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, data->wave_select); +} + +/* + * Set drum voice pitch + */ +void snd_opl3_drum_note_set(opl3_t *opl3, snd_opl3_drum_note_t *data) +{ + unsigned char voice_offset = data->voice; + unsigned short opl3_reg; + + /* Set OPL3 FNUM_LOW register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, data->fnum); + + /* Set OPL3 KEYON_BLOCK register */ + opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, data->octave_f); +} + +/* + * Set drum voice volume and position + */ +void snd_opl3_drum_vol_set(opl3_t *opl3, snd_opl3_drum_voice_t *data, int vel, + snd_midi_channel_t *chan) +{ + unsigned char op_offset = snd_opl3_regmap[data->voice][data->op]; + unsigned char voice_offset = data->voice; + unsigned char reg_val; + unsigned short opl3_reg; + + /* Set OPL3 KSL_LEVEL register */ + reg_val = data->ksl_level; + snd_opl3_calc_volume(®_val, vel, chan); + opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 FEEDBACK_CONNECTION register */ + /* Set output voice connection */ + reg_val = data->feedback_connection | OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); +} + +/* + * Loads drum voices at init time + */ +void snd_opl3_load_drums(opl3_t *opl3) +{ + snd_opl3_drum_voice_set(opl3, &bass_op0); + snd_opl3_drum_voice_set(opl3, &bass_op1); + snd_opl3_drum_note_set(opl3, &bass_note); + + snd_opl3_drum_voice_set(opl3, &hihat); + + snd_opl3_drum_voice_set(opl3, &snare); + snd_opl3_drum_note_set(opl3, &snare_note); + + snd_opl3_drum_voice_set(opl3, &tomtom); + snd_opl3_drum_note_set(opl3, &tomtom_note); + + snd_opl3_drum_voice_set(opl3, &cymbal); +} + +/* + * Switch drum voice on or off + */ +void snd_opl3_drum_switch(opl3_t *opl3, int note, int vel, int on_off, + snd_midi_channel_t *chan) +{ + unsigned char drum_mask; + snd_opl3_drum_voice_t *drum_voice; + + if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE)) + return; + + if ((note < 35) || (note > 81)) + return; + drum_mask = snd_opl3_drum_table[note - 35]; + + if (on_off) { + switch (drum_mask) { + case OPL3_BASSDRUM_ON: + drum_voice = &bass_op1; + break; + case OPL3_HIHAT_ON: + drum_voice = &hihat; + break; + case OPL3_SNAREDRUM_ON: + drum_voice = &snare; + break; + case OPL3_TOMTOM_ON: + drum_voice = &tomtom; + break; + case OPL3_CYMBAL_ON: + drum_voice = &cymbal; + break; + default: + drum_voice = &tomtom; + } + + snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan); + opl3->drum_reg |= drum_mask; + } else { + opl3->drum_reg &= ~drum_mask; + } + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); +} diff -Nru linux/sound/drivers/opl3/opl3_lib.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_lib.c --- linux/sound/drivers/opl3/opl3_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_lib.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,560 @@ +/* + * Copyright (c) by Jaroslav Kysela , + * Hannu Savolainen 1993-1996, + * Rob Hooft + * + * Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips) + * + * Most if code is ported from OSS/Lite. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Hannu Savolainen 1993-1996, Rob Hooft"); +MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)"); +MODULE_LICENSE("GPL"); + +#define chip_t opl3_t + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +void snd_opl2_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * The original 2-OP synth requires a quite long delay + * after writing to a register. + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + outb((unsigned char) cmd, port); + udelay(10); + + outb((unsigned char) val, port + 1); + udelay(30); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +void snd_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * The OPL-3 survives with just two INBs + * after writing to a register. + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + outb((unsigned char) cmd, port); + inb(opl3->l_port); + inb(opl3->l_port); + + outb((unsigned char) val, port + 1); + inb(opl3->l_port); + inb(opl3->l_port); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +void snd_opl3_cs4281_command(opl3_t * opl3, unsigned short cmd, unsigned char val) +{ + unsigned long flags; + unsigned long port; + + /* + * CS4281 requires a special access to I/O registers + */ + + port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port; + + spin_lock_irqsave(&opl3->reg_lock, flags); + + writel((unsigned int)cmd, port << 2); + + writel((unsigned int)val, (port + 1) << 2); + + spin_unlock_irqrestore(&opl3->reg_lock, flags); +} + +static int snd_opl3_detect(opl3_t * opl3) +{ + /* + * This function returns 1 if the FM chip is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, stat2, signature; + + /* Reset timers 1 and 2 */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); + /* Reset the IRQ of the FM chip */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); + signature = stat1 = inb(opl3->l_port); /* Status register */ + if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */ + snd_printd("OPL3: stat1 = 0x%x\n", stat1); + return -ENODEV; + } + /* Set timer1 to 0xff */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff); + /* Unmask and start timer 1 */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START); + /* Now we have to delay at least 80us */ + udelay(200); + /* Read status after timers have expired */ + stat2 = inb(opl3->l_port); + /* Stop the timers */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK); + /* Reset the IRQ of the FM chip */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET); + if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */ + snd_printd("OPL3: stat2 = 0x%x\n", stat2); + return -ENODEV; + } + + /* If the toplevel code knows exactly the type of chip, don't try + to detect it. */ + if (opl3->hardware != OPL3_HW_AUTO) + return 0; + + /* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */ + if (signature == 0x06) { /* OPL2 */ + opl3->hardware = OPL3_HW_OPL2; + } else { + /* + * Detect availability of OPL4 (_experimental_). Works probably + * only after a cold boot. In addition the OPL4 port + * of the chip may not be connected to the PC bus at all. + */ + snd_assert(opl3->r_port != 0, return -ENODEV); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE); + if (inb(opl3->l_port) == 0x02) { /* Have a OPL4 */ + opl3->hardware = OPL3_HW_OPL4; + } else { + opl3->hardware = OPL3_HW_OPL3; + } + } + return 0; +} + +/* + * AdLib timers + */ + +/* + * Timer 1 - 80us + */ + +static int snd_opl3_timer1_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + ticks = timer->sticks; + tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +static int snd_opl3_timer1_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +/* + * Timer 2 - 320us + */ + +static int snd_opl3_timer2_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + ticks = timer->sticks; + tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */ + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +static int snd_opl3_timer2_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + opl3_t *opl3; + + opl3 = snd_timer_chip(timer); + spin_lock_irqsave(&opl3->timer_lock, flags); + tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START; + opl3->timer_enable = tmp; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&opl3->timer_lock, flags); + return 0; +} + +/* + + */ + +static struct _snd_timer_hardware snd_opl3_timer1 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 80000, + ticks: 256, + start: snd_opl3_timer1_start, + stop: snd_opl3_timer1_stop, +}; + +static struct _snd_timer_hardware snd_opl3_timer2 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 320000, + ticks: 256, + start: snd_opl3_timer2_start, + stop: snd_opl3_timer2_stop, +}; + +static int snd_opl3_timer1_init(opl3_t * opl3, int timer_no) +{ + snd_timer_t *timer = NULL; + snd_timer_id_t tid; + int err; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = opl3->card->number; + tid.device = timer_no; + tid.subdevice = 0; + if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) { + strcpy(timer->name, "AdLib timer #1"); + timer->private_data = opl3; + timer->hw = snd_opl3_timer1; + } + opl3->timer1 = timer; + return err; +} + +static int snd_opl3_timer2_init(opl3_t * opl3, int timer_no) +{ + snd_timer_t *timer = NULL; + snd_timer_id_t tid; + int err; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = opl3->card->number; + tid.device = timer_no; + tid.subdevice = 0; + if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) { + strcpy(timer->name, "AdLib timer #2"); + timer->private_data = opl3; + timer->hw = snd_opl3_timer2; + } + opl3->timer2 = timer; + return err; +} + +/* + + */ + +void snd_opl3_interrupt(snd_hwdep_t * hw) +{ + unsigned char status; + opl3_t *opl3; + snd_timer_t *timer; + + if (hw == NULL) + return; + + opl3 = snd_magic_cast(opl3_t, hw->private_data, return); + status = inb(opl3->l_port); +#if 0 + snd_printk("AdLib IRQ status = 0x%x\n", status); +#endif + if (!(status & 0x80)) + return; + + if (status & 0x40) { + timer = opl3->timer1; + snd_timer_interrupt(timer, timer->sticks); + } + if (status & 0x20) { + timer = opl3->timer2; + snd_timer_interrupt(timer, timer->sticks); + } +} + +/* + + */ + +static int snd_opl3_free(opl3_t *opl3) +{ + if (opl3->res_l_port) { + release_resource(opl3->res_l_port); + kfree_nocheck(opl3->res_l_port); + } + if (opl3->res_r_port) { + release_resource(opl3->res_r_port); + kfree_nocheck(opl3->res_r_port); + } + snd_magic_kfree(opl3); + return 0; +} + +static int snd_opl3_dev_free(snd_device_t *device) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, device->device_data, return -ENXIO); + return snd_opl3_free(opl3); +} + +int snd_opl3_create(snd_card_t * card, + unsigned long l_port, + unsigned long r_port, + unsigned short hardware, + int integrated, + opl3_t ** ropl3) +{ + opl3_t *opl3; + int err; + static snd_device_ops_t ops = { + dev_free: snd_opl3_dev_free, + }; + + *ropl3 = NULL; + + opl3 = snd_magic_kcalloc(opl3_t, 0, GFP_KERNEL); + if (opl3 == NULL) + return -ENOMEM; + + if (integrated) + goto __step1; /* ports are already reserved */ + + if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) { + snd_opl3_free(opl3); + return -EBUSY; + } + if (r_port != 0 && + (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) { + snd_opl3_free(opl3); + return -EBUSY; + } + + __step1: + + opl3->card = card; + opl3->hardware = hardware; + opl3->l_port = l_port; + opl3->r_port = r_port; + + spin_lock_init(&opl3->reg_lock); + spin_lock_init(&opl3->timer_lock); + init_MUTEX(&opl3->access_mutex); + + switch (opl3->hardware) { + /* some hardware doesn't support timers */ + case OPL3_HW_OPL3_SV: + case OPL3_HW_OPL3_CS: + case OPL3_HW_OPL3_FM801: + opl3->command = &snd_opl3_command; + break; + case OPL3_HW_OPL3_CS4281: + opl3->command = &snd_opl3_cs4281_command; + break; + default: + opl3->command = &snd_opl2_command; + if ((err = snd_opl3_detect(opl3)) < 0) { + snd_opl3_free(opl3); + snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n", + opl3->l_port, opl3->r_port); + return err; + } + /* detect routine returns correct hardware type */ + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL3: + case OPL3_HW_OPL4: + opl3->command = &snd_opl3_command; + } + } + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ + + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL2: + opl3->max_voices = MAX_OPL2_VOICES; + break; + case OPL3_HW_OPL3: + case OPL3_HW_OPL4: + opl3->max_voices = MAX_OPL3_VOICES; + snd_assert(opl3->r_port != 0, snd_opl3_free(opl3); return -ENODEV); + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, opl3, &ops)) < 0) { + snd_opl3_free(opl3); + return err; + } + + *ropl3 = opl3; + return 0; +} + +int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev) +{ + int err; + + if (timer1_dev >= 0) + if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0) + return err; + if (timer2_dev >= 0) { + if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) { + snd_device_free(opl3->card, opl3->timer1); + opl3->timer1 = NULL; + return err; + } + } + return 0; +} + +int snd_opl3_hwdep_new(opl3_t * opl3, + int device, int seq_device, + snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hw; + snd_card_t *card = opl3->card; + int err; + + if (rhwdep) + *rhwdep = NULL; + + /* create hardware dependent device (direct FM) */ + + if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) { + snd_device_free(card, opl3); + return err; + } + hw->private_data = opl3; +#ifdef CONFIG_SND_OSSEMUL + if (device == 0) { + hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; + sprintf(hw->oss_dev, "dmfm%i", card->number); + } +#endif + strcpy(hw->name, hw->id); + switch (opl3->hardware & OPL3_HW_MASK) { + case OPL3_HW_OPL2: + strcpy(hw->name, "OPL2 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL2; + break; + case OPL3_HW_OPL3: + strcpy(hw->name, "OPL3 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL3; + break; + case OPL3_HW_OPL4: + strcpy(hw->name, "OPL4 FM"); + hw->iface = SNDRV_HWDEP_IFACE_OPL4; + break; + } + + /* operators - only ioctl */ + hw->ops.open = snd_opl3_open; + hw->ops.ioctl = snd_opl3_ioctl; + hw->ops.release = snd_opl3_release; + + opl3->seq_dev_num = seq_device; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, + sizeof(opl3_t*), &opl3->seq_dev) >= 0) { + strcpy(opl3->seq_dev->name, hw->name); + *(opl3_t**)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; + } +#endif + if (rhwdep) + *rhwdep = hw; + return 0; +} + +EXPORT_SYMBOL(snd_opl3_interrupt); +EXPORT_SYMBOL(snd_opl3_create); +EXPORT_SYMBOL(snd_opl3_timer_new); +EXPORT_SYMBOL(snd_opl3_hwdep_new); + +/* opl3_synth.c */ +EXPORT_SYMBOL(snd_opl3_regmap); +EXPORT_SYMBOL(snd_opl3_reset); + +/* + * INIT part + */ + +static int __init alsa_opl3_init(void) +{ + return 0; +} + +static void __exit alsa_opl3_exit(void) +{ +} + +module_init(alsa_opl3_init) +module_exit(alsa_opl3_exit) diff -Nru linux/sound/drivers/opl3/opl3_midi.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_midi.c --- linux/sound/drivers/opl3/opl3_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_midi.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,874 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Midi synth routines for OPL2/OPL3/OPL4 FM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#undef DEBUG_ALLOC +#undef DEBUG_MIDI + +#define __NO_VERSION__ +#include "opl3_voice.h" +#include + +extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; + +extern int use_internal_drums; + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (Rob Hooft ) + */ + +static char opl3_volume_table[128] = +{ + -63, -48, -40, -35, -32, -29, -27, -26, + -24, -23, -21, -20, -19, -18, -18, -17, + -16, -15, -15, -14, -13, -13, -12, -12, + -11, -11, -10, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -6, -6, -6, + -5, -5, -5, -5, -4, -4, -4, -4, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8 +}; + +void snd_opl3_calc_volume(unsigned char *volbyte, int vel, + snd_midi_channel_t *chan) +{ + int oldvol, newvol, n; + int volume; + + volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127); + if (volume > 127) + volume = 127; + + oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK); + + newvol = opl3_volume_table[volume] + oldvol; + if (newvol > OPL3_TOTAL_LEVEL_MASK) + newvol = OPL3_TOTAL_LEVEL_MASK; + else if (newvol < 0) + newvol = 0; + + n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK); + + *volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK); +} + +/* + * Converts the note frequency to block and fnum values for the FM chip + */ +static short opl3_note_table[16] = +{ + 305, 323, /* for pitch bending, -2 semitones */ + 343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, + 686, 726 /* for pitch bending, +2 semitones */ +}; + +static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum, + int note, snd_midi_channel_t *chan) +{ + int block = ((note / 12) & 0x07) - 1; + int idx = (note % 12) + 2; + int freq; + + if (chan->midi_pitchbend) { + int pitchbend = chan->midi_pitchbend; + int segment; + + if (pitchbend > 0x1FFF) + pitchbend = 0x1FFF; + + segment = pitchbend / 0x1000; + freq = opl3_note_table[idx+segment]; + freq += ((opl3_note_table[idx+segment+1] - freq) * + (pitchbend % 0x1000)) / 0x1000; + } else { + freq = opl3_note_table[idx]; + } + + *fnum = (unsigned char) freq; + *blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) | + ((block << 2) & OPL3_BLOCKNUM_MASK); +} + + +#ifdef DEBUG_ALLOC +static void debug_alloc(opl3_t *opl3, char *s, int voice) { + int i; + char *str = "x.24"; + + printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice); + for (i = 0; i < opl3->max_voices; i++) + printk("%c", *(str + opl3->voices[i].state + 1)); + printk("\n"); +} +#endif + +/* + * Get a FM voice (channel) to play a note on. + */ +static int opl3_get_voice(opl3_t *opl3, int instr_4op, + snd_midi_channel_t *chan) { + int chan_4op_1; /* first voice for 4op instrument */ + int chan_4op_2; /* second voice for 4op instrument */ + + snd_opl3_voice_t *vp, *vp2; + unsigned int voice_time; + int i; + +#ifdef DEBUG_ALLOC + char *alloc_type[3] = { "FREE ", "CHEAP ", "EXPENSIVE" }; +#endif + + /* This is our "allocation cost" table */ + enum { + FREE = 0, CHEAP, EXPENSIVE, END + }; + + /* Keeps track of what we are finding */ + struct best { + unsigned int time; + int voice; + } best[END]; + struct best *bp; + + for (i = 0; i < END; i++) { + best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* Look through all the channels for the most suitable. */ + for (i = 0; i < opl3->max_voices; i++) { + vp = &opl3->voices[i]; + + if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL) + /* skip unavailable channels, allocated by + drum voices or by bounded 4op voices) */ + continue; + + voice_time = vp->time; + bp = best; + + chan_4op_1 = ((i < 3) || (i > 8 && i < 12)); + chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15)); + if (instr_4op) { + /* allocate 4op voice */ + /* skip channels unavailable to 4op instrument */ + if (!chan_4op_1) + continue; + + if (vp->state) + /* kill one voice, CHEAP */ + bp++; + /* get state of bounded 2op channel + to be allocated for 4op instrument */ + vp2 = &opl3->voices[i + 3]; + if (vp2->state == SNDRV_OPL3_ST_ON_2OP) { + /* kill two voices, EXPENSIVE */ + bp++; + voice_time = (voice_time > vp->time) ? + voice_time : vp->time; + } + } else { + /* allocate 2op voice */ + if ((chan_4op_1) || (chan_4op_2)) + /* use bounded channels for 2op, CHEAP */ + bp++; + else if (vp->state) + /* kill one voice on 2op channel, CHEAP */ + bp++; + /* raise kill cost to EXPENSIVE for all channels */ + if (vp->state) + bp++; + } + if (voice_time < bp->time) { + bp->time = voice_time; + bp->voice = i; + } + } + + for (i = 0; i < END; i++) { + if (best[i].voice >= 0) { +#ifdef DEBUG_ALLOC + printk("%s %iop allocation on voice %i\n", + alloc_type[i], instr_4op ? 4 : 2, + best[i].voice); +#endif + return best[i].voice; + } + } + /* not found */ + return -1; +} + +/* ------------------------------ */ + +/* + * System timer interrupt function + */ +void snd_opl3_timer_func(unsigned long data) +{ + + opl3_t *opl3 = (opl3_t *)data; + int again = 0; + int i; + + spin_lock(&opl3->sys_timer_lock); + for (i = 0; i < opl3->max_voices; i++) { + snd_opl3_voice_t *vp = &opl3->voices[i]; + if (vp->state > 0 && vp->note_off_check) { + if (vp->note_off == jiffies) + snd_opl3_note_off(opl3, vp->note, 0, vp->chan); + else + again++; + } + } + if (again) { + opl3->tlist.expires = jiffies + 1; /* invoke again */ + add_timer(&opl3->tlist); + } else { + opl3->sys_timer_status = 0; + } + spin_unlock(&opl3->sys_timer_lock); +} + +/* + * Start system timer + */ +void snd_opl3_start_timer(opl3_t *opl3) +{ + unsigned long flags; + spin_lock_irqsave(&opl3->sys_timer_lock, flags); + if (! opl3->sys_timer_status) { + opl3->tlist.expires = jiffies + 1; + add_timer(&opl3->tlist); + opl3->sys_timer_status = 1; + } + spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); +} + +/* ------------------------------ */ + + +static int snd_opl3_oss_map[MAX_OPL3_VOICES] = { + 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14 +}; + +/* + * Start a note. + */ +void snd_opl3_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + snd_seq_instr_t wanted; + snd_seq_kinstr_t *kinstr; + int instr_4op; + + int voice; + snd_opl3_voice_t *vp, *vp2; + unsigned short connect_mask; + unsigned char connection; + unsigned char vol_op[4]; + + int extra_prg = 0; + + unsigned short reg_side; + unsigned char op_offset; + unsigned char voice_offset; + unsigned short opl3_reg; + unsigned char reg_val; + + int key = note; + unsigned char fnum, blocknum; + int i; + + fm_instrument_t *fm; + unsigned long flags; + + opl3 = snd_magic_cast(opl3_t, p, return); + +#ifdef DEBUG_MIDI + snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", + chan->number, chan->midi_program, note, vel); +#endif + wanted.cluster = 0; + wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; + + /* in SYNTH mode, application takes care of voices */ + /* in SEQ mode, drum voice numbers are notes on drum channel */ + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + if (chan->drum_channel) { + /* percussion instruments are located in bank 128 */ + wanted.bank = 128; + wanted.prg = note; + } else { + wanted.bank = chan->gm_bank_select; + wanted.prg = chan->midi_program; + } + } else { + /* Prepare for OSS mode */ + if (chan->number >= MAX_OPL3_VOICES) + return; + + /* OSS instruments are located in bank 127 */ + wanted.bank = 127; + wanted.prg = chan->midi_program; + } + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (use_internal_drums) { + snd_opl3_drum_switch(opl3, note, vel, 1, chan); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + + __extra_prg: + kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0); + if (kinstr == NULL) { + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + + fm = KINSTR_DATA(kinstr); + + switch (fm->type) { + case FM_PATCH_OPL2: + instr_4op = 0; + break; + case FM_PATCH_OPL3: + if (opl3->hardware >= OPL3_HW_OPL3) { + instr_4op = 1; + break; + } + default: + snd_seq_instr_free_use(opl3->ilist, kinstr); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + +#ifdef DEBUG_MIDI + snd_printk(" --> OPL%i instrument: %s\n", + instr_4op ? 3 : 2, kinstr->name); +#endif + /* in SYNTH mode, application takes care of voices */ + /* in SEQ mode, allocate voice on free OPL3 channel */ + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + voice = opl3_get_voice(opl3, instr_4op, chan); + } else { + /* remap OSS voice */ + voice = snd_opl3_oss_map[chan->number]; + } + + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38; + } + + /* kill voice on channel */ + vp = &opl3->voices[voice]; + if (vp->state > 0) { + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; + opl3->command(opl3, opl3_reg, reg_val); + } + if (instr_4op) { + vp2 = &opl3->voices[voice + 3]; + if (vp->state > 0) { + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + + voice_offset + 3); + reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; + opl3->command(opl3, opl3_reg, reg_val); + } + } + + /* set connection register */ + if (instr_4op) { + if ((opl3->connection_reg ^ connect_mask) & connect_mask) { + opl3->connection_reg |= connect_mask; + /* set connection bit */ + opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; + opl3->command(opl3, opl3_reg, opl3->connection_reg); + } + } else { + if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) { + opl3->connection_reg &= ~connect_mask; + /* clear connection bit */ + opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; + opl3->command(opl3, opl3_reg, opl3->connection_reg); + } + } + +#ifdef DEBUG_MIDI + snd_printk(" --> setting OPL3 connection: 0x%x\n", + opl3->connection_reg); +#endif + /* + * calculate volume depending on connection + * between FM operators (see include/opl3.h) + */ + for (i = 0; i < (instr_4op ? 4 : 2); i++) + vol_op[i] = fm->op[i].ksl_level; + + connection = fm->feedback_connection[0] & 0x01; + if (instr_4op) { + connection <<= 1; + connection |= fm->feedback_connection[1] & 0x01; + + snd_opl3_calc_volume(&vol_op[3], vel, chan); + switch (connection) { + case 0x03: + snd_opl3_calc_volume(&vol_op[2], vel, chan); + /* fallthru */ + case 0x02: + snd_opl3_calc_volume(&vol_op[0], vel, chan); + break; + case 0x01: + snd_opl3_calc_volume(&vol_op[1], vel, chan); + } + } else { + snd_opl3_calc_volume(&vol_op[1], vel, chan); + if (connection) + snd_opl3_calc_volume(&vol_op[0], vel, chan); + } + + /* Program the FM voice characteristics */ + for (i = 0; i < (instr_4op ? 4 : 2); i++) { +#ifdef DEBUG_MIDI + snd_printk(" --> programming operator %i\n", i); +#endif + op_offset = snd_opl3_regmap[voice_offset][i]; + + /* Set OPL3 AM_VIB register of requested voice/operator */ + reg_val = fm->op[i].am_vib; + opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 KSL_LEVEL register of requested voice/operator */ + reg_val = vol_op[i]; + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ + reg_val = fm->op[i].attack_decay; + opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ + reg_val = fm->op[i].sustain_release; + opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Select waveform */ + reg_val = fm->op[i].wave_select; + opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + } + + /* Set operator feedback and 2op inter-operator connection */ + reg_val = fm->feedback_connection[0]; + /* Set output voice connection */ + reg_val |= OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + if (instr_4op) { + /* Set 4op inter-operator connection */ + reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT; + /* Set output voice connection */ + reg_val |= OPL3_STEREO_BITS; + if (chan->gm_pan < 43) + reg_val &= ~OPL3_VOICE_TO_RIGHT; + if (chan->gm_pan > 85) + reg_val &= ~OPL3_VOICE_TO_LEFT; + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + + voice_offset + 3); + opl3->command(opl3, opl3_reg, reg_val); + } + + /* + * Special treatment of percussion notes for fm: + * Requested pitch is really program, and pitch for + * device is whatever was specified in the patch library. + */ + if (fm->fix_key) + note = fm->fix_key; + /* + * use transpose if defined in patch library + */ + if (fm->trnsps) + note += (fm->trnsps - 64); + + snd_opl3_calc_pitch(&fnum, &blocknum, note, chan); + + /* Set OPL3 FNUM_LOW register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, fnum); + + opl3->voices[voice].keyon_reg = blocknum; + + /* Set output sound flag */ + blocknum |= OPL3_KEYON_BIT; + +#ifdef DEBUG_MIDI + snd_printk(" --> trigger voice %i\n", voice); +#endif + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, blocknum); + + /* kill note after fixed duration (in centiseconds) */ + if (fm->fix_dur) { + opl3->voices[voice].note_off = jiffies + + (fm->fix_dur * HZ) / 100; + snd_opl3_start_timer(opl3); + opl3->voices[voice].note_off_check = 1; + } else + opl3->voices[voice].note_off_check = 0; + + /* get extra pgm, but avoid possible loops */ + extra_prg = (extra_prg) ? 0 : fm->modes; + + snd_seq_instr_free_use(opl3->ilist, kinstr); + + /* do the bookkeeping */ + vp->time = opl3->use_time++; + vp->note = key; + vp->chan = chan; + + if (instr_4op) { + vp->state = SNDRV_OPL3_ST_ON_4OP; + + vp2 = &opl3->voices[voice + 3]; + vp2->time = opl3->use_time++; + vp2->note = key; + vp2->chan = chan; + vp2->state = SNDRV_OPL3_ST_NOT_AVAIL; + } else { + if (vp->state == SNDRV_OPL3_ST_ON_4OP) { + /* 4op killed by 2op, release bounded voice */ + vp2 = &opl3->voices[voice + 3]; + vp2->time = opl3->use_time++; + vp2->state = SNDRV_OPL3_ST_OFF; + } + vp->state = SNDRV_OPL3_ST_ON_2OP; + } + +#ifdef DEBUG_ALLOC + debug_alloc(opl3, "note on ", voice); +#endif + + /* allocate extra program if specified in patch library */ + if (extra_prg) { + if (extra_prg > 128) { + wanted.bank = 128; + /* percussions start at 35 */ + wanted.prg = extra_prg - 128 + 35 - 1; + } else { + wanted.bank = 0; + wanted.prg = extra_prg - 1; + } +#ifdef DEBUG_MIDI + snd_printk(" *** allocating extra program\n"); +#endif + goto __extra_prg; + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +static void snd_opl3_kill_voice(opl3_t *opl3, int voice) +{ + unsigned short reg_side; + unsigned char voice_offset; + unsigned short opl3_reg; + + snd_opl3_voice_t *vp, *vp2; + + snd_assert(voice < MAX_OPL3_VOICES, return); + + vp = &opl3->voices[voice]; + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + } + + /* kill voice */ +#ifdef DEBUG_MIDI + snd_printk(" --> kill voice %i\n", voice); +#endif + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + /* clear Key ON bit */ + opl3->command(opl3, opl3_reg, vp->keyon_reg); + + /* do the bookkeeping */ + vp->time = opl3->use_time++; + + if (vp->state == SNDRV_OPL3_ST_ON_4OP) { + vp2 = &opl3->voices[voice + 3]; + + vp2->time = opl3->use_time++; + vp2->state = SNDRV_OPL3_ST_OFF; + } + vp->state = SNDRV_OPL3_ST_OFF; +#ifdef DEBUG_ALLOC + debug_alloc(opl3, "note off", voice); +#endif + +} + +/* + * Release a note in response to a midi note off. + */ +void snd_opl3_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + int voice; + snd_opl3_voice_t *vp; + + unsigned long flags; + + opl3 = snd_magic_cast(opl3_t, p, return); + +#ifdef DEBUG_MIDI + snd_printk("Note off, ch %i, inst %i, note %i\n", + chan->number, chan->midi_program, note); +#endif + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + if (chan->drum_channel && use_internal_drums) { + snd_opl3_drum_switch(opl3, note, vel, 0, chan); + spin_unlock_irqrestore(&opl3->voice_lock, flags); + return; + } + /* this loop will hopefully kill all extra voices, because + they are grouped by the same channel and note values */ + for (voice = 0; voice < opl3->max_voices; voice++) { + vp = &opl3->voices[voice]; + if (vp->state > 0 && vp->chan == chan && vp->note == note) { + snd_opl3_kill_voice(opl3, voice); + } + } + } else { + /* remap OSS voices */ + if (chan->number < MAX_OPL3_VOICES) { + voice = snd_opl3_oss_map[chan->number]; + snd_opl3_kill_voice(opl3, voice); + } + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +/* + * key pressure change + */ +void snd_opl3_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Key pressure, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +/* + * terminate note + */ +void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Terminate note, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +static void snd_opl3_update_pitch(opl3_t *opl3, int voice) +{ + unsigned short reg_side; + unsigned char voice_offset; + unsigned short opl3_reg; + + unsigned char fnum, blocknum; + + snd_opl3_voice_t *vp; + + snd_assert(voice < MAX_OPL3_VOICES, return); + + vp = &opl3->voices[voice]; + if (vp->chan == NULL) + return; /* not allocated? */ + + if (voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice - MAX_OPL2_VOICES; + } + + snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan); + + /* Set OPL3 FNUM_LOW register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, fnum); + + vp->keyon_reg = blocknum; + + /* Set output sound flag */ + blocknum |= OPL3_KEYON_BIT; + + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, blocknum); + + vp->time = opl3->use_time++; +} + +/* + * Update voice pitch controller + */ +static void snd_opl3_pitch_ctrl(opl3_t *opl3, snd_midi_channel_t *chan) +{ + int voice; + snd_opl3_voice_t *vp; + + unsigned long flags; + + spin_lock_irqsave(&opl3->voice_lock, flags); + + if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { + for (voice = 0; voice < opl3->max_voices; voice++) { + vp = &opl3->voices[voice]; + if (vp->state > 0 && vp->chan == chan) { + snd_opl3_update_pitch(opl3, voice); + } + } + } else { + /* remap OSS voices */ + if (chan->number < MAX_OPL3_VOICES) { + voice = snd_opl3_oss_map[chan->number]; + snd_opl3_update_pitch(opl3, voice); + } + } + spin_unlock_irqrestore(&opl3->voice_lock, flags); +} + +/* + * Deal with a controler type event. This includes all types of + * control events, not just the midi controllers + */ +void snd_opl3_control(void *p, int type, snd_midi_channel_t *chan) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n", + type, chan->number, chan->midi_program); +#endif + + switch (type) { + case MIDI_CTL_MSB_MODWHEEL: + if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63) + opl3->drum_reg |= OPL3_VIBRATO_DEPTH; + else + opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); + break; + case MIDI_CTL_E2_TREMOLO_DEPTH: + if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63) + opl3->drum_reg |= OPL3_TREMOLO_DEPTH; + else + opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, + opl3->drum_reg); + break; + case MIDI_CTL_PITCHBEND: + snd_opl3_pitch_ctrl(opl3, chan); + break; + } +} + +/* + * NRPN events + */ +void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, + snd_midi_channel_set_t *chset) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("NRPN, ch#: %i, inst#: %i\n", + chan->number, chan->midi_program); +#endif +} + +/* + * receive sysex + */ +void snd_opl3_sysex(void *p, unsigned char *buf, int len, + int parsed, snd_midi_channel_set_t *chset) +{ + opl3_t *opl3; + + opl3 = snd_magic_cast(opl3_t, p, return); +#ifdef DEBUG_MIDI + snd_printk("SYSEX\n"); +#endif +} diff -Nru linux/sound/drivers/opl3/opl3_oss.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_oss.c --- linux/sound/drivers/opl3/opl3_oss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_oss.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,357 @@ +/* + * Interface for OSS sequencer emulation + * + * Copyright (C) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "opl3_voice.h" +#include + +static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); +static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); +static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count); +static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg); + +/* */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* operators */ + +extern snd_midi_op_t opl3_ops; + +static snd_seq_oss_callback_t oss_callback = { + owner: THIS_MODULE, + open: snd_opl3_open_seq_oss, + close: snd_opl3_close_seq_oss, + ioctl: snd_opl3_ioctl_seq_oss, + load_patch: snd_opl3_load_patch_seq_oss, + reset: snd_opl3_reset_seq_oss, +}; + +static int snd_opl3_oss_event_input(snd_seq_event_t *ev, int direct, + void *private_data, int atomic, int hop) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + + if (ev->type != SNDRV_SEQ_EVENT_OSS) + snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset); + return 0; +} + +/* ------------------------------ */ + +static void snd_opl3_oss_free_port(void *private_data) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + + snd_midi_channel_free_set(opl3->oss_chset); +} + +static int snd_opl3_oss_create_port(opl3_t * opl3) +{ + snd_seq_port_callback_t callbacks; + char name[32]; + int voices, opl_ver; + + voices = (opl3->hardware < OPL3_HW_OPL3) ? + MAX_OPL2_VOICES : MAX_OPL3_VOICES; + opl3->oss_chset = snd_midi_channel_alloc_set(voices); + if (opl3->oss_chset == NULL) + return -ENOMEM; + opl3->oss_chset->private_data = opl3; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.event_input = snd_opl3_oss_event_input; + callbacks.private_free = snd_opl3_oss_free_port; + callbacks.private_data = opl3; + + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(name, "OPL%i OSS Port", opl_ver); + + opl3->oss_chset->client = opl3->seq_client; + opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (opl3->oss_chset->port < 0) { + snd_midi_channel_free_set(opl3->oss_chset); + return opl3->oss_chset->port; + } + return 0; +} + +/* ------------------------------ */ + +/* register OSS synth */ +void snd_opl3_init_seq_oss(opl3_t *opl3, char *name) +{ + snd_seq_oss_reg_t *arg; + snd_seq_device_t *dev; + + if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS, + sizeof(snd_seq_oss_reg_t), &dev) < 0) + return; + + opl3->oss_seq_dev = dev; + strncpy(dev->name, name, sizeof(dev->name) - 1); + dev->name[sizeof(dev->name) - 1] = 0; + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + arg->type = SYNTH_TYPE_FM; + if (opl3->hardware < OPL3_HW_OPL3) { + arg->subtype = FM_TYPE_ADLIB; + arg->nvoices = MAX_OPL2_VOICES; + } else { + arg->subtype = FM_TYPE_OPL3; + arg->nvoices = MAX_OPL3_VOICES; + } + arg->oper = oss_callback; + arg->private_data = opl3; + + snd_opl3_oss_create_port(opl3); + + /* register to OSS synth table */ + snd_device_register(opl3->card, dev); +} + +/* unregister */ +void snd_opl3_free_seq_oss(opl3_t *opl3) +{ + if (opl3->oss_seq_dev) { + snd_device_free(opl3->card, opl3->oss_seq_dev); + opl3->oss_seq_dev = NULL; + } +} + +/* ------------------------------ */ + +/* open OSS sequencer */ +static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, closure, return -EINVAL); + int err; + + snd_assert(arg != NULL, return -ENXIO); + + if ((err = snd_opl3_synth_setup(opl3)) < 0) + return err; + + /* fill the argument data */ + arg->private_data = opl3; + arg->addr.client = opl3->oss_chset->client; + arg->addr.port = opl3->oss_chset->port; + + if ((err = snd_opl3_synth_use_inc(opl3)) < 0) + return err; + + opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH; + return 0; +} + +/* close OSS sequencer */ +static int snd_opl3_close_seq_oss(snd_seq_oss_arg_t *arg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + snd_opl3_synth_cleanup(opl3); + + snd_opl3_synth_use_dec(opl3); + return 0; +} + +/* load patch */ + +/* offsets for SBI params */ +#define AM_VIB 0 +#define KSL_LEVEL 2 +#define ATTACK_DECAY 4 +#define SUSTAIN_RELEASE 6 +#define WAVE_SELECT 8 + +/* offset for SBI instrument */ +#define CONNECTION 10 +#define OFFSET_4OP 11 + +/* from sound_config.h */ +#define SBFM_MAXINSTR 256 + +static int snd_opl3_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, + const char *buf, int offs, int count) +{ + opl3_t *opl3; + int err = -EINVAL; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + if ((format == FM_PATCH) || (format == OPL3_PATCH)) { + struct sbi_instrument sbi; + + size_t size; + snd_seq_instr_header_t *put; + snd_seq_instr_data_t *data; + fm_xinstrument_t *xinstr; + + snd_seq_event_t ev; + int i; + + mm_segment_t fs; + + if (count < sizeof(sbi)) { + snd_printk("FM Error: Patch record too short\n"); + return -EINVAL; + } + if (copy_from_user(&sbi, buf, sizeof(sbi))) + return -EFAULT; + + if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { + snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel); + return -EINVAL; + } + + size = sizeof(*put) + sizeof(fm_xinstrument_t); + put = (snd_seq_instr_header_t *)snd_kcalloc(size, GFP_KERNEL); + if (put == NULL) + return -ENOMEM; + /* build header */ + data = &put->data; + data->type = SNDRV_SEQ_INSTR_ATYPE_DATA; + strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3); + /* build data section */ + xinstr = (fm_xinstrument_t *)(data + 1); + xinstr->stype = FM_STRU_INSTR; + + for (i = 0; i < 2; i++) { + xinstr->op[i].am_vib = sbi.operators[AM_VIB + i]; + xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i]; + xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i]; + xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i]; + xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i]; + } + xinstr->feedback_connection[0] = sbi.operators[CONNECTION]; + + if (format == OPL3_PATCH) { + xinstr->type = FM_PATCH_OPL3; + for (i = 0; i < 2; i++) { + xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i]; + xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i]; + xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i]; + xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i]; + xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i]; + } + xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION]; + } else { + xinstr->type = FM_PATCH_OPL2; + } + + put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; + put->id.instr.bank = 127; + put->id.instr.prg = sbi.channel; + put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE; + + memset (&ev, 0, sizeof(ev)); + ev.source.client = SNDRV_SEQ_CLIENT_OSS; + ev.dest = arg->addr; + + ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR; + ev.queue = SNDRV_SEQ_QUEUE_DIRECT; + + fs = snd_enter_user(); + __again: + ev.type = SNDRV_SEQ_EVENT_INSTR_PUT; + ev.data.ext.len = size; + ev.data.ext.ptr = put; + + err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, + opl3->seq_client, 0, 0); + if (err == -EBUSY) { + snd_seq_instr_header_t remove; + + memset (&remove, 0, sizeof(remove)); + remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE; + remove.id.instr = put->id.instr; + + /* remove instrument */ + ev.type = SNDRV_SEQ_EVENT_INSTR_FREE; + ev.data.ext.len = sizeof(remove); + ev.data.ext.ptr = &remove; + + snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, + opl3->seq_client, 0, 0); + goto __again; + } + snd_leave_user(fs); + + kfree(put); + } + return err; +} + +/* ioctl */ +static int snd_opl3_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, + unsigned long ioarg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + switch (cmd) { + case SNDCTL_FM_LOAD_INSTR: + snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); + return -EINVAL; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + case SNDCTL_FM_4OP_ENABLE: + // handled automatically by OPL instrument type + return 0; + + default: + return -EINVAL; + } + return 0; +} + +/* reset device */ +static int snd_opl3_reset_seq_oss(snd_seq_oss_arg_t *arg) +{ + opl3_t *opl3; + + snd_assert(arg != NULL, return -ENXIO); + opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + + return 0; +} diff -Nru linux/sound/drivers/opl3/opl3_seq.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_seq.c --- linux/sound/drivers/opl3/opl3_seq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_seq.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,321 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM + * + * OPL2/3 FM instrument loader: + * alsa-tools/seq/sbiload/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "opl3_voice.h" +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth"); +MODULE_CLASSES("{sound}"); + +int use_internal_drums = 0; +MODULE_PARM(use_internal_drums, "i"); +MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums."); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_opl3_synth_use_inc(opl3_t * opl3) +{ + if (!try_inc_mod_count(opl3->card->module)) + return -EFAULT; + return 0; + +} + +void snd_opl3_synth_use_dec(opl3_t * opl3) +{ + dec_mod_count(opl3->card->module); +} + +int snd_opl3_synth_setup(opl3_t * opl3) +{ + int idx; + + down(&opl3->access_mutex); + if (opl3->used) { + up(&opl3->access_mutex); + return -EBUSY; + } + opl3->used++; + up(&opl3->access_mutex); + + snd_opl3_reset(opl3); + + for (idx = 0; idx < MAX_OPL3_VOICES; idx++) { + opl3->voices[idx].state = SNDRV_OPL3_ST_OFF; + opl3->voices[idx].time = 0; + opl3->voices[idx].keyon_reg = 0x00; + } + opl3->use_time = 0; + opl3->connection_reg = 0x00; + if (opl3->hardware >= OPL3_HW_OPL3) { + /* Enter OPL3 mode */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); + /* Clear 4-op connections */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, + opl3->connection_reg); + opl3->max_voices = MAX_OPL3_VOICES; + } + return 0; +} + +void snd_opl3_synth_cleanup(opl3_t * opl3) +{ + unsigned long flags; + + /* Stop system timer */ + spin_lock_irqsave(&opl3->sys_timer_lock, flags); + if (opl3->sys_timer_status) { + del_timer(&opl3->tlist); + opl3->sys_timer_status = 0; + } + spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); + + snd_opl3_reset(opl3); + down(&opl3->access_mutex); + opl3->used--; + up(&opl3->access_mutex); +} + +int snd_opl3_synth_use(void *private_data, snd_seq_port_subscribe_t * info) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + int err; + + if ((err = snd_opl3_synth_setup(opl3)) < 0) + return err; + + if (use_internal_drums) { + /* Percussion mode */ + opl3->voices[6].state = opl3->voices[7].state = + opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL; + snd_opl3_load_drums(opl3); + opl3->drum_reg = OPL3_PERCUSSION_ENABLE; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg); + } else { + opl3->drum_reg = 0x00; + } + + if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) { + if ((err = snd_opl3_synth_use_inc(opl3)) < 0) + return err; + } + opl3->synth_mode = SNDRV_OPL3_MODE_SEQ; + return 0; +} + +int snd_opl3_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + + snd_opl3_synth_cleanup(opl3); + + if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) + snd_opl3_synth_use_dec(opl3); + return 0; +} + +/* + * MIDI emulation operators + */ +snd_midi_op_t opl3_ops = { + snd_opl3_note_on, + snd_opl3_note_off, + snd_opl3_key_press, + snd_opl3_terminate_note, + snd_opl3_control, + snd_opl3_nrpn, + snd_opl3_sysex, +}; + +static int snd_opl3_synth_event_input(snd_seq_event_t * ev, int direct, + void *private_data, int atomic, int hop) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && + ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { + if (direct) { + snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev, + opl3->seq_client, atomic, hop); + } + } else { + snd_midi_process_event(&opl3_ops, ev, opl3->chset); + } + return 0; +} + +/* ------------------------------ */ + +static void snd_opl3_synth_free_port(void *private_data) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + + snd_midi_channel_free_set(opl3->chset); +} + +static int snd_opl3_synth_create_port(opl3_t * opl3) +{ + snd_seq_port_callback_t callbacks; + char name[32]; + int opl_ver; + + opl3->chset = snd_midi_channel_alloc_set(16); + if (opl3->chset == NULL) + return -ENOMEM; + opl3->chset->private_data = opl3; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_opl3_synth_use; + callbacks.unuse = snd_opl3_synth_unuse; + callbacks.event_input = snd_opl3_synth_event_input; + callbacks.private_free = snd_opl3_synth_free_port; + callbacks.private_data = opl3; + + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(name, "OPL%i Port", opl_ver); + + opl3->chset->client = opl3->seq_client; + opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | + SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (opl3->chset->port < 0) { + snd_midi_channel_free_set(opl3->chset); + return opl3->chset->port; + } + return 0; +} + +/* ------------------------------ */ + +static int snd_opl3_seq_new_device(snd_seq_device_t *dev) +{ + opl3_t *opl3; + int client; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + int opl_ver; + + opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (opl3 == NULL) + return -EINVAL; + + spin_lock_init(&opl3->voice_lock); + + opl3->seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = opl3; + callbacks.allow_output = callbacks.allow_input = 1; + client = opl3->seq_client = + snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; + sprintf(cinfo.name, "OPL%i FM synth", opl_ver); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + snd_opl3_synth_create_port(opl3); + + /* initialize instrument list */ + opl3->ilist = snd_seq_instr_list_new(); + if (opl3->ilist == NULL) { + snd_seq_delete_kernel_client(client); + opl3->seq_client = -1; + return -ENOMEM; + } + opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + snd_seq_fm_init(&opl3->fm_ops, NULL); + + /* setup system timer */ + memset(&opl3->tlist, 0, sizeof(opl3->tlist)); + opl3->tlist.function = snd_opl3_timer_func; + opl3->tlist.data = (unsigned long) opl3; + spin_lock_init(&opl3->sys_timer_lock); + opl3->sys_timer_status = 0; + +#ifdef CONFIG_SND_OSSEMUL + snd_opl3_init_seq_oss(opl3, cinfo.name); +#endif + return 0; +} + +static int snd_opl3_seq_delete_device(snd_seq_device_t *dev) +{ + opl3_t *opl3; + + opl3 = *(opl3_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (opl3 == NULL) + return -EINVAL; + +#ifdef CONFIG_SND_OSSEMUL + snd_opl3_free_seq_oss(opl3); +#endif + if (opl3->seq_client >= 0) { + snd_seq_delete_kernel_client(opl3->seq_client); + opl3->seq_client = -1; + } + if (opl3->ilist) + snd_seq_instr_list_free(&opl3->ilist); + return 0; +} + +static int __init alsa_opl3_seq_init(void) +{ + static snd_seq_dev_ops_t ops = + { + snd_opl3_seq_new_device, + snd_opl3_seq_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, + sizeof(opl3_t*)); +} + +static void __exit alsa_opl3_seq_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); +} + +module_init(alsa_opl3_seq_init) +module_exit(alsa_opl3_seq_exit) diff -Nru linux/sound/drivers/opl3/opl3_synth.c linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_synth.c --- linux/sound/drivers/opl3/opl3_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_synth.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,456 @@ +/* + * Copyright (c) by Uros Bizjak + * + * Routines for OPL2/OPL3/OPL4 control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#define __SND_OSS_COMPAT__ +#include + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + + +/* + * Register offset table for OPL2/3 voices, + * OPL2 / one OPL3 register array side only + */ + +char snd_opl3_regmap[MAX_OPL2_VOICES][4] = +{ +/* OP1 OP2 OP3 OP4 */ +/* ------------------------ */ + { 0x00, 0x03, 0x08, 0x0b }, + { 0x01, 0x04, 0x09, 0x0c }, + { 0x02, 0x05, 0x0a, 0x0d }, + + { 0x08, 0x0b, 0x00, 0x00 }, + { 0x09, 0x0c, 0x00, 0x00 }, + { 0x0a, 0x0d, 0x00, 0x00 }, + + { 0x10, 0x13, 0x00, 0x00 }, /* used by percussive voices */ + { 0x11, 0x14, 0x00, 0x00 }, /* if the percussive mode */ + { 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */ +}; + +/* + * prototypes + */ +static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note); +static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice); +static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params); +static int snd_opl3_set_mode(opl3_t * opl3, int mode); +static int snd_opl3_set_connection(opl3_t * opl3, int connection); + +/* ------------------------------ */ + +/* + * open the device exclusively + */ +int snd_opl3_open(snd_hwdep_t * hw, struct file *file) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + down(&opl3->access_mutex); + if (opl3->used) { + up(&opl3->access_mutex); + return -EAGAIN; + } + opl3->used++; + up(&opl3->access_mutex); + + return 0; +} + +/* + * ioctl for hwdep device: + */ +int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + snd_assert(opl3 != NULL, return -EINVAL); + + switch (cmd) { + /* get information */ + case SNDRV_DM_FM_IOCTL_INFO: + { + snd_dm_fm_info_t info; + + info.fm_mode = opl3->fm_mode; + info.rhythm = opl3->rhythm; + if (copy_to_user((snd_dm_fm_info_t *) arg, &info, sizeof(snd_dm_fm_info_t))) + return -EFAULT; + return 0; + } + + case SNDRV_DM_FM_IOCTL_RESET: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_RESET: +#endif + snd_opl3_reset(opl3); + return 0; + + case SNDRV_DM_FM_IOCTL_PLAY_NOTE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE: +#endif + { + snd_dm_fm_note_t note; + if (copy_from_user(¬e, (snd_dm_fm_note_t *) arg, sizeof(snd_dm_fm_note_t))) + return -EFAULT; + return snd_opl3_play_note(opl3, ¬e); + } + + case SNDRV_DM_FM_IOCTL_SET_VOICE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE: +#endif + { + snd_dm_fm_voice_t voice; + if (copy_from_user(&voice, (snd_dm_fm_voice_t *) arg, sizeof(snd_dm_fm_voice_t))) + return -EFAULT; + return snd_opl3_set_voice(opl3, &voice); + } + + case SNDRV_DM_FM_IOCTL_SET_PARAMS: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS: +#endif + { + snd_dm_fm_params_t params; + if (copy_from_user(¶ms, (snd_dm_fm_params_t *) arg, sizeof(snd_dm_fm_params_t))) + return -EFAULT; + return snd_opl3_set_params(opl3, ¶ms); + } + + case SNDRV_DM_FM_IOCTL_SET_MODE: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_MODE: +#endif + return snd_opl3_set_mode(opl3, (int) arg); + + case SNDRV_DM_FM_IOCTL_SET_CONNECTION: +#ifdef CONFIG_SND_OSSEMUL + case SNDRV_DM_FM_OSS_IOCTL_SET_OPL: +#endif + return snd_opl3_set_connection(opl3, (int) arg); + +#ifdef CONFIG_SND_DEBUG + default: + snd_printk("unknown IOCTL: 0x%x\n", cmd); +#endif + } + return -ENOTTY; +} + +/* + * close the device + */ +int snd_opl3_release(snd_hwdep_t * hw, struct file *file) +{ + opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + + snd_opl3_reset(opl3); + down(&opl3->access_mutex); + opl3->used--; + up(&opl3->access_mutex); + + return 0; +} + +/* ------------------------------ */ + +void snd_opl3_reset(opl3_t * opl3) +{ + unsigned short opl3_reg; + + unsigned short reg_side; + unsigned char voice_offset; + + int max_voices, i; + + max_voices = (opl3->hardware < OPL3_HW_OPL3) ? + MAX_OPL2_VOICES : MAX_OPL3_VOICES; + + for (i = 0; i < max_voices; i++) { + /* Get register array side and offset of voice */ + if (i < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = i; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = i - MAX_OPL2_VOICES; + } + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]); + opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */ + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]); + opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */ + + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, 0x00); /* Note off */ + } + + if (opl3->hardware >= OPL3_HW_OPL3) + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + + opl3->max_voices = MAX_OPL2_VOICES; + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; + + opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT); + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00); /* Melodic mode */ + opl3->rhythm = 0; +} + + +static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note) +{ + unsigned short reg_side; + unsigned char voice_offset; + + unsigned short opl3_reg; + unsigned char reg_val; + + /* Voices 0 - 8 in OPL2 mode */ + /* Voices 0 - 17 in OPL3 mode */ + if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? + MAX_OPL3_VOICES : MAX_OPL2_VOICES)) + return -EINVAL; + + /* Get register array side and offset of voice */ + if (note->voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = note->voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = note->voice - MAX_OPL2_VOICES; + } + + /* Set lower 8 bits of note frequency */ + reg_val = (unsigned char) note->fnum; + opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + reg_val = 0x00; + /* Set output sound flag */ + if (note->key_on) + reg_val |= OPL3_KEYON_BIT; + /* Set octave */ + reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK; + /* Set higher 2 bits of note frequency */ + reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK; + + /* Set OPL3 KEYON_BLOCK register of requested voice */ + opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + return 0; +} + + +static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice) +{ + unsigned short reg_side; + unsigned char op_offset; + unsigned char voice_offset; + + unsigned short opl3_reg; + unsigned char reg_val; + + /* Only operators 1 and 2 */ + if (voice->op > 1) + return -EINVAL; + /* Voices 0 - 8 in OPL2 mode */ + /* Voices 0 - 17 in OPL3 mode */ + if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ? + MAX_OPL3_VOICES : MAX_OPL2_VOICES)) + return -EINVAL; + + /* Get register array side and offset of voice */ + if (voice->voice < MAX_OPL2_VOICES) { + /* Left register block for voices 0 .. 8 */ + reg_side = OPL3_LEFT; + voice_offset = voice->voice; + } else { + /* Right register block for voices 9 .. 17 */ + reg_side = OPL3_RIGHT; + voice_offset = voice->voice - MAX_OPL2_VOICES; + } + /* Get register offset of operator */ + op_offset = snd_opl3_regmap[voice_offset][voice->op]; + + reg_val = 0x00; + /* Set amplitude modulation (tremolo) effect */ + if (voice->am) + reg_val |= OPL3_TREMOLO_ON; + /* Set vibrato effect */ + if (voice->vibrato) + reg_val |= OPL3_VIBRATO_ON; + /* Set sustaining sound phase */ + if (voice->do_sustain) + reg_val |= OPL3_SUSTAIN_ON; + /* Set keyboard scaling bit */ + if (voice->kbd_scale) + reg_val |= OPL3_KSR; + /* Set harmonic or frequency multiplier */ + reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK; + + /* Set OPL3 AM_VIB register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set decreasing volume of higher notes */ + reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK; + /* Set output volume */ + reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK; + + /* Set OPL3 KSL_LEVEL register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set attack phase level */ + reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK; + /* Set decay phase level */ + reg_val |= voice->decay & OPL3_DECAY_MASK; + + /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set sustain phase level */ + reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK; + /* Set release phase level */ + reg_val |= voice->release & OPL3_RELEASE_MASK; + + /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ + opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Set inter-operator feedback */ + reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK; + /* Set inter-operator connection */ + if (voice->connection) + reg_val |= OPL3_CONNECTION_BIT; + /* OPL-3 only */ + if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) { + if (voice->left) + reg_val |= OPL3_VOICE_TO_LEFT; + if (voice->right) + reg_val |= OPL3_VOICE_TO_RIGHT; + } + /* Feedback/connection bits are applicable to voice */ + opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); + opl3->command(opl3, opl3_reg, reg_val); + + /* Select waveform */ + reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK; + opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); + opl3->command(opl3, opl3_reg, reg_val); + + return 0; +} + +static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params) +{ + unsigned char reg_val; + + reg_val = 0x00; + /* Set keyboard split method */ + if (params->kbd_split) + reg_val |= OPL3_KEYBOARD_SPLIT; + opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val); + + reg_val = 0x00; + /* Set amplitude modulation (tremolo) depth */ + if (params->am_depth) + reg_val |= OPL3_TREMOLO_DEPTH; + /* Set vibrato depth */ + if (params->vib_depth) + reg_val |= OPL3_VIBRATO_DEPTH; + /* Set percussion mode */ + if (params->rhythm) { + reg_val |= OPL3_PERCUSSION_ENABLE; + opl3->rhythm = 1; + } else { + opl3->rhythm = 0; + } + /* Play percussion instruments */ + if (params->bass) + reg_val |= OPL3_BASSDRUM_ON; + if (params->snare) + reg_val |= OPL3_SNAREDRUM_ON; + if (params->tomtom) + reg_val |= OPL3_TOMTOM_ON; + if (params->cymbal) + reg_val |= OPL3_CYMBAL_ON; + if (params->hihat) + reg_val |= OPL3_HIHAT_ON; + + opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val); + return 0; +} + +static int snd_opl3_set_mode(opl3_t * opl3, int mode) +{ + if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3)) + return -EINVAL; + + if (mode == SNDRV_DM_FM_MODE_OPL3) { + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE); /* Enter OPL3 mode */ + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL3; + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00); /* Clear 4-op connections */ + } else { + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00); /* Enter OPL2 mode */ + opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2; + } + + return 0; +} + +static int snd_opl3_set_connection(opl3_t * opl3, int connection) +{ + unsigned char reg_val; + + /* OPL-3 only */ + if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3) + return -EINVAL; + + reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 | + OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2); + /* Set 4-op connections */ + opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val); + + return 0; +} diff -Nru linux/sound/drivers/opl3/opl3_voice.h linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_voice.h --- linux/sound/drivers/opl3/opl3_voice.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/opl3/opl3_voice.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,52 @@ +#ifndef __OPL3_VOICE_H +#define __OPL3_VOICE_H + +/* + * Copyright (c) 2000 Uros Bizjak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* Prototypes for opl3_seq.c */ +int snd_opl3_synth_use_inc(opl3_t * opl3); +void snd_opl3_synth_use_dec(opl3_t * opl3); +int snd_opl3_synth_setup(opl3_t * opl3); +void snd_opl3_synth_cleanup(opl3_t * opl3); + +/* Prototypes for opl3_midi.c */ +void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan); +void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan); +void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); +void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); + +void snd_opl3_calc_volume(unsigned char *reg, int vel, snd_midi_channel_t *chan); +void snd_opl3_timer_func(unsigned long data); + +/* Prototypes for opl3_drums.c */ +void snd_opl3_load_drums(opl3_t *opl3); +void snd_opl3_drum_switch(opl3_t *opl3, int note, int on_off, int vel, snd_midi_channel_t *chan); + +/* Prototypes for opl3_oss.c */ +#ifdef CONFIG_SND_OSSEMUL +void snd_opl3_init_seq_oss(opl3_t *opl3, char *name); +void snd_opl3_free_seq_oss(opl3_t *opl3); +#endif + +#endif diff -Nru linux/sound/drivers/serial-u16550.c linux-2.4.19-pre5-mjc/sound/drivers/serial-u16550.c --- linux/sound/drivers/serial-u16550.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/serial-u16550.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,990 @@ +/* + * serial.c + * Copyright (c) by Jaroslav Kysela , + * Isaku Yamahata , + * George Hansper , + * Hannu Savolainen + * + * This code is based on the code from ALSA 0.5.9, but heavily rewritten. + * + * Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com + * Added support for the Midiator MS-124T and for the MS-124W in + * Single Addressed (S/A) or Multiple Burst (M/B) mode, with + * power derived either parasitically from the serial port or + * from a separate power supply. + * + * The new snd_adaptor module parameter allows you to select + * either the default Roland Soundcanvas support (0), which was + * previously included in this driver but was not documented, + * Midiator MS-124T support (1), Midiator MS-124W S/A mode + * support (2), or MS-124W M/B mode support (3). For the + * Midiator MS-124W, you must set the physical M-S and A-B + * switches on the Midiator to match the driver mode you select. + * + * - In Roland Soundcanvas mode, multiple ALSA raw MIDI + * substreams are supported (midiCnD0-midiCnD15). Whenever you + * write to a different substream, the driver sends the + * nonstandard MIDI command sequence F5 NN, where NN is the + * substream number plus 1. Roland modules use this command to + * switch between different "parts", so this feature lets you + * treat each part as a distinct raw MIDI substream. The driver + * provides no way to send F5 00 (no selection) or to not send + * the F5 NN command sequence at all; perhaps it ought to. + * + * - In MS-124T mode, one raw MIDI substream is supported + * (midiCnD0); the snd_outs module parameter is automatically set + * to 1. The driver sends the same data to all four MIDI Out + * connectors. Set the A-B switch and the snd_speed module + * parameter to match (A=19200, B=9600). + * + * Usage example for MS-124T, with A-B switch in A position: + * setserial /dev/ttyS0 uart none + * /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ + * snd_adaptor=1 snd_speed=19200 + * + * - In MS-124W S/A mode, one raw MIDI substream is supported + * (midiCnD0); the snd_outs module parameter is automatically set + * to 1. The driver sends the same data to all four MIDI Out + * connectors at full MIDI speed. + * + * Usage example for S/A mode: + * setserial /dev/ttyS0 uart none + * /sbin/modprobe snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ + * snd_adaptor=2 + * + * - In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI + * substreams; the snd_outs module parameter is automatically set + * to 16. The substream number gives a bitmask of which MIDI Out + * connectors the data should be sent to, with midiCnD1 sending + * to Out 1, midiCnD2 to Out 2, midiCnD4 to Out 3, and midiCnD8 + * to Out 4. Thus midiCnD15 sends the data to all 4 ports. As a + * special case, midiCnD0 also sends to all ports, since it is + * not useful to send the data to no ports. M/B mode has extra + * overhead to select the MIDI Out for each byte, so the + * aggregate data rate across all four MIDI Outs is at most one + * byte every 520 us, as compared with the full MIDI data rate of + * one byte every 320 us per port. + * + * Usage example for M/B mode: + * setserial /dev/ttyS0 uart none + * /sbin/insmod snd-serial-u16550 snd_port=0x3f8 snd_irq=4 \ + * snd_adaptor=3 + * + * - The MS-124W hardware's M/A mode is currently not supported. + * This mode allows the MIDI Outs to act independently at double + * the aggregate throughput of M/B, but does not allow sending + * the same byte simultaneously to multiple MIDI Outs. The M/A + * protocol requires the driver to twiddle the modem control + * lines under timing constraints, so it would be a bit more + * complicated to implement than the other modes. + * + * - Midiator models other than MS-124W and MS-124T are currently + * not supported. Note that the suffix letter is significant; + * the MS-124 and MS-124B are not compatible, nor are the other + * known models MS-101, MS-101B, MS-103, and MS-114. I do have + * documentation that partially covers these models, but no units + * to experiment with. The MS-124W support is tested with a real + * unit. The MS-124T support is untested, but should work. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("MIDI serial"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA, MIDI serial}}"); + +#define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */ +#define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */ +#define SNDRV_SERIAL_MS124W_SA 2 /* Midiator MS-124W in S/A mode */ +#define SNDRV_SERIAL_MS124W_MB 3 /* Midiator MS-124W in M/B mode */ +#define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_MS124W_MB +static char *adaptor_names[] = { + "Soundcanvas", + "MS-124T", + "MS-124W S/A", + "MS-124W M/B" +}; + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x3f8,0x2f8,0x3e8,0x2e8 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,4,5,7,9,10,11,14,15 */ +static int snd_speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */ +static int snd_base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud base */ +static int snd_outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */ +static int snd_adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Serial MIDI."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Serial MIDI."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_enable, "Enable UART16550A chip."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for UART16550A chip."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for UART16550A chip."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_speed, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_speed, "Speed in bauds."); +MODULE_PARM_SYNTAX(snd_speed, SNDRV_ENABLED ",allows:{9600,19200,38400,57600,115200},dialog:list"); +MODULE_PARM(snd_base, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_base, "Base for divisor in bauds."); +MODULE_PARM_SYNTAX(snd_base, SNDRV_ENABLED ",allows:{57600,115200,230400,460800},dialog:list"); +MODULE_PARM(snd_outs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_outs, "Number of MIDI outputs."); +MODULE_PARM_SYNTAX(snd_outs, SNDRV_ENABLED ",allows:{{1,16}},dialog:list"); +MODULE_PARM(snd_adaptor, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_adaptor, "Type of adaptor."); +MODULE_PARM_SYNTAX(snd_adaptor, SNDRV_ENABLED ",allows:{{0=Soundcanvas,1=MS-124T,2=MS-124W S/A,3=MS-124W M/B}},dialog:list"); + +/*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/ /* Address outs as 0-3 instead of bitmap */ + +#define SNDRV_SERIAL_MAX_OUTS 16 /* max 64, min 16 */ + +#define TX_BUFF_SIZE (1<<9) /* Must be 2^n */ +#define TX_BUFF_MASK (TX_BUFF_SIZE - 1) + +#define SERIAL_MODE_NOT_OPENED (0) +#define SERIAL_MODE_INPUT_OPEN (1 << 0) +#define SERIAL_MODE_OUTPUT_OPEN (1 << 1) +#define SERIAL_MODE_INPUT_TRIGGERED (1 << 2) +#define SERIAL_MODE_OUTPUT_TRIGGERED (1 << 3) + +typedef struct _snd_uart16550 { + snd_card_t *card; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_output[SNDRV_SERIAL_MAX_OUTS]; + snd_rawmidi_substream_t *midi_input; + + int filemode; //open status of file + + spinlock_t open_lock; + + int irq; + + unsigned long base; + struct resource *res_base; + + unsigned int speed; + unsigned int speed_base; + unsigned char divisor; + + unsigned char old_divisor_lsb; + unsigned char old_divisor_msb; + unsigned char old_line_ctrl_reg; + + // parameter for using of write loop + short int fifo_limit; //used in uart16550 + short int fifo_count; //used in uart16550 + + // type of adaptor + int adaptor; + + // outputs + int prev_out; + unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS]; + + // write buffer and its writing/reading position + unsigned char tx_buff[TX_BUFF_SIZE]; + int buff_in_count; + int buff_in; + int buff_out; + + // wait timer + unsigned int timer_running:1; + struct timer_list buffer_timer; + +} snd_uart16550_t; + +static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +inline static void snd_uart16550_add_timer(snd_uart16550_t *uart) +{ + if (! uart->timer_running) { + /* timer 38600bps * 10bit * 16byte */ + uart->buffer_timer.expires = jiffies + (HZ+255)/256; + uart->timer_running = 1; + add_timer(&uart->buffer_timer); + } +} + +inline static void snd_uart16550_del_timer(snd_uart16550_t *uart) +{ + if (uart->timer_running) { + del_timer(&uart->buffer_timer); + uart->timer_running = 0; + } +} + +/* This macro is only used in snd_uart16550_io_loop */ +inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart) +{ + unsigned short buff_out = uart->buff_out; + outb(uart->tx_buff[buff_out], uart->base + UART_TX); + uart->fifo_count++; + buff_out++; + buff_out &= TX_BUFF_MASK; + uart->buff_out = buff_out; + uart->buff_in_count--; +} + +/* This loop should be called with interrupts disabled + * We don't want to interrupt this, + * as we're already handling an interupt + */ +static void snd_uart16550_io_loop(snd_uart16550_t * uart) +{ + unsigned char c, status; + + /* Read Loop */ + while ((status = inb(uart->base + UART_LSR)) & UART_LSR_DR) { + /* while receive data ready */ + c = inb(uart->base + UART_RX); + if (uart->filemode & SERIAL_MODE_INPUT_OPEN) { + snd_rawmidi_receive(uart->midi_input, &c, 1); + } + if (status & UART_LSR_OE) + snd_printk("%s: Overrun on device at 0x%lx\n", + uart->rmidi->name, uart->base); + } + + /* no need of check SERIAL_MODE_OUTPUT_OPEN because if not, + buffer is never filled. */ + /* Check write status */ + if (status & UART_LSR_THRE) { + uart->fifo_count = 0; + } + if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) { + /* Can't use FIFO, must send only when CTS is true */ + status = inb(uart->base + UART_MSR); + if (uart->fifo_count == 0 && (status & UART_MSR_CTS) + && uart->buff_in_count > 0) + snd_uart16550_buffer_output(uart); + } else { + /* Write loop */ + while (uart->fifo_count < uart->fifo_limit /* Can we write ? */ + && uart->buff_in_count > 0) /* Do we want to? */ + snd_uart16550_buffer_output(uart); + } + if (uart->irq < 0 && uart->buff_in_count > 0) + snd_uart16550_add_timer(uart); +} + +/* NOTES ON SERVICING INTERUPTS + * --------------------------- + * After receiving a interrupt, it is important to indicate to the UART that + * this has been done. + * For a Rx interupt, this is done by reading the received byte. + * For a Tx interupt this is done by either: + * a) Writing a byte + * b) Reading the IIR + * It is particularly important to read the IIR if a Tx interupt is received + * when there is no data in tx_buff[], as in this case there no other + * indication that the interupt has been serviced, and it remains outstanding + * indefinitely. This has the curious side effect that and no further interupts + * will be generated from this device AT ALL!!. + * It is also desirable to clear outstanding interupts when the device is + * opened/closed. + * + * + * Note that some devices need OUT2 to be set before they will generate + * interrupts at all. (Possibly tied to an internal pull-up on CTS?) + */ +static void snd_uart16550_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + snd_uart16550_t *uart; + + uart = (snd_uart16550_t *) dev_id; + spin_lock(&uart->open_lock); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) { + spin_unlock(&uart->open_lock); + return; + } + inb(uart->base + UART_IIR); /* indicate to the UART that the interupt has been serviced */ + snd_uart16550_io_loop(uart); + spin_unlock(&uart->open_lock); +} + +/* When the polling mode, this function calls snd_uart16550_io_loop. */ +static void snd_uart16550_buffer_timer(unsigned long data) +{ + snd_uart16550_t *uart; + + uart = (snd_uart16550_t *)data; + spin_lock(&uart->open_lock); + snd_uart16550_del_timer(uart); + snd_uart16550_io_loop(uart); + spin_unlock(&uart->open_lock); +} + +/* + * this method probes, if an uart sits on given port + * return 0 if found + * return negative error if not found + */ +static int __init snd_uart16550_detect(unsigned int io_base) +{ + int ok; + unsigned char c; + + if (check_region(io_base, 8)) + return -EBUSY; + + /* Do some vague tests for the presence of the uart */ + if (io_base == 0) + return -ENODEV; /* Not configured */ + + ok = 1; /* uart detected unless one of the following tests should fail */ + /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */ + outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */ + c = inb(io_base + UART_IER); + /* The top four bits of the IER should always == 0 */ + if ((c & 0xf0) != 0) + ok = 0; /* failed */ + + outb(0xaa, io_base + UART_SCR); + /* Write arbitrary data into the scratch reg */ + c = inb(io_base + UART_SCR); + /* If it comes back, it's OK */ + if (c != 0xaa) + ok = 0; /* failed */ + + outb(0x55, io_base + UART_SCR); + /* Write arbitrary data into the scratch reg */ + c = inb(io_base + UART_SCR); + /* If it comes back, it's OK */ + if (c != 0x55) + ok = 0; /* failed */ + + return ok; +} + +static void snd_uart16550_do_open(snd_uart16550_t * uart) +{ + char byte; + + /* Initialize basic variables */ + uart->buff_in_count = 0; + uart->buff_in = 0; + uart->buff_out = 0; + uart->fifo_limit = 1; + uart->fifo_count = 0; + uart->timer_running = 0; + + outb(UART_FCR_ENABLE_FIFO /* Enable FIFO's (if available) */ + | UART_FCR_CLEAR_RCVR /* Clear receiver FIFO */ + | UART_FCR_CLEAR_XMIT /* Clear transmitter FIFO */ + | UART_FCR_TRIGGER_4 /* Set FIFO trigger at 4-bytes */ + /* NOTE: interupt generated after T=(time)4-bytes + * if less than UART_FCR_TRIGGER bytes received + */ + ,uart->base + UART_FCR); /* FIFO Control Register */ + + if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0) + uart->fifo_limit = 16; + if (uart->divisor != 0) { + uart->old_line_ctrl_reg = inb(uart->base + UART_LCR); + outb(UART_LCR_DLAB /* Divisor latch access bit */ + ,uart->base + UART_LCR); /* Line Control Register */ + uart->old_divisor_lsb = inb(uart->base + UART_DLL); + uart->old_divisor_msb = inb(uart->base + UART_DLM); + + outb(uart->divisor + ,uart->base + UART_DLL); /* Divisor Latch Low */ + outb(0 + ,uart->base + UART_DLM); /* Divisor Latch High */ + /* DLAB is reset to 0 in next outb() */ + } + /* Set serial parameters (parity off, etc) */ + outb(UART_LCR_WLEN8 /* 8 data-bits */ + | 0 /* 1 stop-bit */ + | 0 /* parity off */ + | 0 /* DLAB = 0 */ + ,uart->base + UART_LCR); /* Line Control Register */ + + switch (uart->adaptor) { + default: + outb(UART_MCR_RTS /* Set Request-To-Send line active */ + | UART_MCR_DTR /* Set Data-Terminal-Ready line active */ + | UART_MCR_OUT2 /* Set OUT2 - not always required, but when + * it is, it is ESSENTIAL for enabling interrupts + */ + ,uart->base + UART_MCR); /* Modem Control Register */ + break; + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2, + uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are both asserted. */ + outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2, + uart->base + UART_MCR); + break; + } + + if (uart->irq < 0) { + byte = (0 & UART_IER_RDI) /* Disable Receiver data interupt */ + |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */ + ; + } else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) { + byte = UART_IER_RDI /* Enable Receiver data interrupt */ + | UART_IER_MSI /* Enable Modem status interrupt */ + ; + } else { + byte = UART_IER_RDI /* Enable Receiver data interupt */ + | UART_IER_THRI /* Enable Transmitter holding register empty interupt */ + ; + } + outb(byte, uart->base + UART_IER); /* Interupt enable Register */ + + inb(uart->base + UART_LSR); /* Clear any pre-existing overrun indication */ + inb(uart->base + UART_IIR); /* Clear any pre-existing transmit interrupt */ + inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */ +} + +static void snd_uart16550_do_close(snd_uart16550_t * uart) +{ + if (uart->irq < 0) + snd_uart16550_del_timer(uart); + + /* NOTE: may need to disable interrupts before de-registering out handler. + * For now, the consequences are harmless. + */ + + outb((0 & UART_IER_RDI) /* Disable Receiver data interupt */ + |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */ + ,uart->base + UART_IER); /* Interupt enable Register */ + + switch (uart->adaptor) { + default: + outb((0 & UART_MCR_RTS) /* Deactivate Request-To-Send line */ + |(0 & UART_MCR_DTR) /* Deactivate Data-Terminal-Ready line */ + |(0 & UART_MCR_OUT2) /* Deactivate OUT2 */ + ,uart->base + UART_MCR); /* Modem Control Register */ + break; + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states; leave it powered. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2), + uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are both asserted; leave it powered. */ + outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2), + uart->base + UART_MCR); + break; + } + + inb(uart->base + UART_IIR); /* Clear any outstanding interupts */ + + /* Restore old divisor */ + if (uart->divisor != 0) { + outb(UART_LCR_DLAB /* Divisor latch access bit */ + ,uart->base + UART_LCR); /* Line Control Register */ + outb(uart->old_divisor_lsb + ,uart->base + UART_DLL); /* Divisor Latch Low */ + outb(uart->old_divisor_msb + ,uart->base + UART_DLM); /* Divisor Latch High */ + /* Restore old LCR (data bits, stop bits, parity, DLAB) */ + outb(uart->old_line_ctrl_reg + ,uart->base + UART_LCR); /* Line Control Register */ + } +} + +static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_open(uart); + uart->filemode |= SERIAL_MODE_INPUT_OPEN; + uart->midi_input = substream; + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +} + +static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + uart->filemode &= ~SERIAL_MODE_INPUT_OPEN; + uart->midi_input = NULL; + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_close(uart); + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +} + +static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&uart->open_lock, flags); + if (up) { + uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED; + } else { + uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED; + } + spin_unlock_irqrestore(&uart->open_lock, flags); +} + +static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_open(uart); + uart->filemode |= SERIAL_MODE_OUTPUT_OPEN; + uart->midi_output[substream->number] = substream; + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +}; + +static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&uart->open_lock, flags); + uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN; + uart->midi_output[substream->number] = NULL; + if (uart->filemode == SERIAL_MODE_NOT_OPENED) + snd_uart16550_do_close(uart); + spin_unlock_irqrestore(&uart->open_lock, flags); + return 0; +}; + +inline static void snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte) +{ + unsigned short buff_in = uart->buff_in; + uart->tx_buff[buff_in] = byte; + buff_in++; + buff_in &= TX_BUFF_MASK; + uart->buff_in = buff_in; + uart->buff_in_count++; + if (uart->irq < 0) /* polling mode */ + snd_uart16550_add_timer(uart); +} + +static void snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte) +{ + if (uart->buff_in_count == 0 /* Buffer empty? */ + && (uart->adaptor != SNDRV_SERIAL_MS124W_SA || + (uart->fifo_count == 0 /* FIFO empty? */ + && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */ + + /* Tx Buffer Empty - try to write immediately */ + if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) { + /* Transmitter holding register (and Tx FIFO) empty */ + uart->fifo_count = 1; + outb(midi_byte, uart->base + UART_TX); + } else { + if (uart->fifo_count < uart->fifo_limit) { + uart->fifo_count++; + outb(midi_byte, uart->base + UART_TX); + } else { + /* Cannot write (buffer empty) - put char in buffer */ + snd_uart16550_write_buffer(uart, midi_byte); + } + } + } else { + if (uart->buff_in_count >= TX_BUFF_SIZE) { + snd_printk("%s: Buffer overrun on device at 0x%lx\n", + uart->rmidi->name, uart->base); + return; + } + snd_uart16550_write_buffer(uart, midi_byte); + } +} + +static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + unsigned char midi_byte, addr_byte; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + char first; + + /* Interupts are disabled during the updating of the tx_buff, + * since it is 'bad' to have two processes updating the same + * variables (ie buff_in & buff_out) + */ + + spin_lock_irqsave(&uart->open_lock, flags); + + if (uart->irq < 0) //polling + snd_uart16550_io_loop(uart); + + if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) { + while (1) { + /* buffer full? */ + /* in this mode we need two bytes of space */ + if (uart->buff_in_count > TX_BUFF_SIZE - 2) + break; + if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1) + break; +#if SNDRV_SERIAL_MS124W_MB_NOCOMBO + /* select exactly one of the four ports */ + addr_byte = (1 << (substream->number + 4)) | 0x08; +#else + /* select any combination of the four ports */ + addr_byte = (substream->number << 4) | 0x08; + /* ...except none */ + if (addr_byte == 0x08) addr_byte = 0xf8; +#endif + snd_uart16550_output_byte(uart, substream, addr_byte); + /* send midi byte */ + snd_uart16550_output_byte(uart, substream, midi_byte); + } + } else { + first = 0; + while (1) { + /* buffer full? */ + if (uart->buff_in_count >= TX_BUFF_SIZE) + break; + if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1) + break; + if (first == 0 && uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS && + uart->prev_out != substream->number) { + /* Roland Soundcanvas part selection */ + /* If this substream of the data is different previous + substream in this uart, send the change part event */ + uart->prev_out = substream->number; + /* change part */ + snd_uart16550_output_byte(uart, substream, 0xf5); + /* data */ + snd_uart16550_output_byte(uart, substream, uart->prev_out + 1); + /* If midi_byte is a data byte, send the previous status byte */ + if (midi_byte < 0x80) + snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]); + } + /* send midi byte */ + snd_uart16550_output_byte(uart, substream, midi_byte); + if (midi_byte >= 0x80 && midi_byte < 0xf0) + uart->prev_status[uart->prev_out] = midi_byte; + first = 1; + } + } + spin_unlock_irqrestore(&uart->open_lock, flags); +} + +static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&uart->open_lock, flags); + if (up) { + uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED; + } else { + uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED; + } + spin_unlock_irqrestore(&uart->open_lock, flags); + if (up) + snd_uart16550_output_write(substream); +} + +static snd_rawmidi_ops_t snd_uart16550_output = +{ + open: snd_uart16550_output_open, + close: snd_uart16550_output_close, + trigger: snd_uart16550_output_trigger, +}; + +static snd_rawmidi_ops_t snd_uart16550_input = +{ + open: snd_uart16550_input_open, + close: snd_uart16550_input_close, + trigger: snd_uart16550_input_trigger, +}; + +static int snd_uart16550_free(snd_uart16550_t *uart) +{ + if (uart->irq >= 0) + free_irq(uart->irq, (void *)uart); + if (uart->res_base) { + release_resource(uart->res_base); + kfree_nocheck(uart->res_base); + } + snd_magic_kfree(uart); + return 0; +}; + +static int snd_uart16550_dev_free(snd_device_t *device) +{ + snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, device->device_data, return -ENXIO); + return snd_uart16550_free(uart); +} + +static int __init snd_uart16550_create(snd_card_t * card, + unsigned long iobase, + int irq, + unsigned int speed, + unsigned int base, + int adaptor, + snd_uart16550_t **ruart) +{ + static snd_device_ops_t ops = { + dev_free: snd_uart16550_dev_free, + }; + snd_uart16550_t *uart; + int err; + + + if ((uart = snd_magic_kcalloc(snd_uart16550_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + uart->adaptor = adaptor; + uart->card = card; + spin_lock_init(&uart->open_lock); + uart->irq = -1; + if ((uart->res_base = request_region(iobase, 8, "Serial MIDI")) == NULL) { + snd_printk("unable to grab ports 0x%lx-0x%lx\n", iobase, iobase + 8 - 1); + return -EBUSY; + } + uart->base = iobase; + if (irq >= 0) { + if (request_irq(irq, snd_uart16550_interrupt, + SA_INTERRUPT, "Serial MIDI", (void *) uart)) { + uart->irq = -1; + snd_printk("irq %d busy. Using Polling.\n", irq); + } else { + uart->irq = irq; + } + } + uart->divisor = base / speed; + uart->speed = base / (unsigned int)uart->divisor; + uart->speed_base = base; + uart->prev_out = -1; + memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS); + uart->buffer_timer.function = snd_uart16550_buffer_timer; + uart->buffer_timer.data = (unsigned long)uart; + uart->timer_running = 0; + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) { + snd_uart16550_free(uart); + return err; + } + + switch (uart->adaptor) { + case SNDRV_SERIAL_MS124W_SA: + case SNDRV_SERIAL_MS124W_MB: + /* MS-124W can draw power from RTS and DTR if they + are in opposite states. */ + outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR); + break; + case SNDRV_SERIAL_MS124T: + /* MS-124T can draw power from RTS and/or DTR (preferably + both) if they are asserted. */ + outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR); + break; + default: + break; + } + + if (ruart) + *ruart = uart; + + return 0; +} + +static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, snd_rawmidi_t **rmidi) +{ + snd_rawmidi_t *rrawmidi; + int err; + + if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, 1, &rrawmidi)) < 0) + return err; + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input); + snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output); + sprintf(rrawmidi->name, "uart16550 MIDI #%d", device); + rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rrawmidi->private_data = uart; + if (rmidi) + *rmidi = rrawmidi; + return 0; +} + +static int __init snd_serial_probe(int dev) +{ + snd_card_t *card; + snd_uart16550_t *uart; + int err; + + if (!snd_enable[dev]) + return -ENOENT; + + switch (snd_adaptor[dev]) { + case SNDRV_SERIAL_SOUNDCANVAS: + break; + case SNDRV_SERIAL_MS124T: + case SNDRV_SERIAL_MS124W_SA: + snd_outs[dev] = 1; + break; + case SNDRV_SERIAL_MS124W_MB: + snd_outs[dev] = 16; + break; + default: + snd_printk("Adaptor type is out of range 0-%d (%d)\n", + SNDRV_SERIAL_MAX_ADAPTOR, snd_adaptor[dev]); + return -ENODEV; + } + + if (snd_outs[dev] < 1 || snd_outs[dev] > SNDRV_SERIAL_MAX_OUTS) { + snd_printk("Count of outputs is out of range 1-%d (%d)\n", + SNDRV_SERIAL_MAX_OUTS, snd_outs[dev]); + return -ENODEV; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "Serial"); + strcpy(card->shortname, "Serial midi (uart16550A)"); + + if ((err = snd_uart16550_detect(snd_port[dev])) <= 0) { + snd_card_free(card); + printk(KERN_ERR "no UART detected at 0x%lx\n", (long)snd_port[dev]); + return err; + } + + if ((err = snd_uart16550_create(card, + snd_port[dev], + snd_irq[dev], + snd_speed[dev], + snd_base[dev], + snd_adaptor[dev], + &uart)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_uart16550_rmidi(uart, 0, snd_outs[dev], &uart->rmidi)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d adaptor %s", + card->shortname, + uart->base, + uart->irq, + uart->speed, + (int)uart->divisor, + snd_outs[dev], + adaptor_names[uart->adaptor]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_serial_cards[dev] = card; + return 0; +} + +static int __init alsa_card_serial_init(void) +{ + int dev = 0; + int cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (snd_serial_probe(dev) == 0) + cards++; + } + + if (cards == 0) { +#ifdef MODULE + printk(KERN_ERR "serial midi soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_serial_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (snd_serial_cards[dev] != NULL) + snd_card_free(snd_serial_cards[dev]); + } +} + +module_init(alsa_card_serial_init) +module_exit(alsa_card_serial_exit) + +#ifndef MODULE + +/* format is: snd-serial=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_speed,snd_base,snd_outs */ + +static int __init alsa_card_serial_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_speed[nr_dev]) == 2 && + get_option(&str,&snd_base[nr_dev]) == 2 && + get_option(&str,&snd_outs[nr_dev]) == 2 && + get_option(&str,&snd_adaptor[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-serial=", alsa_card_serial_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/drivers/virmidi.c linux-2.4.19-pre5-mjc/sound/drivers/virmidi.c --- linux/sound/drivers/virmidi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/drivers/virmidi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,185 @@ +/* + * Dummy soundcard for virtual rawmidi devices + * + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * VIRTUAL RAW MIDI DEVICE CARDS + * + * This dummy card contains up to 4 virtual rawmidi devices. + * They are not real rawmidi devices but just associated with sequencer + * clients, so that any input/output sources can be connected as a raw + * MIDI device arbitrary. + * Also, multiple access is allowed to a single rawmidi device. + * + * Typical usage is like following: + * - Load snd-virmidi module. + * # modprobe snd-virmidi snd_index=2 + * Then, sequencer clients 72:0 to 75:0 will be created, which are + * mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively. + * + * - Connect input/output via aconnect. + * % aconnect 64:0 72:0 # keyboard input redirection 64:0 -> 72:0 + * % aconnect 72:0 65:0 # output device redirection 72:0 -> 65:0 + * + * - Run application using a midi device (eg. /dev/snd/midiC1D0) + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALSA,Virtual rawmidi device}}"); + +#define MAX_MIDI_DEVICES 8 + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for virmidi soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for virmidi soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_midi_devs, "MIDI devices # (1-8)"); +MODULE_PARM_SYNTAX(snd_midi_devs, SNDRV_ENABLED ",allows:{{1,8}}"); + +typedef struct snd_card_virmidi { + snd_card_t *card; + snd_rawmidi_t *midi[MAX_MIDI_DEVICES]; +} snd_card_virmidi_t; + +static snd_card_t *snd_virmidi_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_virmidi_probe(int dev) +{ + snd_card_t *card; + struct snd_card_virmidi *vmidi; + int idx, err; + + if (!snd_enable[dev]) + return -ENODEV; + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_virmidi)); + if (card == NULL) + return -ENOMEM; + vmidi = (struct snd_card_virmidi *)card->private_data; + vmidi->card = card; + + if (snd_midi_devs[dev] > MAX_MIDI_DEVICES) { + snd_printk("too much midi devices for virmidi %d: force to use %d\n", dev, MAX_MIDI_DEVICES); + snd_midi_devs[dev] = MAX_MIDI_DEVICES; + } + for (idx = 0; idx < snd_midi_devs[dev]; idx++) { + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0) + goto __nodev; + rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + vmidi->midi[idx] = rmidi; + strcpy(rmidi->name, "Virtual Raw MIDI"); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; + } + + strcpy(card->driver, "VirMIDI"); + strcpy(card->shortname, "VirMIDI"); + sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); + if ((err = snd_card_register(card)) == 0) { + snd_virmidi_cards[dev] = card; + return 0; + } + __nodev: + snd_card_free(card); + return err; +} + +static int __init alsa_card_virmidi_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_card_virmidi_probe(dev) < 0) { +#ifdef MODULE + printk(KERN_ERR "Card-VirMIDI #%i not found or device busy\n", dev + 1); +#endif + break; + } + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_virmidi_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_virmidi_cards[dev]); +} + +module_init(alsa_card_virmidi_init) +module_exit(alsa_card_virmidi_exit) + +#ifndef MODULE + +/* format is: snd-virmidi=snd_enable,snd_index,snd_id,snd_midi_devs */ + +static int __init alsa_card_virmidi_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_midi_devs[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-virmidi=", alsa_card_virmidi_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/i2c/Makefile linux-2.4.19-pre5-mjc/sound/i2c/Makefile --- linux/sound/i2c/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/i2c/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,29 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _i2c.o + +list-multi := snd-i2c.o snd-cs8427.o snd-tea6330t.o + +export-objs := i2c.o cs8427.o tea6330t.o + +snd-i2c-objs := i2c.o +snd-cs8427-objs := cs8427.o +snd-tea6330t-objs := tea6330t.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o +obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o + +include $(TOPDIR)/Rules.make + +snd-i2c.o: $(snd-i2c-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-i2c-objs) + +snd-cs8427.o: $(snd-cs8427-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs8427-objs) + +snd-tea6330t.o: $(snd-tea6330t-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-tea6330t-objs) diff -Nru linux/sound/i2c/cs8427.c linux-2.4.19-pre5-mjc/sound/i2c/cs8427.c --- linux/sound/i2c/cs8427.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/i2c/cs8427.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,470 @@ +/* + * Routines for control of the CS8427 via i2c bus + * IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic"); +MODULE_LICENSE("GPL"); + +#define chip_t snd_i2c_device_t + +#define CS8427_ADDR (0x20>>1) /* fixed address */ + +typedef struct { + snd_pcm_substream_t *substream; + char hw_status[24]; /* hardware status */ + char def_status[24]; /* default status */ + char pcm_status[24]; /* PCM private status */ + char hw_udata[32]; + snd_kcontrol_t *pcm_ctl; +} cs8427_stream_t; + +typedef struct { + unsigned char regmap[0x14]; /* map of first 1 + 13 registers */ + cs8427_stream_t playback; + cs8427_stream_t capture; +} cs8427_t; + +static unsigned char swapbits(unsigned char val) +{ + int bit; + unsigned char res = 0; + for (bit = 0; bit < 8; bit++) { + res |= val & 1; + res <<= 1; + val >>= 1; + } + return res; +} + +int snd_cs8427_detect(snd_i2c_bus_t *bus, unsigned char addr) +{ + int res; + + snd_i2c_lock(bus); + res = snd_i2c_probeaddr(bus, CS8427_ADDR | (addr & 7)); + snd_i2c_unlock(bus); + return res; +} + +static int snd_cs8427_reg_write(snd_i2c_device_t *device, unsigned char reg, unsigned char val) +{ + int err; + unsigned char buf[2]; + + buf[0] = reg & 0x7f; + buf[1] = val; + if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) { + snd_printk("unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n", buf[0], buf[1], err); + return err < 0 ? err : -EREMOTE; + } + return 0; +} + +static int snd_cs8427_reg_read(snd_i2c_device_t *device, unsigned char reg) +{ + int err; + unsigned char buf; + + if ((err = snd_i2c_sendbytes(device, ®, 1)) != 1) { + snd_printk("unable to send register 0x%x byte to CS8427\n", reg); + return err < 0 ? err : -EREMOTE; + } + if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) { + snd_printk("unable to read register 0x%x byte from CS8427\n", reg); + return err < 0 ? err : -EREMOTE; + } + return buf; +} + +static int snd_cs8427_select_corudata(snd_i2c_device_t *device, int udata) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + int err; + + udata = udata ? CS8427_BSEL : 0; + if (udata != (chip->regmap[CS8427_REG_CSDATABUF] & udata)) { + chip->regmap[CS8427_REG_CSDATABUF] &= ~CS8427_BSEL; + chip->regmap[CS8427_REG_CSDATABUF] |= udata; + err = snd_cs8427_reg_write(device, CS8427_REG_CSDATABUF, chip->regmap[CS8427_REG_CSDATABUF]); + if (err < 0) + return err; + } + return 0; +} + +static int snd_cs8427_send_corudata(snd_i2c_device_t *device, + int udata, + unsigned char *ndata, + int count) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status; + char data[32]; + int err, idx; + + if (!memcmp(hw_data, ndata, count)) + return 0; + if ((err = snd_cs8427_select_corudata(device, udata)) < 0) + return err; + memcpy(hw_data, data, count); + if (udata) { + memset(data, 0, sizeof(data)); + if (memcmp(hw_data, data, 32) == 0) { + chip->regmap[CS8427_REG_UDATABUF] &= ~CS8427_UBMMASK; + chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS | CS8427_EFTUI; + if ((err = snd_cs8427_reg_write(device, CS8427_REG_UDATABUF, chip->regmap[CS8427_REG_UDATABUF])) < 0) + return err; + return 0; + } + } + data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF; + for (idx = 0; idx < count; idx++) + data[idx + 1] = swapbits(ndata[idx]); + if (snd_i2c_sendbytes(device, data, count) != count) + return -EREMOTE; + return 1; +} + +static void snd_cs8427_free(snd_i2c_device_t *device) +{ + if (device->private_data) + snd_magic_kfree(device->private_data); +} + +int snd_cs8427_create(snd_i2c_bus_t *bus, + unsigned char addr, + snd_i2c_device_t **r_cs8427) +{ + static unsigned char initvals1[] = { + CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC, + /* CS8427_REG_CLOCKSOURCE: RMCK to OMCK, no validity, disable mutes, TCBL=output */ + CS8427_SWCLK, + /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs, normal stereo operation */ + 0x00, + /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial, Rx=>serial */ + CS8427_TXDSERIAL | CS8427_SPDAES3RECEIVER, + /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs, output time base = OMCK, input time base = + covered input clock, recovered input clock source is Envy24 */ + CS8427_INC, + /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S, 24-bit, 64*Fsi */ + CS8427_SIDEL | CS8427_SILRPOL, + /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format = I2S, 24-bit, 64*Fsi */ + CS8427_SODEL | CS8427_SOLRPOL, + }; + static unsigned char initvals2[] = { + CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC, + /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence, biphase, parity status bits */ + CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR, + /* CS8427_REG_CSDATABUF: + Registers 32-55 window to CS buffer + Inhibit D->E transfers from overwriting first 5 bytes of CS data. + Inhibit D->E transfers (all) of CS data. + Allow E->F transfer of CS data. + One byte mode; both A/B channels get same written CB data. + A channel info is output to chip's EMPH* pin. */ + CS8427_CBMR | CS8427_DETCI, + /* CS8427_REG_UDATABUF: + Use internal buffer to transmit User (U) data. + Chip's U pin is an output. + Transmit all O's for user data. + Inhibit D->E transfers. + Inhibit E->F transfers. */ + CS8427_UD | CS8427_EFTUI | CS8427_DETUI, + }; + int err; + cs8427_t *chip; + snd_i2c_device_t *device; + unsigned char buf[32 + 1]; + + if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7), &device)) < 0) + return err; + chip = device->private_data = snd_magic_kcalloc(cs8427_t, 0, GFP_KERNEL); + if (chip == NULL) { + snd_i2c_device_free(device); + return -ENOMEM; + } + device->private_free = snd_cs8427_free; + + snd_i2c_lock(bus); + if ((err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER)) != CS8427_VER8427A) { + snd_i2c_unlock(bus); + snd_printk("unable to find CS8427 signature (expected 0x%x, read 0x%x), initialization is not completed\n", CS8427_VER8427A, err); + return -EFAULT; + } + /* turn off run bit while making changes to configuration */ + if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00)) < 0) + goto __fail; + /* send initial values */ + memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6); + if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) { + err = err < 0 ? err : -EREMOTE; + goto __fail; + } + /* Turn off CS8427 interrupt stuff that is not used in hardware */ + memset(buf, 0, 8); + /* from address 9 to 16 */ + buf[0] = 9; /* register */ + if ((err = snd_i2c_sendbytes(device, buf, 8)) != 8) + goto __fail; + /* send transfer initialization sequence */ + memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3); + if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) { + err = err < 0 ? err : -EREMOTE; + goto __fail; + } + /* write default channel status bytes */ + buf[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF; + buf[1] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0)); + buf[2] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8)); + buf[3] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16)); + buf[4] = swapbits((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24)); + memset(buf + 5, 0, sizeof(buf)-5); + memcpy(chip->playback.def_status, buf + 1, 24); + memcpy(chip->playback.pcm_status, buf + 1, 24); + if ((err = snd_i2c_sendbytes(device, buf, 33)) != 33) + goto __fail; + /* turn on run bit and rock'n'roll */ + chip->regmap[CS8427_REG_CLOCKSOURCE] = initvals1[4] | CS8427_RUN; + if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE])) < 0) + goto __fail; + +#if 0 // it's nice for read tests + { + char buf[128]; + buf[0] = 0x81; + snd_i2c_sendbytes(device, buf, 1); + snd_i2c_readbytes(device, buf, 127); + } +#endif + + snd_i2c_unlock(bus); + if (r_cs8427) + *r_cs8427 = device; + return 0; + + __fail: + snd_i2c_unlock(bus); + snd_i2c_device_free(device); + return err < 0 ? err : -EREMOTE; +} + +static int snd_cs8427_in_status_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_cs8427_in_status_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + int data; + + snd_i2c_lock(device->bus); + data = snd_cs8427_reg_read(device, 15); + snd_i2c_unlock(device->bus); + if (data < 0) + return data; + ucontrol->value.integer.value[0] = data; + return 0; +} + +static int snd_cs8427_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cs8427_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + + snd_i2c_lock(device->bus); + memcpy(ucontrol->value.iec958.status, chip->playback.def_status, 23); + snd_i2c_unlock(device->bus); + return 0; +} + +static int snd_cs8427_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); + cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + unsigned char *status = kcontrol->private_value ? chip->playback.pcm_status : chip->playback.def_status; + int err, change; + + snd_i2c_lock(device->bus); + change = memcmp(ucontrol->value.iec958.status, status, 23) != 0; + memcpy(status, ucontrol->value.iec958.status, 23); + if (change && (kcontrol->private_value ? chip->playback.substream != NULL : chip->playback.substream == NULL)) { + err = snd_cs8427_send_corudata(device, 0, status, 23); + if (err < 0) + change = err; + } + snd_i2c_unlock(device->bus); + return change; +} + +static int snd_cs8427_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cs8427_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, 23); + return 0; +} + +#define CONTROLS (sizeof(snd_cs8427_iec958_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs8427_iec958_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + info: snd_cs8427_in_status_info, + name: "IEC958 CS8427 Input Status", + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + get: snd_cs8427_in_status_get, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + info: snd_cs8427_spdif_mask_info, + get: snd_cs8427_spdif_mask_get, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_cs8427_spdif_info, + get: snd_cs8427_spdif_get, + put: snd_cs8427_spdif_put, + private_value: 0 +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_cs8427_spdif_info, + get: snd_cs8427_spdif_get, + put: snd_cs8427_spdif_put, + private_value: 1 +}}; + +int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427, + snd_pcm_substream_t *play_substream, + snd_pcm_substream_t *cap_substream) +{ + cs8427_t *chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + snd_kcontrol_t *kctl; + int idx, err; + + snd_assert(play_substream && cap_substream, return -EINVAL); + for (idx = 0; idx < CONTROLS; idx++) { + kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427); + if (kctl == NULL) + return -ENOMEM; + kctl->id.device = play_substream->pcm->device; + kctl->id.subdevice = play_substream->number; + err = snd_ctl_add(cs8427->bus->card, kctl); + if (err < 0) + return err; + if (!strcmp(kctl->id.name, SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM))) + chip->playback.pcm_ctl = kctl; + } + + snd_assert(chip->playback.pcm_ctl, return -EIO); + return 0; +} + +int snd_cs8427_iec958_active(snd_i2c_device_t *cs8427, int active) +{ + cs8427_t *chip; + + snd_assert(cs8427, return -ENXIO); + chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + if (active) + memcpy(chip->playback.pcm_status, chip->playback.def_status, 24); + chip->playback.pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(cs8427->bus->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->playback.pcm_ctl->id); + return 0; +} + +int snd_cs8427_iec958_pcm(snd_i2c_device_t *cs8427, unsigned int rate) +{ + cs8427_t *chip; + char *status; + int err; + + snd_assert(cs8427, return -ENXIO); + chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + status = chip->playback.pcm_status; + snd_i2c_lock(cs8427->bus); + if (status[0] & IEC958_AES0_PROFESSIONAL) { + status[0] &= ~IEC958_AES0_PRO_FS; + switch (rate) { + case 32000: status[0] |= IEC958_AES0_PRO_FS_32000; break; + case 44100: status[0] |= IEC958_AES0_PRO_FS_44100; break; + case 48000: status[0] |= IEC958_AES0_PRO_FS_48000; break; + default: status[0] |= IEC958_AES0_PRO_FS_NOTID; break; + } + } else { + status[3] &= ~IEC958_AES3_CON_FS; + switch (rate) { + case 32000: status[3] |= IEC958_AES3_CON_FS_32000; break; + case 44100: status[3] |= IEC958_AES3_CON_FS_44100; break; + case 48000: status[3] |= IEC958_AES3_CON_FS_48000; break; + } + } + err = snd_cs8427_send_corudata(cs8427, 0, status, 23); + if (err > 0) + snd_ctl_notify(cs8427->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->playback.pcm_ctl->id); + snd_i2c_unlock(cs8427->bus); + return err < 0 ? err : 0; +} + +EXPORT_SYMBOL(snd_cs8427_detect); +EXPORT_SYMBOL(snd_cs8427_create); +EXPORT_SYMBOL(snd_cs8427_iec958_build); +EXPORT_SYMBOL(snd_cs8427_iec958_active); +EXPORT_SYMBOL(snd_cs8427_iec958_pcm); diff -Nru linux/sound/i2c/i2c.c linux-2.4.19-pre5-mjc/sound/i2c/i2c.c --- linux/sound/i2c/i2c.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/i2c/i2c.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,324 @@ +/* + * Generic i2c interface for ALSA + * + * (c) 1998 Gerd Knorr + * Modified for the ALSA driver by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Generic i2c interface for ALSA"); +MODULE_LICENSE("GPL"); + +static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count); +static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr); + +static snd_i2c_ops_t snd_i2c_bit_ops = { + sendbytes: snd_i2c_bit_sendbytes, + readbytes: snd_i2c_bit_readbytes, + probeaddr: snd_i2c_bit_probeaddr, +}; + +static int snd_i2c_bus_free(snd_i2c_bus_t *bus) +{ + snd_i2c_bus_t *slave; + snd_i2c_device_t *device; + + snd_assert(bus != NULL, return -EINVAL); + while (!list_empty(&bus->devices)) { + device = snd_i2c_device(bus->devices.next); + snd_i2c_device_free(device); + } + if (bus->master) + list_del(&bus->buses); + else { + while (!list_empty(&bus->buses)) { + slave = snd_i2c_slave_bus(bus->buses.next); + snd_device_free(bus->card, slave); + } + } + if (bus->private_free) + bus->private_free(bus); + snd_magic_kfree(bus); + return 0; +} + +static int snd_i2c_bus_dev_free(snd_device_t *device) +{ + snd_i2c_bus_t *bus = snd_magic_cast(snd_i2c_bus_t, device->device_data, return -ENXIO); + return snd_i2c_bus_free(bus); +} + +int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t *master, snd_i2c_bus_t **ri2c) +{ + snd_i2c_bus_t *bus; + int err; + static snd_device_ops_t ops = { + dev_free: snd_i2c_bus_dev_free, + }; + + *ri2c = NULL; + bus = (snd_i2c_bus_t *)snd_magic_kcalloc(snd_i2c_bus_t, 0, GFP_KERNEL); + if (bus == NULL) + return -ENOMEM; + spin_lock_init(&bus->lock); + INIT_LIST_HEAD(&bus->devices); + INIT_LIST_HEAD(&bus->buses); + bus->card = card; + bus->ops = &snd_i2c_bit_ops; + if (master) { + list_add_tail(&bus->buses, &master->buses); + bus->master = master; + } + strncpy(bus->name, name, sizeof(bus->name) - 1); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &ops)) < 0) { + snd_i2c_bus_free(bus); + return err; + } + *ri2c = bus; + return 0; +} + +int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice) +{ + snd_i2c_device_t *device; + + *rdevice = NULL; + snd_assert(bus != NULL, return -EINVAL); + device = (snd_i2c_device_t *)snd_magic_kcalloc(snd_i2c_device_t, 0, GFP_KERNEL); + if (device == NULL) + return -ENOMEM; + device->addr = addr; + strncpy(device->name, name, sizeof(device->name)-1); + list_add_tail(&device->list, &bus->devices); + device->bus = bus; + *rdevice = device; + return 0; +} + +int snd_i2c_device_free(snd_i2c_device_t *device) +{ + if (device->bus) + list_del(&device->list); + if (device->private_free) + device->private_free(device); + snd_magic_kfree(device); + return 0; +} + +int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + return device->bus->ops->sendbytes(device, bytes, count); +} + + +int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + return device->bus->ops->readbytes(device, bytes, count); +} + +int snd_i2c_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + return bus->ops->probeaddr(bus, addr); +} + +/* + * bit-operations + */ + +static inline void snd_i2c_bit_hw_start(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->start) + bus->hw_ops.bit->start(bus); +} + +static inline void snd_i2c_bit_hw_stop(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->stop) + bus->hw_ops.bit->stop(bus); +} + +static void snd_i2c_bit_direction(snd_i2c_bus_t *bus, int clock, int data) +{ + if (bus->hw_ops.bit->direction) + bus->hw_ops.bit->direction(bus, clock, data); +} + +static void snd_i2c_bit_set(snd_i2c_bus_t *bus, int clock, int data) +{ + bus->hw_ops.bit->setlines(bus, clock, data); +} + +#if 0 +static int snd_i2c_bit_clock(snd_i2c_bus_t *bus) +{ + if (bus->hw_ops.bit->getclock) + return bus->hw_ops.bit->getclock(bus); + return -ENXIO; +} +#endif + +static int snd_i2c_bit_data(snd_i2c_bus_t *bus, int ack) +{ + return bus->hw_ops.bit->getdata(bus, ack); +} + +static void snd_i2c_bit_start(snd_i2c_bus_t *bus) +{ + snd_i2c_bit_hw_start(bus); + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_set(bus, 1, 0); + snd_i2c_bit_set(bus, 0, 0); +} + +static void snd_i2c_bit_stop(snd_i2c_bus_t *bus) +{ + snd_i2c_bit_set(bus, 0, 0); + snd_i2c_bit_set(bus, 1, 0); + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_hw_stop(bus); +} + +static void snd_i2c_bit_send(snd_i2c_bus_t *bus, int data) +{ + snd_i2c_bit_set(bus, 0, data); + snd_i2c_bit_set(bus, 1, data); + snd_i2c_bit_set(bus, 0, data); +} + +static int snd_i2c_bit_ack(snd_i2c_bus_t *bus) +{ + int ack; + + snd_i2c_bit_set(bus, 0, 1); + snd_i2c_bit_set(bus, 1, 1); + snd_i2c_bit_direction(bus, 1, 0); /* SCL - wr, SDA - rd */ + ack = snd_i2c_bit_data(bus, 1); + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_set(bus, 0, 1); + return ack ? -EREMOTEIO : 0; +} + +static int snd_i2c_bit_sendbyte(snd_i2c_bus_t *bus, unsigned char data) +{ + int i, err; + + for (i = 7; i >= 0; i--) + snd_i2c_bit_send(bus, !!(data & (1 << i))); + if ((err = snd_i2c_bit_ack(bus)) < 0) + return err; + return 0; +} + +static int snd_i2c_bit_readbyte(snd_i2c_bus_t *bus, int last) +{ + int i; + unsigned char data = 0; + + snd_i2c_bit_set(bus, 0, 1); + snd_i2c_bit_direction(bus, 1, 0); /* SCL - wr, SDA - rd */ + for (i = 7; i >= 0; i--) { + snd_i2c_bit_set(bus, 1, 1); + if (snd_i2c_bit_data(bus, 0)) + data |= (1 << i); + snd_i2c_bit_set(bus, 0, 1); + } + snd_i2c_bit_direction(bus, 1, 1); /* SCL - wr, SDA - wr */ + snd_i2c_bit_send(bus, !!last); + return data; +} + +static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + snd_i2c_bus_t *bus = device->bus; + int err, res = 0; + + if (device->flags & SND_I2C_DEVICE_ADDRTEN) + return -EIO; /* not yet implemented */ + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) + return err; + while (count-- > 0) { + if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) + return err; + res++; + } + snd_i2c_bit_stop(bus); + return res; +} + +static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + snd_i2c_bus_t *bus = device->bus; + int err, res = 0; + + if (device->flags & SND_I2C_DEVICE_ADDRTEN) + return -EIO; /* not yet implemented */ + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) + return err; + while (count-- > 0) { + if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) + return err; + *bytes++ = (unsigned char)err; + res++; + } + snd_i2c_bit_stop(bus); + return res; +} + +static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + int err; + + if (addr & 0x8000) /* 10-bit address */ + return -EIO; /* not yet implemented */ + if (addr & 0x7f80) /* invalid address */ + return -EINVAL; + snd_i2c_bit_start(bus); + if ((err = snd_i2c_bit_sendbyte(bus, addr << 1)) < 0) + return err; + snd_i2c_bit_stop(bus); + return 1; /* present */ +} + +EXPORT_SYMBOL(snd_i2c_bus_create); +EXPORT_SYMBOL(snd_i2c_device_create); +EXPORT_SYMBOL(snd_i2c_device_free); +EXPORT_SYMBOL(snd_i2c_sendbytes); +EXPORT_SYMBOL(snd_i2c_readbytes); +EXPORT_SYMBOL(snd_i2c_probeaddr); + +static int __init alsa_i2c_init(void) +{ + return 0; +} + +static void __exit alsa_i2c_exit(void) +{ +} + +module_init(alsa_i2c_init) +module_exit(alsa_i2c_exit) diff -Nru linux/sound/i2c/tea6330t.c linux-2.4.19-pre5-mjc/sound/i2c/tea6330t.c --- linux/sound/i2c/tea6330t.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/i2c/tea6330t.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,372 @@ +/* + * Routines for control of the TEA6330T circuit via i2c bus + * Sound fader control circuit for car radios by Philips Semiconductors + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of the TEA6330T circuit via i2c bus"); +MODULE_LICENSE("GPL"); + +#define chip_t tea6330t_t + +#define TEA6330T_ADDR (0x80>>1) /* fixed address */ + +#define TEA6330T_SADDR_VOLUME_LEFT 0x00 /* volume left */ +#define TEA6330T_SADDR_VOLUME_RIGHT 0x01 /* volume right */ +#define TEA6330T_SADDR_BASS 0x02 /* bass control */ +#define TEA6330T_SADDR_TREBLE 0x03 /* treble control */ +#define TEA6330T_SADDR_FADER 0x04 /* fader control */ +#define TEA6330T_MFN 0x20 /* mute control for selected channels */ +#define TEA6330T_FCH 0x10 /* select fader channels - front or rear */ +#define TEA6330T_SADDR_AUDIO_SWITCH 0x05 /* audio switch */ +#define TEA6330T_GMU 0x80 /* mute control, general mute */ +#define TEA6330T_EQN 0x40 /* equalizer switchover (0=equalizer-on) */ + +int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer) +{ + int res; + + snd_i2c_lock(bus); + res = snd_i2c_probeaddr(bus, TEA6330T_ADDR); + snd_i2c_unlock(bus); + return res; +} + +#if 0 +static void snd_tea6330t_set(tea6330t_t *tea, + unsigned char addr, unsigned char value) +{ +#if 0 + printk("set - 0x%x/0x%x\n", addr, value); +#endif + snd_i2c_write(tea->bus, TEA6330T_ADDR, addr, value, 1); +} +#endif + +#define TEA6330T_MASTER_VOLUME(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_master_volume, \ + get: snd_tea6330t_get_master_volume, put: snd_tea6330t_put_master_volume } + +static int snd_tea6330t_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 43; + return 0; +} + +static int snd_tea6330t_get_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + snd_i2c_lock(tea->bus); + ucontrol->value.integer.value[0] = tea->mleft - 0x14; + ucontrol->value.integer.value[1] = tea->mright - 0x14; + snd_i2c_unlock(tea->bus); + return 0; +} + +static int snd_tea6330t_put_master_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, count, err; + unsigned char bytes[3]; + unsigned char val1, val2; + + val1 = (ucontrol->value.integer.value[0] % 44) + 0x14; + val2 = (ucontrol->value.integer.value[1] % 44) + 0x14; + snd_i2c_lock(tea->bus); + change = val1 != tea->mleft || val2 != tea->mright; + tea->mleft = val1; + tea->mright = val2; + count = 0; + if (tea->regs[TEA6330T_SADDR_VOLUME_LEFT] != 0) { + bytes[count++] = TEA6330T_SADDR_VOLUME_LEFT; + bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = tea->mleft; + } + if (tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] != 0) { + if (count == 0) + bytes[count++] = TEA6330T_SADDR_VOLUME_RIGHT; + bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = tea->mright; + } + if (count > 0) { + if ((err = snd_i2c_sendbytes(tea->device, bytes, count)) < 0) + change = err; + } + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_MASTER_SWITCH(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_master_switch, \ + get: snd_tea6330t_get_master_switch, put: snd_tea6330t_put_master_switch } + +static int snd_tea6330t_info_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_tea6330t_get_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + snd_i2c_lock(tea->bus); + ucontrol->value.integer.value[0] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1; + ucontrol->value.integer.value[1] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1; + snd_i2c_unlock(tea->bus); + return 0; +} + +static int snd_tea6330t_put_master_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[3]; + unsigned char oval1, oval2, val1, val2; + + val1 = ucontrol->value.integer.value[0] & 1; + val2 = ucontrol->value.integer.value[1] & 1; + snd_i2c_lock(tea->bus); + oval1 = tea->regs[TEA6330T_SADDR_VOLUME_LEFT] == 0 ? 0 : 1; + oval2 = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] == 0 ? 0 : 1; + change = val1 != oval1 || val2 != oval2; + tea->regs[TEA6330T_SADDR_VOLUME_LEFT] = val1 ? tea->mleft : 0; + tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = val2 ? tea->mright : 0; + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + bytes[1] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT]; + bytes[2] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT]; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 3)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_BASS(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_bass, \ + get: snd_tea6330t_get_bass, put: snd_tea6330t_put_bass } + +static int snd_tea6330t_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tea->max_bass; + return 0; +} + +static int snd_tea6330t_get_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tea->bass; + return 0; +} + +static int snd_tea6330t_put_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[2]; + unsigned char val1; + + val1 = ucontrol->value.integer.value[0] % (tea->max_bass + 1); + snd_i2c_lock(tea->bus); + tea->bass = val1; + val1 += tea->equalizer ? 7 : 3; + change = tea->regs[TEA6330T_SADDR_BASS] != val1; + bytes[0] = TEA6330T_SADDR_BASS; + bytes[1] = tea->regs[TEA6330T_SADDR_BASS] = val1; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_TREBLE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_tea6330t_info_treble, \ + get: snd_tea6330t_get_treble, put: snd_tea6330t_put_treble } + +static int snd_tea6330t_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tea->max_treble; + return 0; +} + +static int snd_tea6330t_get_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tea->treble; + return 0; +} + +static int snd_tea6330t_put_treble(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + tea6330t_t *tea = snd_kcontrol_chip(kcontrol); + int change, err; + unsigned char bytes[2]; + unsigned char val1; + + val1 = ucontrol->value.integer.value[0] % (tea->max_treble + 1); + snd_i2c_lock(tea->bus); + tea->treble = val1; + val1 += 3; + change = tea->regs[TEA6330T_SADDR_TREBLE] != val1; + bytes[0] = TEA6330T_SADDR_TREBLE; + bytes[1] = tea->regs[TEA6330T_SADDR_TREBLE] = val1; + if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0) + change = err; + snd_i2c_unlock(tea->bus); + return change; +} + +#define TEA6330T_CONTROLS (sizeof(snd_tea6330t_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_tea6330t_controls[] = { +TEA6330T_MASTER_SWITCH("Master Playback Switch", 0), +TEA6330T_MASTER_VOLUME("Master Playback Volume", 0), +TEA6330T_BASS("Tone Control - Bass", 0), +TEA6330T_TREBLE("Tone Control - Treble", 0) +}; + +static void snd_tea6330_free(snd_i2c_device_t *device) +{ + tea6330t_t *tea = snd_magic_cast(tea6330t_t, device->private_data, return); + snd_magic_kfree(tea); +} + +int snd_tea6330t_update_mixer(snd_card_t * card, + snd_i2c_bus_t *bus, + int equalizer, int fader) +{ + snd_i2c_device_t *device; + tea6330t_t *tea; + snd_kcontrol_new_t *knew; + int idx, err = -ENOMEM; + u8 default_treble, default_bass; + unsigned char bytes[7]; + + tea = snd_magic_kcalloc(tea6330t_t, 0, GFP_KERNEL); + if (tea == NULL) + return -ENOMEM; + if ((err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device)) < 0) { + snd_magic_kfree(tea); + return err; + } + tea->device = device; + tea->bus = bus; + tea->equalizer = equalizer; + tea->fader = fader; + device->private_data = tea; + device->private_free = snd_tea6330_free; + + snd_i2c_lock(bus); + + /* turn fader off and handle equalizer */ + tea->regs[TEA6330T_SADDR_FADER] = 0x3f; + tea->regs[TEA6330T_SADDR_AUDIO_SWITCH] = equalizer ? 0 : TEA6330T_EQN; + /* initialize mixer */ + if (!tea->equalizer) { + tea->max_bass = 9; + tea->max_treble = 8; + default_bass = 3 + 4; + tea->bass = 4; + default_treble = 3 + 4; + tea->treble = 4; + } else { + tea->max_bass = 5; + tea->max_treble = 0; + default_bass = 7 + 4; + tea->bass = 4; + default_treble = 3; + tea->treble = 0; + } + tea->mleft = tea->mright = 0x14; + tea->regs[TEA6330T_SADDR_BASS] = default_bass; + tea->regs[TEA6330T_SADDR_TREBLE] = default_treble; + + /* compose I2C message and put the hardware to initial state */ + bytes[0] = TEA6330T_SADDR_VOLUME_LEFT; + for (idx = 0; idx < 6; idx++) + bytes[idx+1] = tea->regs[idx]; + if ((err = snd_i2c_sendbytes(device, bytes, 7)) < 0) + goto __error; + + strcat(card->mixername, ",TEA6330T"); + if ((err = snd_component_add(card, "TEA6330T")) < 0) + goto __error; + + for (idx = 0; idx < TEA6330T_CONTROLS; idx++) { + knew = &snd_tea6330t_controls[idx]; + if (tea->treble == 0 && !strcmp(knew->name, "Tone Control - Treble")) + continue; + if ((err = snd_ctl_add(card, snd_ctl_new1(knew, tea))) < 0) + goto __error; + } + + snd_i2c_unlock(bus); + return 0; + + __error: + snd_i2c_unlock(bus); + snd_i2c_device_free(device); + return err; +} + +EXPORT_SYMBOL(snd_tea6330t_detect); +EXPORT_SYMBOL(snd_tea6330t_update_mixer); + +/* + * INIT part + */ + +static int __init alsa_tea6330t_init(void) +{ + return 0; +} + +static void __exit alsa_tea6330t_exit(void) +{ +} + +module_init(alsa_tea6330t_init) +module_exit(alsa_tea6330t_exit) diff -Nru linux/sound/isa/Config.help linux-2.4.19-pre5-mjc/sound/isa/Config.help --- linux/sound/isa/Config.help Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/Config.help Mon Apr 8 22:31:23 2002 @@ -0,0 +1,99 @@ +CONFIG_SND_AD1816A + Say 'Y' or 'M' to include support for Analog Devices SoundPort AD1816A or + compatible sound chips. + +CONFIG_SND_AD1848 + Say 'Y' or 'M' to include support for AD1848 (Analog Devices) or CS4248 + (Cirrus Logic - Crystal Semiconductors) chips. Please, for newer chips + from Cirrus Logic, use CS4231, CS4232 or CS4236+ driver. + +CONFIG_SND_CS4231 + Say 'Y' or 'M' to include support for CS4231 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4232 + Say 'Y' or 'M' to include support for CS4232 chips from Cirrus Logic - + Crystal Semiconductors. + +CONFIG_SND_CS4236 + Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239 + chips from Cirrus Logic - Crystal Semiconductors. + +CONFIG_SND_ES968 + Say 'Y' or 'M' to include support for ESS AudioDrive ES968 chip. + +CONFIG_SND_ES1688 + Say 'Y' or 'M' to include support for ESS AudioDrive ES688 or ES1688 chips. + +CONFIG_SND_ES18XX + Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips. + +CONFIG_SND_GUSCLASSIC + Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard. + +CONFIG_SND_GUSEXTREME + Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard. + +CONFIG_SND_GUSMAX + Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard. + +CONFIG_SND_INTERWAVE + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro, + Panasonic PCA761AW). + +CONFIG_SND_INTERWAVE_STB + Say 'Y' or 'M' to include support for AMD InterWave based soundcards + with TEA6330T bass and treble regulator (UltraSound 32-Pro). + +CONFIG_SND_OPTI92X_AD1848 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + AD1848 codec. + +CONFIG_SND_OPTI92X_CS4231 + Say 'Y' or 'M' to include support for Opti92x soundcards equiped with + CS4231 codec. + +CONFIG_SND_OPTI93X + Say 'Y' or 'M' to include support for Opti93x soundcards. + +CONFIG_SND_SB8 + Say 'Y' or 'M' to include support for Sound Blaster 1.0/2.0/Pro (8-bit) + soundcards or 100% compatible from Creative. + +CONFIG_SND_SB16 + Say 'Y' or 'M' to include support for Sound Blaster 16 (including + Plug and Play version). + +CONFIG_SND_SBAWE + Say 'Y' or 'M' to include support for Sound Blaster AWE (including + Plug and Play version). + +CONFIG_SND_SB16_CSP + Say 'Y' to include support for CSP core. This special coprocessor + can do variable tasks like various compression and decompression + algorithms. + +CONFIG_SND_WAVEFRONT + Say 'Y' or 'M' to include support for Turtle Beach Maui, Tropez + and Tropez+ soundcards based on Wavefront chip. + +CONFIG_SND_ALS100 + Say 'Y' or 'M' to include support for Avance Logic ALS100, ALS110, + ALS120 and ALS200 soundcards. + +CONFIG_SND_AZT2320 + Say 'Y' or 'M' to include support for Aztech Systems AZT2320 soundcard. + +CONFIG_SND_CMI8330 + Say 'Y' or 'M' to include support for C-Media CMI8330 based soundcards. + +CONFIG_SND_DT0197H + Say 'Y' or 'M' to include support for Diamond Technologies DT-0197H + soundcards. + +CONFIG_SND_OPL3SA2 + Say 'Y' or 'M' to include support for Yamaha OPL3SA2 or OPL3SA3 chips. + +CONFIG_SND_SGALAXY + Say 'Y' or 'M' to include support for Aztech Sound Galaxy. diff -Nru linux/sound/isa/Config.in linux-2.4.19-pre5-mjc/sound/isa/Config.in --- linux/sound/isa/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/Config.in Mon Apr 8 22:31:23 2002 @@ -0,0 +1,36 @@ +# ALSA ISA drivers + +mainmenu_option next_comment +comment 'ISA devices' + +dep_tristate 'Analog Devices SoundPort AD1816A' CONFIG_SND_AD1816A $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Generic AD1848/CS4248 driver' CONFIG_SND_AD1848 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4231 driver' CONFIG_SND_CS4231 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4232 driver' CONFIG_SND_CS4232 $CONFIG_SND +dep_tristate 'Generic Cirrus Logic CS4236+ driver' CONFIG_SND_CS4236 $CONFIG_SND +dep_tristate 'Generic ESS ES968 driver' CONFIG_SND_ES968 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Generic ESS ES688/ES1688 driver' CONFIG_SND_ES1688 $CONFIG_SND +dep_tristate 'Generic ESS ES18xx driver' CONFIG_SND_ES18XX $CONFIG_SND +dep_tristate 'Gravis UltraSound Classic' CONFIG_SND_GUSCLASSIC $CONFIG_SND +dep_tristate 'Gravis UltraSound Extreme' CONFIG_SND_GUSEXTREME $CONFIG_SND +dep_tristate 'Gravis UltraSound MAX' CONFIG_SND_GUSMAX $CONFIG_SND +dep_tristate 'AMD InterWave, Gravis UltraSound PnP' CONFIG_SND_INTERWAVE $CONFIG_SND +dep_tristate 'AMD InterWave + TEA6330T (UltraSound 32-Pro)' CONFIG_SND_INTERWAVE_STB $CONFIG_SND +dep_tristate 'OPTi 82C92x - AD1848' CONFIG_SND_OPTI92X_AD1848 $CONFIG_SND +dep_tristate 'OPTi 82C92x - CS4231' CONFIG_SND_OPTI92X_CS4231 $CONFIG_SND +dep_tristate 'OPTi 82C93x' CONFIG_SND_OPTI93X $CONFIG_SND +dep_tristate 'Sound Blaster 1.0/2.0/Pro (8-bit)' CONFIG_SND_SB8 $CONFIG_SND +dep_tristate 'Sound Blaster 16 (PnP)' CONFIG_SND_SB16 $CONFIG_SND +dep_tristate 'Sound Blaster AWE (32,64) (PnP)' CONFIG_SND_SBAWE $CONFIG_SND +if [ "$CONFIG_SND_SB16" != "n" -o "$CONFIG_SND_SBAWE" != "n" ]; then + bool ' Sound Blaster 16/AWE CSP support' CONFIG_SND_SB16_CSP +fi +dep_tristate 'Turtle Beach Maui,Tropez,Tropez+ (Wavefront)' CONFIG_SND_WAVEFRONT $CONFIG_SND +dep_tristate 'Avance Logic ALS100/ALS120' CONFIG_SND_ALS100 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Aztech Systems AZT2320' CONFIG_SND_AZT2320 $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'C-Media CMI8330' CONFIG_SND_CMI8330 $CONFIG_SND +dep_tristate 'Diamond Technologies DT-0197H' CONFIG_SND_DT0197H $CONFIG_SND $CONFIG_ISAPNP +dep_tristate 'Yamaha OPL3-SA2/SA3' CONFIG_SND_OPL3SA2 $CONFIG_SND +dep_tristate 'Aztech Sound Galaxy' CONFIG_SND_SGALAXY $CONFIG_SND + +endmenu diff -Nru linux/sound/isa/Makefile linux-2.4.19-pre5-mjc/sound/isa/Makefile --- linux/sound/isa/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,52 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := isa.o + +subdir-y := ad1816a ad1848 cs423x es1688 gus opti9xx sb wavefront +subdir-m := $(subdir-y) + +list-multi := snd-als100.o snd-azt2320.o snd-cmi8330.o snd-dt0197h.o \ + snd-es18xx.o snd-opl3sa2.o snd-sgalaxy.o + +snd-als100-objs := als100.o +snd-azt2320-objs := azt2320.o +snd-cmi8330-objs := cmi8330.o +snd-dt0197h-objs := dt0197h.o +snd-es18xx-objs := es18xx.o +snd-opl3sa2-objs := opl3sa2.o +snd-sgalaxy-objs := sgalaxy.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-als100.o +obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o +obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o +obj-$(CONFIG_SND_DT0197H) += snd-dt0197h.o +obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o +obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o +obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o + +include $(TOPDIR)/Rules.make + +snd-als100.o: $(snd-als100-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-als100-objs) + +snd-azt2320.o: $(snd-azt2320-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-azt2320-objs) + +snd-cmi8330.o: $(snd-cmi8330-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cmi8330-objs) + +snd-dt0197h.o: $(snd-dt0197h-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-dt0197h-objs) + +snd-es18xx.o: $(snd-es18xx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es18xx-objs) + +snd-opl3sa2.o: $(snd-opl3sa2-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opl3sa2-objs) + +snd-sgalaxy.o: $(snd-sgalaxy-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sgalaxy-objs) diff -Nru linux/sound/isa/ad1816a/Makefile linux-2.4.19-pre5-mjc/sound/isa/ad1816a/Makefile --- linux/sound/isa/ad1816a/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/ad1816a/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,24 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ad1816a.o + +list-multi := snd-ad1816a-lib.o snd-ad1816a.o + +export-objs := ad1816a_lib.o + +snd-ad1816a-lib-objs := ad1816a_lib.o +snd-ad1816a-objs := ad1816a.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o snd-ad1816a-lib.o + +include $(TOPDIR)/Rules.make + +snd-ad1816a-lib.o: $(snd-ad1816a-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-lib-objs) + +snd-ad1816a.o: $(snd-ad1816a-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1816a-objs) diff -Nru linux/sound/isa/ad1816a/ad1816a.c linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a.c --- linux/sound/isa/ad1816a/ad1816a.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,399 @@ + +/* + card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards. + Copyright (C) 2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t ad1816a_t + +#define PFX "ad1816a: " + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("AD1816A, AD1815"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Highscreen,Sound-Boostar 16 3D}," + "{Analog Devices,AD1815}," + "{Analog Devices,AD1816A}," + "{TerraTec,Base 64}," + "{TerraTec,AudioSystem EWS64S}," + "{Aztech/Newcom SC-16 3D}," + "{Shark Predator ISA}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ad1816a based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ad1816a based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ad1816a based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "1st DMA # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "2nd DMA # for ad1816a driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +struct snd_card_ad1816a { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_ad1816a_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_ad1816a_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_ad1816a_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_AD1816A(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \ + ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \ + } + +static struct isapnp_card_id snd_ad1816a_pnpids[] __devinitdata = { + /* Highscreen Sound-Boostar 16 3D */ + ISAPNP_AD1816A('M','D','K',0x1605,'A','D','S',0x7180,0x7181), + /* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */ + ISAPNP_AD1816A('L','W','C',0x1061,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1815 */ + ISAPNP_AD1816A('A','D','S',0x7150,'A','D','S',0x7150,0x7151), + /* Analog Devices AD1816A - added by Kenneth Platz */ + ISAPNP_AD1816A('A','D','S',0x7181,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Terratec Base 64 */ + ISAPNP_AD1816A('T','E','R',0x1411,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Terratec AudioSystem EWS64S */ + ISAPNP_AD1816A('T','E','R',0x1112,'A','D','S',0x7180,0x7181), + /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */ + ISAPNP_AD1816A('A','Z','T',0x1022,'A','Z','T',0x1018,0x2002), + /* Shark Predator ISA - added by Ken Arromdee */ + ISAPNP_AD1816A('S','M','M',0x7180,'A','D','S',0x7180,0x7181), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_ad1816a_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-ad1816a" + + +#ifdef __ISAPNP__ +static int __init snd_card_ad1816a_isapnp(int dev, + struct snd_card_ad1816a *acard) +{ + const struct isapnp_card_id *id = snd_ad1816a_isapnp_id[dev]; + struct isapnp_card *card = snd_ad1816a_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_port[dev], 16); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], + 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev) < 0) { + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[2].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + return 0; + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev) < 0) { + /* not fatal error */ + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + return 0; +} + +static void snd_card_ad1816a_deactivate(struct snd_card_ad1816a *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_ad1816a_free(snd_card_t *card) +{ + struct snd_card_ad1816a *acard = (struct snd_card_ad1816a *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_ad1816a_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_ad1816a_probe(int dev) +{ + int error; + snd_card_t *card; + struct snd_card_ad1816a *acard; + ad1816a_t *chip; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_ad1816a))) == NULL) + return -ENOMEM; + acard = (struct snd_card_ad1816a *)card->private_data; + card->private_free = snd_card_ad1816a_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_ad1816a_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_ad1816a_create(card, snd_port[dev], + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_ad1816a_mixer(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "AD1816A"); + strcpy(card->shortname, "ADI SoundPort AD1816A"); + sprintf(card->longname, "%s soundcard, SS at 0x%lx, irq %d, dma %d&%d", + card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_ad1816a_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_ad1816a_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_ad1816a_isapnp_cards[dev] = card; + snd_ad1816a_isapnp_id[dev] = id; + res = snd_card_ad1816a_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_ad1816a_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_ad1816a_pnpids, snd_ad1816a_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no AD1816A based soundcards found.\n"); +#endif /* MODULE */ + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_ad1816a_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_ad1816a_cards[dev]); +} + +module_init(alsa_card_ad1816a_init) +module_exit(alsa_card_ad1816a_exit) + +#ifndef MODULE + +/* format is: snd-ad1816a=snd_enable,snd_index,snd_id,snd_port, + snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq, + snd_dma1,snd_dma2 */ + +static int __init alsa_card_ad1816a_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ad1816a=", alsa_card_ad1816a_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/ad1816a/ad1816a_lib.c linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a_lib.c --- linux/sound/isa/ad1816a/ad1816a_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/ad1816a/ad1816a_lib.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,965 @@ + +/* + ad1816a.c - lowlevel code for Analog Devices AD1816A chip. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip"); +MODULE_LICENSE("GPL"); + +#define chip_t ad1816a_t + +static inline int snd_ad1816a_busy_wait(ad1816a_t *chip) +{ + int timeout; + + for (timeout = 1000; timeout-- > 0; udelay(10)) + if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY) + return 0; + + snd_printk("chip busy.\n"); + return -EBUSY; +} + +inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg) +{ + snd_ad1816a_busy_wait(chip); + return inb(AD1816A_REG(reg)); +} + +inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg, + unsigned char value) +{ + snd_ad1816a_busy_wait(chip); + outb(value, AD1816A_REG(reg)); +} + +inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + snd_ad1816a_out(chip, reg, + (value & mask) | (snd_ad1816a_in(chip, reg) & ~mask)); +} + +static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg) +{ + snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); + return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) | + (snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8); +} + +static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg, + unsigned short value) +{ + snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); + snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff); + snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff); +} + +static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg, + unsigned short mask, unsigned short value) +{ + snd_ad1816a_write(chip, reg, + (value & mask) | (snd_ad1816a_read(chip, reg) & ~mask)); +} + + +static unsigned char snd_ad1816a_get_format(ad1816a_t *chip, + unsigned int format, int channels) +{ + unsigned char retval = AD1816A_FMT_LINEAR_8; + + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + retval = AD1816A_FMT_ULAW_8; + break; + case SNDRV_PCM_FORMAT_A_LAW: + retval = AD1816A_FMT_ALAW_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + retval = AD1816A_FMT_LINEAR_16_LIT; + break; + case SNDRV_PCM_FORMAT_S16_BE: + retval = AD1816A_FMT_LINEAR_16_BIG; + } + return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval; +} + +static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (chip->mode & mode) { + spin_unlock_irqrestore(&chip->lock, flags); + return -EAGAIN; + } + + switch ((mode &= AD1816A_MODE_OPEN)) { + case AD1816A_MODE_PLAYBACK: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_PLAYBACK_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff); + break; + case AD1816A_MODE_CAPTURE: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_CAPTURE_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_CAPTURE_IRQ_ENABLE, 0xffff); + break; + case AD1816A_MODE_TIMER: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_TIMER_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_IRQ_ENABLE, 0xffff); + } + chip->mode |= mode; + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + switch ((mode &= AD1816A_MODE_OPEN)) { + case AD1816A_MODE_PLAYBACK: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_PLAYBACK_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000); + break; + case AD1816A_MODE_CAPTURE: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_CAPTURE_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_CAPTURE_IRQ_ENABLE, 0x0000); + break; + case AD1816A_MODE_TIMER: + snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, + AD1816A_TIMER_IRQ_PENDING, 0x00); + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_IRQ_ENABLE, 0x0000); + } + if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN)) + chip->mode = 0; + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what, + int channel, int cmd) +{ + int error = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&chip->lock); + cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00; + if (what & AD1816A_PLAYBACK_ENABLE) + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE, cmd); + if (what & AD1816A_CAPTURE_ENABLE) + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE, cmd); + spin_unlock(&chip->lock); + break; + default: + snd_printk("invalid trigger mode 0x%x.\n", what); + error = -EINVAL; + } + + return error; +} + +static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE, + SNDRV_PCM_STREAM_PLAYBACK, cmd); +} + +static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE, + SNDRV_PCM_STREAM_CAPTURE, cmd); +} + +static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size; + + spin_lock_irqsave(&chip->lock, flags); + + chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); + + snd_dma_program(chip->dma1, runtime->dma_addr, size, + DMA_MODE_WRITE | DMA_AUTOINIT); + + snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_FMT_ALL | AD1816A_FMT_STEREO, + snd_ad1816a_get_format(chip, runtime->format, + runtime->channels)); + + snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT, + snd_pcm_lib_period_bytes(substream) / 4 - 1); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size; + + spin_lock_irqsave(&chip->lock, flags); + + chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); + + snd_dma_program(chip->dma2, runtime->dma_addr, size, + DMA_MODE_READ | DMA_AUTOINIT); + + snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_FMT_ALL | AD1816A_FMT_STEREO, + snd_ad1816a_get_format(chip, runtime->format, + runtime->channels)); + + snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT, + snd_pcm_lib_period_bytes(substream) / 4 - 1); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + + +static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(chip->mode & AD1816A_MODE_PLAYBACK)) + return 0; + ptr = chip->p_dma_size - snd_dma_residue(chip->dma1); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(chip->mode & AD1816A_MODE_CAPTURE)) + return 0; + ptr = chip->c_dma_size - snd_dma_residue(chip->dma2); + return bytes_to_frames(substream->runtime, ptr); +} + + +static void snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, dev_id, return); + unsigned char status; + + spin_lock(&chip->lock); + status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS); + spin_unlock(&chip->lock); + + if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream) + snd_pcm_period_elapsed(chip->playback_substream); + + if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream) + snd_pcm_period_elapsed(chip->capture_substream); + + if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + + spin_lock(&chip->lock); + snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); + spin_unlock(&chip->lock); +} + + +static snd_pcm_hardware_t snd_ad1816a_playback = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 55200, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ad1816a_capture = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 55200, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ad1816a_timer_close(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_timer_chip(timer); + snd_ad1816a_close(chip, AD1816A_MODE_TIMER); + return 0; +} + +static int snd_ad1816a_timer_open(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_timer_chip(timer); + snd_ad1816a_open(chip, AD1816A_MODE_TIMER); + return 0; +} + +static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer) +{ + snd_assert(timer != NULL, return 0); + + return 10000; +} + +static int snd_ad1816a_timer_start(snd_timer_t *timer) +{ + unsigned short bits; + unsigned long flags; + ad1816a_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->lock, flags); + bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE); + + if (!(bits & AD1816A_TIMER_ENABLE)) { + snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT, + timer->sticks & 0xffff); + + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_ENABLE, 0xffff); + } + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_timer_stop(snd_timer_t *timer) +{ + unsigned long flags; + ad1816a_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->lock, flags); + + snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, + AD1816A_TIMER_ENABLE, 0x0000); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static struct _snd_timer_hardware snd_ad1816a_timer_table = { + flags: SNDRV_TIMER_HW_AUTO, + resolution: 10000, + ticks: 65535, + open: snd_ad1816a_timer_open, + close: snd_ad1816a_timer_close, + c_resolution: snd_ad1816a_timer_resolution, + start: snd_ad1816a_timer_start, + stop: snd_ad1816a_timer_stop, +}; + + +static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int error; + + if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0) + return error; + snd_pcm_set_sync(substream); + runtime->hw = snd_ad1816a_playback; + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + chip->playback_substream = substream; + return 0; +} + +static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int error; + + if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0) + return error; + snd_pcm_set_sync(substream); + runtime->hw = snd_ad1816a_capture; + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + chip->capture_substream = substream; + return 0; +} + +static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK); + return 0; +} + +static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream) +{ + ad1816a_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE); + return 0; +} + + +static void snd_ad1816a_init(ad1816a_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); + snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, + AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); + snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, + AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); + snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000); + snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG, + AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff); + snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000); + snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000); + + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_ad1816a_probe(ad1816a_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) { + case 0: + chip->hardware = AD1816A_HW_AD1815; + break; + case 1: + chip->hardware = AD1816A_HW_AD18MAX10; + break; + case 3: + chip->hardware = AD1816A_HW_AD1816A; + break; + default: + chip->hardware = AD1816A_HW_AUTO; + } + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_ad1816a_free(ad1816a_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma1 >= 0) { + snd_dma_disable(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + snd_dma_disable(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_ad1816a_dev_free(snd_device_t *device) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, device->device_data, return -ENXIO); + return snd_ad1816a_free(chip); +} + +static const char *snd_ad1816a_chip_id(ad1816a_t *chip) +{ + switch (chip->hardware) { + case AD1816A_HW_AD1816A: return "AD1816A"; + case AD1816A_HW_AD1815: return "AD1815"; + case AD1816A_HW_AD18MAX10: return "AD18max10"; + default: + snd_printk("Unknown chip version %d:%d.\n", + chip->version, chip->hardware); + return "AD1816A - unknown"; + } +} + +int snd_ad1816a_create(snd_card_t *card, + unsigned long port, int irq, int dma1, int dma2, + ad1816a_t **rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_ad1816a_dev_free, + }; + int error; + ad1816a_t *chip; + + *rchip = NULL; + + chip = snd_magic_kcalloc(ad1816a_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + + if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) { + snd_ad1816a_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma1, "AD1816A - 1")) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->dma1 = dma1; + if (request_dma(dma2, "AD1816A - 2")) { + snd_ad1816a_free(chip); + return -EBUSY; + } + chip->dma2 = dma2; + + chip->card = card; + chip->port = port; + spin_lock_init(&chip->lock); + + if ((error = snd_ad1816a_probe(chip))) { + snd_ad1816a_free(chip); + return error; + } + + snd_ad1816a_init(chip); + + /* Register device */ + if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ad1816a_free(chip); + return error; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_ad1816a_playback_ops = { + open: snd_ad1816a_playback_open, + close: snd_ad1816a_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ad1816a_hw_params, + hw_free: snd_ad1816a_hw_free, + prepare: snd_ad1816a_playback_prepare, + trigger: snd_ad1816a_playback_trigger, + pointer: snd_ad1816a_playback_pointer, +}; + +static snd_pcm_ops_t snd_ad1816a_capture_ops = { + open: snd_ad1816a_capture_open, + close: snd_ad1816a_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ad1816a_hw_params, + hw_free: snd_ad1816a_hw_free, + prepare: snd_ad1816a_capture_prepare, + trigger: snd_ad1816a_capture_trigger, + pointer: snd_ad1816a_capture_pointer, +}; + +static void snd_ad1816a_pcm_free(snd_pcm_t *pcm) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm) +{ + int error; + snd_pcm_t *pcm; + + if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm))) + return error; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_ad1816a_pcm_free; + pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; + + strcpy(pcm->name, snd_ad1816a_chip_id(chip)); + snd_ad1816a_init(chip); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_ad1816a_timer_free(snd_timer_t *timer) +{ + ad1816a_t *chip = snd_magic_cast(ad1816a_t, timer->private_data, return); + chip->timer = NULL; +} + +int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + int error; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = device; + tid.subdevice = 0; + if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0) + return error; + strcpy(timer->name, snd_ad1816a_chip_id(chip)); + timer->private_data = chip; + timer->private_free = snd_ad1816a_timer_free; + chip->timer = timer; + timer->hw = snd_ad1816a_timer_table; + if (rtimer) + *rtimer = timer; + return 0; +} + +/* + * + */ + +static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Line", "Mix", "CD", "Synth", "Video", + "Mic", "Phone", + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 7; + if (uinfo->value.enumerated.item > 6) + uinfo->value.enumerated.item = 6; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL); + spin_unlock_irqrestore(&chip->lock, flags); + ucontrol->value.enumerated.item[0] = (val >> 12) & 7; + ucontrol->value.enumerated.item[1] = (val >> 4) & 7; + return 0; +} + +static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short val; + int change; + + if (ucontrol->value.enumerated.item[0] > 6 || + ucontrol->value.enumerated.item[1] > 6) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] << 12) | + (ucontrol->value.enumerated.item[1] << 4); + spin_lock_irqsave(&chip->lock, flags); + change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val; + snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_single, \ + get: snd_ad1816a_get_single, put: snd_ad1816a_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short old_val, val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->lock, flags); + old_val = snd_ad1816a_read(chip, reg); + val = (old_val & ~(mask << shift)) | val; + change = val != old_val; + snd_ad1816a_write(chip, reg, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_double, \ + get: snd_ad1816a_get_double, put: snd_ad1816a_put_double, \ + private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_ad1816a_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift_left) & mask; + ucontrol->value.integer.value[1] = (val >> shift_right) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1816a_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short old_val, val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->lock, flags); + old_val = snd_ad1816a_read(chip, reg); + val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != old_val; + snd_ad1816a_write(chip, reg, val1); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define AD1816A_CONTROLS (sizeof(snd_ad1816a_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ad1816a_controls[] = { +AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), +AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), +AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), +AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), +AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 63, 1), +AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), +AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), +AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), +AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), +AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), +AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), +AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_ad1816a_info_mux, + get: snd_ad1816a_get_mux, + put: snd_ad1816a_put_mux, +}, +AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), +AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), +AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), +AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), +}; + +int snd_ad1816a_mixer(ad1816a_t *chip) +{ + snd_card_t *card; + int err, idx; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_ad1816a_chip_id(chip)); + + for (idx = 0; idx < AD1816A_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_ad1816a_create); +EXPORT_SYMBOL(snd_ad1816a_pcm); +EXPORT_SYMBOL(snd_ad1816a_mixer); + +static int __init alsa_ad1816a_init(void) +{ + return 0; +} + +static void __exit alsa_ad1816a_exit(void) +{ +} + +module_init(alsa_ad1816a_init) +module_exit(alsa_ad1816a_exit) + diff -Nru linux/sound/isa/ad1848/Makefile linux-2.4.19-pre5-mjc/sound/isa/ad1848/Makefile --- linux/sound/isa/ad1848/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/ad1848/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ad1848.o + +list-multi := snd-ad1848-lib.o snd-ad1848.o + +export-objs := ad1848_lib.o + +snd-ad1848-lib-objs := ad1848_lib.o +snd-ad1848-objs := ad1848.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CMI8330) += snd-ad1848-lib.o +obj-$(CONFIG_SND_SGALAXY) += snd-ad1848-lib.o +obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o + +include $(TOPDIR)/Rules.make + +snd-ad1848-lib.o: $(snd-ad1848-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-lib-objs) + +snd-ad1848.o: $(snd-ad1848-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ad1848-objs) diff -Nru linux/sound/isa/ad1848/ad1848.c linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848.c --- linux/sound/isa/ad1848/ad1848.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,179 @@ +/* + * Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha) + * Copyright (c) by Tugrul Galatali , + * Jaroslav Kysela + * Based on card-4232.c by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t ad1848_t + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Tugrul Galatali , Jaroslav Kysela "); +MODULE_DESCRIPTION("AD1848/AD1847/CS4248"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Analog Devices,AD1848}," + "{Analog Devices,AD1847}," + "{Crystal Semiconductors,CS4248}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for AD1848 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for AD1848 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable AD1848 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for AD1848 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for AD1848 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for AD1848 driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); + +static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_ad1848_probe(int dev) +{ + snd_card_t *card; + ad1848_t *chip; + snd_pcm_t *pcm; + int err; + + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify snd_irq\n"); + return -EINVAL; + } + if (snd_dma1[dev] == SNDRV_AUTO_DMA) { + snd_printk("specify snd_dma1\n"); + return -EINVAL; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ad1848_create(card, snd_port[dev], + snd_irq[dev], + snd_dma1[dev], + AD1848_HW_DETECT, + &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ad1848_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + strcpy(card->driver, "AD1848"); + strcpy(card->shortname, pcm->name); + + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_ad1848_cards[dev] = card; + return 0; +} + +static int __init alsa_card_ad1848_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) + if (snd_card_ad1848_probe(dev) >= 0) + cards++; + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "AD1848 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_ad1848_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_ad1848_cards[idx]); +} + +module_init(alsa_card_ad1848_init) +module_exit(alsa_card_ad1848_exit) + +#ifndef MODULE + +/* format is: snd-ad1848=snd_enable,snd_index,snd_id,snd_port, + snd_irq,snd_dma1 */ + +static int __init alsa_card_ad1848_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ad1848=", alsa_card_ad1848_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/ad1848/ad1848_lib.c linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848_lib.c --- linux/sound/isa/ad1848/ad1848_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/ad1848/ad1848_lib.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1174 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of AD1848/AD1847/CS4248 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MAIN_OBJECT_FILE +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248"); +MODULE_LICENSE("GPL"); + +#define chip_t ad1848_t + +#if 0 +#define SNDRV_DEBUG_MCE +#endif + +/* + * Some variables + */ + +static unsigned char freq_bits[14] = { + /* 5510 */ 0x00 | AD1848_XTAL2, + /* 6620 */ 0x0E | AD1848_XTAL2, + /* 8000 */ 0x00 | AD1848_XTAL1, + /* 9600 */ 0x0E | AD1848_XTAL1, + /* 11025 */ 0x02 | AD1848_XTAL2, + /* 16000 */ 0x02 | AD1848_XTAL1, + /* 18900 */ 0x04 | AD1848_XTAL2, + /* 22050 */ 0x06 | AD1848_XTAL2, + /* 27042 */ 0x04 | AD1848_XTAL1, + /* 32000 */ 0x06 | AD1848_XTAL1, + /* 33075 */ 0x0C | AD1848_XTAL2, + /* 37800 */ 0x08 | AD1848_XTAL2, + /* 44100 */ 0x0A | AD1848_XTAL2, + /* 48000 */ 0x0C | AD1848_XTAL1 +}; + +static unsigned int rates[14] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27042, 32000, 33075, 37800, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: 14, + list: rates, + mask: 0, +}; + +static unsigned char snd_ad1848_original_image[16] = +{ + 0x00, /* 00 - lic */ + 0x00, /* 01 - ric */ + 0x9f, /* 02 - la1ic */ + 0x9f, /* 03 - ra1ic */ + 0x9f, /* 04 - la2ic */ + 0x9f, /* 05 - ra2ic */ + 0xbf, /* 06 - loc */ + 0xbf, /* 07 - roc */ + 0x20, /* 08 - dfr */ + AD1848_AUTOCALIB, /* 09 - ic */ + 0x00, /* 0a - pc */ + 0x00, /* 0b - ti */ + 0x00, /* 0c - mi */ + 0x00, /* 0d - lbc */ + 0x00, /* 0e - dru */ + 0x00, /* 0f - drl */ +}; + +/* + * Basic I/O functions + */ + +void snd_ad1848_out(ad1848_t *chip, + unsigned char reg, + unsigned char value) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + outb(chip->image[reg] = value, AD1848P(chip, REG)); + mb(); +#if 0 + printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); +#endif +} + +void snd_ad1848_dout(ad1848_t *chip, + unsigned char reg, + unsigned char value) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + outb(value, AD1848P(chip, REG)); + mb(); +} + +unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg) +{ + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("auto calibration time out - reg = 0x%x\n", reg); +#endif + outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); + mb(); + return inb(AD1848P(chip, REG)); +} + +#ifdef CONFIG_SND_DEBUG + +void snd_ad1848_debug(ad1848_t *chip) +{ + printk("AD1848 REGS: INDEX = 0x%02x ", inb(AD1848P(chip, REGSEL))); + printk(" STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00)); + printk(" 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08)); + printk(" 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01)); + printk(" 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09)); + printk(" 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02)); + printk(" 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a)); + printk(" 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03)); + printk(" 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b)); + printk(" 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04)); + printk(" 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c)); + printk(" 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05)); + printk(" 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d)); + printk(" 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06)); + printk(" 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e)); + printk(" 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07)); + printk(" 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f)); +} + +#endif + +/* + * AD1848 detection / MCE routines + */ + +void snd_ad1848_mce_up(ad1848_t *chip) +{ + unsigned long flags; + int timeout; + + for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("mce_up - auto calibration time out (0)\n"); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + chip->mce_bit |= AD1848_MCE; + timeout = inb(AD1848P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); + if (!(timeout & AD1848_MCE)) + outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +void snd_ad1848_mce_down(ad1848_t *chip) +{ + unsigned long flags; + int timeout; + signed long time; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (timeout = 5; timeout > 0; timeout--) + inb(AD1848P(chip, REGSEL)); + /* end of cleanup sequence */ + for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) + udelay(100); +#if 0 + printk("(1) timeout = %i\n", timeout); +#endif +#ifdef CONFIG_SND_DEBUG + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL)); +#endif + chip->mce_bit &= ~AD1848_MCE; + timeout = inb(AD1848P(chip, REGSEL)); + outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & AD1848_MCE) == 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + /* calibration process */ + + for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--); + if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) { + snd_printd("mce_down - auto calibration time out (1)\n"); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } +#if 0 + printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); +#endif + time = HZ / 4; + while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (2)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } +#if 0 + printk("(3) jiffies = %li\n", jiffies); +#endif + time = HZ / 10; + while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (3)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + printk("(4) jiffies = %li\n", jiffies); + snd_printk("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL))); +#endif +} + +static unsigned int snd_ad1848_get_count(unsigned char format, + unsigned int size) +{ + switch (format & 0xe0) { + case AD1848_LINEAR_16: + size >>= 1; + break; + } + if (format & AD1848_STEREO) + size >>= 1; + return size; +} + +static int snd_ad1848_trigger(ad1848_t *chip, unsigned char what, + int channel, int cmd) +{ + int result = 0; + +#if 0 + printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS))); +#endif + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->image[AD1848_IFACE_CTRL] & what) { + spin_unlock(&chip->reg_lock); + return 0; + } + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what); + chip->mode |= AD1848_MODE_RUNNING; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->image[AD1848_IFACE_CTRL] & what)) { + spin_unlock(&chip->reg_lock); + return 0; + } + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what); + chip->mode &= ~AD1848_MODE_RUNNING; + } else { + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +/* + * CODEC I/O + */ + +static unsigned char snd_ad1848_get_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < 14; i++) + if (rate == rates[i]) + return freq_bits[i]; + snd_BUG(); + return freq_bits[13]; +} + +static int snd_ad1848_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static unsigned char snd_ad1848_get_format(int format, int channels) +{ + unsigned char rformat; + + rformat = AD1848_LINEAR_8; + switch (format) { + case SNDRV_PCM_FORMAT_A_LAW: rformat = AD1848_ALAW_8; break; + case SNDRV_PCM_FORMAT_MU_LAW: rformat = AD1848_ULAW_8; break; + case SNDRV_PCM_FORMAT_S16_LE: rformat = AD1848_LINEAR_16; break; + } + if (channels > 1) + rformat |= AD1848_STEREO; +#if 0 + snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif + return rformat; +} + +static void snd_ad1848_calibrate_mute(ad1848_t *chip, int mute) +{ + unsigned long flags; + + mute = mute ? 1 : 0; + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->calibrate_mute == mute) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + if (!mute) { + snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]); + } + snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]); + snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]); + snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]); + snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]); + chip->calibrate_mute = mute; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_ad1848_set_data_format(ad1848_t *chip, snd_pcm_hw_params_t *hw_params) +{ + if (hw_params == NULL) { + chip->image[AD1848_DATA_FORMAT] = 0x20; + } else { + chip->image[AD1848_DATA_FORMAT] = + snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) | + snd_ad1848_get_rate(params_rate(hw_params)); + } + // snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]); +} + +static int snd_ad1848_open(ad1848_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + if (chip->mode & AD1848_MODE_OPEN) { + up(&chip->open_mutex); + return -EAGAIN; + } + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (1)\n"); +#endif + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | + AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO | + AD1848_CALIB_MODE); + chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB; + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (2)\n"); +#endif + + snd_ad1848_set_data_format(chip, NULL); + + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("open: (3)\n"); +#endif + + /* ok. now enable and ack CODEC IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE; + snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = mode; + up(&chip->open_mutex); + + return 0; +} + +static void snd_ad1848_close(ad1848_t *chip) +{ + unsigned long flags; + + down(&chip->open_mutex); + if (!chip->mode) { + up(&chip->open_mutex); + return; + } + /* disable IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE; + snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* now disable capture & playback */ + + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | + AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); + snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + + /* clear IRQ again */ + spin_lock_irqsave(&chip->reg_lock, flags); + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + outb(0, AD1848P(chip, STATUS)); /* clear IRQ */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = 0; + up(&chip->open_mutex); +} + +/* + * ok.. exported functions.. + */ + +static int snd_ad1848_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd); +} + +static int snd_ad1848_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd); +} + +static int snd_ad1848_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + snd_ad1848_calibrate_mute(chip, 1); + snd_ad1848_set_data_format(chip, hw_params); + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + snd_ad1848_calibrate_mute(chip, 0); + return 0; +} + +static int snd_ad1848_playback_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1848_playback_prepare(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO); + snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); + snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ad1848_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + snd_ad1848_calibrate_mute(chip, 1); + snd_ad1848_set_data_format(chip, hw_params); + snd_ad1848_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_down(chip); + snd_ad1848_calibrate_mute(chip, 0); + return 0; +} + +static int snd_ad1848_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ad1848_capture_prepare(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); + snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); + snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, dev_id, return); + + if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream && + (chip->mode & AD1848_MODE_RUNNING)) + snd_pcm_period_elapsed(chip->playback_substream); + if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream && + (chip->mode & AD1848_MODE_RUNNING)) + snd_pcm_period_elapsed(chip->capture_substream); + outb(0, AD1848P(chip, STATUS)); /* clear global interrupt bit */ +} + +static snd_pcm_uframes_t snd_ad1848_playback_pointer(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE)) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ad1848_capture_pointer(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE)) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static int snd_ad1848_probe(ad1848_t * chip) +{ + unsigned long flags; + int i, id, rev, ad1847; + unsigned char *ptr; + +#if 0 + snd_ad1848_debug(chip); +#endif + id = ad1847 = 0; + for (i = 0; i < 1000; i++) { + mb(); + if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) + udelay(500); + else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); + snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa); + snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45); + rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT); + if (rev == 0x65) { + id = 1; + ad1847 = 1; + break; + } + if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) { + id = 1; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + } + if (id != 1) + return -ENODEV; /* no valid device found */ + if (chip->hardware == AD1848_HW_DETECT) { + if (ad1847) { + chip->hardware = AD1848_HW_AD1847; + } else { + chip->hardware = AD1848_HW_AD1848; + rev = snd_ad1848_in(chip, AD1848_MISC_INFO); + if (rev & 0x80) { + chip->hardware = AD1848_HW_CS4248; + } else if (rev & 0x0a) { + chip->hardware = AD1848_HW_CMI8330; + } + } + } + spin_lock_irqsave(&chip->reg_lock, flags); + inb(AD1848P(chip, STATUS)); /* clear any pendings IRQ */ + outb(0, AD1848P(chip, STATUS)); + mb(); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->image[AD1848_MISC_INFO] = 0x00; + chip->image[AD1848_IFACE_CTRL] = + (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA; + ptr = (unsigned char *) &chip->image; + snd_ad1848_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */ + snd_ad1848_out(chip, i, *ptr++); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_ad1848_mce_up(chip); + snd_ad1848_mce_down(chip); + return 0; /* all things are ok.. */ +} + +/* + + */ + +static snd_pcm_hardware_t snd_ad1848_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ad1848_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + + */ + +static int snd_ad1848_playback_open(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0) + return err; + chip->playback_substream = substream; + runtime->hw = snd_ad1848_playback; + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ad1848_capture_open(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0) + return err; + chip->capture_substream = substream; + runtime->hw = snd_ad1848_capture; + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ad1848_playback_close(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + + chip->mode &= ~AD1848_MODE_PLAY; + chip->playback_substream = NULL; + snd_ad1848_close(chip); + return 0; +} + +static int snd_ad1848_capture_close(snd_pcm_substream_t * substream) +{ + ad1848_t *chip = snd_pcm_substream_chip(substream); + + chip->mode &= ~AD1848_MODE_CAPTURE; + chip->capture_substream = NULL; + snd_ad1848_close(chip); + return 0; +} + +static int snd_ad1848_free(ad1848_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma >= 0) { + snd_dma_disable(chip->dma); + free_dma(chip->dma); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_ad1848_dev_free(snd_device_t *device) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, device->device_data, return -ENXIO); + return snd_ad1848_free(chip); +} + +static const char *snd_ad1848_chip_id(ad1848_t *chip) +{ + switch (chip->hardware) { + case AD1848_HW_AD1847: return "AD1847"; + case AD1848_HW_AD1848: return "AD1848"; + case AD1848_HW_CS4248: return "CS4248"; + case AD1848_HW_CMI8330: return "CMI8330/C3D"; + default: return "???"; + } +} + +int snd_ad1848_create(snd_card_t * card, + unsigned long port, + int irq, int dma, + unsigned short hardware, + ad1848_t ** rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_ad1848_dev_free, + }; + ad1848_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(ad1848_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->port = port; + chip->irq = -1; + chip->dma = -1; + chip->hardware = hardware; + memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image)); + + if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) { + snd_ad1848_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) { + snd_ad1848_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma, "AD1848")) { + snd_ad1848_free(chip); + return -EBUSY; + } + chip->dma = dma; + + if (snd_ad1848_probe(chip) < 0) { + snd_ad1848_free(chip); + return -ENODEV; + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ad1848_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_ad1848_playback_ops = { + open: snd_ad1848_playback_open, + close: snd_ad1848_playback_close, + ioctl: snd_ad1848_ioctl, + hw_params: snd_ad1848_playback_hw_params, + hw_free: snd_ad1848_playback_hw_free, + prepare: snd_ad1848_playback_prepare, + trigger: snd_ad1848_playback_trigger, + pointer: snd_ad1848_playback_pointer, +}; + +static snd_pcm_ops_t snd_ad1848_capture_ops = { + open: snd_ad1848_capture_open, + close: snd_ad1848_capture_close, + ioctl: snd_ad1848_ioctl, + hw_params: snd_ad1848_capture_hw_params, + hw_free: snd_ad1848_capture_hw_free, + prepare: snd_ad1848_capture_prepare, + trigger: snd_ad1848_capture_trigger, + pointer: snd_ad1848_capture_pointer, +}; + +static void snd_ad1848_pcm_free(snd_pcm_t *pcm) +{ + ad1848_t *chip = snd_magic_cast(ad1848_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops); + + pcm->private_free = snd_ad1848_pcm_free; + pcm->private_data = chip; + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + strcpy(pcm->name, snd_ad1848_chip_id(chip)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_ad1848_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line", "Aux", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ad1848_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->reg_lock, flags); + left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left; + right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right; + change = left != chip->image[AD1848_LEFT_INPUT] || + right != chip->image[AD1848_RIGHT_INPUT]; + snd_ad1848_out(chip, AD1848_LEFT_INPUT, left); + snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_ad1848_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ad1848_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_ad1848_out(chip, left_reg, val1); + snd_ad1848_out(chip, right_reg, val2); + } else { + val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != chip->image[left_reg]; + snd_ad1848_out(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define AD1848_CONTROLS (sizeof(snd_ad1848_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ad1848_controls[] = { +AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), +AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), +AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_ad1848_info_mux, + get: snd_ad1848_get_mux, + put: snd_ad1848_put_mux, +}, +AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), +AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) +}; + +int snd_ad1848_mixer(ad1848_t *chip) +{ + snd_card_t *card; + snd_pcm_t *pcm; + int err, idx; + + snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); + + pcm = chip->pcm; + card = chip->card; + + strcpy(card->mixername, pcm->name); + + for (idx = 0; idx < AD1848_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1848_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_ad1848_in); +EXPORT_SYMBOL(snd_ad1848_out); +EXPORT_SYMBOL(snd_ad1848_dout); +EXPORT_SYMBOL(snd_ad1848_mce_up); +EXPORT_SYMBOL(snd_ad1848_mce_down); +EXPORT_SYMBOL(snd_ad1848_interrupt); +EXPORT_SYMBOL(snd_ad1848_create); +EXPORT_SYMBOL(snd_ad1848_pcm); +EXPORT_SYMBOL(snd_ad1848_mixer); +EXPORT_SYMBOL(snd_ad1848_info_single); +EXPORT_SYMBOL(snd_ad1848_get_single); +EXPORT_SYMBOL(snd_ad1848_put_single); +EXPORT_SYMBOL(snd_ad1848_info_double); +EXPORT_SYMBOL(snd_ad1848_get_double); +EXPORT_SYMBOL(snd_ad1848_put_double); + +/* + * INIT part + */ + +static int __init alsa_ad1848_init(void) +{ + return 0; +} + +static void __exit alsa_ad1848_exit(void) +{ +} + +module_init(alsa_ad1848_init) +module_exit(alsa_ad1848_exit) diff -Nru linux/sound/isa/als100.c linux-2.4.19-pre5-mjc/sound/isa/als100.c --- linux/sound/isa/als100.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/als100.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,423 @@ + +/* + card-als100.c - driver for Avance Logic ALS100 based soundcards. + Copyright (C) 1999-2000 by Massimo Piccioni + + Thanks to Pierfrancesco 'qM2' Passerini. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t sb_t + +#define PFX "als100: " + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Avance Logic ALS1X0"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Avance Logic,ALS100 - PRO16PNP}," + "{Avance Logic,ALS110}," + "{Avance Logic,ALS120}," + "{Avance Logic,ALS200}," + "{3D Melody,MF1000}," + "{Digimate,3D Sound}," + "{Avance Logic,ALS120}," + "{RTL,RTL3000}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for als100 based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for als100 based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable als100 based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for als100 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for als100 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for als100 driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for als100 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for als100 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for als100 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); +MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for als100 driver."); +MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC); + +struct snd_card_als100 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; + struct isapnp_dev *devopl; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_als100_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_als100_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_als100_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_ALS100(_va, _vb, _vc, _device, _audio, _mpu401, _opl) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio), \ + ISAPNP_DEVICE_ID('@', 'X', '@', _mpu401), \ + ISAPNP_DEVICE_ID('@', 'H', '@', _opl) } \ + } + +static struct isapnp_card_id snd_als100_pnpids[] __devinitdata = { + /* ALS100 - PRO16PNP */ + ISAPNP_ALS100('A','L','S',0x0001,0x0001,0x0001,0x0001), + /* ALS110 - MF1000 - Digimate 3D Sound */ + ISAPNP_ALS100('A','L','S',0x0110,0x1001,0x1001,0x1001), + /* ALS120 */ + ISAPNP_ALS100('A','L','S',0x0120,0x2001,0x2001,0x2001), + /* ALS200 */ + ISAPNP_ALS100('A','L','S',0x0200,0x0020,0x0020,0x0001), + /* RTL3000 */ + ISAPNP_ALS100('R','T','L',0x3000,0x2001,0x2001,0x2001), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_als100_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-als100" + + +#ifdef __ISAPNP__ +static int __init snd_card_als100_isapnp(int dev, struct snd_card_als100 *acard) +{ + const struct isapnp_card_id *id = snd_als100_isapnp_id[dev]; + struct isapnp_card *card = snd_als100_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->devopl->active) { + acard->dev = acard->devmpu = acard->devopl = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], + 1); + if (snd_dma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_dma8[dev] = pdev->dma_resource[1].start; + snd_dma16[dev] = pdev->dma_resource[0].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev)<0) { + snd_mpu_port[dev] = -1; + return 0; + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + pdev = acard->devopl; + if (pdev == NULL || pdev->prepare(pdev)<0) { + snd_fm_port[dev] = -1; + return 0; + } + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "OPL isapnp configure failure\n"); + snd_fm_port[dev] = -1; + acard->devopl = NULL; + } else { + snd_fm_port[dev] = pdev->resource[0].start; + } + + return 0; +} + +static void snd_card_als100_deactivate(struct snd_card_als100 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } + if (acard->devopl) { + acard->devopl->deactivate(acard->devopl); + acard->devopl = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_als100_free(snd_card_t *card) +{ + struct snd_card_als100 *acard = (struct snd_card_als100 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_als100_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_als100_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_als100 *acard; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_als100))) == NULL) + return -ENOMEM; + acard = (struct snd_card_als100 *)card->private_data; + card->private_free = snd_card_als100_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_als100_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + printk(KERN_ERR PFX "you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, snd_port[dev], + snd_irq[dev], + snd_sb16dsp_interrupt, + snd_dma8[dev], + snd_dma16[dev], + SB_HW_ALS100, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "ALS100"); + strcpy(card->shortname, "Avance Logic ALS100"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, chip->name, chip->port, + snd_irq[dev], snd_dma8[dev], snd_dma16[dev]); + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_als100_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_als100_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_als100_isapnp_cards[dev] = card; + snd_als100_isapnp_id[dev] = id; + res = snd_card_als100_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_als100_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_als100_pnpids, snd_als100_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no ALS100 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_als100_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_als100_cards[dev]); +} + +module_init(alsa_card_als100_init) +module_exit(alsa_card_als100_exit) + +#ifndef MODULE + +/* format is: snd-als100=snd_enable,snd_index,snd_id,snd_port, + snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq, + snd_dma8,snd_dma16 */ + +static int __init alsa_card_als100_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2 && + get_option(&str,&snd_dma16[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-als100=", alsa_card_als100_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/azt2320.c linux-2.4.19-pre5-mjc/sound/isa/azt2320.c --- linux/sound/isa/azt2320.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/azt2320.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,446 @@ + +/* + card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards. + Copyright (C) 1999-2000 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + This driver should provide support for most Aztech AZT2320 based cards. + Several AZT2316 chips are also supported/tested, but autoprobe doesn't + work: all module option have to be set. + + No docs available for us at Aztech headquarters !!! Unbelievable ... + No other help obtained. + + Thanks to Rainer Wiesner for the WSS + activation method (full-duplex audio!). +*/ + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t cs4231_t + +#define PFX "azt2320: " + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Aztech Systems AZT2320"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Aztech Systems,PRO16V}," + "{Aztech Systems,AZT2320}," + "{Aztech Systems,AZT3300}," + "{Aztech Systems,AZT2320}," + "{Aztech Systems,AZT3000}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for azt2320 based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for azt2320 based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable azt2320 based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wss_port, "WSS Port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "1st DMA # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "2nd DMA # for azt2320 driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +struct snd_card_azt2320 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_azt2320_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_azt2320_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_azt2320_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_AZT2320(_va, _vb, _vc, _device, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401), } \ + } + +static struct isapnp_card_id snd_azt2320_pnpids[] __devinitdata = { + /* PRO16V */ + ISAPNP_AZT2320('A','Z','T',0x1008,0x1008,0x2001), + /* Aztech Sound Galaxy 16 */ + ISAPNP_AZT2320('A','Z','T',0x2320,0x0001,0x0002), + /* Packard Bell Sound III 336 AM/SP */ + ISAPNP_AZT2320('A','Z','T',0x3000,0x1003,0x2001), + /* AT3300 */ + ISAPNP_AZT2320('A','Z','T',0x3002,0x1004,0x2001), + /* --- */ + ISAPNP_AZT2320('A','Z','T',0x3005,0x1003,0x2001), + /* --- */ + ISAPNP_AZT2320('A','Z','T',0x3011,0x1003,0x2001), + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_azt2320_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-azt2320" + + +#ifdef __ISAPNP__ +static int __init snd_card_azt2320_isapnp(int dev, struct snd_card_azt2320 *acard) +{ + const struct isapnp_card_id *id = snd_azt2320_isapnp_id[dev]; + struct isapnp_card *card = snd_azt2320_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_wss_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_wss_port[dev], + 4); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], + 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev) < 0) { + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_wss_port[dev] = pdev->resource[2].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + snd_mpu_port[dev] = -1; + return 0; + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev) < 0) { + /* not fatal error */ + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + return 0; +} + +static void snd_card_azt2320_deactivate(struct snd_card_azt2320 *acard) +{ + if (acard->dev) + acard->dev->deactivate(acard->dev); + if (acard->devmpu) + acard->devmpu->deactivate(acard->devmpu); +} +#endif /* __ISAPNP__ */ + +/* same of snd_sbdsp_command by Jaroslav Kysela */ +static int __init snd_card_azt2320_command(unsigned long port, unsigned char val) +{ + int i; + unsigned long limit; + + limit = jiffies + HZ / 10; + for (i = 50000; i && (limit - jiffies) > 0; i--) + if (!(inb(port + 0x0c) & 0x80)) { + outb(val, port + 0x0c); + return 0; + } + return -EBUSY; +} + +static int __init snd_card_azt2320_enable_wss(unsigned long port) +{ + int error; + + if ((error = snd_card_azt2320_command(port, 0x09))) + return error; + if ((error = snd_card_azt2320_command(port, 0x00))) + return error; + + mdelay(5); + return 0; +} + +static void snd_card_azt2320_free(snd_card_t *card) +{ + struct snd_card_azt2320 *acard = (struct snd_card_azt2320 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_azt2320_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_azt2320_probe(int dev) +{ + int error; + snd_card_t *card; + struct snd_card_azt2320 *acard; + cs4231_t *chip; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_azt2320))) == NULL) + return -ENOMEM; + acard = (struct snd_card_azt2320 *)card->private_data; + card->private_free = snd_card_azt2320_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_azt2320_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#endif /* __ISAPNP__ */ + + if ((error = snd_card_azt2320_enable_wss(snd_port[dev]))) { + snd_card_free(card); + return error; + } + + if ((error = snd_cs4231_create(card, snd_wss_port[dev], -1, + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, 0, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", + snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "AZT2320"); + strcpy(card->shortname, "Aztech AZT2320"); + sprintf(card->longname, "%s soundcard, WSS at 0x%lx, irq %i, dma %i&%i", + card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_azt2320_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_azt2320_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_azt2320_isapnp_cards[dev] = card; + snd_azt2320_isapnp_id[dev] = id; + res = snd_card_azt2320_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif + +static int __init alsa_card_azt2320_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_azt2320_pnpids, snd_azt2320_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no AZT2320 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_azt2320_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_azt2320_cards[dev]); +} + +module_init(alsa_card_azt2320_init) +module_exit(alsa_card_azt2320_exit) + +#ifndef MODULE + +/* format is: snd-azt2320=snd_enable,snd_index,snd_id,snd_port, + snd_wss_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_mpu_irq,snd_dma1,snd_dma2 */ + +static int __init alsa_card_azt2320_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-azt2320=", alsa_card_azt2320_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/cmi8330.c linux-2.4.19-pre5-mjc/sound/isa/cmi8330.c --- linux/sound/isa/cmi8330.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/cmi8330.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,537 @@ +/* + * Driver for C-Media's CMI8330 soundcards. + * Copyright (c) by George Talusan + * http://www.undergrad.math.uwaterloo.ca/~gstalusa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * NOTES + * + * The extended registers contain mixer settings which are largely + * untapped for the time being. + * + * MPU401 and SPDIF are not supported yet. I don't have the hardware + * to aid in coding and testing, so I won't bother. + * + * To quickly load the module, + * + * modprobe -a snd-card-cmi8330 snd_sbport=0x220 snd_sbirq=5 snd_sbdma8=1 + * snd_sbdma16=5 snd_wssport=0x530 snd_wssirq=11 snd_wssdma=0 + * + * This card has two mixers and two PCM devices. I've cheesed it such + * that recording and playback can be done through the same device. + * The driver "magically" routes the capturing to the AD1848 codec, + * and playback to the SB16 codec. This allows for full-duplex mode + * to some extent. + * The utilities in alsa-utils are aware of both devices, so passing + * the appropriate parameters to amixer and alsactl will give you + * full control over both mixers. + */ + +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("George Talusan "); +MODULE_DESCRIPTION("C-Media CMI8330"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int snd_sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int snd_sbdma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int snd_sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int snd_wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int snd_wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for CMI8330 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for CMI8330 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CMI8330 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif + +MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sbport, "Port # for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); +MODULE_PARM(snd_sbirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_sbirq, "IRQ # for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{5},dialog:list"); +MODULE_PARM(snd_sbdma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_sbdma8, "DMA8 for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbdma8, SNDRV_DMA8_DESC ",prefers:{1}"); +MODULE_PARM(snd_sbdma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_sbdma16, "DMA16 for CMI8330 SB driver."); +MODULE_PARM_SYNTAX(snd_sbdma16, SNDRV_ENABLED ",allows:{{5},{7}},prefers:{5},dialog:list"); + +MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wssport, "Port # for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80,0xf40,0xc0}},prefers:{0x530},base:16,dialog:list"); +MODULE_PARM(snd_wssirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_wssirq, "IRQ # for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(snd_wssirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{11},dialog:list"); +MODULE_PARM(snd_wssdma, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_wssdma, "DMA for CMI8330 WSS driver."); +MODULE_PARM_SYNTAX(snd_wssdma, SNDRV_DMA8_DESC ",prefers:{0}"); + +#define CMI8330_RMUX3D 16 +#define CMI8330_MUTEMUX 17 +#define CMI8330_OUTPUTVOL 18 +#define CMI8330_MASTVOL 19 +#define CMI8330_LINVOL 20 +#define CMI8330_CDINVOL 21 +#define CMI8330_WAVVOL 22 +#define CMI8330_RECMUX 23 +#define CMI8330_WAVGAIN 24 +#define CMI8330_LINGAIN 25 +#define CMI8330_CDINGAIN 26 + +static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = +{ + 0x0, /* 16 - recording mux */ + 0x40, /* 17 - mute mux */ + 0x0, /* 18 - vol */ + 0x0, /* 19 - master volume */ + 0x0, /* 20 - line-in volume */ + 0x0, /* 21 - cd-in volume */ + 0x0, /* 22 - wave volume */ + 0x0, /* 23 - mute/rec mux */ + 0x0, /* 24 - wave rec gain */ + 0x0, /* 25 - line-in rec gain */ + 0x0 /* 26 - cd-in rec gain */ +}; + +struct snd_cmi8330 { +#ifdef __ISAPNP__ + struct isapnp_dev *cap; + struct isapnp_dev *play; +#endif +}; + +static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_cmi8330_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_cmi8330_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_CMI8330(_va, _vb, _vc, _device, _audio1, _audio2) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio1), \ + ISAPNP_DEVICE_ID('@', 'X', '@', _audio2), } \ + } + +static struct isapnp_card_id snd_cmi8330_pnpids[] __devinitdata = +{ + ISAPNP_CMI8330('C','M','I',0x0001,0x0001,0x0001), + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_cmi8330_pnpids); + +#endif + +#define CMI8330_CONTROLS (sizeof(snd_cmi8330_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cmi8330_controls[] __devinitdata = { +AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), +AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), +AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), +AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 0), +AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0), +AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0), +AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0), +AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0), +AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), +AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), +AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), +AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), +AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0), +AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0), +AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0), +AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0), +AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0), +AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0), +AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0), +AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0), +AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1), +AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0), +AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1), +AD1848_SINGLE("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1), +AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 1, 1), +}; + +static int __init snd_cmi8330_mixer(snd_card_t *card, ad1848_t *chip) +{ + int idx, err; + + strcpy(card->mixername, "CMI8330/C3D"); + + for (idx = 0; idx < CMI8330_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmi8330_controls[idx], chip))) < 0) + return err; + + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_cmi8330_isapnp(int dev, struct snd_cmi8330 *acard) +{ + const struct isapnp_card_id *id = snd_cmi8330_isapnp_id[dev]; + struct isapnp_card *card = snd_cmi8330_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->cap = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->cap->active) { + acard->cap = NULL; + return -EBUSY; + } + acard->play = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->play->active) { + acard->cap = acard->play = NULL; + return -EBUSY; + } + + pdev = acard->cap; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + /* allocate AD1848 resources */ + if (snd_wssport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_wssport[dev], 8); + if (snd_wssdma[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_wssdma[dev], 1); + if (snd_wssirq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_wssirq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("(AD1848) PnP configure failure\n"); + return -EBUSY; + } + snd_wssport[dev] = pdev->resource[0].start; + snd_wssdma[dev] = pdev->dma_resource[0].start; + snd_wssirq[dev] = pdev->irq_resource[0].start; + + /* allocate SB16 resources */ + pdev = acard->play; + if (pdev->prepare(pdev)<0) { + acard->cap->deactivate(acard->cap); + return -EAGAIN; + } + if (snd_sbport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_sbport[dev], 16); + if (snd_sbdma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_sbdma8[dev], 1); + if (snd_sbdma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_sbdma16[dev], 1); + if (snd_sbirq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_sbirq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("CMI8330/C3D (SB16) PnP configure failure\n"); + acard->cap->deactivate(acard->cap); + return -EBUSY; + } + snd_sbport[dev] = pdev->resource[0].start; + snd_sbdma8[dev] = pdev->dma_resource[0].start; + snd_sbdma16[dev] = pdev->dma_resource[1].start; + snd_sbirq[dev] = pdev->irq_resource[0].start; + + return 0; +} + +static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard) +{ + if (acard->cap) { + acard->cap->deactivate(acard->cap); + acard->cap = NULL; + } + if (acard->play) { + acard->play->deactivate(acard->play); + acard->play = NULL; + } +} +#endif + +static void snd_cmi8330_free(snd_card_t *card) +{ + struct snd_cmi8330 *acard = (struct snd_cmi8330 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_cmi8330_deactivate(acard); +#endif + } +} + +static int __init snd_cmi8330_probe(int dev) +{ + snd_card_t *card; + struct snd_cmi8330 *acard; + ad1848_t *chip_wss; + sb_t *chip_sb; + unsigned long flags; + int i, err; + snd_pcm_t *pcm, *wss_pcm, *sb_pcm; + snd_pcm_str_t *pstr; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_wssport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_wssport\n"); + return -EINVAL; + } + if (snd_sbport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_sbport\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_cmi8330)); + if (card == NULL) { + snd_printk("could not get a new card\n"); + return -ENOMEM; + } + acard = (struct snd_cmi8330 *)card->private_data; + card->private_free = snd_cmi8330_free; + +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_cmi8330_isapnp(dev, acard)) < 0) { + snd_printk("PnP detection failed\n"); + snd_card_free(card); + return err; + } +#endif + + if ((err = snd_ad1848_create(card, + snd_wssport[dev] + 4, + snd_wssirq[dev], + snd_wssdma[dev], + AD1848_HW_DETECT, + &chip_wss)) < 0) { + snd_printk("(AD1848) device busy??\n"); + snd_card_free(card); + return err; + } + if (chip_wss->hardware != AD1848_HW_CMI8330) { + snd_printk("(AD1848) not found during probe\n"); + snd_card_free(card); + return -ENODEV; + } + if ((err = snd_ad1848_pcm(chip_wss, 0, &wss_pcm)) < 0) { + snd_printk("(AD1848) no enough memory??\n"); + snd_card_free(card); + return err; + } + + if ((err = snd_sbdsp_create(card, snd_sbport[dev], + snd_sbirq[dev], + snd_sb16dsp_interrupt, + snd_sbdma8[dev], + snd_sbdma16[dev], + SB_HW_AUTO, &chip_sb)) < 0) { + snd_printk("(SB16) device busy??\n"); + snd_card_free(card); + return err; + } + if ((err = snd_sb16dsp_pcm(chip_sb, 1, &sb_pcm)) < 0) { + snd_printk("(SB16) no enough memory??\n"); + snd_card_free(card); + return err; + } + + if (chip_sb->hardware != SB_HW_16) { + snd_printk("(SB16) not found during probe\n"); + snd_card_free(card); + return -ENODEV; + } + + memcpy(&chip_wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image)); + + spin_lock_irqsave(&chip_wss->reg_lock, flags); + snd_ad1848_out(chip_wss, AD1848_MISC_INFO, /* switch on MODE2 */ + chip_wss->image[AD1848_MISC_INFO] |= 0x40); + spin_unlock_irqrestore(&chip_wss->reg_lock, flags); + + if ((err = snd_cmi8330_mixer(card, chip_wss)) < 0) { + snd_printk("failed to create mixers\n"); + snd_card_free(card); + return err; + } + spin_lock_irqsave(&chip_wss->reg_lock, flags); + for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) + snd_ad1848_out(chip_wss, i, chip_wss->image[i]); + spin_unlock_irqrestore(&chip_wss->reg_lock, flags); + + /* + * KLUDGE ALERT + * disable AD1848 playback + * disable SB16 capture + */ + pcm = wss_pcm; + pstr = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; + snd_magic_kfree(pstr->substream); + pstr->substream = 0; + pstr->substream_count = 0; + + pcm = sb_pcm; + pstr = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + snd_magic_kfree(pstr->substream); + pstr->substream = 0; + pstr->substream_count = 0; + + strcpy(card->driver, "CMI8330/C3D"); + strcpy(card->shortname, "C-Media CMI8330/C3D"); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + wss_pcm->name, + chip_wss->port, + snd_wssirq[dev], + snd_wssdma[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + snd_cmi8330_cards[dev] = card; + return 0; +} + +static void __exit alsa_card_cmi8330_exit(void) +{ + int i; + + for (i = 0; i < SNDRV_CARDS; i++) + snd_card_free(snd_cmi8330_cards[i]); +} + +#ifdef __ISAPNP__ +static int __init snd_cmi8330_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_cmi8330_isapnp_cards[dev] = card; + snd_cmi8330_isapnp_id[dev] = id; + res = snd_cmi8330_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_cmi8330_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_cmi8330_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_cmi8330_pnpids, snd_cmi8330_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "CMI8330 not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +module_init(alsa_card_cmi8330_init) +module_exit(alsa_card_cmi8330_exit) + +#ifndef MODULE + +/* format is: snd-cmi8330=snd_enable,snd_index,snd_id,snd_isapnp, + snd_sbport,snd_sbirq, + snd_sbdma8,snd_sbdma16, + snd_wssport,snd_wssirq, + snd_wssdma */ + +static int __init alsa_card_cmi8330_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_sbport[nr_dev]) == 2 && + get_option(&str,&snd_sbirq[nr_dev]) == 2 && + get_option(&str,&snd_sbdma8[nr_dev]) == 2 && + get_option(&str,&snd_sbdma16[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wssport[nr_dev]) == 2 && + get_option(&str,&snd_wssirq[nr_dev]) == 2 && + get_option(&str,&snd_wssdma[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-cmi8330=", alsa_card_cmi8330_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/cs423x/Makefile linux-2.4.19-pre5-mjc/sound/isa/cs423x/Makefile --- linux/sound/isa/cs423x/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,46 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _cs423x.o + +list-multi := snd-cs4231-lib.o snd-cs4236-lib.o \ + snd-cs4231.o snd-cs4232.o snd-cs4236.o + +export-objs := cs4231_lib.o cs4236_lib.o + +snd-cs4231-lib-objs := cs4231_lib.o +snd-cs4236-lib-objs := cs4236_lib.o +snd-cs4231-objs := cs4231.o +snd-cs4232-objs := cs4232.o +snd-cs4236-objs := cs4236.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o +obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o snd-cs4231-lib.o +obj-$(CONFIG_SND_GUSMAX) += snd-cs4231-lib.o +obj-$(CONFIG_SND_INTERWAVE) += snd-cs4231-lib.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o +obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o + +include $(TOPDIR)/Rules.make + +snd-cs4231-lib.o: $(snd-cs4231-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-lib-objs) + +snd-cs4236-lib.o: $(snd-cs4236-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-lib-objs) + +snd-cs4231.o: $(snd-cs4231-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4231-objs) + +snd-cs4232.o: $(snd-cs4232-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4232-objs) + +snd-cs4236.o: $(snd-cs4236-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4236-objs) diff -Nru linux/sound/isa/cs423x/cs4231.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231.c --- linux/sound/isa/cs423x/cs4231.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,211 @@ +/* + * Generic driver for CS4231 chips + * Copyright (c) by Jaroslav Kysela + * Originally the CS4232/CS4232A driver, modified for use on CS4231 by + * Tugrul Galatali + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t cs4231_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Generic CS4231"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Crystal Semiconductors,CS4231}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for CS4231 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for CS4231 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CS4231 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4231 driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +static snd_card_t *snd_cs4231_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_card_cs4231_probe(int dev) +{ + snd_card_t *card; + struct snd_card_cs4231 *acard; + snd_pcm_t *pcm = NULL; + cs4231_t *chip; + int err; + + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_irq[dev] == SNDRV_AUTO_IRQ) { + snd_printk("specify snd_irq\n"); + return -EINVAL; + } + if (snd_dma1[dev] == SNDRV_AUTO_DMA) { + snd_printk("specify snd_dma1\n"); + return -EINVAL; + } + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_card_cs4231 *)card->private_data; + if (snd_mpu_port[dev] < 0) + snd_mpu_port[dev] = SNDRV_AUTO_PORT; + if ((err = snd_cs4231_create(card, snd_port[dev], -1, + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, + 0, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR "cs4231: MPU401 not detected\n"); + } + strcpy(card->driver, "CS4231"); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]); + if (snd_dma2[dev] >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_cs4231_cards[dev] = card; + return 0; +} + +static int __init alsa_card_cs4231_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_card_cs4231_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "CS4231 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_cs4231_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_cs4231_cards[idx]); +} + +module_init(alsa_card_cs4231_init) +module_exit(alsa_card_cs4231_exit) + +#ifndef MODULE + +/* format is: snd-cs4231=snd_enable,snd_index,snd_id, + snd_port,snd_mpu_port,snd_irq,snd_mpu_irq, + snd_dma1,snd_dma2 */ + +static int __init alsa_card_cs4231_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs4231=", alsa_card_cs4231_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/cs423x/cs4231_lib.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231_lib.c --- linux/sound/isa/cs423x/cs4231_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4231_lib.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1838 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of CS4231(A)/CS4232/InterWave & compatible chips + * + * Bugs: + * - sometimes record brokes playback with WSS portion of + * Yamaha OPL3-SA3 chip + * - CS4231 (GUS MAX) - still trouble with occasional noises + * - broken initialization? + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips"); +MODULE_LICENSE("GPL"); + +#define chip_t cs4231_t + +#if 0 +#define SNDRV_DEBUG_MCE +#endif + +/* + * Some variables + */ + +static unsigned char freq_bits[14] = { + /* 5510 */ 0x00 | CS4231_XTAL2, + /* 6620 */ 0x0E | CS4231_XTAL2, + /* 8000 */ 0x00 | CS4231_XTAL1, + /* 9600 */ 0x0E | CS4231_XTAL1, + /* 11025 */ 0x02 | CS4231_XTAL2, + /* 16000 */ 0x02 | CS4231_XTAL1, + /* 18900 */ 0x04 | CS4231_XTAL2, + /* 22050 */ 0x06 | CS4231_XTAL2, + /* 27042 */ 0x04 | CS4231_XTAL1, + /* 32000 */ 0x06 | CS4231_XTAL1, + /* 33075 */ 0x0C | CS4231_XTAL2, + /* 37800 */ 0x08 | CS4231_XTAL2, + /* 44100 */ 0x0A | CS4231_XTAL2, + /* 48000 */ 0x0C | CS4231_XTAL1 +}; + +static unsigned int rates[14] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27042, 32000, 33075, 37800, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: 14, + list: rates, + mask: 0, +}; + +static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime) +{ + return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); +} + +static unsigned char snd_cs4231_original_image[32] = +{ + 0x00, /* 00/00 - lic */ + 0x00, /* 01/01 - ric */ + 0x9f, /* 02/02 - la1ic */ + 0x9f, /* 03/03 - ra1ic */ + 0x9f, /* 04/04 - la2ic */ + 0x9f, /* 05/05 - ra2ic */ + 0xbf, /* 06/06 - loc */ + 0xbf, /* 07/07 - roc */ + 0x20, /* 08/08 - pdfr */ + CS4231_AUTOCALIB, /* 09/09 - ic */ + 0x00, /* 0a/10 - pc */ + 0x00, /* 0b/11 - ti */ + CS4231_MODE2, /* 0c/12 - mi */ + 0xfc, /* 0d/13 - lbc */ + 0x00, /* 0e/14 - pbru */ + 0x00, /* 0f/15 - pbrl */ + 0x80, /* 10/16 - afei */ + 0x01, /* 11/17 - afeii */ + 0x9f, /* 12/18 - llic */ + 0x9f, /* 13/19 - rlic */ + 0x00, /* 14/20 - tlb */ + 0x00, /* 15/21 - thb */ + 0x00, /* 16/22 - la3mic/reserved */ + 0x00, /* 17/23 - ra3mic/reserved */ + 0x00, /* 18/24 - afs */ + 0x00, /* 19/25 - lamoc/version */ + 0xcf, /* 1a/26 - mioc */ + 0x00, /* 1b/27 - ramoc/reserved */ + 0x20, /* 1c/28 - cdfr */ + 0x00, /* 1d/29 - res4 */ + 0x00, /* 1e/30 - cbru */ + 0x00, /* 1f/31 - cbrl */ +}; + +/* + * Basic I/O functions + */ + +void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + int timeout; + unsigned char tmp; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + if (chip->calibrate_mute) { + chip->image[reg] &= mask; + chip->image[reg] |= value; + } else { + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + mb(); + tmp = (chip->image[reg] & mask) | value; + outb(tmp, CS4231P(chip, REG)); + chip->image[reg] = tmp; + mb(); + } +} + +static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(10); + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + outb(value, CS4231P(chip, REG)); + mb(); +} + +void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char value) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + outb(value, CS4231P(chip, REG)); + chip->image[reg] = value; + mb(); +#if 0 + printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); +#endif +} + +unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg) +{ + int timeout; + + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); +#endif + outb(chip->mce_bit | reg, CS4231P(chip, REGSEL)); + mb(); + return inb(CS4231P(chip, REG)); +} + +void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val) +{ + outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL)); + outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG)); + outb(val, CS4231P(chip, REG)); + chip->eimage[CS4236_REG(reg)] = val; +#if 0 + printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val); +#endif +} + +unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg) +{ + outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL)); + outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG)); +#if 1 + return inb(CS4231P(chip, REG)); +#else + { + unsigned char res; + res = inb(CS4231P(chip, REG)); + printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res); + return res; + } +#endif +} + +#ifdef CONFIG_SND_DEBUG + +void snd_cs4231_debug(cs4231_t *chip) +{ + printk("CS4231 REGS: INDEX = 0x%02x ", inb(CS4231P(chip, REGSEL))); + printk(" STATUS = 0x%02x\n", inb(CS4231P(chip, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00)); + printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10)); + printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01)); + printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11)); + printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02)); + printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12)); + printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03)); + printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13)); + printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04)); + printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14)); + printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05)); + printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15)); + printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06)); + printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16)); + printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07)); + printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17)); + printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08)); + printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18)); + printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09)); + printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19)); + printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a)); + printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a)); + printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b)); + printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b)); + printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c)); + printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c)); + printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d)); + printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d)); + printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e)); + printk(" 0x1e: ply lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e)); + printk(" 0x0f: rec upr count = 0x%02x ", snd_cs4231_in(chip, 0x0f)); + printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f)); +} + +#endif + +/* + * CS4231 detection / MCE routines + */ + +static void snd_cs4231_busy_wait(cs4231_t *chip) +{ + int timeout; + + /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ + for (timeout = 5; timeout > 0; timeout--) + inb(CS4231P(chip, REGSEL)); + /* end of cleanup sequence */ + for (timeout = 250; + timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); + timeout--) + udelay(10); +} + +void snd_cs4231_mce_up(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (timeout = 250; timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); timeout--) + udelay(100); +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("mce_up - auto calibration time out (0)\n"); +#endif + chip->mce_bit |= CS4231_MCE; + timeout = inb(CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); + if (!(timeout & CS4231_MCE)) + outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +void snd_cs4231_mce_down(cs4231_t *chip) +{ + unsigned long flags; + int timeout; + signed long time; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_busy_wait(chip); +#if 0 + printk("(1) timeout = %i\n", timeout); +#endif +#ifdef CONFIG_SND_DEBUG + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", CS4231P(chip, REGSEL)); +#endif + chip->mce_bit &= ~CS4231_MCE; + timeout = inb(CS4231P(chip, REGSEL)); + outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & CS4231_MCE) == 0 || + !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + snd_cs4231_busy_wait(chip); + + /* calibration process */ + + for (timeout = 500; timeout > 0 && (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0; timeout--) + udelay(10); + if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) { + snd_printd("cs4231_mce_down - auto calibration time out (1)\n"); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } +#if 0 + printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); +#endif + time = HZ / 4; + while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (2)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } +#if 0 + printk("(3) jiffies = %li\n", jiffies); +#endif + time = HZ / 10; + while (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (time <= 0) { + snd_printk("mce_down - auto calibration time out (3)\n"); + return; + } + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + spin_lock_irqsave(&chip->reg_lock, flags); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + printk("(4) jiffies = %li\n", jiffies); + snd_printk("mce_down - exit = 0x%x\n", inb(CS4231P(chip, REGSEL))); +#endif +} + +static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size) +{ + switch (format & 0xe0) { + case CS4231_LINEAR_16: + case CS4231_LINEAR_16_BIG: + size >>= 1; + break; + case CS4231_ADPCM_16: + return size >> 2; + } + if (format & CS4231_STEREO) + size >>= 1; + return size; +} + +static int snd_cs4231_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + +#if 0 + printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(CS4231P(card, STATUS))); +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == chip->playback_substream) { + what |= CS4231_PLAYBACK_ENABLE; + snd_pcm_trigger_done(s, substream); + } else if (s == chip->capture_substream) { + what |= CS4231_RECORD_ENABLE; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) + chip->image[CS4231_IFACE_CTRL] |= what; + else + chip->image[CS4231_IFACE_CTRL] &= ~what; + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock(&chip->reg_lock); + break; + } + default: + result = -EINVAL; + break; + } +#if 0 + snd_cs4231_debug(chip); +#endif + return result; +} + +/* + * CODEC I/O + */ + +static unsigned char snd_cs4231_get_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < 14; i++) + if (rate == rates[i]) + return freq_bits[i]; + // snd_BUG(); + return freq_bits[13]; +} + +static unsigned char snd_cs4231_get_format(cs4231_t *chip, + int format, + int channels) +{ + unsigned char rformat; + + rformat = CS4231_LINEAR_8; + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: rformat = CS4231_ULAW_8; break; + case SNDRV_PCM_FORMAT_A_LAW: rformat = CS4231_ALAW_8; break; + case SNDRV_PCM_FORMAT_S16_LE: rformat = CS4231_LINEAR_16; break; + case SNDRV_PCM_FORMAT_S16_BE: rformat = CS4231_LINEAR_16_BIG; break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: rformat = CS4231_ADPCM_16; break; + } + if (channels > 1) + rformat |= CS4231_STEREO; +#if 0 + snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif + return rformat; +} + +static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute) +{ + unsigned long flags; + + mute = mute ? 1 : 0; + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->calibrate_mute == mute) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + if (!mute) { + snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]); + } + snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]); + snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]); + snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); + if (chip->hardware == CS4231_HW_INTERWAVE) { + snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]); + snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]); + snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]); + snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]); + } + chip->calibrate_mute = mute; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4231_playback_format(cs4231_t *chip, + snd_pcm_hw_params_t *params, + unsigned char pdfr) +{ + unsigned long flags; + int full_calib = 1; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + if (chip->hardware == CS4231_HW_CS4231A || + (chip->hardware & CS4231_HW_CS4232_MASK)) { + spin_lock_irqsave(&chip->reg_lock, flags); + if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10); + full_calib = 0; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + if (full_calib) { + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ? + (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) : + pdfr); + } else { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + } + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +static void snd_cs4231_capture_format(cs4231_t *chip, + snd_pcm_hw_params_t *params, + unsigned char cdfr) +{ + unsigned long flags; + int full_calib = 1; + + down(&chip->mce_mutex); + snd_cs4231_calibrate_mute(chip, 1); + if (chip->hardware == CS4231_HW_CS4231A || + (chip->hardware & CS4231_HW_CS4232_MASK)) { + spin_lock_irqsave(&chip->reg_lock, flags); + if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */ + (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); + snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20); + full_calib = 0; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + if (full_calib) { + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->hardware != CS4231_HW_INTERWAVE) { + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, + ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) | + (cdfr & 0x0f)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + } + } + snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + } + snd_cs4231_calibrate_mute(chip, 0); + up(&chip->mce_mutex); +} + +/* + * Timer interface + */ + +static unsigned long snd_cs4231_timer_resolution(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + if (chip->hardware & CS4231_HW_CS4236B_MASK) + return 14467; + else + return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920; +} + +static int snd_cs4231_timer_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned int ticks; + cs4231_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->reg_lock, flags); + ticks = timer->sticks; + if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || + (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || + (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { + snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8)); + snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4231_timer_stop(snd_timer_t * timer) +{ + unsigned long flags; + cs4231_t *chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4231_init(cs4231_t *chip) +{ + unsigned long flags; + + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (1)\n"); +#endif + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO | + CS4231_CALIB_MODE); + chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (2)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); +#endif + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (4)\n"); +#endif + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE + snd_printk("init: (5)\n"); +#endif +} + +static int snd_cs4231_open(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + if ((chip->mode & mode) || + ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) { + up(&chip->open_mutex); + return -EAGAIN; + } + if (chip->mode & CS4231_MODE_OPEN) { + chip->mode |= mode; + up(&chip->open_mutex); + return 0; + } + /* ok. now enable and ack CODEC IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE; + snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | + CS4231_RECORD_IRQ | + CS4231_TIMER_IRQ); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->mode = mode; + up(&chip->open_mutex); + return 0; +} + +static void snd_cs4231_close(cs4231_t *chip, unsigned int mode) +{ + unsigned long flags; + + down(&chip->open_mutex); + chip->mode &= ~mode; + if (chip->mode & CS4231_MODE_OPEN) { + up(&chip->open_mutex); + return; + } + snd_cs4231_calibrate_mute(chip, 1); + + /* disable IRQ */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE; + snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); + + /* now disable record & playback */ + + if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | + CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + } + + /* clear IRQ again */ + snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + outb(0, CS4231P(chip, STATUS)); /* clear IRQ */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_cs4231_calibrate_mute(chip, 0); + + chip->mode = 0; + up(&chip->open_mutex); +} + +/* + * timer open/close + */ + +static int snd_cs4231_timer_open(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_open(chip, CS4231_MODE_TIMER); + return 0; +} + +static int snd_cs4231_timer_close(snd_timer_t * timer) +{ + cs4231_t *chip = snd_timer_chip(timer); + snd_cs4231_close(chip, CS4231_MODE_TIMER); + return 0; +} + +static struct _snd_timer_hardware snd_cs4231_timer_table = +{ + flags: SNDRV_TIMER_HW_AUTO, + resolution: 9945, + ticks: 65535, + open: snd_cs4231_timer_open, + close: snd_cs4231_timer_close, + c_resolution: snd_cs4231_timer_resolution, + start: snd_cs4231_timer_start, + stop: snd_cs4231_timer_stop, +}; + +/* + * ok.. exported functions.. + */ + +static int snd_cs4231_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_pdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + chip->set_playback_format(chip, hw_params, new_pdfr); + return 0; +} + +static int snd_cs4231_playback_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4231_playback_prepare(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->p_dma_size = size; + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1; + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + snd_cs4231_debug(chip); +#endif + return 0; +} + +static int snd_cs4231_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + unsigned char new_cdfr; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | + snd_cs4231_get_rate(params_rate(hw_params)); + chip->set_capture_format(chip, hw_params, new_cdfr); + return 0; +} + +static int snd_cs4231_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4231_capture_prepare(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->c_dma_size = size; + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1; + if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); + } else { + snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count); + snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8)); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4231_overrange(cs4231_t *chip) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&chip->reg_lock, flags); + res = snd_cs4231_in(chip, CS4231_TEST_INIT); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */ + chip->capture_substream->runtime->overrange++; +} + +void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, dev_id, return); + unsigned char status; + + status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); + if (status & CS4231_TIMER_IRQ) { + if (chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + } + if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { + if (status & CS4231_PLAYBACK_IRQ) { + if (chip->mode & CS4231_MODE_PLAY) + snd_pcm_period_elapsed(chip->playback_substream); + if (chip->mode & CS4231_MODE_RECORD) { + snd_cs4231_overrange(chip); + snd_pcm_period_elapsed(chip->capture_substream); + } + } + } else { + if (status & CS4231_PLAYBACK_IRQ) + snd_pcm_period_elapsed(chip->playback_substream); + if (status & CS4231_RECORD_IRQ) { + snd_cs4231_overrange(chip); + snd_pcm_period_elapsed(chip->capture_substream); + } + } + + spin_lock(&chip->reg_lock); + snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); + spin_unlock(&chip->reg_lock); +} + +static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) + return 0; + ptr = chip->p_dma_size - snd_dma_residue(chip->dma1); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) + return 0; + ptr = chip->c_dma_size - snd_dma_residue(chip->dma2); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static int snd_cs4231_probe(cs4231_t *chip) +{ + unsigned long flags; + int i, id, rev; + unsigned char *ptr; + unsigned int hw; + +#if 0 + snd_cs4231_debug(chip); +#endif + id = 0; + for (i = 0; i < 50; i++) { + mb(); + if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) + udelay(2000); + else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2); + id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (id == 0x0a) + break; /* this is valid value */ + } + } + snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id); + if (id != 0x0a) + return -ENODEV; /* no valid device found */ + + if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { + rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7; + snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev); + if (rev == 0x80) { + chip->hardware = CS4231_HW_CS4231; + } else if (rev == 0xa0) { + chip->hardware = CS4231_HW_CS4231A; + } else if (rev == 0xa2) { + chip->hardware = CS4231_HW_CS4232; + } else if (rev == 0xb2) { + chip->hardware = CS4231_HW_CS4232A; + } else if (rev == 0x83) { + chip->hardware = CS4231_HW_CS4236; + } else if (rev == 0x03) { + chip->hardware = CS4231_HW_CS4236B; + } else { + snd_printk("unknown CS chip with version 0x%x\n", rev); + return -ENODEV; /* unknown CS4231 chip? */ + } + } + spin_lock_irqsave(&chip->reg_lock, flags); + inb(CS4231P(chip, STATUS)); /* clear any pendings IRQ */ + outb(0, CS4231P(chip, STATUS)); + mb(); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->image[CS4231_MISC_INFO] = CS4231_MODE2; + switch (chip->hardware) { + case CS4231_HW_INTERWAVE: + chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3; + break; + case CS4231_HW_CS4235: + case CS4231_HW_CS4236B: + case CS4231_HW_CS4237B: + case CS4231_HW_CS4238B: + case CS4231_HW_CS4239: + if (hw == CS4231_HW_DETECT3) + chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3; + else + chip->hardware = CS4231_HW_CS4236; + break; + } + + chip->image[CS4231_IFACE_CTRL] = + (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | + (chip->single_dma ? CS4231_SINGLE_DMA : 0); + chip->image[CS4231_ALT_FEATURE_1] = 0x80; + chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; + ptr = (unsigned char *) &chip->image; + snd_cs4231_mce_down(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 32; i++) /* ok.. fill all CS4231 registers */ + snd_cs4231_out(chip, i, *ptr++); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_up(chip); + snd_cs4231_mce_down(chip); + + mdelay(2); + + /* ok.. try check hardware version for CS4236+ chips */ + if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { + if (chip->hardware == CS4231_HW_CS4236B) { + rev = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff); + id = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_cs4236_ext_out(chip, CS4236_VERSION, rev); + snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id); + if ((id & 0x1f) == 0x1d) { /* CS4235 */ + chip->hardware = CS4231_HW_CS4235; + switch (id >> 5) { + case 4: + case 5: + case 6: + break; + default: + snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x0b) { /* CS4236/B */ + switch (id >> 5) { + case 4: + case 5: + case 6: + case 7: + chip->hardware = CS4231_HW_CS4236B; + break; + default: + snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x08) { /* CS4237B */ + chip->hardware = CS4231_HW_CS4237B; + switch (id >> 5) { + case 4: + case 5: + case 6: + case 7: + break; + default: + snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x09) { /* CS4238B */ + chip->hardware = CS4231_HW_CS4238B; + switch (id >> 5) { + case 5: + case 6: + case 7: + break; + default: + snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id); + } + } else if ((id & 0x1f) == 0x1e) { /* CS4239 */ + chip->hardware = CS4231_HW_CS4239; + switch (id >> 5) { + case 4: + case 5: + case 6: + break; + default: + snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id); + } + } else { + snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id); + } + } + } + return 0; /* all things are ok.. */ +} + +/* + + */ + +static snd_pcm_hardware_t snd_cs4231_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_cs4231_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + + */ + +static int snd_cs4231_playback_open(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_playback; + + /* hardware bug in InterWave chipset */ + if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3) + runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW; + + /* hardware limitation of cheap chips */ + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; + + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + + if (chip->claim_dma) { + if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0) + return err; + } + + if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) { + if (chip->release_dma) + chip->release_dma(chip, chip->dma_private_data, chip->dma1); + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->playback_substream = substream; + snd_pcm_set_sync(substream); + chip->rate_constraint(runtime); + return 0; +} + +static int snd_cs4231_capture_open(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + runtime->hw = snd_cs4231_capture; + + /* hardware limitation of cheap chips */ + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; + + if (chip->claim_dma) { + if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0) + return err; + } + + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + + if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) { + if (chip->release_dma) + chip->release_dma(chip, chip->dma_private_data, chip->dma2); + snd_free_pages(runtime->dma_area, runtime->dma_bytes); + return err; + } + chip->capture_substream = substream; + snd_pcm_set_sync(substream); + chip->rate_constraint(runtime); + return 0; +} + +static int snd_cs4231_playback_close(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_PLAY); + return 0; +} + +static int snd_cs4231_capture_close(snd_pcm_substream_t * substream) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_cs4231_close(chip, CS4231_MODE_RECORD); + return 0; +} + +#ifdef CONFIG_PM + +static void snd_cs4231_suspend(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) + chip->image[reg] = snd_cs4231_in(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4231_resume(cs4231_t *chip) +{ + int reg; + unsigned long flags; + int timeout; + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) { + switch (reg) { + case CS4231_VERSION: + break; + default: + snd_cs4231_out(chip, reg, chip->image[reg]); + break; + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + snd_cs4231_mce_down(chip); +#else + /* The following is a workaround to avoid freeze after resume on TP600E. + This is the first half of copy of snd_cs4231_mce_down(), but doesn't + include rescheduling. -- iwai + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4231_busy_wait(chip); + chip->mce_bit &= ~CS4231_MCE; + timeout = inb(CS4231P(chip, REGSEL)); + outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); + if (timeout == 0x80) + snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); + if ((timeout & CS4231_MCE) == 0 || + !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + snd_cs4231_busy_wait(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#endif +} + +static int snd_cs4231_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + if (chip->suspend) + (*chip->suspend)(chip); + break; + case PM_RESUME: + if (chip->resume) + (*chip->resume)(chip); + break; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_cs4231_free(cs4231_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_cport) { + release_resource(chip->res_cport); + kfree_nocheck(chip->res_cport); + } + if (chip->irq >= 0) { + disable_irq(chip->irq); + if (!(chip->hwshare & CS4231_HWSHARE_IRQ)) + free_irq(chip->irq, (void *) chip); + } + if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) { + snd_dma_disable(chip->dma1); + free_dma(chip->dma1); + } + if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) { + snd_dma_disable(chip->dma2); + free_dma(chip->dma2); + } +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->timer) + snd_device_free(chip->card, chip->timer); + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs4231_dev_free(snd_device_t *device) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + return snd_cs4231_free(chip); +} + +const char *snd_cs4231_chip_id(cs4231_t *chip) +{ + switch (chip->hardware) { + case CS4231_HW_CS4231: return "CS4231"; + case CS4231_HW_CS4231A: return "CS4231A"; + case CS4231_HW_CS4232: return "CS4232"; + case CS4231_HW_CS4232A: return "CS4232A"; + case CS4231_HW_CS4235: return "CS4235"; + case CS4231_HW_CS4236B: return "CS4236B"; + case CS4231_HW_CS4237B: return "CS4237B"; + case CS4231_HW_CS4238B: return "CS4238B"; + case CS4231_HW_CS4239: return "CS4239"; + case CS4231_HW_INTERWAVE: return "AMD InterWave"; + case CS4231_HW_OPL3SA2: return chip->card->shortname; + default: return "???"; + } +} + +int snd_cs4231_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_cs4231_dev_free, + }; + cs4231_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + chip->hardware = hardware; + chip->hwshare = hwshare; + + if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->port = port; + if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) { + snd_cs4231_free(chip); + return -ENODEV; + } + chip->cport = cport; + if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) { + snd_cs4231_free(chip); + return -EBUSY; + } + chip->dma1 = dma1; + if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) { + snd_cs4231_free(chip); + return -EBUSY; + } + if (dma1 == dma2 || dma2 < 0) { + chip->single_dma = 1; + chip->dma2 = chip->dma1; + } else + chip->dma2 = dma2; + + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->rate_constraint = snd_cs4231_xrate; + chip->set_playback_format = snd_cs4231_playback_format; + chip->set_capture_format = snd_cs4231_capture_format; + memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); + + /* global setup */ + if (snd_cs4231_probe(chip) < 0) { + snd_cs4231_free(chip); + return -ENODEV; + } + snd_cs4231_init(chip); + + if (chip->hardware & CS4231_HW_CS4232_MASK) { + if (chip->res_cport == NULL) + snd_printk("CS4232 control port features are not accessible\n"); + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs4231_free(chip); + return err; + } + +#ifdef CONFIG_PM + /* Power Management */ + chip->suspend = snd_cs4231_suspend; + chip->resume = snd_cs4231_resume; + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_cs4231_pm_callback); + if (chip->pm_dev) + chip->pm_dev->data = chip; +#endif + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_cs4231_playback_ops = { + open: snd_cs4231_playback_open, + close: snd_cs4231_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4231_playback_hw_params, + hw_free: snd_cs4231_playback_hw_free, + prepare: snd_cs4231_playback_prepare, + trigger: snd_cs4231_trigger, + pointer: snd_cs4231_playback_pointer, +}; + +static snd_pcm_ops_t snd_cs4231_capture_ops = { + open: snd_cs4231_capture_open, + close: snd_cs4231_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4231_capture_hw_params, + hw_free: snd_cs4231_capture_hw_free, + prepare: snd_cs4231_capture_prepare, + trigger: snd_cs4231_trigger, + pointer: snd_cs4231_capture_pointer, +}; + +static void snd_cs4231_pcm_free(snd_pcm_t *pcm) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_cs4231_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0) + return err; + + spin_lock_init(&chip->reg_lock); + init_MUTEX(&chip->mce_mutex); + init_MUTEX(&chip->open_mutex); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops); + + /* global setup */ + pcm->private_data = chip; + pcm->private_free = snd_cs4231_pcm_free; + pcm->info_flags = 0; + if (chip->single_dma) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + if (chip->hardware != CS4231_HW_INTERWAVE) + pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; + strcpy(pcm->name, snd_cs4231_chip_id(chip)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + chip->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_cs4231_timer_free(snd_timer_t *timer) +{ + cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return); + chip->timer = NULL; +} + +int snd_cs4231_timer(cs4231_t *chip, int device, snd_timer_t **rtimer) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + int err; + + /* Timer initialization */ + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = device; + tid.subdevice = 0; + if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0) + return err; + strcpy(timer->name, snd_cs4231_chip_id(chip)); + timer->private_data = chip; + timer->private_free = snd_cs4231_timer_free; + timer->hw = snd_cs4231_timer_table; + chip->timer = timer; + if (rtimer) + *rtimer = timer; + return 0; +} + +/* + * MIXER part + */ + +static int snd_cs4231_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line", "Aux", "Mic", "Mix" + }; + static char *opl3sa_texts[4] = { + "Line", "CD", "Mic", "Mix" + }; + static char *gusmax_texts[4] = { + "Line", "Synth", "Mic", "Mix" + }; + char **ptexts = texts; + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + + snd_assert(chip->card != NULL, return -EINVAL); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + if (!strcmp(chip->card->driver, "GUS MAX")) + ptexts = gusmax_texts; + switch (chip->hardware) { + case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break; + case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break; + } + strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_cs4231_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4231_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->reg_lock, flags); + left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left; + right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right; + change = left != chip->image[CS4231_LEFT_INPUT] || + right != chip->image[CS4231_RIGHT_INPUT]; + snd_cs4231_out(chip, CS4231_LEFT_INPUT, left); + snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_cs4231_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_cs4231_out(chip, left_reg, val1); + snd_cs4231_out(chip, right_reg, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4231_controls[] = { +CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), +CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1), +CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1), +CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), +CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_cs4231_info_mux, + get: snd_cs4231_get_mux, + put: snd_cs4231_put_mux, +}, +CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), +CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1) +}; + +int snd_cs4231_mixer(cs4231_t *chip) +{ + snd_card_t *card; + int err, idx; + + snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < CS4231_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +EXPORT_SYMBOL(snd_cs4231_out); +EXPORT_SYMBOL(snd_cs4231_in); +EXPORT_SYMBOL(snd_cs4231_outm); +EXPORT_SYMBOL(snd_cs4236_ext_out); +EXPORT_SYMBOL(snd_cs4236_ext_in); +EXPORT_SYMBOL(snd_cs4231_mce_up); +EXPORT_SYMBOL(snd_cs4231_mce_down); +EXPORT_SYMBOL(snd_cs4231_interrupt); +EXPORT_SYMBOL(snd_cs4231_chip_id); +EXPORT_SYMBOL(snd_cs4231_create); +EXPORT_SYMBOL(snd_cs4231_pcm); +EXPORT_SYMBOL(snd_cs4231_mixer); +EXPORT_SYMBOL(snd_cs4231_timer); +EXPORT_SYMBOL(snd_cs4231_info_single); +EXPORT_SYMBOL(snd_cs4231_get_single); +EXPORT_SYMBOL(snd_cs4231_put_single); +EXPORT_SYMBOL(snd_cs4231_info_double); +EXPORT_SYMBOL(snd_cs4231_get_double); +EXPORT_SYMBOL(snd_cs4231_put_double); + +/* + * INIT part + */ + +static int __init alsa_cs4231_init(void) +{ + return 0; +} + +static void __exit alsa_cs4231_exit(void) +{ +} + +module_init(alsa_cs4231_init) +module_exit(alsa_cs4231_exit) diff -Nru linux/sound/isa/cs423x/cs4232.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4232.c --- linux/sound/isa/cs423x/cs4232.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4232.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,2 @@ +#define CS4232 +#include "cs4236.c" diff -Nru linux/sound/isa/cs423x/cs4236.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236.c --- linux/sound/isa/cs423x/cs4236.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,641 @@ +/* + * Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t cs4231_t + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifdef CS4232 +MODULE_DESCRIPTION("Cirrus Logic CS4232"); +MODULE_DEVICES("{{Turtle Beach,TBS-2000}," + "{Turtle Beach,Tropez Plus}," + "{SIC CrystalWave 32}," + "{Hewlett Packard,Omnibook 5500}," + "{TerraTec,Maestro 32/96}," + "{Philips,PCA70PS}}"); +#else +MODULE_DESCRIPTION("Cirrus Logic CS4235-9"); +MODULE_DEVICES("{{Crystal Semiconductors,CS4235}," + "{Crystal Semiconductors,CS4236}," + "{Crystal Semiconductors,CS4237}," + "{Crystal Semiconductors,CS4238}," + "{Crystal Semiconductors,CS4239}," + "{Acer,AW37}," + "{Acer,AW35/Pro}," + "{Crystal,3D}," + "{Crystal Computer,TidalWave128}," + "{Dell,Optiplex GX1}," + "{Dell,Workstation 400 sound}," + "{EliteGroup,P5TX-LA sound}," + "{Gallant,SC-70P}," + "{Gateway,E1000 Onboard CS4236B}," + "{Genius,Sound Maker 3DJ}," + "{Hewlett Packard,HP6330 sound}," + "{IBM,PC 300PL sound}," + "{IBM,Aptiva 2137 E24}," + "{IBM,IntelliStation M Pro}," + "{Intel,Marlin Spike Mobo CS4235}," + "{Guillemot,MaxiSound 16 PnP}," + "{NewClear,3D}," + "{TerraTec,AudioSystem EWS64L/XL}," + "{Typhoon Soundsystem,CS4236B}," + "{Turtle Beach,Malibu}," + "{Unknown,Digital PC 5000 Onboard}}"); +#endif + +#ifdef CS4232 +#define IDENT "CS4232" +#else +#define IDENT "CS4236+" +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " IDENT " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " IDENT " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable " IDENT " soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_cport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_cport, "Control port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_cport, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sb_port, "SB port # for " IDENT " driver (optional)."); +MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for " IDENT " driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); + +struct snd_card_cs4236 { + struct resource *res_sb_port; +#ifdef __ISAPNP__ + struct isapnp_dev *wss; + struct isapnp_dev *ctrl; + struct isapnp_dev *mpu; +#endif +}; + +static snd_card_t *snd_cs4236_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_cs4236_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_cs4236_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \ + } +#define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \ + ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \ + } +#define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \ + } + + +#ifdef CS4232 +static struct isapnp_card_id snd_card_pnpids[] __devinitdata = { + /* Philips PCA70PS */ + ISAPNP_CS4232_1('C','S','C',0x0d32,0x0000,0x0010,0xb006), + /* TerraTec Maestro 32/96 (CS4232) */ + ISAPNP_CS4232('C','S','C',0x1a32,0x0000,0x0010,0x0003), + /* HP Omnibook 5500 onboard */ + ISAPNP_CS4232('C','S','C',0x4232,0x0000,0x0002,0x0003), + /* Turtle Beach TBS-2000 (CS4232) */ + ISAPNP_CS4232('C','S','C',0x7532,0x0000,0x0010,0xb006), + /* Turtle Beach Tropez Plus (CS4232) */ + ISAPNP_CS4232_1('C','S','C',0x7632,0x0000,0x0010,0xb006), + /* SIC CrystalWave 32 (CS4232) */ + ISAPNP_CS4232('C','S','C',0xf032,0x0000,0x0010,0x0003), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; +#else /* CS4236 */ +static struct isapnp_card_id snd_card_pnpids[] __devinitdata = { + /* Intel Marlin Spike Motherboard - CS4235 */ + ISAPNP_CS4232('C','S','C',0x0225,0x0000,0x0010,0x0003), + /* Intel Marlin Spike Motherboard (#2) - CS4235 */ + ISAPNP_CS4232('C','S','C',0x0225,0x0100,0x0110,0x0103), + /* Genius Sound Maker 3DJ - CS4237B */ + ISAPNP_CS4232('C','S','C',0x0437,0x0000,0x0010,0x0003), + /* Digital PC 5000 Onboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0x0735,0x0000,0x0010), + /* some uknown CS4236B */ + ISAPNP_CS4232('C','S','C',0x0b35,0x0000,0x0010,0x0003), + /* CS4235 on mainboard without MPU */ + ISAPNP_CS4232_WOMPU('C','S','C',0x1425,0x0100,0x0110), + /* Gateway E1000 Onboard CS4236B */ + ISAPNP_CS4232('C','S','C',0x1335,0x0000,0x0010,0x0003), + /* HP 6330 Onboard sound */ + ISAPNP_CS4232('C','S','C',0x1525,0x0100,0x0110,0x0103), + /* Crystal Computer TidalWave128 */ + ISAPNP_CS4232('C','S','C',0x1e37,0x0000,0x0010,0x0003), + /* ACER AW37 - CS4235 */ + ISAPNP_CS4232('C','S','C',0x4236,0x0000,0x0010,0x0003), + /* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */ + ISAPNP_CS4232('C','S','C',0x4237,0x0000,0x0010,0x0003), + /* Crystal 3D - CS4237B */ + ISAPNP_CS4232('C','S','C',0x4336,0x0000,0x0010,0x0003), + /* Typhoon Soundsystem PnP - CS4236B */ + ISAPNP_CS4232('C','S','C',0x4536,0x0000,0x0010,0x0003), + /* TerraTec AudioSystem EWS64XL - CS4236B */ + ISAPNP_CS4232('C','S','C',0xa836,0xa800,0xa810,0xa803), + /* Crystal Semiconductors CS4237B */ + ISAPNP_CS4232('C','S','C',0x4637,0x0000,0x0010,0x0003), + /* NewClear 3D - CX4237B-XQ3 */ + ISAPNP_CS4232('C','S','C',0x4837,0x0000,0x0010,0x0003), + /* Dell Optiplex GX1 - CS4236B */ + ISAPNP_CS4232('C','S','C',0x6835,0x0000,0x0010,0x0003), + /* Dell P410 motherboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0x6835,0x0000,0x0010), + /* Dell Workstation 400 Onboard - CS4236B */ + ISAPNP_CS4232('C','S','C',0x6836,0x0000,0x0010,0x0003), + /* Turtle Beach Malibu - CS4237B */ + ISAPNP_CS4232('C','S','C',0x7537,0x0000,0x0010,0x0003), + /* CS4235 - onboard */ + ISAPNP_CS4232('C','S','C',0x8025,0x0100,0x0110,0x0103), + /* IBM PC 300PL Onboard - CS4236B */ + ISAPNP_CS4232_WOMPU('C','S','C',0xe836,0x0000,0x0010), + /* IBM Aptiva 2137 E24 Onboard - CS4237B */ + ISAPNP_CS4232('C','S','C',0x8037,0x0000,0x0010,0x0003), + /* IBM IntelliStation M Pro motherboard */ + ISAPNP_CS4232_WOMPU('C','S','C',0xc835,0x0000,0x0010), + /* Guillemot MaxiSound 16 PnP - CS4236B */ + ISAPNP_CS4232('C','S','C',0x9836,0x0000,0x0010,0x0003), + /* Gallant SC-70P */ + ISAPNP_CS4232('C','S','C',0x9837,0x0000,0x0010,0x0003), + /* ACER AW37/Pro - CS4235 */ + ISAPNP_CS4232('C','S','C',0xd925,0x0000,0x0010,0x0003), + /* ACER AW35/Pro - CS4237B */ + ISAPNP_CS4232('C','S','C',0xd937,0x0000,0x0010,0x0003), + /* CS4235 without MPU401 */ + ISAPNP_CS4232_WOMPU('C','S','C',0xe825,0x0100,0x0110), + /* CS4236B */ + ISAPNP_CS4232('C','S','C',0xf235,0x0000,0x0010,0x0003), + /* CS4236B */ + ISAPNP_CS4232('C','S','C',0xf238,0x0000,0x0010,0x0003), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; +#endif + +ISAPNP_CARD_TABLE(snd_card_pnpids); + +static int __init snd_card_cs4236_isapnp(int dev, struct snd_card_cs4236 *acard) +{ + const struct isapnp_card_id *id = snd_cs4236_isapnp_id[dev]; + struct isapnp_card *card = snd_cs4236_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->wss->active) { + acard->wss = NULL; + return -EBUSY; + } + acard->ctrl = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->ctrl->active) { + acard->wss = acard->ctrl = NULL; + return -EBUSY; + } + if (id->devs[2].vendor && id->devs[2].function) { + acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->mpu->active) { + acard->wss = acard->ctrl = acard->mpu = NULL; + return -EBUSY; + } + } + + /* WSS initialization */ + pdev = acard->wss; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 4); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_sb_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_sb_port[dev], 16); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev] < 0 ? 4 : snd_dma2[dev], 1); + if (pdev->activate(pdev)<0) { + printk(KERN_ERR IDENT " isapnp configure failed for WSS (out of resources?)\n"); + return -EBUSY; + } + snd_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_sb_port[dev] = pdev->resource[2].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start == 4 ? -1 : pdev->dma_resource[1].start; + snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n", + snd_port[dev], snd_fm_port[dev], snd_sb_port[dev]); + snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n", + snd_irq[dev], snd_dma1[dev], snd_dma2[dev]); + /* CTRL initialization */ + pdev = acard->ctrl; + if (pdev->prepare(pdev) < 0) { + acard->wss->deactivate(acard->wss); + return -EAGAIN; + } + if (snd_cport[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_cport[dev], 8); + if (pdev->activate(pdev)<0) { + printk(KERN_ERR IDENT " isapnp configure failed for control (out of resources?)\n"); + acard->wss->deactivate(acard->wss); + return -EBUSY; + } + snd_cport[dev] = pdev->resource[0].start; + snd_printdd("isapnp CTRL: control port=0x%lx\n", snd_cport[dev]); + /* MPU initialization */ + if (acard->mpu) { + pdev = acard->mpu; + if (pdev->prepare(pdev) < 0) { + acard->wss->deactivate(acard->wss); + acard->ctrl->deactivate(acard->ctrl); + return -EAGAIN; + } + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_mpu_port[dev] = SNDRV_AUTO_PORT; + snd_mpu_irq[dev] = SNDRV_AUTO_IRQ; + printk(KERN_ERR IDENT " isapnp configure failed for MPU (out of resources?)\n"); + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + if (pdev->irq_resource[0].flags & IORESOURCE_IRQ) { + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } else { + snd_mpu_irq[dev] = -1; /* disable interrupt */ + } + } + snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", snd_mpu_port[dev], snd_mpu_irq[dev]); + } + return 0; +} + +static void snd_card_cs4236_deactivate(struct snd_card_cs4236 *acard) +{ + if (acard->wss) { + acard->wss->deactivate(acard->wss); + acard->wss = NULL; + } + if (acard->ctrl) { + acard->ctrl->deactivate(acard->ctrl); + acard->ctrl = NULL; + } + if (acard->mpu) { + acard->mpu->deactivate(acard->mpu); + acard->mpu = NULL; + } +} +#endif + +static void snd_card_cs4236_free(snd_card_t *card) +{ + struct snd_card_cs4236 *acard = (struct snd_card_cs4236 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_cs4236_deactivate(acard); +#endif + if (acard->res_sb_port) { + release_resource(acard->res_sb_port); + kfree_nocheck(acard->res_sb_port); + } + } +} + +static int __init snd_card_cs4236_probe(int dev) +{ + snd_card_t *card; + struct snd_card_cs4236 *acard; + snd_pcm_t *pcm = NULL; + cs4231_t *chip; + opl3_t *opl3; + int err; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_cport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_cport\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_cs4236)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_card_cs4236 *)card->private_data; + card->private_free = snd_card_cs4236_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_card_cs4236_isapnp(dev, acard))<0) { + printk(KERN_ERR "isapnp detection failed and probing for " IDENT " is not supported\n"); + snd_card_free(card); + return -ENXIO; + } +#endif + if (snd_mpu_port[dev] < 0) + snd_mpu_port[dev] = SNDRV_AUTO_PORT; + if (snd_fm_port[dev] < 0) + snd_fm_port[dev] = SNDRV_AUTO_PORT; + if (snd_sb_port[dev] < 0) + snd_sb_port[dev] = SNDRV_AUTO_PORT; + if (snd_sb_port[dev] != SNDRV_AUTO_PORT) + if ((acard->res_sb_port = request_region(snd_sb_port[dev], 16, IDENT " SB")) == NULL) { + printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", snd_sb_port[dev]); + snd_card_free(card); + return -ENOMEM; + } + +#ifdef CS4232 + if ((err = snd_cs4231_create(card, + snd_port[dev], + snd_cport[dev], + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, + 0, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + +#else /* CS4236 */ + if ((err = snd_cs4236_create(card, + snd_port[dev], + snd_cport[dev], + snd_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, + 0, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4236_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } +#endif + + if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) { + if (snd_opl3_create(card, + snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_OPL3_CS, 0, &opl3) < 0) { + printk(KERN_ERR IDENT ": OPL3 not detected\n"); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + } + + if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) { + if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], + snd_mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) + printk(KERN_ERR IDENT ": MPU401 not detected\n"); + } + strcpy(card->driver, pcm->name); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", + pcm->name, + chip->port, + snd_irq[dev], + snd_dma1[dev]); + if (snd_dma1[dev] >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_cs4236_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_cs4236_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_cs4236_isapnp_cards[dev] = card; + snd_cs4236_isapnp_id[dev] = id; + res = snd_card_cs4236_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_cs423x_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_card_cs4236_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_card_pnpids, snd_cs4236_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + printk(KERN_ERR IDENT " soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_cs423x_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_cs4236_cards[idx]); +} + +module_init(alsa_card_cs423x_init) +module_exit(alsa_card_cs423x_exit) + +#ifndef MODULE + +/* format is: snd-cs4232=snd_enable,snd_index,snd_id,snd_isapnp,snd_port, + snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port, + snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size, + snd_dma2,snd_dma2_size */ +/* format is: snd-cs4236=snd_enable,snd_index,snd_id,snd_isapnp,snd_port, + snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port, + snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size, + snd_dma2,snd_dma2_size */ + +static int __init alsa_card_cs423x_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_cport[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +#ifdef CS4232 +__setup("snd-cs4232=", alsa_card_cs423x_setup); +#else /* CS4236 */ +__setup("snd-cs4236=", alsa_card_cs423x_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/cs423x/cs4236_lib.c linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236_lib.c --- linux/sound/isa/cs423x/cs4236_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/cs423x/cs4236_lib.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,980 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of CS4235/4236B/4237B/4238B/4239 chips + * + * Note: + * ----- + * + * Bugs: + * ----- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Indirect control registers (CS4236B+) + * + * C0 + * D8: WSS reset (all chips) + * + * C1 (all chips except CS4236) + * D7-D5: version + * D4-D0: chip id + * 11101 - CS4235 + * 01011 - CS4236B + * 01000 - CS4237B + * 01001 - CS4238B + * 11110 - CS4239 + * + * C2 + * D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239) + * D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B) + * + * C3 + * D7: 3D Enable (CS4237B) + * D6: 3D Mono Enable (CS4237B) + * D5: 3D Serial Output (CS4237B,CS4238B) + * D4: 3D Enable (CS4235,CS4238B,CS4239) + * + * C4 + * D7: consumer serial port enable (CS4237B,CS4238B) + * D6: channels status block reset (CS4237B,CS4238B) + * D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B) + * D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B) + * + * C5 lower channel status (digital serial data description) (CS4237B,CS4238B) + * D7-D6: first two bits of category code + * D5: lock + * D4-D3: pre-emphasis (0 = none, 1 = 50/15us) + * D2: copy/copyright (0 = copy inhibited) + * D1: 0 = digital audio / 1 = non-digital audio + * + * C6 upper channel status (digital serial data description) (CS4237B,CS4238B) + * D7-D6: sample frequency (0 = 44.1kHz) + * D5: generation status (0 = no indication, 1 = original/commercially precaptureed data) + * D4-D0: category code (upper bits) + * + * C7 reserved (must write 0) + * + * C8 wavetable control + * D7: volume control interrupt enable (CS4235,CS4239) + * D6: hardware volume control format (CS4235,CS4239) + * D3: wavetable serial port enable (all chips) + * D2: DSP serial port switch (all chips) + * D1: disable MCLK (all chips) + * D0: force BRESET low (all chips) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of CS4235/4236B/4237B/4238B/4239 chips"); +MODULE_LICENSE("GPL"); + +#define chip_t cs4231_t + +/* + * + */ + +static unsigned char snd_cs4236_ext_map[18] = { + /* CS4236_LEFT_LINE */ 0xff, + /* CS4236_RIGHT_LINE */ 0xff, + /* CS4236_LEFT_MIC */ 0xdf, + /* CS4236_RIGHT_MIC */ 0xdf, + /* CS4236_LEFT_MIX_CTRL */ 0xe0 | 0x18, + /* CS4236_RIGHT_MIX_CTRL */ 0xe0, + /* CS4236_LEFT_FM */ 0xbf, + /* CS4236_RIGHT_FM */ 0xbf, + /* CS4236_LEFT_DSP */ 0xbf, + /* CS4236_RIGHT_DSP */ 0xbf, + /* CS4236_RIGHT_LOOPBACK */ 0xbf, + /* CS4236_DAC_MUTE */ 0xe0, + /* CS4236_ADC_RATE */ 0x01, /* 48kHz */ + /* CS4236_DAC_RATE */ 0x01, /* 48kHz */ + /* CS4236_LEFT_MASTER */ 0xbf, + /* CS4236_RIGHT_MASTER */ 0xbf, + /* CS4236_LEFT_WAVE */ 0xbf, + /* CS4236_RIGHT_WAVE */ 0xbf +}; + +/* + * + */ + +static void snd_cs4236_ctrl_out(cs4231_t *chip, unsigned char reg, unsigned char val) +{ + outb(reg, chip->cport + 3); + outb(chip->cimage[reg] = val, chip->cport + 4); +} + +static unsigned char snd_cs4236_ctrl_in(cs4231_t *chip, unsigned char reg) +{ + outb(reg, chip->cport + 3); + return inb(chip->cport + 4); +} + +/* + * PCM + */ + +#define CLOCKS 8 + +static ratnum_t clocks[CLOCKS] = { + { num: 16934400, den_min: 353, den_max: 353, den_step: 1 }, + { num: 16934400, den_min: 529, den_max: 529, den_step: 1 }, + { num: 16934400, den_min: 617, den_max: 617, den_step: 1 }, + { num: 16934400, den_min: 1058, den_max: 1058, den_step: 1 }, + { num: 16934400, den_min: 1764, den_max: 1764, den_step: 1 }, + { num: 16934400, den_min: 2117, den_max: 2117, den_step: 1 }, + { num: 16934400, den_min: 2558, den_max: 2558, den_step: 1 }, + { num: 16934400/16, den_min: 21, den_max: 192, den_step: 1 } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: CLOCKS, + rats: clocks, +}; + +static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime) +{ + return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); +} + +static unsigned char divisor_to_rate_register(unsigned int divisor) +{ + switch (divisor) { + case 353: return 1; + case 529: return 2; + case 617: return 3; + case 1058: return 4; + case 1764: return 5; + case 2117: return 6; + case 2558: return 7; + default: + snd_runtime_check(divisor >= 21 && divisor <= 192, return 192); + return divisor; + } +} + +static void snd_cs4236_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr) +{ + unsigned long flags; + unsigned char rate = divisor_to_rate_register(params->rate_den); + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set fast playback format change and clean playback FIFO */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); + snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10); + snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4236_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr) +{ + unsigned long flags; + unsigned char rate = divisor_to_rate_register(params->rate_den); + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set fast capture format change and clean capture FIFO */ + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); + snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20); + snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +#ifdef CONFIG_PM + +static void snd_cs4236_suspend(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) + chip->image[reg] = snd_cs4231_in(chip, reg); + for (reg = 0; reg < 18; reg++) + chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg)); + for (reg = 2; reg < 9; reg++) + chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4236_resume(cs4231_t *chip) +{ + int reg; + unsigned long flags; + + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + for (reg = 0; reg < 32; reg++) { + switch (reg) { + case CS4236_EXT_REG: + case CS4231_VERSION: + case 27: /* why? CS4235 - master left */ + case 29: /* why? CS4235 - master right */ + break; + default: + snd_cs4231_out(chip, reg, chip->image[reg]); + break; + } + } + for (reg = 0; reg < 18; reg++) + snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]); + for (reg = 2; reg < 9; reg++) { + switch (reg) { + case 7: + break; + default: + snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); +} + +#endif /* CONFIG_PM */ + +int snd_cs4236_create(snd_card_t * card, + unsigned long port, + unsigned long cport, + int irq, int dma1, int dma2, + unsigned short hardware, + unsigned short hwshare, + cs4231_t ** rchip) +{ + cs4231_t *chip; + unsigned char ver1, ver2; + int err, reg; + + *rchip = NULL; + if (hardware == CS4231_HW_DETECT) + hardware = CS4231_HW_DETECT3; + if (cport < 0x100) { + snd_printk("please, specify control port for CS4236+ chips\n"); + return -ENODEV; + } + if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0) + return err; + if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) { + snd_device_free(card, chip); + return -ENODEV; + } +#if 0 + { + int idx; + for (idx = 0; idx < 8; idx++) + snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx)); + for (idx = 0; idx < 9; idx++) + snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx)); + } +#endif + ver1 = snd_cs4236_ctrl_in(chip, 1); + ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); + snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2); + if (ver1 != ver2) { + snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport); + snd_device_free(card, chip); + return -ENODEV; + } + snd_cs4236_ctrl_out(chip, 0, 0x00); + snd_cs4236_ctrl_out(chip, 2, 0xff); + snd_cs4236_ctrl_out(chip, 3, 0x00); + snd_cs4236_ctrl_out(chip, 4, 0x80); + snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE); + snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); + snd_cs4236_ctrl_out(chip, 7, 0x00); + /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */ + /* is working with this setup, other hardware should have */ + /* different signal paths and this value should be selectable */ + /* in the future */ + snd_cs4236_ctrl_out(chip, 8, 0x8c); + chip->rate_constraint = snd_cs4236_xrate; + chip->set_playback_format = snd_cs4236_playback_format; + chip->set_capture_format = snd_cs4236_capture_format; +#ifdef CONFIG_PM + chip->suspend = snd_cs4236_suspend; + chip->resume = snd_cs4236_resume; +#endif + + /* initialize extended registers */ + for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++) + snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]); + + /* initialize compatible but more featured registers */ + snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40); + snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40); + snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); + snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); + snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); + snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); + snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); + snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff); + snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); + switch (chip->hardware) { + case CS4231_HW_CS4235: + case CS4231_HW_CS4239: + snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff); + snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff); + break; + } + + *rchip = chip; + return 0; +} + +int snd_cs4236_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0) + return err; + pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER + */ + +#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_single, \ + get: snd_cs4236_get_single, put: snd_cs4236_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_cs4236_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_cs4236_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_cs4236_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val; + change = val != chip->eimage[CS4236_REG(reg)]; + snd_cs4236_ext_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_single, \ + get: snd_cs4236_get_singlec, put: snd_cs4236_put_singlec, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_cs4236_get_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_cs4236_put_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->cimage[reg] & ~(mask << shift)) | val; + change = val != chip->cimage[reg]; + snd_cs4236_ctrl_out(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4236_get_double, put: snd_cs4236_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_cs4236_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_cs4236_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_cs4236_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1; + val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; + change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)]; + snd_cs4236_ext_out(chip, left_reg, val1); + snd_cs4236_ext_out(chip, right_reg, val2); + } else { + val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != chip->eimage[CS4236_REG(left_reg)]; + snd_cs4236_ext_out(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4236_get_double1, put: snd_cs4236_put_double1, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_cs4236_get_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_cs4236_put_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)]; + snd_cs4231_out(chip, left_reg, val1); + snd_cs4236_ext_out(chip, right_reg, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_MASTER_DIGITAL(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4236_get_master_digital, put: snd_cs4236_put_master_digital, \ + private_value: 71 << 24 } + +static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol) +{ + return (vol < 64) ? 63 - vol : 64 + (71 - vol); +} + +static int snd_cs4236_get_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f); + ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4236_put_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1, val2; + + val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f); + val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f); + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1; + val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2; + change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)]; + snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1); + snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4235_OUTPUT_ACCU(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_double, \ + get: snd_cs4235_get_output_accu, put: snd_cs4235_put_output_accu, \ + private_value: 3 << 24 } + +static inline int snd_cs4235_mixer_output_accu_get_volume(int vol) +{ + switch ((vol >> 5) & 3) { + case 0: return 1; + case 1: return 3; + case 2: return 2; + case 3: return 0; + } + return 3; +} + +static inline int snd_cs4235_mixer_output_accu_set_volume(int vol) +{ + switch (vol & 3) { + case 0: return 3 << 5; + case 1: return 0 << 5; + case 2: return 2 << 5; + case 3: return 1 << 5; + } + return 1 << 5; +} + +static int snd_cs4235_get_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]); + ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4235_put_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1, val2; + + val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]); + val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]); + spin_lock_irqsave(&chip->reg_lock, flags); + val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1; + val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2; + change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER]; + snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1); + snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define CS4236_CONTROLS (sizeof(snd_cs4236_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_controls[] = { + +CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_MASTER_DIGITAL("Master Digital Volume", 0), + +CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), + +CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), + +CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), +CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1), + +CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), +CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1), + +CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), +CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1), + +CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), + +CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1), +CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), + +CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), + +CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), + +CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), +CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), + +CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), + +CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1) +}; + +#define CS4235_CONTROLS (sizeof(snd_cs4235_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4235_controls[] = { + +CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), +CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1), + +CS4235_OUTPUT_ACCU("Playback Volume", 0), + +CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_MASTER_DIGITAL("Master Digital Volume", 0), + +CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), + +CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), + +CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), + +CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), + +CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), + +CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), + +CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1), +CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0), + +CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), + +CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), +CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), + +CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), + +CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), + +CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), +}; + +#define CS4236_IEC958_ENABLE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_cs4236_info_single, \ + get: snd_cs4236_get_iec958_switch, put: snd_cs4236_put_iec958_switch, \ + private_value: 1 << 16 } + +static int snd_cs4236_get_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0; +#if 0 + printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", + snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), + snd_cs4236_ctrl_in(chip, 3), + snd_cs4236_ctrl_in(chip, 4), + snd_cs4236_ctrl_in(chip, 5), + snd_cs4236_ctrl_in(chip, 6), + snd_cs4236_ctrl_in(chip, 8)); +#endif + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4236_put_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs4231_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short enable, val; + + enable = ucontrol->value.integer.value[0] & 1; + + down(&chip->mce_mutex); + snd_cs4231_mce_up(chip); + spin_lock_irqsave(&chip->reg_lock, flags); + val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1); + change = val != chip->image[CS4231_ALT_FEATURE_1]; + snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val); + val = snd_cs4236_ctrl_in(chip, 4) | 0xc0; + snd_cs4236_ctrl_out(chip, 4, val); + udelay(100); + val &= ~0x40; + snd_cs4236_ctrl_out(chip, 4, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_cs4231_mce_down(chip); + up(&chip->mce_mutex); + +#if 0 + printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", + snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), + snd_cs4236_ctrl_in(chip, 3), + snd_cs4236_ctrl_in(chip, 4), + snd_cs4236_ctrl_in(chip, 5), + snd_cs4236_ctrl_in(chip, 6), + snd_cs4236_ctrl_in(chip, 8)); +#endif + return change; +} + +#define CS4236_IEC958_CONTROLS (sizeof(snd_cs4236_iec958_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_iec958_controls[] = { +CS4236_IEC958_ENABLE("IEC958 Output Enable", 0), +CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0), +CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0), +CS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0), +CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0), +CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0) +}; + +#define CS4236_3D_CONTROLS_CS4235 (sizeof(snd_cs4236_3d_controls_cs4235)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4235[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1) +}; + +#define CS4236_3D_CONTROLS_CS4237 (sizeof(snd_cs4236_3d_controls_cs4237)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4237[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), +CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1), +CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0), +CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) +}; + +#define CS4236_3D_CONTROLS_CS4238 (sizeof(snd_cs4236_3d_controls_cs4238)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4238[] = { +CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0), +CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1), +CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1), +CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0) +}; + +int snd_cs4236_mixer(cs4231_t *chip) +{ + snd_card_t *card; + int err, idx, count; + snd_kcontrol_new_t *kcontrol; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + card = chip->card; + strcpy(card->mixername, snd_cs4231_chip_id(chip)); + + if (chip->hardware == CS4231_HW_CS4235 || + chip->hardware == CS4231_HW_CS4239) { + for (idx = 0; idx < CS4235_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0) + return err; + } + } else { + for (idx = 0; idx < CS4236_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0) + return err; + } + } + switch (chip->hardware) { + case CS4231_HW_CS4235: + case CS4231_HW_CS4239: + count = CS4236_3D_CONTROLS_CS4235; + kcontrol = snd_cs4236_3d_controls_cs4235; + break; + case CS4231_HW_CS4237B: + count = CS4236_3D_CONTROLS_CS4237; + kcontrol = snd_cs4236_3d_controls_cs4237; + break; + case CS4231_HW_CS4238B: + count = CS4236_3D_CONTROLS_CS4238; + kcontrol = snd_cs4236_3d_controls_cs4238; + break; + default: + count = -1; + kcontrol = NULL; + } + for (idx = 0; idx < count; idx++, kcontrol++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0) + return err; + } + if (chip->hardware == CS4231_HW_CS4237B || + chip->hardware == CS4231_HW_CS4238B) { + for (idx = 0; idx < CS4236_IEC958_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0) + return err; + } + } + return 0; +} + +EXPORT_SYMBOL(snd_cs4236_create); +EXPORT_SYMBOL(snd_cs4236_pcm); +EXPORT_SYMBOL(snd_cs4236_mixer); + +/* + * INIT part + */ + +static int __init alsa_cs4236_init(void) +{ + return 0; +} + +static void __exit alsa_cs4236_exit(void) +{ +} + +module_init(alsa_cs4236_init) +module_exit(alsa_cs4236_exit) diff -Nru linux/sound/isa/dt0197h.c linux-2.4.19-pre5-mjc/sound/isa/dt0197h.c --- linux/sound/isa/dt0197h.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/dt0197h.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,393 @@ + +/* + card-dt0197h.c - driver for Diamond Technologies DT-0197H based soundcards. + Copyright (C) 1999 by Massimo Piccioni + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include +#include + +#define chip_t sb_t + +#define PFX "dt0197h: " + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("Diamond Technologies DT-0197H"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Diamond Technologies,DT-0197H}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* PnP setup */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for dt0197h based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for dt0197h based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable dt0197h based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for dt0197h driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +struct snd_card_dt0197h { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; + struct isapnp_dev *devopl; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_dt0197h_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_dt0197h_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_dt0197h_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_dt0197h_pnpids[] __devinitdata = { + /* DT197A30 */ + { + ISAPNP_CARD_ID('R','W','B',0x1688), + devs: { ISAPNP_DEVICE_ID('@','@','@',0x0001), + ISAPNP_DEVICE_ID('@','X','@',0x0001), + ISAPNP_DEVICE_ID('@','H','@',0x0001) } + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_dt0197h_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-dt0197h" + + +#ifdef __ISAPNP__ +static int __init snd_card_dt0197h_isapnp(int dev, struct snd_card_dt0197h *acard) +{ + const struct isapnp_card_id *id = snd_dt0197h_isapnp_id[dev]; + struct isapnp_card *card = snd_dt0197h_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devmpu->active) { + acard->dev = acard->devmpu = NULL; + return -EBUSY; + } + acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->devopl->active) { + acard->dev = acard->devmpu = acard->devopl = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_dma8[dev] = pdev->dma_resource[0].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + pdev = acard->devmpu; + if (pdev || pdev->prepare(pdev)<0) + return 0; + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], + 2); + if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], + 1); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "MPU-401 isapnp configure failure\n"); + snd_mpu_port[dev] = -1; + acard->devmpu = NULL; + } else { + snd_mpu_port[dev] = pdev->resource[0].start; + snd_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + pdev = acard->devopl; + if (pdev == NULL || pdev->prepare(pdev)<0) + return 0; + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4); + + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "OPL isapnp configure failure\n"); + snd_fm_port[dev] = -1; + acard->devopl = NULL; + } else { + snd_fm_port[dev] = pdev->resource[0].start; + } + + return 0; +} + +static void snd_card_dt0197h_deactivate(struct snd_card_dt0197h *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } + if (acard->devmpu) { + acard->devmpu->deactivate(acard->devmpu); + acard->devmpu = NULL; + } + if (acard->devopl) { + acard->devopl->deactivate(acard->devopl); + acard->devopl = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_card_dt0197h_free(snd_card_t *card) +{ + struct snd_card_dt0197h *acard = (struct snd_card_dt0197h *)card->private_data; + + if (acard != NULL) { +#ifdef __ISAPNP__ + snd_card_dt0197h_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_dt0197h_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_dt0197h *acard; + opl3_t *opl3; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_dt0197h))) == NULL) + return -ENOMEM; + acard = (struct snd_card_dt0197h *)card->private_data; + card->private_free = snd_card_dt0197h_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_dt0197h_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + printk(KERN_ERR PFX "you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, snd_port[dev], + snd_irq[dev], + snd_sb16dsp_interrupt, + snd_dma8[dev], + -1, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if (snd_mpu_port[dev] > 0) { + if (snd_mpu401_uart_new(card, 0, + MPU401_HW_MPU401, + snd_mpu_port[dev], 0, + snd_mpu_irq[dev], + SA_INTERRUPT, + NULL) < 0) + printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n", + snd_mpu_port[dev]); + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, + snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx ?\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { + if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, "DT-0197H"); + strcpy(card->shortname, "Diamond Tech. DT-0197H"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, chip->name, chip->port, + snd_irq[dev], snd_dma8[dev]); + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_dt0197h_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_dt0197h_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_dt0197h_isapnp_cards[dev] = card; + snd_dt0197h_isapnp_id[dev] = id; + res = snd_card_dt0197h_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_dt0197h_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_dt0197h_pnpids, snd_dt0197h_isapnp_detect); +#else + printk(KERN_ERR PFX "you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + printk(KERN_ERR "no DT-0197H based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_dt0197h_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_dt0197h_cards[dev]); +} + +module_init(alsa_card_dt0197h_init) +module_exit(alsa_card_dt0197h_exit) + +#ifndef MODULE + +/* format is: snd-dt0197h=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_mpu_irq,snd_dma8,snd_dma8_size */ + +static int __init alsa_card_dt0197h_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-dt0197h=", alsa_card_dt0197h_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/es1688/Makefile linux-2.4.19-pre5-mjc/sound/isa/es1688/Makefile --- linux/sound/isa/es1688/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/es1688/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,25 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _es1688.o + +list-multi := snd-es1688-lib.o snd-es1688.o + +export-objs := es1688_lib.o + +snd-es1688-lib-objs := es1688_lib.o +snd-es1688-objs := es1688.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-es1688-lib.o + +include $(TOPDIR)/Rules.make + +snd-es1688-lib.o: $(snd-es1688-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-lib-objs) + +snd-es1688.o: $(snd-es1688-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1688-objs) diff -Nru linux/sound/isa/es1688/es1688.c linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688.c --- linux/sound/isa/es1688/es1688.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,242 @@ +/* + * Driver for generic ESS AudioDrive ESx688 soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define chip_t es1688_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ESS ESx688 AudioDrive"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," + "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102}," + "{ESS,ES688 AudioDrive,pnp:ESS6881}," + "{ESS,ES1688 AudioDrive,pnp:ESS1681}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ESx688 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ESx688 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ESx688 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for ESx688 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_audiodrive_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dmas[] = {1, 3, 0, -1}; + int irq, dma, mpu_irq; + snd_card_t *card; + es1688_t *chip; + opl3_t *opl3; + snd_pcm_t *pcm; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + mpu_irq = snd_mpu_irq[dev]; + dma = snd_dma8[dev]; + if (dma == SNDRV_AUTO_DMA) { + if ((dma = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA\n"); + return -EBUSY; + } + } + + if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev], + irq, mpu_irq, dma, + ES1688_HW_AUTO, &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) { + printk(KERN_ERR "es1688: opl3 not detected at 0x%lx\n", chip->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (mpu_irq >= 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, + chip->mpu_port, 0, + mpu_irq, + SA_INTERRUPT, + NULL)) < 0) { + snd_card_free(card); + return err; + } + } + strcpy(card->driver, "ES1688"); + strcpy(card->shortname, pcm->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, irq, dma); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_audiodrive_cards[dev] = card; + return 0; + +} + +static int __init snd_audiodrive_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_audiodrive_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_es1688_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards = 0; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_audiodrive_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "ESS AudioDrive ES1688 soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_es1688_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_audiodrive_cards[idx]); +} + +module_init(alsa_card_es1688_init) +module_exit(alsa_card_es1688_exit) + +#ifndef MODULE + +/* format is: snd-es1688=snd_enable,snd_index,snd_id, + snd_port,snd_mpu_port, + snd_irq,snd_mpu_irq, + snd_dma8 */ + +static int __init alsa_card_es1688_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1688=", alsa_card_es1688_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/es1688/es1688_lib.c linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688_lib.c --- linux/sound/isa/es1688/es1688_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/es1688/es1688_lib.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1060 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of ESS ES1688/688/488 chip + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ESS ESx688 lowlevel module"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); + +#define chip_t es1688_t + +static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val) +{ + int i; + + for (i = 10000; i; i--) + if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) { + outb(val, ES1688P(chip, COMMAND)); + return 1; + } +#ifdef CONFIG_SND_DEBUG + printk("snd_es1688_dsp_command: timeout (0x%x)\n", val); +#endif + return 0; +} + +static int snd_es1688_dsp_get_byte(es1688_t *chip) +{ + int i; + + for (i = 1000; i; i--) + if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) + return inb(ES1688P(chip, READ)); + snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL))); + return -ENODEV; +} + +static int snd_es1688_write(es1688_t *chip, + unsigned char reg, unsigned char data) +{ + if (!snd_es1688_dsp_command(chip, reg)) + return 0; + return snd_es1688_dsp_command(chip, data); +} + +int snd_es1688_read(es1688_t *chip, unsigned char reg) +{ + /* Read a byte from an extended mode register of ES1688 */ + if (!snd_es1688_dsp_command(chip, 0xc0)) + return -1; + if (!snd_es1688_dsp_command(chip, reg)) + return -1; + return snd_es1688_dsp_get_byte(chip); +} + +void snd_es1688_mixer_write(es1688_t *chip, + unsigned char reg, unsigned char data) +{ + outb(reg, ES1688P(chip, MIXER_ADDR)); + udelay(10); + outb(data, ES1688P(chip, MIXER_DATA)); + udelay(10); +} + +unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg) +{ + unsigned char result; + + outb(reg, ES1688P(chip, MIXER_ADDR)); + udelay(10); + result = inb(ES1688P(chip, MIXER_DATA)); + udelay(10); + return result; +} + +static int snd_es1688_reset(es1688_t *chip) +{ + int i; + + outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */ + udelay(10); + outb(0, ES1688P(chip, RESET)); + udelay(30); + for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++); + if (inb(ES1688P(chip, READ)) != 0xaa) { + snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port); + return -ENODEV; + } + snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */ + return 0; +} + +static int snd_es1688_probe(es1688_t *chip) +{ + unsigned long flags; + unsigned short major, minor, hw; + int i; + + /* + * initialization sequence + */ + + spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ + inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ + inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */ + + if (snd_es1688_reset(chip) < 0) { + snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ))); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + snd_es1688_dsp_command(chip, 0xe7); /* return identification */ + + for (i = 1000, major = minor = 0; i; i--) { + if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) { + if (major == 0) { + major = inb(ES1688P(chip, READ)); + } else { + minor = inb(ES1688P(chip, READ)); + } + } + } + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor); + + chip->version = (major << 8) | minor; + if (!chip->version) + return -ENODEV; /* probably SB */ + + hw = ES1688_HW_AUTO; + switch (chip->version & 0xfff0) { + case 0x4880: + snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port); + return -ENODEV; + case 0x6880: + hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688; + break; + default: + snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version); + return -ENODEV; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ + snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* enable joystick, but disable OPL3 */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_es1688_mixer_write(chip, 0x40, 0x01); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + return 0; +} + +static int snd_es1688_init(es1688_t * chip, int enable) +{ + static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1}; + unsigned long flags; + int cfg, irq_bits, dma, dma_bits, tmp, tmp1; + + /* ok.. setup MPU-401 port and joystick and OPL3 */ + cfg = 0x01; /* enable joystick, but disable OPL3 */ + if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) { + tmp = (chip->mpu_port & 0x0f0) >> 4; + if (tmp <= 3) { + switch (chip->mpu_irq) { + case 9: + tmp1 = 4; + break; + case 5: + tmp1 = 5; + break; + case 7: + tmp1 = 6; + break; + case 10: + tmp1 = 7; + break; + default: + tmp1 = 0; + } + if (tmp1) { + cfg |= (tmp << 3) | (tmp1 << 5); + } + } + } +#if 0 + snd_printk("mpu cfg = 0x%x\n", cfg); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_mixer_write(chip, 0x40, cfg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_read(chip, 0xb1); + snd_es1688_read(chip, 0xb2); + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (enable) { + cfg = 0xf0; /* enable only DMA counter interrupt */ + irq_bits = irqs[chip->irq & 0x0f]; + if (irq_bits < 0) { + snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq); +#if 0 + irq_bits = 0; + cfg = 0x10; +#endif + return -EINVAL; + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + cfg = 0xf0; /* extended mode DMA enable */ + dma = chip->dma8; + if (dma > 3 || dma == 2) { + snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma); +#if 0 + dma_bits = 0; + cfg = 0x00; /* disable all DMA */ +#endif + return -EINVAL; + } else { + dma_bits = dma; + if (dma != 3) + dma_bits++; + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + } else { + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ + snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + } + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_read(chip, 0xb1); + snd_es1688_read(chip, 0xb2); + snd_es1688_reset(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +/* + + */ + +static ratnum_t clocks[2] = { + { + num: 795444, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 397722, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: 2, + rats: clocks, +}; + +static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int bits, divider; + + if (runtime->rate_num == clocks[0].num) + bits = 256 - runtime->rate_den; + else + bits = 128 - runtime->rate_den; + /* set filter register */ + divider = 256 - 7160000*20/(8*82*runtime->rate); + /* write result to hardware */ + snd_es1688_write(chip, 0xa1, bits); + snd_es1688_write(chip, 0xa2, divider); +} + +static int snd_es1688_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_es1688_trigger(es1688_t *chip, int cmd, unsigned char value) +{ + int val; + + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + value = 0x00; + } else if (cmd != SNDRV_PCM_TRIGGER_START) { + return -EINVAL; + } + spin_lock(&chip->reg_lock); + chip->trigger_value = value; + val = snd_es1688_read(chip, 0xb8); + if ((val < 0) || (val & 0x0f) == value) { + spin_unlock(&chip->reg_lock); + return -EINVAL; /* something is wrong */ + } +#if 0 + printk("trigger: val = 0x%x, value = 0x%x\n", val, value); + printk("trigger: residue = 0x%x\n", get_dma_residue(chip->dma8)); +#endif + snd_es1688_write(chip, 0xb8, (val & 0xf0) | value); + spin_unlock(&chip->reg_lock); + return 0; +} + +static int snd_es1688_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_es1688_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_es1688_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_reset(chip); + snd_es1688_set_rate(chip, substream); + snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */ + snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); + snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ + if (runtime->channels == 1) { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit mono */ + snd_es1688_write(chip, 0xb6, 0x80); + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0xd0); + } else { + /* 16. bit mono */ + snd_es1688_write(chip, 0xb6, 0x00); + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xf4); + } + } else { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit stereo */ + snd_es1688_write(chip, 0xb6, 0x80); + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0x98); + } else { + /* 16. bit stereo */ + snd_es1688_write(chip, 0xb6, 0x00); + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xbc); + } + } + snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); + snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); + snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + count = -count; + snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xa4, (unsigned char) count); + snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_es1688_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + return snd_es1688_trigger(chip, cmd, 0x05); +} + +static int snd_es1688_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma_size = size; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_reset(chip); + snd_es1688_set_rate(chip, substream); + snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF); + snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */ + snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); + snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ + if (runtime->channels == 1) { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit mono */ + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0xd0); + } else { + /* 16. bit mono */ + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xf4); + } + } else { + if (snd_pcm_format_width(runtime->format) == 8) { + /* 8. bit stereo */ + snd_es1688_write(chip, 0xb7, 0x51); + snd_es1688_write(chip, 0xb7, 0x98); + } else { + /* 16. bit stereo */ + snd_es1688_write(chip, 0xb7, 0x71); + snd_es1688_write(chip, 0xb7, 0xbc); + } + } + snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); + snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); + spin_unlock_irqrestore(&chip->reg_lock, flags); + /* --- */ + count = -count; + snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1688_write(chip, 0xa4, (unsigned char) count); + snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_es1688_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + return snd_es1688_trigger(chip, cmd, 0x0f); +} + +void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1688_t *chip = snd_magic_cast(es1688_t, dev_id, return); + + if (chip->trigger_value == 0x05) /* ok.. playback is active */ + snd_pcm_period_elapsed(chip->playback_substream); + if (chip->trigger_value == 0x0f) /* ok.. capture is active */ + snd_pcm_period_elapsed(chip->capture_substream); + + inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */ +} + +static snd_pcm_uframes_t snd_es1688_playback_pointer(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->trigger_value != 0x05) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_es1688_capture_pointer(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->trigger_value != 0x0f) + return 0; + ptr = chip->dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_es1688_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_es1688_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + + */ + +static int snd_es1688_playback_open(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->capture_substream != NULL) + return -EAGAIN; + chip->playback_substream = substream; + runtime->hw = snd_es1688_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_es1688_capture_open(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->playback_substream != NULL) + return -EAGAIN; + chip->capture_substream = substream; + runtime->hw = snd_es1688_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_es1688_playback_close(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + return 0; +} + +static int snd_es1688_capture_close(snd_pcm_substream_t * substream) +{ + es1688_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + return 0; +} + +static int snd_es1688_free(es1688_t *chip) +{ + if (chip->res_port) { + snd_es1688_init(chip, 0); + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma8 >= 0) { + disable_dma(chip->dma8); + free_dma(chip->dma8); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1688_dev_free(snd_device_t *device) +{ + es1688_t *chip = snd_magic_cast(es1688_t, device->device_data, return -ENXIO); + return snd_es1688_free(chip); +} + +static const char *snd_es1688_chip_id(es1688_t *chip) +{ + static char tmp[16]; + sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f); + return tmp; +} + +int snd_es1688_create(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + int irq, + int mpu_irq, + int dma8, + unsigned short hardware, + es1688_t **rchip) +{ + static snd_device_ops_t ops = { + dev_free: snd_es1688_dev_free, + }; + + es1688_t *chip; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(es1688_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->irq = -1; + chip->dma8 = -1; + + if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) { + snd_es1688_free(chip); + return -EBUSY; + } + if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) { + snd_es1688_free(chip); + return -EBUSY; + } + chip->irq = irq; + if (request_dma(dma8, "ES1688")) { + snd_es1688_free(chip); + return -EBUSY; + } + chip->dma8 = dma8; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + chip->card = card; + chip->port = port; + mpu_port &= ~0x000f; + if (mpu_port < 0x300 || mpu_port > 0x330) + mpu_port = 0; + chip->mpu_port = mpu_port; + chip->mpu_irq = mpu_irq; + chip->hardware = hardware; + + if ((err = snd_es1688_probe(chip)) < 0) { + snd_es1688_free(chip); + return err; + } + if ((err = snd_es1688_init(chip, 1)) < 0) { + snd_es1688_free(chip); + return err; + } + + /* Register device */ + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1688_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static snd_pcm_ops_t snd_es1688_playback_ops = { + open: snd_es1688_playback_open, + close: snd_es1688_playback_close, + ioctl: snd_es1688_ioctl, + hw_params: snd_es1688_hw_params, + hw_free: snd_es1688_hw_free, + prepare: snd_es1688_playback_prepare, + trigger: snd_es1688_playback_trigger, + pointer: snd_es1688_playback_pointer, +}; + +static snd_pcm_ops_t snd_es1688_capture_ops = { + open: snd_es1688_capture_open, + close: snd_es1688_capture_close, + ioctl: snd_es1688_ioctl, + hw_params: snd_es1688_hw_params, + hw_free: snd_es1688_hw_free, + prepare: snd_es1688_capture_prepare, + trigger: snd_es1688_capture_trigger, + pointer: snd_es1688_capture_pointer, +}; + +static void snd_es1688_pcm_free(snd_pcm_t *pcm) +{ + es1688_t *chip = snd_magic_cast(es1688_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_es1688_pcm(es1688_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_es1688_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + sprintf(pcm->name, snd_es1688_chip_id(chip)); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_es1688_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[9] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es1688_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7; + return 0; +} + +static int snd_es1688_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char oval, nval; + int change; + + if (ucontrol->value.enumerated.item[0] > 8) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV); + nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15); + change = nval != oval; + if (change) + snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1688_info_single, \ + get: snd_es1688_get_single, put: snd_es1688_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1688_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es1688_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char oval, nval; + + nval = (ucontrol->value.integer.value[0] & mask); + if (invert) + nval = mask - nval; + nval <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_es1688_mixer_read(chip, reg); + nval = (oval & ~(mask << shift)) | nval; + change = nval != oval; + if (change) + snd_es1688_mixer_write(chip, reg, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1688_info_double, \ + get: snd_es1688_get_double, put: snd_es1688_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1688_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg < 0xa0) + left = snd_es1688_mixer_read(chip, left_reg); + else + left = snd_es1688_read(chip, left_reg); + if (left_reg != right_reg) { + if (right_reg < 0xa0) + right = snd_es1688_mixer_read(chip, right_reg); + else + right = snd_es1688_read(chip, right_reg); + } else + right = left; + spin_unlock_irqrestore(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es1688_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1688_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + if (left_reg < 0xa0) + oval1 = snd_es1688_mixer_read(chip, left_reg); + else + oval1 = snd_es1688_read(chip, left_reg); + if (right_reg < 0xa0) + oval2 = snd_es1688_mixer_read(chip, right_reg); + else + oval2 = snd_es1688_read(chip, right_reg); + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + if (change) { + if (left_reg < 0xa0) + snd_es1688_mixer_write(chip, left_reg, val1); + else + snd_es1688_write(chip, left_reg, val1); + if (right_reg < 0xa0) + snd_es1688_mixer_write(chip, right_reg, val1); + else + snd_es1688_write(chip, right_reg, val1); + } + } else { + if (left_reg < 0xa0) + oval1 = snd_es1688_mixer_read(chip, left_reg); + else + oval1 = snd_es1688_read(chip, left_reg); + val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval1; + if (change) { + if (left_reg < 0xa0) + snd_es1688_mixer_write(chip, left_reg, val1); + else + snd_es1688_write(chip, left_reg, val1); + } + + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define ES1688_CONTROLS (sizeof(snd_es1688_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_es1688_controls[] = { +ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0), +ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0), +ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0), +ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0), +ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_es1688_info_mux, + get: snd_es1688_get_mux, + put: snd_es1688_put_mux, +}, +}; + +#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2) + +static unsigned char snd_es1688_init_table[][2] = { + { ES1688_MASTER_DEV, 0 }, + { ES1688_PCM_DEV, 0 }, + { ES1688_LINE_DEV, 0 }, + { ES1688_CD_DEV, 0 }, + { ES1688_FM_DEV, 0 }, + { ES1688_MIC_DEV, 0 }, + { ES1688_AUX_DEV, 0 }, + { ES1688_SPEAKER_DEV, 0 }, + { ES1688_RECLEV_DEV, 0 }, + { ES1688_REC_DEV, 0x17 } +}; + +int snd_es1688_mixer(es1688_t *chip) +{ + snd_card_t *card; + int err, idx; + unsigned char reg, val; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_es1688_chip_id(chip)); + + for (idx = 0; idx < ES1688_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0) + return err; + } + for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) { + reg = snd_es1688_init_table[idx][0]; + val = snd_es1688_init_table[idx][1]; + if (reg < 0xa0) + snd_es1688_mixer_write(chip, reg, val); + else + snd_es1688_write(chip, reg, val); + } + return 0; +} + +EXPORT_SYMBOL(snd_es1688_mixer_write); +EXPORT_SYMBOL(snd_es1688_mixer_read); +EXPORT_SYMBOL(snd_es1688_interrupt); +EXPORT_SYMBOL(snd_es1688_create); +EXPORT_SYMBOL(snd_es1688_pcm); +EXPORT_SYMBOL(snd_es1688_mixer); + +/* + * INIT part + */ + +static int __init alsa_es1688_init(void) +{ + return 0; +} + +static void __exit alsa_es1688_exit(void) +{ +} + +module_init(alsa_es1688_init) +module_exit(alsa_es1688_exit) diff -Nru linux/sound/isa/es18xx.c linux-2.4.19-pre5-mjc/sound/isa/es18xx.c --- linux/sound/isa/es18xx.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/es18xx.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2315 @@ +/* + * Driver for generic ESS AudioDrive ES18xx soundcards + * Copyright (c) by Christian Fischbach + * Copyright (c) by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* GENERAL NOTES: + * + * BUGS: + * - There are pops (we can't delay in trigger function, cause midlevel + * often need to trigger down and then up very quickly). + * Any ideas? + * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it. + */ + +/* + * ES1868 NOTES: + * - The chip has one half duplex pcm (with very limited full duplex support). + * + * - Duplex stereophonic sound is impossible. + * - Record and playback must share the same frequency rate. + * + * - The driver use dma2 for playback and dma1 for capture. + */ + +/* + * ES1869 NOTES: + * + * - there are a first full duplex pcm and a second playback only pcm + * (incompatible with first pcm capture) + * + * - there is support for the capture volume and ESS Spatializer 3D effect. + * + * - contrarily to some pages in DS_1869.PDF the rates can be set + * independently. + * + * BUGS: + * + * - There is a major trouble I noted: + * + * using both channel for playback stereo 16 bit samples at 44100 Hz + * the second pcm (Audio1) DMA slows down irregularly and sound is garbled. + * + * The same happens using Audio1 for captureing. + * + * The Windows driver does not suffer of this (although it use Audio1 + * only for captureing). I'm unable to discover why. + * + */ + + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define PFX "es18xx: " + +struct _snd_es18xx { + unsigned long port; /* port of ESS chip */ + unsigned long mpu_port; /* MPU-401 port of ESS chip */ + unsigned long fm_port; /* FM port */ + unsigned long ctrl_port; /* Control port of ESS chip */ + struct resource *res_port; + struct resource *res_mpu_port; + struct resource *res_ctrl_port; + int irq; /* IRQ number of ESS chip */ + int dma1; /* DMA1 */ + int dma2; /* DMA2 */ + unsigned short version; /* version of ESS chip */ + int caps; /* Chip capabilities */ + unsigned short audio2_vol; /* volume level of audio2 */ + + unsigned short active; /* active channel mask */ + unsigned int dma1_size; + unsigned int dma2_size; + unsigned int dma1_shift; + unsigned int dma2_shift; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_a_substream; + snd_pcm_substream_t *capture_a_substream; + snd_pcm_substream_t *playback_b_substream; + + snd_rawmidi_t *rmidi; + + snd_kcontrol_t *hw_volume; + snd_kcontrol_t *hw_switch; + snd_kcontrol_t *master_volume; + snd_kcontrol_t *master_switch; + + spinlock_t reg_lock; + spinlock_t mixer_lock; + spinlock_t ctrl_lock; +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + unsigned char pm_reg; +#endif +}; + +#define AUDIO1_IRQ 0x01 +#define AUDIO2_IRQ 0x02 +#define HWV_IRQ 0x04 +#define MPU_IRQ 0x08 + +#define ES18XX_PCM2 0x0001 /* Has two useable PCM */ +#define ES18XX_SPATIALIZER 0x0002 /* Has 3D Spatializer */ +#define ES18XX_RECMIX 0x0004 /* Has record mixer */ +#define ES18XX_DUPLEX_MONO 0x0008 /* Has mono duplex only */ +#define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */ +#define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */ +#define ES18XX_AUXB 0x0040 /* AuxB mixer control */ +#define ES18XX_HWV 0x0080 /* Has hardware volume */ +#define ES18XX_MONO 0x0100 /* Mono_in mixer control */ +#define ES18XX_I2S 0x0200 /* I2S mixer control */ +#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ +#define ES18XX_CONTROL 0x0800 /* Has control ports */ + +/* Power Management */ +#define ES18XX_PM 0x07 +#define ES18XX_PM_GPO0 0x01 +#define ES18XX_PM_GPO1 0x02 +#define ES18XX_PM_PDR 0x03 +#define ES18XX_PM_ANA 0x04 +#define ES18XX_PM_FM 0x06 +#define ES18XX_PM_SUS 0x08 + +typedef struct _snd_es18xx es18xx_t; + +#define chip_t es18xx_t + +/* Lowlevel */ + +#define DAC1 0x01 +#define ADC1 0x02 +#define DAC2 0x04 +#define MILLISECOND 10000 + +static int snd_es18xx_dsp_command(es18xx_t *chip, unsigned char val) +{ + int i; + + for(i = MILLISECOND; i; i--) + if ((inb(chip->port + 0x0C) & 0x80) == 0) { + outb(val, chip->port + 0x0C); + return 0; + } + snd_printk("dsp_command: timeout (0x%x)\n", val); + return -EINVAL; +} + +static int snd_es18xx_dsp_get_byte(es18xx_t *chip) +{ + int i; + + for(i = MILLISECOND/10; i; i--) + if (inb(chip->port + 0x0C) & 0x40) + return inb(chip->port + 0x0A); + snd_printk("dsp_get_byte failed: 0x%lx = 0x%x!!!\n", chip->port + 0x0A, inb(chip->port + 0x0A)); + return -ENODEV; +} + +#undef REG_DEBUG + +static int snd_es18xx_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, data); + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x set to %02x\n", reg, data); +#endif + return ret; +} + +static int snd_es18xx_read(es18xx_t *chip, unsigned char reg) +{ + unsigned long flags; + int ret, data; + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, 0xC0); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + data = snd_es18xx_dsp_get_byte(chip); + ret = data; +#ifdef REG_DEBUG + snd_printk("Reg %02x now is %02x (%d)\n", reg, data, ret); +#endif + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +/* Return old value */ +static int snd_es18xx_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + int ret; + unsigned char old, new, oval; + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + ret = snd_es18xx_dsp_command(chip, 0xC0); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + ret = snd_es18xx_dsp_get_byte(chip); + if (ret < 0) { + goto end; + } + old = ret; + oval = old & mask; + if (val != oval) { + ret = snd_es18xx_dsp_command(chip, reg); + if (ret < 0) + goto end; + new = (old & ~mask) | (val & mask); + ret = snd_es18xx_dsp_command(chip, new); + if (ret < 0) + goto end; +#ifdef REG_DEBUG + snd_printk("Reg %02x was %02x, set to %02x (%d)\n", reg, old, new, ret); +#endif + } + ret = oval; + end: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +inline void snd_es18xx_mixer_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + outb(data, chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x set to %02x\n", reg, data); +#endif +} + +inline int snd_es18xx_mixer_read(es18xx_t *chip, unsigned char reg) +{ + unsigned long flags; + int data; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + data = inb(chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x now is %02x\n", reg, data); +#endif + return data; +} + +/* Return old value */ +static inline int snd_es18xx_mixer_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + unsigned char old, new, oval; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + old = inb(chip->port + 0x05); + oval = old & mask; + if (val != oval) { + new = (old & ~mask) | (val & mask); + outb(new, chip->port + 0x05); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return oval; +} + +static inline int snd_es18xx_mixer_writable(es18xx_t *chip, unsigned char reg, + unsigned char mask) +{ + int old, expected, new; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, chip->port + 0x04); + old = inb(chip->port + 0x05); + expected = old ^ mask; + outb(expected, chip->port + 0x05); + new = inb(chip->port + 0x05); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x, now is %02x\n", reg, old, expected, new); +#endif + return expected == new; +} + + +static int snd_es18xx_reset(es18xx_t *chip) +{ + int i; + outb(0x03, chip->port + 0x06); + inb(chip->port + 0x06); + outb(0x00, chip->port + 0x06); + for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++); + if (inb(chip->port + 0x0A) != 0xAA) + return -1; + return 0; +} + +static int snd_es18xx_reset_fifo(es18xx_t *chip) +{ + outb(0x02, chip->port + 0x06); + inb(chip->port + 0x06); + outb(0x00, chip->port + 0x06); + return 0; +} + +static ratnum_t new_clocks[2] = { + { + num: 793800, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 768000, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = { + nrats: 2, + rats: new_clocks, +}; + +static ratnum_t old_clocks[2] = { + { + num: 795444, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 397722, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks = { + nrats: 2, + rats: old_clocks, +}; + + +static void snd_es18xx_rate_set(es18xx_t *chip, + snd_pcm_substream_t *substream, + int mode) +{ + unsigned int bits, div0; + snd_pcm_runtime_t *runtime = substream->runtime; + if (chip->caps & ES18XX_NEW_RATE) { + if (runtime->rate_num == new_clocks[0].num) + bits = 128 - runtime->rate_den; + else + bits = 256 - runtime->rate_den; + } else { + if (runtime->rate_num == old_clocks[0].num) + bits = 256 - runtime->rate_den; + else + bits = 128 - runtime->rate_den; + } + + /* set filter register */ + div0 = 256 - 7160000*20/(8*82*runtime->rate); + + if ((chip->caps & ES18XX_PCM2) && mode == DAC2) { + snd_es18xx_mixer_write(chip, 0x70, bits); + snd_es18xx_mixer_write(chip, 0x72, div0); + } else { + snd_es18xx_write(chip, 0xA1, bits); + snd_es18xx_write(chip, 0xA2, div0); + } +} + +static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int shift, err; + + shift = 0; + if (params_channels(hw_params) == 2) + shift++; + if (snd_pcm_format_width(params_format(hw_params)) == 16) + shift++; + + switch (substream->number) { + case 0: + if ((chip->caps & ES18XX_DUPLEX_MONO) && + (chip->capture_a_substream) && + params_channels(hw_params) != 1) { + _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); + return -EBUSY; + } + chip->dma2_shift = shift; + break; + case 1: + chip->dma1_shift = shift; + break; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es18xx_playback1_prepare(es18xx_t *chip, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma2_size = size; + + snd_es18xx_rate_set(chip, substream, DAC2); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_mixer_write(chip, 0x74, count & 0xff); + snd_es18xx_mixer_write(chip, 0x76, count >> 8); + + /* Set format */ + snd_es18xx_mixer_bits(chip, 0x7A, 0x07, + ((runtime->channels == 1) ? 0x00 : 0x02) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04)); + + /* Set DMA controller */ + snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_playback1_trigger(es18xx_t *chip, + snd_pcm_substream_t * substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->active & DAC2) + return 0; + chip->active |= DAC2; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->active & DAC2)) + return 0; + chip->active &= ~DAC2; + } else { + return -EINVAL; + } + + if (cmd == SNDRV_PCM_TRIGGER_START) { + /* Start DMA */ + if (chip->dma2 >= 4) + snd_es18xx_mixer_write(chip, 0x78, 0xb3); + else + snd_es18xx_mixer_write(chip, 0x78, 0x93); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(100000); + if (chip->caps & ES18XX_PCM2) + /* Restore Audio 2 volume */ + snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol); + else + /* Enable PCM output */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + } + else { + /* Stop DMA */ + snd_es18xx_mixer_write(chip, 0x78, 0x00); +#ifdef AVOID_POPS + udelay(25000); + if (chip->caps & ES18XX_PCM2) + /* Set Audio 2 volume to 0 */ + snd_es18xx_mixer_write(chip, 0x7C, 0); + else + /* Disable PCM output */ + snd_es18xx_dsp_command(chip, 0xD3); +#endif + } + + return 0; +} + +static int snd_es18xx_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int shift, err; + + shift = 0; + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->playback_a_substream && + params_channels(hw_params) != 1) { + _snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS); + return -EBUSY; + } + if (params_channels(hw_params) == 2) + shift++; + if (snd_pcm_format_width(params_format(hw_params)) == 16) + shift++; + chip->dma1_shift = shift; + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + return 0; +} + +static int snd_es18xx_capture_prepare(snd_pcm_substream_t *substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + + snd_es18xx_reset_fifo(chip); + + /* Set stereo/mono */ + snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); + + snd_es18xx_rate_set(chip, substream, ADC1); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_write(chip, 0xA4, count & 0xff); + snd_es18xx_write(chip, 0xA5, count >> 8); + +#ifdef AVOID_POPS + udelay(100000); +#endif + + /* Set format */ + snd_es18xx_write(chip, 0xB7, + snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); + snd_es18xx_write(chip, 0xB7, 0x90 | + ((runtime->channels == 1) ? 0x40 : 0x08) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); + + /* Set DMA controler */ + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->active & ADC1) + return 0; + chip->active |= ADC1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->active & ADC1)) + return 0; + chip->active &= ~ADC1; + } else { + return -EINVAL; + } + + if (cmd == SNDRV_PCM_TRIGGER_START) + /* Start DMA */ + snd_es18xx_write(chip, 0xB8, 0x0f); + else + /* Stop DMA */ + snd_es18xx_write(chip, 0xB8, 0x00); + return 0; +} + +static int snd_es18xx_playback2_prepare(es18xx_t *chip, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + + snd_es18xx_reset_fifo(chip); + + /* Set stereo/mono */ + snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01); + + snd_es18xx_rate_set(chip, substream, DAC1); + + /* Transfer Count Reload */ + count = 0x10000 - count; + snd_es18xx_write(chip, 0xA4, count & 0xff); + snd_es18xx_write(chip, 0xA5, count >> 8); + + /* Set format */ + snd_es18xx_write(chip, 0xB6, + snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00); + snd_es18xx_write(chip, 0xB7, + snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71); + snd_es18xx_write(chip, 0xB7, 0x90 | + (runtime->channels == 1 ? 0x40 : 0x08) | + (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) | + (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20)); + + /* Set DMA controler */ + snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + return 0; +} + +static int snd_es18xx_playback2_trigger(es18xx_t *chip, + snd_pcm_substream_t *substream, + int cmd) +{ + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (chip->active & DAC1) + return 0; + chip->active |= DAC1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (!(chip->active & DAC1)) + return 0; + chip->active &= ~DAC1; + } else { + return -EINVAL; + } + + if (cmd == SNDRV_PCM_TRIGGER_START) { + /* Start DMA */ + snd_es18xx_write(chip, 0xB8, 0x05); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(100000); + /* Enable Audio 1 */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + } + else { + /* Stop DMA */ + snd_es18xx_write(chip, 0xB8, 0x00); +#ifdef AVOID_POPS + /* Avoid pops */ + udelay(25000); + /* Disable Audio 1 */ + snd_es18xx_dsp_command(chip, 0xD3); +#endif + } + return 0; +} + +static int snd_es18xx_playback_prepare(snd_pcm_substream_t *substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + switch (substream->number) { + case 0: + return snd_es18xx_playback1_prepare(chip, substream); + case 1: + return snd_es18xx_playback2_prepare(chip, substream); + } + return -EINVAL; +} + +static int snd_es18xx_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + switch (substream->number) { + case 0: + return snd_es18xx_playback1_trigger(chip, substream, cmd); + case 1: + return snd_es18xx_playback2_trigger(chip, substream, cmd); + } + return -EINVAL; +} + +static void snd_es18xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, dev_id, return); + unsigned char status; + + + if (chip->caps & ES18XX_CONTROL) { + /* Read Interrupt status */ + status = inb(chip->ctrl_port + 6); + } else { + /* Read Interrupt status */ + status = snd_es18xx_mixer_read(chip, 0x7f) >> 4; + } +#if 0 + else { + status = 0; + if (inb(chip->port + 0x0C) & 0x01) + status |= AUDIO1_IRQ; + if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80) + status |= AUDIO2_IRQ; + if ((chip->caps & ES18XX_HWV) && + snd_es18xx_mixer_read(chip, 0x64) & 0x10) + status |= HWV_IRQ; + } +#endif + + /* Audio 1 & Audio 2 */ + if (status & AUDIO2_IRQ) { + if (chip->active & DAC2) + snd_pcm_period_elapsed(chip->playback_a_substream); + /* ack interrupt */ + snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00); + } + if (status & AUDIO1_IRQ) { + /* ok.. capture is active */ + if (chip->active & ADC1) + snd_pcm_period_elapsed(chip->capture_a_substream); + /* ok.. playback2 is active */ + else if (chip->active & DAC1) + snd_pcm_period_elapsed(chip->playback_b_substream); + /* ack interrupt */ + inb(chip->port + 0x0E); + } + + /* MPU */ + if ((status & MPU_IRQ) && chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + + /* Hardware volume */ + if (status & HWV_IRQ) { + int split = snd_es18xx_mixer_read(chip, 0x64) & 0x80; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); + if (!split) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + /* ack interrupt */ + snd_es18xx_mixer_write(chip, 0x66, 0x00); + } + +} + +static snd_pcm_uframes_t snd_es18xx_playback_pointer(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int pos; + + switch (substream->number) { + case 0: + if (!(chip->active & DAC2)) + return 0; + pos = chip->dma2_size - snd_dma_residue(chip->dma2); + return pos >> chip->dma2_shift; + case 1: + if (!(chip->active & DAC1)) + return 0; + pos = chip->dma1_size - snd_dma_residue(chip->dma1); + return pos >> chip->dma1_shift; + } + return 0; +} + +static snd_pcm_uframes_t snd_es18xx_capture_pointer(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + int pos; + + if (!(chip->active & ADC1)) + return 0; + pos = chip->dma1_size - snd_dma_residue(chip->dma1); + return pos >> chip->dma1_shift; +} + +static snd_pcm_hardware_t snd_es18xx_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_es18xx_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_es18xx_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es18xx_t *chip = snd_pcm_substream_chip(substream); + + switch (substream->number) { + case 0: + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->capture_a_substream && + chip->capture_a_substream->runtime->channels != 1) + return -EAGAIN; + chip->playback_a_substream = substream; + break; + case 1: + if (chip->capture_a_substream) + return -EAGAIN; + chip->playback_b_substream = substream; + break; + default: + snd_BUG(); + return -EINVAL; + } + substream->runtime->hw = snd_es18xx_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); + return 0; +} + +static int snd_es18xx_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es18xx_t *chip = snd_pcm_substream_chip(substream); + + if (chip->playback_b_substream) + return -EAGAIN; + if ((chip->caps & ES18XX_DUPLEX_MONO) && + chip->playback_a_substream && + chip->playback_a_substream->runtime->channels != 1) + return -EAGAIN; + chip->capture_a_substream = substream; + substream->runtime->hw = snd_es18xx_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks); + return 0; +} + +static int snd_es18xx_playback_close(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + switch (substream->number) { + case 0: + chip->playback_a_substream = NULL; + break; + case 1: + chip->playback_b_substream = NULL; + break; + default: + snd_BUG(); + return -EINVAL; + } + + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_es18xx_capture_close(snd_pcm_substream_t * substream) +{ + es18xx_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_a_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +/* + * MIXER part + */ + +static int snd_es18xx_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es18xx_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es18xx_mixer_read(chip, 0x1c) & 0x07; + return 0; +} + +static int snd_es18xx_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = ucontrol->value.enumerated.item[0]; + + if (val > 7) + return -EINVAL; + return snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val; +} + +static int snd_es18xx_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es18xx_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = snd_es18xx_mixer_read(chip, 0x50); + ucontrol->value.integer.value[0] = !!(val & 8); + return 0; +} + +static int snd_es18xx_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char oval, nval; + int change; + nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; + oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c; + change = nval != oval; + if (change) { + snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04); + snd_es18xx_mixer_write(chip, 0x50, nval); + } + return change; +} + +static int snd_es18xx_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 63; + return 0; +} + +static int snd_es18xx_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f; + ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f; + return 0; +} + +static int snd_es18xx_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es18xx_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40); + ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40); + return 0; +} + +static void snd_es18xx_hwv_free(snd_kcontrol_t *kcontrol) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_volume = NULL; + chip->master_switch = NULL; + chip->hw_volume = NULL; + chip->hw_switch = NULL; +} + +static int snd_es18xx_reg_bits(es18xx_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + if (reg < 0xa0) + return snd_es18xx_mixer_bits(chip, reg, mask, val); + else + return snd_es18xx_bits(chip, reg, mask, val); +} + +static int snd_es18xx_reg_read(es18xx_t *chip, unsigned char reg) +{ + if (reg < 0xa0) + return snd_es18xx_mixer_read(chip, reg); + else + return snd_es18xx_read(chip, reg); +} + +#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es18xx_info_single, \ + get: snd_es18xx_get_single, put: snd_es18xx_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es18xx_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es18xx_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int val; + + val = snd_es18xx_reg_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es18xx_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + mask <<= shift; + val <<= shift; + return snd_es18xx_reg_bits(chip, reg, mask, val) != val; +} + +#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es18xx_info_double, \ + get: snd_es18xx_get_double, put: snd_es18xx_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es18xx_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es18xx_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + left = snd_es18xx_reg_read(chip, left_reg); + if (left_reg != right_reg) + right = snd_es18xx_reg_read(chip, right_reg); + else + right = left; + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es18xx_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es18xx_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, mask1, mask2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + mask1 = mask << shift_left; + mask2 = mask << shift_right; + if (left_reg != right_reg) { + change = 0; + if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1) + change = 1; + if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2) + change = 1; + } else { + change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, + val1 | val2) != (val1 | val2)); + } + return change; +} + +static snd_kcontrol_new_t snd_es18xx_base_controls[] = { +ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), +ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), +ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), +ES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), +ES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), +ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), +ES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), +ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0), +ES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), +ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), +ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_es18xx_info_mux, + get: snd_es18xx_get_mux, + put: snd_es18xx_put_mux, +} +}; + +static snd_kcontrol_new_t snd_es18xx_mono_in_control = +ES18XX_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0); + +static snd_kcontrol_new_t snd_es18xx_recmix_controls[] = { +ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), +ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), +ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), +ES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), +ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), +ES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), +ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0) +}; + +static snd_kcontrol_new_t snd_es18xx_pcm1_controls[] = { +ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0), +}; + +static snd_kcontrol_new_t snd_es18xx_pcm2_controls[] = { +ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), +ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0) +}; + +static snd_kcontrol_new_t snd_es18xx_spatializer_controls[] = { +ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Switch", + info: snd_es18xx_info_spatializer_enable, + get: snd_es18xx_get_spatializer_enable, + put: snd_es18xx_put_spatializer_enable, +} +}; + +static snd_kcontrol_new_t snd_es18xx_micpre1_control = +ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0); + +static snd_kcontrol_new_t snd_es18xx_micpre2_control = +ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0); + +static snd_kcontrol_new_t snd_es18xx_hw_volume_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es18xx_info_hw_volume, + get: snd_es18xx_get_hw_volume, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Switch", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es18xx_info_hw_switch, + get: snd_es18xx_get_hw_switch, +}, +ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), +}; + +#if 0 +static int __init snd_es18xx_config_read(es18xx_t *chip, unsigned char reg) +{ + int data; + unsigned long flags; + spin_lock_irqsave(&chip->ctrl_lock, flags); + outb(reg, chip->ctrl_port); + data = inb(chip->ctrl_port + 1); + spin_unlock_irqrestore(&chip->ctrl_lock, flags); + return data; +} +#endif + +static void __init snd_es18xx_config_write(es18xx_t *chip, + unsigned char reg, unsigned char data) +{ + /* No need for spinlocks, this function is used only in + otherwise protected init code */ + outb(reg, chip->ctrl_port); + outb(data, chip->ctrl_port + 1); +#ifdef REG_DEBUG + snd_printk("Config reg %02x set to %02x\n", reg, data); +#endif +} + +static int __init snd_es18xx_initialize(es18xx_t *chip) +{ + int mask = 0; + + /* enable extended mode */ + snd_es18xx_dsp_command(chip, 0xC6); + /* Reset mixer registers */ + snd_es18xx_mixer_write(chip, 0x00, 0x00); + + /* Audio 1 DMA demand mode (4 bytes/request) */ + snd_es18xx_write(chip, 0xB9, 2); + if (chip->caps & ES18XX_CONTROL) { + /* Hardware volume IRQ */ + snd_es18xx_config_write(chip, 0x27, chip->irq); + if (chip->fm_port > SNDRV_AUTO_PORT) { + /* FM I/O */ + snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8); + snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff); + } + if (chip->mpu_port > SNDRV_AUTO_PORT) { + /* MPU-401 I/O */ + snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8); + snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff); + /* MPU-401 IRQ */ + snd_es18xx_config_write(chip, 0x28, chip->irq); + } + /* Audio1 IRQ */ + snd_es18xx_config_write(chip, 0x70, chip->irq); + /* Audio2 IRQ */ + snd_es18xx_config_write(chip, 0x72, chip->irq); + /* Audio1 DMA */ + snd_es18xx_config_write(chip, 0x74, chip->dma1); + /* Audio2 DMA */ + snd_es18xx_config_write(chip, 0x75, chip->dma2); + + /* Enable Audio 1 IRQ */ + snd_es18xx_write(chip, 0xB1, 0x50); + /* Enable Audio 2 IRQ */ + snd_es18xx_mixer_write(chip, 0x7A, 0x40); + /* Enable Audio 1 DMA */ + snd_es18xx_write(chip, 0xB2, 0x50); + /* Enable MPU and hardware volume interrupt */ + snd_es18xx_mixer_write(chip, 0x64, 0x42); + } + else { + int irqmask, dma1mask, dma2mask; + switch (chip->irq) { + case 2: + case 9: + irqmask = 0; + break; + case 5: + irqmask = 1; + break; + case 7: + irqmask = 2; + break; + case 10: + irqmask = 3; + break; + default: + snd_printk("invalid irq %d\n", chip->irq); + return -ENODEV; + } + switch (chip->dma1) { + case 0: + dma1mask = 1; + break; + case 1: + dma1mask = 2; + break; + case 3: + dma1mask = 3; + break; + default: + snd_printk("invalid dma1 %d\n", chip->dma1); + return -ENODEV; + } + switch (chip->dma2) { + case 0: + dma2mask = 0; + break; + case 1: + dma2mask = 1; + break; + case 3: + dma2mask = 2; + break; + case 5: + dma2mask = 3; + break; + default: + snd_printk("invalid dma2 %d\n", chip->dma2); + return -ENODEV; + } + + /* Enable and set Audio 1 IRQ */ + snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2)); + /* Enable and set Audio 1 DMA */ + snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2)); + /* Set Audio 2 DMA */ + snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask); + /* Enable Audio 2 IRQ and DMA + Set capture mixer input */ + snd_es18xx_mixer_write(chip, 0x7A, 0x68); + /* Enable and set hardware volume interrupt */ + snd_es18xx_mixer_write(chip, 0x64, 0x06); + if (chip->mpu_port > SNDRV_AUTO_PORT) { + /* MPU401 share irq with audio + Joystick enabled + FM enabled */ + snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1); + } + snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01); + } + if (chip->caps & ES18XX_NEW_RATE) { + /* Change behaviour of register A1 + 4x oversampling + 2nd channel DAC asynchronous */ + snd_es18xx_mixer_write(chip, 0x71, 0x32); + } + if (!(chip->caps & ES18XX_PCM2)) { + /* Enable DMA FIFO */ + snd_es18xx_write(chip, 0xB7, 0x80); + } + if (chip->caps & ES18XX_SPATIALIZER) { + /* Set spatializer parameters to recommended values */ + snd_es18xx_mixer_write(chip, 0x54, 0x8f); + snd_es18xx_mixer_write(chip, 0x56, 0x95); + snd_es18xx_mixer_write(chip, 0x58, 0x94); + snd_es18xx_mixer_write(chip, 0x5a, 0x80); + } + /* Mute input source */ + if (chip->caps & ES18XX_MUTEREC) + mask = 0x10; + if (chip->caps & ES18XX_RECMIX) + snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask); + else { + snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask); + snd_es18xx_write(chip, 0xb4, 0x00); + } +#ifndef AVOID_POPS + /* Enable PCM output */ + snd_es18xx_dsp_command(chip, 0xD1); +#endif + + return 0; +} + +static int __init snd_es18xx_identify(es18xx_t *chip) +{ + int hi,lo; + + /* reset */ + if (snd_es18xx_reset(chip) < 0) { + snd_printk("reset at 0x%lx failed!!!\n", chip->port); + return -ENODEV; + } + + snd_es18xx_dsp_command(chip, 0xe7); + hi = snd_es18xx_dsp_get_byte(chip); + if (hi < 0) { + return hi; + } + lo = snd_es18xx_dsp_get_byte(chip); + if ((lo & 0xf0) != 0x80) { + return -ENODEV; + } + if (hi == 0x48) { + chip->version = 0x488; + return 0; + } + if (hi != 0x68) { + return -ENODEV; + } + if ((lo & 0x0f) < 8) { + chip->version = 0x688; + return 0; + } + + outb(0x40, chip->port + 0x04); + hi = inb(chip->port + 0x05); + lo = inb(chip->port + 0x05); + if (hi != lo) { + chip->version = hi << 8 | lo; + chip->ctrl_port = inb(chip->port + 0x05) << 8; + chip->ctrl_port += inb(chip->port + 0x05); + + if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) + return -EBUSY; + + return 0; + } + + /* If has Hardware volume */ + if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) { + /* If has Audio2 */ + if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) { + /* If has volume count */ + if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) { + chip->version = 0x1887; + } else { + chip->version = 0x1888; + } + } else { + chip->version = 0x1788; + } + } + else + chip->version = 0x1688; + return 0; +} + +static int __init snd_es18xx_probe(es18xx_t *chip) +{ + if (snd_es18xx_identify(chip) < 0) { + printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port); + return -ENODEV; + } + + switch (chip->version) { + case 0x1868: + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1869: + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1878: + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1879: + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV; + break; + case 0x1887: + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; + break; + case 0x1888: + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV; + break; + default: + snd_printk("[0x%lx] unsupported chip ES%x\n", + chip->port, chip->version); + return -ENODEV; + } + + snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version); + + return snd_es18xx_initialize(chip); +} + +static snd_pcm_ops_t snd_es18xx_playback_ops = { + open: snd_es18xx_playback_open, + close: snd_es18xx_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es18xx_playback_hw_params, + prepare: snd_es18xx_playback_prepare, + trigger: snd_es18xx_playback_trigger, + pointer: snd_es18xx_playback_pointer, +}; + +static snd_pcm_ops_t snd_es18xx_capture_ops = { + open: snd_es18xx_capture_open, + close: snd_es18xx_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es18xx_capture_hw_params, + prepare: snd_es18xx_capture_prepare, + trigger: snd_es18xx_capture_trigger, + pointer: snd_es18xx_capture_pointer, +}; + +static void snd_es18xx_pcm_free(snd_pcm_t *pcm) +{ + es18xx_t *codec = snd_magic_cast(es18xx_t, pcm->private_data, return); + codec->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __init snd_es18xx_pcm(es18xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + char str[16]; + int err; + + if (rpcm) + *rpcm = NULL; + sprintf(str, "ES%x", chip->version); + if (chip->caps & ES18XX_PCM2) { + err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm); + } else { + err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm); + } + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops); + + /* global setup */ + pcm->private_data = chip; + pcm->private_free = snd_es18xx_pcm_free; + pcm->info_flags = 0; + if (chip->caps & ES18XX_DUPLEX_SAME) + pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; + sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* Power Management support functions */ +#ifdef CONFIG_PM +static void snd_es18xx_suspend(es18xx_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + + /* power down */ + chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); + chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg); + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void snd_es18xx_resume(es18xx_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* restore PM register, we won't wake till (not 0x07) i/o activity though */ + snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +/* callback for control API */ +static int snd_es18xx_set_power_state(snd_card_t *card, unsigned int power_state) +{ + es18xx_t *chip = (es18xx_t *) card->power_state_private_data; + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_es18xx_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_es18xx_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_es18xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + snd_es18xx_suspend(chip); + break; + case PM_RESUME: + snd_es18xx_resume(chip); + break; + } + return 0; +} +#endif /* CONFIG_PM */ + +static int snd_es18xx_free(es18xx_t *chip) +{ +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_ctrl_port) { + release_resource(chip->res_ctrl_port); + kfree_nocheck(chip->res_ctrl_port); + } + if (chip->res_mpu_port) { + release_resource(chip->res_mpu_port); + kfree_nocheck(chip->res_mpu_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); + if (chip->dma1 >= 0) { + disable_dma(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + disable_dma(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_es18xx_dev_free(snd_device_t *device) +{ + es18xx_t *chip = snd_magic_cast(es18xx_t, device->device_data, return -ENXIO); + return snd_es18xx_free(chip); +} + +static int __init snd_es18xx_new_device(snd_card_t * card, + unsigned long port, + unsigned long mpu_port, + unsigned long fm_port, + int irq, int dma1, int dma2, + es18xx_t ** rchip) +{ + es18xx_t *chip; + static snd_device_ops_t ops = { + dev_free: snd_es18xx_dev_free, + }; + int err; + + *rchip = NULL; + chip = snd_magic_kcalloc(es18xx_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + spin_lock_init(&chip->ctrl_lock); + chip->card = card; + chip->port = port; + chip->mpu_port = mpu_port; + chip->fm_port = fm_port; + chip->irq = -1; + chip->dma1 = -1; + chip->dma2 = -1; + chip->audio2_vol = 0x00; + chip->active = 0; + + if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); + return -EBUSY; + } + + if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq); + return -EBUSY; + } + chip->irq = irq; + + if (request_dma(dma1, "ES18xx DMA 1")) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1); + return -EBUSY; + } + chip->dma1 = dma1; + + if (request_dma(dma2, "ES18xx DMA 2")) { + snd_es18xx_free(chip); + printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2); + return -EBUSY; + } + chip->dma2 = dma2; + + if (snd_es18xx_probe(chip) < 0) { + snd_es18xx_free(chip); + return -ENODEV; + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es18xx_free(chip); + return err; + } + *rchip = chip; + return 0; +} + +static int __init snd_es18xx_mixer(es18xx_t *chip) +{ + snd_card_t *card; + int err, idx; + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < sizeof(snd_es18xx_base_controls) / + sizeof(snd_es18xx_base_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip); + if (chip->caps & ES18XX_HWV) { + switch (idx) { + case 0: + chip->master_volume = kctl; + kctl->private_free = snd_es18xx_hwv_free; + break; + case 1: + chip->master_switch = kctl; + kctl->private_free = snd_es18xx_hwv_free; + break; + } + } + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + if (chip->caps & ES18XX_PCM2) { + for (idx = 0; idx < sizeof(snd_es18xx_pcm2_controls) / + sizeof(snd_es18xx_pcm2_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0) + return err; + } + } else { + for (idx = 0; idx < sizeof(snd_es18xx_pcm1_controls) / + sizeof(snd_es18xx_pcm1_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0) + return err; + } + } + + if (chip->caps & ES18XX_MONO) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_mono_in_control, chip))) < 0) + return err; + } + if (chip->caps & ES18XX_RECMIX) { + for (idx = 0; idx < sizeof(snd_es18xx_recmix_controls) / + sizeof(snd_es18xx_recmix_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0) + return err; + } + } + switch (chip->version) { + default: + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0) + return err; + break; + case 0x1869: + case 0x1879: + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0) + return err; + break; + } + if (chip->caps & ES18XX_SPATIALIZER) { + for (idx = 0; idx < sizeof(snd_es18xx_spatializer_controls) / + sizeof(snd_es18xx_spatializer_controls[0]); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0) + return err; + } + } + if (chip->caps & ES18XX_HWV) { + for (idx = 0; idx < sizeof(snd_es18xx_hw_volume_controls) / + sizeof(snd_es18xx_hw_volume_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip); + if (idx == 0) + chip->hw_volume = kctl; + else + chip->hw_switch = kctl; + kctl->private_free = snd_es18xx_hwv_free; + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + + } + } + return 0; +} + + +/* Card level */ + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Christian Fischbach , Abramo Bagnara "); +MODULE_DESCRIPTION("ESS ES18xx AudioDrive"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES1868 PnP AudioDrive}," + "{ESS,ES1869 PnP AudioDrive}," + "{ESS,ES1878 PnP AudioDrive}," + "{ESS,ES1879 PnP AudioDrive}," + "{ESS,ES1887 PnP AudioDrive}," + "{ESS,ES1888 PnP AudioDrive}," + "{ESS,ES1887 AudioDrive}," + "{ESS,ES1888 AudioDrive}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ +#ifndef __ISAPNP__ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +#else +static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#endif +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ES18xx soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ES18xx soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ES18xx soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x330,0x30},{0x800,0xffe,0x2}},prefers:{0x330,0x300},base:16,dialog:combo"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x800,0xffc,0x4}},prefers:{0x388},base:16,dialog:combo"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC ",prefers:{5}"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA 1 # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC ",prefers:{1}"); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA 2 # for ES18xx driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{0},{1},{3},{5}},dialog:list,prefers:{0}"); + +struct snd_audiodrive { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devc; +#endif +}; + +static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_audiodrive_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_audiodrive_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_ES18XX(_va, _vb, _vc, _device, _audio, _control) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _control) } \ + } + +static struct isapnp_card_id snd_audiodrive_pnpids[] __devinitdata = { + /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x1868,0x0000), + /* ESS 1868 (integrated on Maxisound Cards) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x8601,0x8600), + /* ESS 1868 (integrated on Maxisound Cards) */ + ISAPNP_ES18XX('E','S','S',0x1868,0x8611,0x8610), + /* ESS ES1869 Plug and Play AudioDrive */ + ISAPNP_ES18XX('E','S','S',0x0003,0x1869,0x0006), + /* ESS 1869 */ + ISAPNP_ES18XX('E','S','S',0x1869,0x1869,0x0006), + /* ESS 1878 */ + ISAPNP_ES18XX('E','S','S',0x1878,0x1878,0x0004), + /* ESS 1879 */ + ISAPNP_ES18XX('E','S','S',0x1879,0x1879,0x0009), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_audiodrive_pnpids); + +static int __init snd_audiodrive_isapnp(int dev, struct snd_audiodrive *acard) +{ + const struct isapnp_card_id *id = snd_audiodrive_isapnp_id[dev]; + struct isapnp_card *card = snd_audiodrive_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + acard->devc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devc->active) { + acard->dev = acard->devc = NULL; + return -EBUSY; + } + /* Control port initialization */ + if (acard->devc->prepare(acard->devc)<0) + return -EAGAIN; + if (acard->devc->activate(acard->devc)<0) { + printk(KERN_ERR PFX "isapnp control configure failure (out of resources?)\n"); + return -EAGAIN; + } + snd_printdd("isapnp: port=0x%lx\n", acard->devc->resource[0].start); + /* PnP initialization */ + pdev = acard->dev; + if (pdev->prepare(pdev)<0) { + acard->devc->deactivate(acard->devc); + return -EAGAIN; + } + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_mpu_port[dev], 2); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n"); + acard->devc->deactivate(acard->devc); + return -EBUSY; + } + /* ok. hack using Vendor-Defined Card-Level registers */ + /* skip csn and logdev initialization - already done in isapnp_configure */ + isapnp_cfg_begin(pdev->bus->number, pdev->devfn); + isapnp_write_byte(0x27, pdev->irq_resource[0].start); /* Hardware Volume IRQ Number */ + if (snd_mpu_port[dev] > SNDRV_AUTO_PORT) + isapnp_write_byte(0x28, pdev->irq); /* MPU-401 IRQ Number */ + isapnp_write_byte(0x72, pdev->irq_resource[0].start); /* second IRQ */ + isapnp_cfg_end(); + snd_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_mpu_port[dev] = pdev->resource[2].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", snd_port[dev], snd_fm_port[dev], snd_mpu_port[dev]); + snd_printdd("isapnp ES18xx: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]); + return 0; +} + +static void snd_audiodrive_deactivate(struct snd_audiodrive *acard) +{ + if (acard->devc) { + acard->devc->deactivate(acard->devc); + acard->devc = NULL; + } + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void snd_audiodrive_free(snd_card_t *card) +{ + struct snd_audiodrive *acard = (struct snd_audiodrive *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_audiodrive_deactivate(acard); +#endif + } +} + +static int __init snd_audiodrive_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1}; + static int possible_dmas[] = {1, 0, 3, 5, -1}; + int irq, dma1, dma2; + snd_card_t *card; + struct snd_audiodrive *acard; + snd_rawmidi_t *rmidi = NULL; + es18xx_t *chip; + opl3_t *opl3; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_audiodrive)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_audiodrive *)card->private_data; + card->private_free = snd_audiodrive_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_audiodrive_isapnp(dev, acard)) < 0) { + snd_card_free(card); + return err; + } +#endif + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_es18xx_new_device(card, + snd_port[dev], + snd_mpu_port[dev], + snd_fm_port[dev], + irq, dma1, dma2, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es18xx_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { + printk(KERN_ERR PFX "opl3 not detected at 0x%lx\n", chip->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX, + chip->mpu_port, 0, + irq, 0, + &rmidi)) < 0) { + snd_card_free(card); + return err; + } + chip->rmidi = rmidi; + } + +#ifdef CONFIG_PM + /* Power Management */ + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_es18xx_pm_callback); + if (chip->pm_dev) { + chip->pm_dev->data = chip; + /* set control api callback */ + card->set_power_state = snd_es18xx_set_power_state; + card->power_state_private_data = chip; + } +#endif + sprintf(card->driver, "ES%x", chip->version); + sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d", + card->shortname, + chip->port, + irq, dma1, dma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_audiodrive_cards[dev] = card; + return 0; +} + +static int __init snd_audiodrive_probe_legacy_port(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + snd_port[dev] = port; + res = snd_audiodrive_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + + +#ifdef __ISAPNP__ +static int __init snd_audiodrive_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_audiodrive_isapnp_cards[dev] = card; + snd_audiodrive_isapnp_id[dev] = id; + res = snd_audiodrive_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_es18xx_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; + int dev, cards = 0; + + /* legacy non-auto cards at first */ + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_audiodrive_probe(dev) >= 0) + cards++; + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards at last */ + cards += isapnp_probe_cards(snd_audiodrive_pnpids, snd_audiodrive_isapnp_detect); +#endif + if(!cards) { +#ifdef MODULE + printk(KERN_ERR "ESS AudioDrive ES18xx soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_es18xx_exit(void) +{ + int idx; + + for(idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_audiodrive_cards[idx]); +} + +module_init(alsa_card_es18xx_init) +module_exit(alsa_card_es18xx_exit) + +#ifndef MODULE + +/* format is: snd-es18xx=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port,snd_irq, + snd_dma1,snd_dma2 */ + +static int __init alsa_card_es18xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-es18xx=", alsa_card_es18xx_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/gus/Makefile linux-2.4.19-pre5-mjc/sound/isa/gus/Makefile --- linux/sound/isa/gus/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,62 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _gus.o + +list-multi := snd-gus-lib.o snd-gusclassic.o snd-gusextreme.o \ + snd-gusmax.o snd-interwave.o snd-interwave-stb.o + +export-objs := gus_main.o gus_volume.o + +snd-gus-lib-objs := gus_main.o \ + gus_io.o gus_irq.o gus_timer.o \ + gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \ + gus_pcm.o gus_mixer.o \ + gus_uart.o \ + gus_reset.o +snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o + +snd-gusclassic-objs := gusclassic.o +snd-gusextreme-objs := gusextreme.o +snd-gusmax-objs := gusmax.o +snd-interwave-objs := interwave.o +snd-interwave-stb-objs := interwave-stb.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o +obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o +obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o +obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o +obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_GUSCLASSIC) += snd-gus-synth.o + obj-$(CONFIG_SND_GUSMAX) += snd-gus-synth.o + obj-$(CONFIG_SND_GUSEXTREME) += snd-gus-synth.o + obj-$(CONFIG_SND_INTERWAVE) += snd-gus-synth.o + obj-$(CONFIG_SND_INTERWAVE_STB) += snd-gus-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-gus-lib.o: $(snd-gus-lib-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-lib-objs) + +snd-gus-synth.o: $(snd-gus-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gus-synth-objs) + +snd-gusclassic.o: $(snd-gusclassic-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusclassic-objs) + +snd-gusextreme.o: $(snd-gusextreme-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusextreme-objs) + +snd-gusmax.o: $(snd-gusmax-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-gusmax-objs) + +snd-interwave.o: $(snd-interwave-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-objs) + +snd-interwave-stb.o: $(snd-interwave-stb-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-interwave-stb-objs) diff -Nru linux/sound/isa/gus/gus_dma.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dma.c --- linux/sound/isa/gus/gus_dma.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dma.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,245 @@ +/* + * Routines for GF1 DMA control + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +void snd_gf1_dma_ack(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00); + snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_dma_program(snd_gus_card_t * gus, + unsigned int addr, + unsigned long buf_addr, + unsigned int count, + unsigned int cmd) +{ + unsigned long flags; + unsigned int address; + unsigned char dma_cmd; + unsigned int address_high; + + // snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count); + + if (gus->gf1.dma1 > 3) { + if (gus->gf1.enh_mode) { + address = addr >> 1; + } else { + if (addr & 0x1f) { + snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr); + return; + } + address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1); + } + } else { + address = addr; + } + + dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd; +#if 0 + dma_cmd |= 0x08; +#endif + if (dma_cmd & SNDRV_GF1_DMA_16BIT) { + count++; + count &= ~1; /* align */ + } + if (gus->gf1.dma1 > 3) { + dma_cmd |= SNDRV_GF1_DMA_WIDTH16; + count++; + count &= ~1; /* align */ + } + snd_gf1_dma_ack(gus); + snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE); +#if 0 + snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + if (gus->gf1.enh_mode) { + address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f); + snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high); + } else + snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); + snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + /* PCM block have bigger priority than synthesizer one */ + if (gus->gf1.dma_data_pcm) { + block = gus->gf1.dma_data_pcm; + if (gus->gf1.dma_data_pcm_last == block) { + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = NULL; + } else { + gus->gf1.dma_data_pcm = block->next; + } + } else if (gus->gf1.dma_data_synth) { + block = gus->gf1.dma_data_synth; + if (gus->gf1.dma_data_synth_last == block) { + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = NULL; + } else { + gus->gf1.dma_data_synth = block->next; + } + } else { + block = NULL; + } + if (block) { + gus->gf1.dma_ack = block->ack; + gus->gf1.dma_private_data = block->private_data; + } + return block; +} + + +static void snd_gf1_dma_interrupt(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + snd_gf1_dma_ack(gus); + if (gus->gf1.dma_ack) + gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); + spin_lock(&gus->dma_lock); + if (gus->gf1.dma_data_pcm == NULL && + gus->gf1.dma_data_synth == NULL) { + gus->gf1.dma_ack = NULL; + gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; + spin_unlock(&gus->dma_lock); + return; + } + block = snd_gf1_dma_next_block(gus); + spin_unlock(&gus->dma_lock); + snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); + kfree(block); +#if 0 + printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd); +#endif +} + +int snd_gf1_dma_init(snd_gus_card_t * gus) +{ + down(&gus->dma_mutex); + gus->gf1.dma_shared++; + if (gus->gf1.dma_shared > 1) { + up(&gus->dma_mutex); + return 0; + } + gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt; + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = NULL; + up(&gus->dma_mutex); + return 0; +} + +int snd_gf1_dma_done(snd_gus_card_t * gus) +{ + snd_gf1_dma_block_t *block; + + down(&gus->dma_mutex); + gus->gf1.dma_shared--; + if (!gus->gf1.dma_shared) { + snd_dma_disable(gus->gf1.dma1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE); + snd_gf1_dma_ack(gus); + while ((block = gus->gf1.dma_data_pcm)) { + gus->gf1.dma_data_pcm = block->next; + kfree(block); + } + while ((block = gus->gf1.dma_data_synth)) { + gus->gf1.dma_data_synth = block->next; + kfree(block); + } + gus->gf1.dma_data_pcm_last = + gus->gf1.dma_data_synth_last = NULL; + } + up(&gus->dma_mutex); + return 0; +} + +int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, + snd_gf1_dma_block_t * __block, + int atomic, + int synth) +{ + unsigned long flags; + snd_gf1_dma_block_t *block; + + block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL); + if (block == NULL) { + snd_printk("gf1: DMA transfer failure; not enough memory\n"); + return -ENOMEM; + } + *block = *__block; + block->next = NULL; +#if 0 + printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd); +#endif +#if 0 + printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last); + printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm); +#endif + spin_lock_irqsave(&gus->dma_lock, flags); + if (synth) { + if (gus->gf1.dma_data_synth_last) { + gus->gf1.dma_data_synth_last->next = block; + gus->gf1.dma_data_synth_last = block; + } else { + gus->gf1.dma_data_synth = + gus->gf1.dma_data_synth_last = block; + } + } else { + if (gus->gf1.dma_data_pcm_last) { + gus->gf1.dma_data_pcm_last->next = block; + gus->gf1.dma_data_pcm_last = block; + } else { + gus->gf1.dma_data_pcm = + gus->gf1.dma_data_pcm_last = block; + } + } + if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) { + gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER; + block = snd_gf1_dma_next_block(gus); + spin_unlock_irqrestore(&gus->dma_lock, flags); + if (block == NULL) + return 0; + snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); + kfree(block); + return 0; + } + spin_unlock_irqrestore(&gus->dma_lock, flags); + return 0; +} diff -Nru linux/sound/isa/gus/gus_dram.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dram.c --- linux/sound/isa/gus/gus_dram.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_dram.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,104 @@ +/* + * Copyright (c) by Jaroslav Kysela + * DRAM access routines + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + + +static int snd_gus_dram_poke(snd_gus_card_t *gus, char *_buffer, + unsigned int address, unsigned int size) +{ + unsigned long flags; + unsigned int size1, size2; + char buffer[512], *pbuffer; + + while (size > 0) { + if (copy_from_user(buffer, _buffer, 512)) + return -EFAULT; + size1 = size > 512 ? 512 : size; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + snd_gf1_dram_addr(gus, address); + outsb(GUSP(gus, DRAM), buffer, size1); + spin_unlock_irqrestore(&gus->reg_lock, flags); + address += size1; + } else { + pbuffer = buffer; + size2 = size1; + while (size2--) + snd_gf1_poke(gus, address++, *pbuffer++); + } + size -= size1; + _buffer += size1; + } + return 0; +} + + +int snd_gus_dram_write(snd_gus_card_t *gus, char *buffer, + unsigned int address, unsigned int size) +{ + return snd_gus_dram_poke(gus, buffer, address, size); +} + +static int snd_gus_dram_peek(snd_gus_card_t *gus, char *_buffer, + unsigned int address, unsigned int size, + int rom) +{ + unsigned long flags; + unsigned int size1, size2; + char buffer[512], *pbuffer; + + while (size > 0) { + size1 = size > 512 ? 512 : size; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01); + snd_gf1_dram_addr(gus, address); + insb(GUSP(gus, DRAM), buffer, size1); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + spin_unlock_irqrestore(&gus->reg_lock, flags); + address += size1; + } else { + pbuffer = buffer; + size2 = size1; + while (size2--) + *pbuffer++ = snd_gf1_peek(gus, address++); + } + if (copy_to_user(_buffer, buffer, size1)) + return -EFAULT; + size -= size1; + _buffer += size1; + } + return 0; +} + +int snd_gus_dram_read(snd_gus_card_t *gus, char *buffer, + unsigned int address, unsigned int size, + int rom) +{ + return snd_gus_dram_peek(gus, buffer, address, size, rom); +} diff -Nru linux/sound/isa/gus/gus_instr.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_instr.c --- linux/sound/isa/gus/gus_instr.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_instr.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,174 @@ +/* + * Routines for Gravis UltraSound soundcards - Synthesizer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +/* + * + */ + +int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (wave->format & IWFFFF_WAVE_ROM) + return 0; /* it's probably ok - verify the address? */ + if (wave->format & IWFFFF_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF, + NULL, wave->size, + wave->format & IWFFFF_WAVE_16BIT, 1, + wave->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, + block->ptr, wave->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + wave->address.memory = block->ptr; + return 0; +} + +int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, + wave->format & IWFFFF_WAVE_ROM ? 1 : 0); +} + +int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + if (wave->format & IWFFFF_WAVE_ROM) + return 0; /* it's probably ok - verify the address? */ + return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); +} + +/* + * + */ + +int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (wave->format & GF1_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_GF1, + NULL, wave->size, + wave->format & GF1_WAVE_16BIT, 1, + wave->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, + block->ptr, wave->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + wave->address.memory = block->ptr; + return 0; +} + +int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0); +} + +int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); +} + +/* + * + */ + +int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gf1_mem_block_t *block; + int err; + + if (instr->format & SIMPLE_WAVE_STEREO) + return -EINVAL; /* not supported */ + block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE, + NULL, instr->size, + instr->format & SIMPLE_WAVE_16BIT, 1, + instr->share_id); + if (block == NULL) + return -ENOMEM; + err = snd_gus_dram_write(gus, data, block->ptr, instr->size); + if (err < 0) { + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); + snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); + snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); + return err; + } + instr->address.memory = block->ptr; + return 0; +} + +int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, + char *data, long len, int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0); +} + +int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, + int atomic) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + + return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory); +} diff -Nru linux/sound/isa/gus/gus_io.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_io.c --- linux/sound/isa/gus/gus_io.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_io.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,532 @@ +/* + * Copyright (c) by Jaroslav Kysela + * I/O routines for GF1/InterWave synthesizer chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +void snd_gf1_delay(snd_gus_card_t * gus) +{ + int i; + + for (i = 0; i < 6; i++) { + mb(); + inb(GUSP(gus, DRAM)); + } +} + +/* + * ======================================================================= + */ + +/* + * ok.. stop of control registers (wave & ramp) need some special things.. + * big UltraClick (tm) elimination... + */ + +static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned char value; + + outb(reg | 0x80, gus->gf1.reg_regsel); + mb(); + value = inb(gus->gf1.reg_data8); + mb(); + outb(reg, gus->gf1.reg_regsel); + mb(); + outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8); + mb(); +} + +static inline void __snd_gf1_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + outb(data, gus->gf1.reg_data8); + mb(); +} + +static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus, + unsigned char reg) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + return inb(gus->gf1.reg_data8); +} + +static inline void __snd_gf1_write16(snd_gus_card_t * gus, + unsigned char reg, unsigned int data) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) data, gus->gf1.reg_data16); + mb(); +} + +static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus, + unsigned char reg) +{ + outb(reg, gus->gf1.reg_regsel); + mb(); + return inw(gus->gf1.reg_data16); +} + +static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus, + unsigned char reg, unsigned char data) +{ + outb(reg, gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); + outb(data, gus->gf1.reg_timerdata); + inb(gus->gf1.reg_timerctrl); + inb(gus->gf1.reg_timerctrl); +} + +static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, int w_16bit) +{ + if (gus->gf1.enh_mode) { + if (w_16bit) + addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f); + __snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03)); + } else if (w_16bit) + addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1); + __snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11)); + __snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5)); +} + +static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus, + unsigned char reg, short w_16bit) +{ + unsigned int res; + + res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800; + res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff; + if (gus->gf1.enh_mode) { + res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26; + if (w_16bit) + res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f); + } else if (w_16bit) + res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f); + return res; +} + + +/* + * ======================================================================= + */ + +void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + __snd_gf1_ctrl_stop(gus, reg); +} + +void snd_gf1_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + __snd_gf1_write8(gus, reg, data); +} + +unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg) +{ + return __snd_gf1_look8(gus, reg); +} + +void snd_gf1_write16(snd_gus_card_t * gus, + unsigned char reg, + unsigned int data) +{ + __snd_gf1_write16(gus, reg, data); +} + +unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg) +{ + return __snd_gf1_look16(gus, reg); +} + +void snd_gf1_adlib_write(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + __snd_gf1_adlib_write(gus, reg, data); +} + +void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, short w_16bit) +{ + __snd_gf1_write_addr(gus, reg, addr, w_16bit); +} + +unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, + unsigned char reg, + short w_16bit) +{ + return __snd_gf1_read_addr(gus, reg, w_16bit); +} + +/* + + */ + +void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_ctrl_stop(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_i_write8(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write8(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_look8(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_i_write16(snd_gus_card_t * gus, + unsigned char reg, + unsigned int data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write16(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg) +{ + unsigned long flags; + unsigned short res; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_look16(gus, reg); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_i_adlib_write(snd_gus_card_t * gus, + unsigned char reg, + unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_adlib_write(gus, reg, data); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, + unsigned int addr, short w_16bit) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + __snd_gf1_write_addr(gus, reg, addr, w_16bit); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, + unsigned char reg, short w_16bit) +{ + unsigned int res; + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + res = __snd_gf1_read_addr(gus, reg, w_16bit); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +/* + + */ + +void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr) +{ + outb(0x43, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(0x44, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); +} + +void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(data, gus->gf1.reg_dram); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr) +{ + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + res = inb(gus->gf1.reg_dram); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data) +{ + unsigned long flags; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_pokew - GF1!!!\n"); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + mb(); + outw(data, gus->gf1.reg_data16); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr) +{ + unsigned long flags; + unsigned short res; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_peekw - GF1!!!\n"); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + mb(); + res = inw(gus->gf1.reg_data16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return res; +} + +void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, + unsigned short value, unsigned int count) +{ + unsigned long port; + unsigned long flags; + +#ifdef CONFIG_SND_DEBUG + if (!gus->interwave) + snd_printk("snd_gf1_dram_setmem - GF1!!!\n"); +#endif + addr &= ~1; + count >>= 1; + port = GUSP(gus, GF1DATALOW); + spin_lock_irqsave(&gus->reg_lock, flags); + outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); + mb(); + outw((unsigned short) addr, gus->gf1.reg_data16); + mb(); + outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); + mb(); + outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); + mb(); + outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); + while (count--) + outw(value, port); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +/* + + */ + +void snd_gf1_select_active_voices(snd_gus_card_t * gus) +{ + unsigned short voices; + + static unsigned short voices_tbl[32 - 14 + 1] = + { + 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, + 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 + }; + + voices = gus->gf1.active_voices; + if (voices > 32) + voices = 32; + if (voices < 14) + voices = 14; + if (gus->gf1.enh_mode) + voices = 32; + gus->gf1.active_voices = voices; + gus->gf1.playback_freq = + gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14]; + if (!gus->gf1.enh_mode) { + snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1)); + udelay(100); + } +} + +#ifdef CONFIG_SND_DEBUG + +void snd_gf1_print_voice_registers(snd_gus_card_t * gus) +{ + unsigned char mode; + int voice, ctrl; + + voice = gus->gf1.active_voice; + printk(" -%i- GF1 voice ctrl, ramp ctrl = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d)); + printk(" -%i- GF1 frequency = 0x%x\n", voice, snd_gf1_i_read16(gus, 1)); + printk(" -%i- GF1 loop start, end = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4)); + printk(" -%i- GF1 ramp start, end, rate = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6)); + printk(" -%i- GF1 volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 9)); + printk(" -%i- GF1 position = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4)); + if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) { /* enhanced mode */ + mode = snd_gf1_i_read8(gus, 0x15); + printk(" -%i- GFA1 mode = 0x%x\n", voice, mode); + if (mode & 0x01) { /* Effect processor */ + printk(" -%i- GFA1 effect address = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4)); + printk(" -%i- GFA1 effect volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16)); + printk(" -%i- GFA1 effect volume final = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d)); + printk(" -%i- GFA1 effect acumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14)); + } + if (mode & 0x20) { + printk(" -%i- GFA1 left offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4); + printk(" -%i- GFA1 left offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4); + printk(" -%i- GFA1 right offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4); + printk(" -%i- GFA1 right offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4); + } else + printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); + } else + printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); +} + +void snd_gf1_print_global_registers(snd_gus_card_t * gus) +{ + unsigned char global_mode = 0x00; + + printk(" -G- GF1 active voices = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES)); + if (gus->interwave) { + global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE); + printk(" -G- GF1 global mode = 0x%x\n", global_mode); + } + if (global_mode & 0x02) /* LFO enabled? */ + printk(" -G- GF1 LFO base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE)); + printk(" -G- GF1 voices IRQ read = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ)); + printk(" -G- GF1 DRAM DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL)); + printk(" -G- GF1 DRAM DMA high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW)); + printk(" -G- GF1 DRAM IO high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW)); + if (!gus->interwave) + printk(" -G- GF1 record DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL)); + printk(" -G- GF1 DRAM IO 16 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16)); + if (gus->gf1.enh_mode) { + printk(" -G- GFA1 memory config = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG)); + printk(" -G- GFA1 memory control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL)); + printk(" -G- GFA1 FIFO record base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR)); + printk(" -G- GFA1 FIFO playback base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR)); + printk(" -G- GFA1 interleave control = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE)); + } +} + +void snd_gf1_print_setup_registers(snd_gus_card_t * gus) +{ + printk(" -S- mix control = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG))); + printk(" -S- IRQ status = 0x%x\n", inb(GUSP(gus, IRQSTAT))); + printk(" -S- timer control = 0x%x\n", inb(GUSP(gus, TIMERCNTRL))); + printk(" -S- timer data = 0x%x\n", inb(GUSP(gus, TIMERDATA))); + printk(" -S- status read = 0x%x\n", inb(GUSP(gus, REGCNTRLS))); + printk(" -S- Sound Blaster control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL)); + printk(" -S- AdLib timer 1/2 = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2)); + printk(" -S- reset = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)); + if (gus->interwave) { + printk(" -S- compatibility = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY)); + printk(" -S- decode control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL)); + printk(" -S- version number = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER)); + printk(" -S- MPU-401 emul. control A/B = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B)); + printk(" -S- emulation IRQ = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ)); + } +} + +void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit) +{ + if (!w_16bit) { + while (count-- > 0) + printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++)); + } else { + while (count-- > 0) { + printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8)); + addr += 2; + } + } +} + +#endif diff -Nru linux/sound/isa/gus/gus_irq.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_irq.c --- linux/sound/isa/gus/gus_irq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_irq.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,158 @@ +/* + * Routine for IRQ handling from GF1/InterWave chip + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG +#define STAT_ADD(x) ((x)++) +#else +#define STAT_ADD(x) while (0) { ; } +#endif + +void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + snd_gus_card_t * gus = snd_magic_cast(snd_gus_card_t, dev_id, return); + unsigned char status; + int loop = 100; + + __again: + status = inb(gus->gf1.reg_irqstat); + if (status == 0) + return; + // snd_printk("IRQ: status = 0x%x\n", status); + if (status & 0x02) { + STAT_ADD(gus->gf1.interrupt_stat_midi_in); + gus->gf1.interrupt_handler_midi_in(gus); + } + if (status & 0x01) { + STAT_ADD(gus->gf1.interrupt_stat_midi_out); + gus->gf1.interrupt_handler_midi_out(gus); + } + if (status & (0x20 | 0x40)) { + unsigned int already, _current_; + unsigned char voice_status, voice; + snd_gus_voice_t *pvoice; + + already = 0; + while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) { + voice = voice_status & 0x1f; + _current_ = 1 << voice; + if (already & _current_) + continue; /* multi request */ + already |= _current_; /* mark request */ +#if 0 + printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE))); +#endif + pvoice = &gus->gf1.voices[voice]; + if (pvoice->use) { + if (!(voice_status & 0x80)) { /* voice position IRQ */ + STAT_ADD(pvoice->interrupt_stat_wave); + pvoice->handler_wave(gus, pvoice); + } + if (!(voice_status & 0x40)) { /* volume ramp IRQ */ + STAT_ADD(pvoice->interrupt_stat_volume); + pvoice->handler_volume(gus, pvoice); + } + } else { + STAT_ADD(gus->gf1.interrupt_stat_voice_lost); + snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + } + } + } + if (status & 0x04) { + STAT_ADD(gus->gf1.interrupt_stat_timer1); + gus->gf1.interrupt_handler_timer1(gus); + } + if (status & 0x08) { + STAT_ADD(gus->gf1.interrupt_stat_timer2); + gus->gf1.interrupt_handler_timer2(gus); + } + if (status & 0x80) { + if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) { + STAT_ADD(gus->gf1.interrupt_stat_dma_write); + gus->gf1.interrupt_handler_dma_write(gus); + } + if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) { + STAT_ADD(gus->gf1.interrupt_stat_dma_read); + gus->gf1.interrupt_handler_dma_read(gus); + } + } + if (--loop > 0) + goto __again; +} + +#ifdef CONFIG_SND_DEBUG +static void snd_gus_irq_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_gus_card_t *gus; + snd_gus_voice_t *pvoice; + int idx; + + gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out); + snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in); + snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1); + snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2); + snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write); + snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read); + snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n", + idx, + pvoice->interrupt_stat_wave, + pvoice->interrupt_stat_volume); + } +} + +void snd_gus_irq_profile_init(snd_gus_card_t *gus) +{ + snd_info_entry_t *entry; + + gus->irq_entry = NULL; + entry = snd_info_create_card_entry(gus->card, "gusirq", gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 512; + entry->c.text.read = snd_gus_irq_info_read; + entry->private_data = gus; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + gus->irq_entry = entry; +} + +void snd_gus_irq_profile_done(snd_gus_card_t *gus) +{ + if (gus->irq_entry) { + snd_info_unregister(gus->irq_entry); + gus->irq_entry = NULL; + } +} +#endif diff -Nru linux/sound/isa/gus/gus_lfo.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_lfo.c --- linux/sound/isa/gus/gus_lfo.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_lfo.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,430 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of LFO generators (tremolo & vibrato) for + * GF1/InterWave chips... + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include + +/* + * called by engine routines + */ + +static signed char snd_gf1_lfo_compute_value(snd_gus_card_t * gus, + unsigned char *ptr) +{ + unsigned int twaveinc, depth_delta; + signed int result; + unsigned short control, twave, depth, depth_final; + unsigned char *ptr1; + + control = *(unsigned short *) (ptr + 0x00); + ptr1 = ptr + ((control & 0x4000) >> 12); + /* 1. add TWAVEINC to TWAVE and write the result back */ + /* LFO update rate is 689Hz, effect timer is in ms */ + if (gus->gf1.timer_slave) + twaveinc = (689 * gus->gf1.timer_master_gus->gf1.effect_timer) / 1000; + else + twaveinc = (689 * gus->gf1.effect_timer) / 1000; + if (!twaveinc) + twaveinc++; +#if 0 + printk("twaveinc = 0x%x, effect_timer = %i\n", twaveinc, gus->gf1.effect_timer); +#endif + + depth = *(unsigned short *) (ptr1 + 0x0a); + depth_final = *(unsigned char *) (ptr + 0x02) << 5; + if (depth != depth_final) { + depth_delta = ((twaveinc * *(ptr + 0x03)) + *(unsigned short *) (ptr + 0x04)); + *(unsigned short *) (ptr + 0x04) = depth_delta % 8000; + depth_delta /= 8000; + if (depth < depth_final) { + if (depth + depth_delta > depth_final) + depth = depth_final; + else + depth += depth_delta; + } + if (depth > depth_final) { + if (depth - depth_delta < depth_final) + depth = depth_final; + else + depth -= depth_delta; + } + *(unsigned short *) (ptr1 + 0x0a) = depth; + } + twaveinc *= (unsigned int) control & 0x7ff; + twaveinc += *(unsigned short *) (ptr + 0x06); + *(unsigned short *) (ptr + 0x06) = twaveinc % 1000; + + twave = *(unsigned short *) (ptr1 + 0x08); + twave += (unsigned short) (twaveinc / (unsigned int) 1000); + *(unsigned short *) (ptr1 + 0x08) = twave; + + if (!(control & 0x2000)) { + /* 2. if shift is low */ + if (twave & 0x4000) { /* bit 14 high -> invert TWAVE 13-0 */ + twave ^= 0x3fff; + twave &= ~0x4000; + } + /* TWAVE bit 15 is exclusive or'd with the invert bit (12) */ + twave ^= (control & 0x1000) << 3; + } else { + /* 2. if shift is high */ + if (twave & 0x8000) /* bit 15 high -> invert TWAVE 14-0 */ + twave ^= 0x7fff; + /* the invert bit (12) is used as sign bit */ + if (control & 0x1000) + twave |= 0x8000; + else + twave &= ~0x8000; + } + /* 3. multiply the 14-bit LFO waveform magnitude by 13-bit DEPTH */ +#if 0 + printk("c=0x%x,tw=0x%x,to=0x%x,d=0x%x,df=0x%x,di=0x%x,r=0x%x,r1=%i\n", + control, twave, + *(unsigned short *) (ptr1 + 0x08), + depth, depth_final, *(ptr + 0x03), + (twave & 0x7fff) * depth, ((twave & 0x7fff) * depth) >> 21); +#endif + result = (twave & 0x7fff) * depth; + if (result) { + /* shift */ + result >>= 21; + result &= 0x3f; + } + /* add sign */ + if (twave & 0x8000) + result = -result; +#if 0 + printk("lfo final value = %i\n", result); +#endif + return result; +} + +static void snd_gf1_lfo_register_setup(snd_gus_card_t * gus, + snd_gf1_voice_t * voice, + int lfo_type) +{ + unsigned long flags; + + if (gus->gf1.enh_mode) { + CLI(&flags); + gf1_select_voice(gus, voice->number); + if (lfo_type & 1) { + snd_gf1_write8(gus, GF1_VB_FREQUENCY_LFO, voice->lfo_fc); + voice->lfo_fc = 0; + } + if (lfo_type & 2) { + snd_gf1_write8(gus, GF1_VB_VOLUME_LFO, voice->lfo_volume); + voice->lfo_volume = 0; + } + STI(&flags); + } else { + /* + * ok.. with old GF1 chip can be only vibrato emulated... + * volume register can be in volume ramp state, so tremolo isn't simple.. + */ + if (!(lfo_type & 1)) + return; +#if 0 + if (voice->lfo_fc) + printk("setup - %i = %i\n", voice->number, voice->lfo_fc); +#endif + CLI(&flags); + gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, GF1_VW_FREQUENCY, voice->fc_register + voice->lfo_fc); + STI(&flags); + } +} + +void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice) +{ + unsigned char *ptr; + +#if 0 + if (voice->number != 0) + return; +#endif + ptr = gus->gf1.lfos + ((voice->number) << 5); + /* 1. vibrato */ + if (*(unsigned short *) (ptr + 0x00) & 0x8000) + voice->lfo_fc = snd_gf1_lfo_compute_value(gus, ptr); + /* 2. tremolo */ + ptr += 16; + if (*(unsigned short *) (ptr + 0x00) & 0x8000) + voice->lfo_volume = snd_gf1_lfo_compute_value(gus, ptr); + /* 3. register setup */ + snd_gf1_lfo_register_setup(gus, voice, 3); +} + +/* + + */ + +void snd_gf1_lfo_init(snd_gus_card_t * gus) +{ + if (gus->gf1.hw_lfo) { + snd_gf1_i_write16(gus, GF1_GW_LFO_BASE, 0x0000); + snd_gf1_dram_setmem(gus, 0, 0x0000, 1024); + /* now enable LFO */ + snd_gf1_i_write8(gus, GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, GF1_GB_GLOBAL_MODE) | 0x02); + } + if (gus->gf1.sw_lfo) { +#if 1 + gus->gf1.lfos = snd_calloc(1024); + if (!gus->gf1.lfos) +#endif + gus->gf1.sw_lfo = 0; + } +} + +void snd_gf1_lfo_done(snd_gus_card_t * gus) +{ + if (gus->gf1.sw_lfo) { + if (gus->gf1.lfos) { + snd_gf1_free(gus->gf1.lfos, 1024); + gus->gf1.lfos = NULL; + } + } +} + +void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, + struct ULTRA_STRU_IW_LFO_PROGRAM *program) +{ + unsigned int lfo_addr, wave_select; + + wave_select = (program->freq_and_control & 0x4000) >> 12; + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) { +#if 0 + printk("LMCI = 0x%x\n", snd_gf1_i_look8(gus, 0x53)); + printk("lfo_program: lfo_addr=0x%x,wave_sel=0x%x,fac=0x%x,df=0x%x,di=0x%x,twave=0x%x,depth=0x%x\n", + lfo_addr, wave_select, + program->freq_and_control, + program->depth_final, + program->depth_inc, + program->twave, + program->depth); +#endif + snd_gf1_poke(gus, lfo_addr + 0x02, program->depth_final); + snd_gf1_poke(gus, lfo_addr + 0x03, program->depth_inc); + snd_gf1_pokew(gus, lfo_addr + 0x08 + wave_select, program->twave); + snd_gf1_pokew(gus, lfo_addr + 0x0a + wave_select, program->depth); + snd_gf1_pokew(gus, lfo_addr + 0x00, program->freq_and_control); +#if 0 + { + int i = 0; + for (i = 0; i < 16; i++) + printk("%02x:", snd_gf1_peek(gus, lfo_addr + i)); + printk("\n"); + } +#endif + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(ptr + 0x02) = program->depth_final; + *(ptr + 0x03) = program->depth_inc; + *(unsigned short *) (ptr + 0x08 + wave_select) = program->twave; + *(unsigned short *) (ptr + 0x0a + wave_select) = program->depth; + *(unsigned short *) (ptr + 0x00) = program->freq_and_control; + } +} + +void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, snd_gf1_peekw(gus, lfo_addr + 0x00) | 0x8000); + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(unsigned short *) (ptr + 0x00) |= 0x8000; + } +} + +void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, + snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x8000); + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + *(unsigned short *) (ptr + 0x00) &= ~0x8000; + } +} + +void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, + int lfo_type, int freq) +{ + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) + snd_gf1_pokew(gus, lfo_addr + 0x00, + (snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x7ff) | (freq & 0x7ff)); + if (gus->gf1.sw_lfo) { + unsigned long flags; + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + CLI(&flags); + *(unsigned short *) (ptr + 0x00) &= ~0x7ff; + *(unsigned short *) (ptr + 0x00) |= freq & 0x7ff; + STI(&flags); + } +} + +void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, + int lfo_type, int depth) +{ + unsigned long flags; + unsigned int lfo_addr; + unsigned short control = 0; + unsigned char *ptr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + ptr = gus->gf1.lfos + lfo_addr; + if (gus->gf1.hw_lfo) + control = snd_gf1_peekw(gus, lfo_addr + 0x00); + if (gus->gf1.sw_lfo) + control = *(unsigned short *) (ptr + 0x00); + if (depth < 0) { + control |= 0x1000; + depth = -depth; + } else + control &= ~0x1000; + if (gus->gf1.hw_lfo) { + CLI(&flags); + snd_gf1_poke(gus, lfo_addr + 0x02, (unsigned char) depth); + snd_gf1_pokew(gus, lfo_addr + 0x0a + ((control & 0x4000) >> 12), depth << 5); + snd_gf1_pokew(gus, lfo_addr + 0x00, control); + STI(&flags); + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + + CLI(&flags); + *(ptr + 0x02) = (unsigned char) depth; + *(unsigned short *) (ptr + 0x0a + ((control & 0x4000) >> 12)) = depth << 5; + *(unsigned short *) (ptr + 0x00) = control; + STI(&flags); + } +} + +void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, + int freq, int current_depth, int depth, int sweep, + int shape) +{ + struct ULTRA_STRU_IW_LFO_PROGRAM program; + + program.freq_and_control = 0x8000 | (freq & 0x7ff); + if (shape & ULTRA_STRU_IW_LFO_SHAPE_POSTRIANGLE) + program.freq_and_control |= 0x2000; + if (depth < 0) { + program.freq_and_control |= 0x1000; + depth = -depth; + } + program.twave = 0; + program.depth = current_depth; + program.depth_final = depth; + if (sweep) { + program.depth_inc = (unsigned char) (((int) ((depth << 5) - current_depth) << 9) / (sweep * 4410L)); + if (!program.depth_inc) + program.depth_inc++; + } else + program.depth = (unsigned short) (depth << 5); + snd_gf1_lfo_program(gus, voice, lfo_type, &program); +} + +void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type) +{ + unsigned long flags; + unsigned int lfo_addr; + + lfo_addr = (voice << 5) | (lfo_type << 4); + if (gus->gf1.hw_lfo) { + snd_gf1_pokew(gus, lfo_addr + 0x00, 0x0000); + CLI(&flags); + gf1_select_voice(gus, voice); + snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0); + STI(&flags); + } + if (gus->gf1.sw_lfo) { + unsigned char *ptr = gus->gf1.lfos + lfo_addr; + snd_gf1_voice_t *pvoice; + + *(unsigned short *) (ptr + 0x00) = 0; + *(unsigned short *) (ptr + 0x04) = 0; + *(unsigned short *) (ptr + 0x06) = 0; + if (gus->gf1.syn_voices) { + pvoice = gus->gf1.syn_voices + voice; + if (lfo_type == ULTRA_LFO_VIBRATO) + pvoice->lfo_fc = 0; + else + pvoice->lfo_volume = 0; + snd_gf1_lfo_register_setup(gus, pvoice, lfo_type == ULTRA_LFO_VIBRATO ? 1 : 2); + } else if (gus->gf1.enh_mode) { + CLI(&flags); + gf1_select_voice(gus, voice); + snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0); + STI(&flags); + } + } +} + +void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *data) +{ + int lfo_type; + int lfo_command; + int temp1, temp2; + + lfo_type = *data >> 7; + lfo_command = *data & 0x7f; + switch (lfo_command) { + case ULTRA_LFO_SETUP: /* setup */ + temp1 = snd_gf1_get_word(data, 2); + temp2 = snd_gf1_get_word(data, 4); + snd_gf1_lfo_setup(gus, voice, lfo_type, temp1 & 0x7ff, 0, temp2 > 255 ? 255 : temp2, snd_gf1_get_byte(data, 1), (temp1 & 0x2000) >> 13); + break; + case ULTRA_LFO_FREQ: /* freq */ + snd_gf1_lfo_change_depth(gus, voice, lfo_type, snd_gf1_get_word(data, 2)); + break; + case ULTRA_LFO_DEPTH: /* depth */ + snd_gf1_lfo_change_freq(gus, voice, lfo_type, snd_gf1_get_word(data, 2)); + break; + case ULTRA_LFO_ENABLE: /* enable */ + snd_gf1_lfo_enable(gus, voice, lfo_type); + break; + case ULTRA_LFO_DISABLE: /* disable */ + snd_gf1_lfo_disable(gus, voice, lfo_type); + break; + case ULTRA_LFO_SHUTDOWN: /* shutdown */ + snd_gf1_lfo_shutdown(gus, voice, lfo_type); + break; + } +} diff -Nru linux/sound/isa/gus/gus_main.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_main.c --- linux/sound/isa/gus/gus_main.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_main.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,519 @@ +/* + * Routines for Gravis UltraSound soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards"); +MODULE_LICENSE("GPL"); + +#define chip_t snd_gus_card_t + +static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches); + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +int snd_gus_use_inc(snd_gus_card_t * gus) +{ + MOD_INC_USE_COUNT; + if (!try_inc_mod_count(gus->card->module)) { + MOD_DEC_USE_COUNT; + return 0; + } + return 1; +} + +void snd_gus_use_dec(snd_gus_card_t * gus) +{ + dec_mod_count(gus->card->module); + MOD_DEC_USE_COUNT; +} + +static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_gus_joystick_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = gus->joystick_dac & 31; + return 0; +} + +static int snd_gus_joystick_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval; + + nval = ucontrol->value.integer.value[0] & 31; + spin_lock_irqsave(&gus->reg_lock, flags); + change = gus->joystick_dac != nval; + gus->joystick_dac = nval; + snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_gus_joystick_control = { + iface: SNDRV_CTL_ELEM_IFACE_CARD, + name: "Joystick Speed", + info: snd_gus_joystick_info, + get: snd_gus_joystick_get, + put: snd_gus_joystick_put +}; + +static void snd_gus_init_control(snd_gus_card_t *gus) +{ + if (!gus->ace_flag) + snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus)); +} + +/* + * + */ + +static int snd_gus_free(snd_gus_card_t *gus) +{ + if (gus->gf1.res_port2 == NULL) + goto __hw_end; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (gus->seq_dev) { + snd_device_free(gus->card, gus->seq_dev); + gus->seq_dev = NULL; + } +#endif + snd_gf1_stop(gus); + snd_gus_init_dma_irq(gus, 0); + __hw_end: + if (gus->gf1.res_port1) { + release_resource(gus->gf1.res_port1); + kfree_nocheck(gus->gf1.res_port1); + } + if (gus->gf1.res_port2) { + release_resource(gus->gf1.res_port2); + kfree_nocheck(gus->gf1.res_port2); + } + if (gus->gf1.irq >= 0) + free_irq(gus->gf1.irq, (void *) gus); + if (gus->gf1.dma1 >= 0) { + disable_dma(gus->gf1.dma1); + free_dma(gus->gf1.dma1); + } + if (!gus->equal_dma && gus->gf1.dma2 >= 0) { + disable_dma(gus->gf1.dma2); + free_dma(gus->gf1.dma2); + } + snd_magic_kfree(gus); + return 0; +} + +static int snd_gus_dev_free(snd_device_t *device) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, device->device_data, return -ENXIO); + return snd_gus_free(gus); +} + +int snd_gus_create(snd_card_t * card, + unsigned long port, + int irq, int dma1, int dma2, + int timer_dev, + int voices, + int pcm_channels, + int effect, + snd_gus_card_t **rgus) +{ + snd_gus_card_t *gus; + int err; + static snd_device_ops_t ops = { + dev_free: snd_gus_dev_free, + }; + + *rgus = NULL; + gus = snd_magic_kcalloc(snd_gus_card_t, 0, GFP_KERNEL); + if (gus == NULL) + return -ENOMEM; + gus->gf1.irq = -1; + gus->gf1.dma1 = -1; + gus->gf1.dma2 = -1; + gus->card = card; + gus->gf1.port = port; + /* fill register variables for speedup */ + gus->gf1.reg_page = GUSP(gus, GF1PAGE); + gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL); + gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH); + gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW); + gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT); + gus->gf1.reg_dram = GUSP(gus, DRAM); + gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL); + gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA); + /* allocate resources */ + if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) { + snd_gus_free(gus); + return -EBUSY; + } + if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) { + snd_gus_free(gus); + return -EBUSY; + } + if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.irq = irq; + if (request_dma(dma1, "GUS - 1")) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.dma1 = dma1; + if (dma2 >= 0 && dma1 != dma2) { + if (request_dma(dma2, "GUS - 2")) { + snd_gus_free(gus); + return -EBUSY; + } + gus->gf1.dma2 = dma2; + } else { + gus->gf1.dma2 = gus->gf1.dma1; + gus->equal_dma = 1; + } + gus->timer_dev = timer_dev; + if (voices < 14) + voices = 14; + if (voices > 32) + voices = 32; + if (pcm_channels < 0) + pcm_channels = 0; + if (pcm_channels > 8) + pcm_channels = 8; + pcm_channels++; + pcm_channels &= ~1; + gus->gf1.effect = effect ? 1 : 0; + gus->gf1.active_voices = voices; + gus->gf1.pcm_channels = pcm_channels; + gus->gf1.volume_ramp = 25; + gus->gf1.smooth_pan = 1; + spin_lock_init(&gus->reg_lock); + spin_lock_init(&gus->voice_alloc); + spin_lock_init(&gus->active_voice_lock); + spin_lock_init(&gus->event_lock); + spin_lock_init(&gus->dma_lock); + spin_lock_init(&gus->pcm_volume_level_lock); + spin_lock_init(&gus->uart_cmd_lock); + init_MUTEX(&gus->dma_mutex); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) { + snd_gus_free(gus); + return err; + } + *rgus = gus; + return 0; +} + +/* + * Memory detection routine for plain GF1 soundcards + */ + +static int snd_gus_detect_memory(snd_gus_card_t * gus) +{ + int l, idx, local; + unsigned char d; + + snd_gf1_poke(gus, 0L, 0xaa); + snd_gf1_poke(gus, 1L, 0x55); + if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) { + snd_printk("plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port); + return -ENOMEM; + } + for (idx = 1, d = 0xab; idx < 4; idx++, d++) { + local = idx << 18; + snd_gf1_poke(gus, local, d); + snd_gf1_poke(gus, local + 1, d + 1); + if (snd_gf1_peek(gus, local) != d || + snd_gf1_peek(gus, local + 1) != d + 1 || + snd_gf1_peek(gus, 0L) != 0xaa) + break; + } +#if 1 + gus->gf1.memory = idx << 18; +#else + gus->gf1.memory = 256 * 1024; +#endif + for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) { + gus->gf1.mem_alloc.banks_8[l].address = + gus->gf1.mem_alloc.banks_8[l].size = 0; + gus->gf1.mem_alloc.banks_16[l].address = l << 18; + gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0; + } + gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory; + return 0; /* some memory were detected */ +} + +static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches) +{ + snd_card_t *card; + unsigned long flags; + int irq, dma1, dma2; + static unsigned char irqs[16] = + {0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; + static unsigned char dmas[8] = + {6, 1, 0, 2, 0, 3, 4, 5}; + + snd_assert(gus != NULL, return -EINVAL); + card = gus->card; + snd_assert(card != NULL, return -EINVAL); + + gus->mix_cntrl_reg &= 0xf8; + gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */ + if (gus->codec_flag || gus->ess_flag) { + gus->mix_cntrl_reg &= ~1; /* enable LINE IN */ + gus->mix_cntrl_reg |= 4; /* enable MIC */ + } + dma1 = gus->gf1.dma1; + dma1 = dma1 < 0 ? -dma1 : dma1; + dma1 = dmas[dma1 & 7]; + dma2 = gus->gf1.dma2; + dma2 = dma2 < 0 ? -dma2 : dma2; + dma2 = dmas[dma2 & 7]; +#if 0 + printk("dma1 = %i, dma2 = %i\n", gus->gf1.dma1, gus->gf1.dma2); +#endif + dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3); + + if ((dma1 & 7) == 0 || (dma2 & 7) == 0) { + snd_printk("Error! DMA isn't defined.\n"); + return -EINVAL; + } + irq = gus->gf1.irq; + irq = irq < 0 ? -irq : irq; + irq = irqs[irq & 0x0f]; + if (irq == 0) { + snd_printk("Error! IRQ isn't defined.\n"); + return -EINVAL; + } + irq |= 0x40; +#if 0 + card->mixer.mix_ctrl_reg |= 0x10; +#endif + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(5, GUSP(gus, REGCNTRLS)); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(0x00, GUSP(gus, IRQDMACNTRLREG)); + outb(0, GUSP(gus, REGCNTRLS)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + udelay(100); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(dma1, GUSP(gus, IRQDMACNTRLREG)); + if (latches) { + outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(irq, GUSP(gus, IRQDMACNTRLREG)); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + + udelay(100); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(dma1, GUSP(gus, IRQDMACNTRLREG)); + if (latches) { + outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(irq, GUSP(gus, IRQDMACNTRLREG)); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + + snd_gf1_delay(gus); + + if (latches) + gus->mix_cntrl_reg |= 0x08; /* enable latches */ + else + gus->mix_cntrl_reg &= ~0x08; /* disable latches */ + spin_lock_irqsave(&gus->reg_lock, flags); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + outb(0, GUSP(gus, GF1PAGE)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + return 0; +} + +static int snd_gus_check_version(snd_gus_card_t * gus) +{ + unsigned long flags; + unsigned char val, rev; + snd_card_t *card; + + card = gus->card; + spin_lock_irqsave(&gus->reg_lock, flags); + outb(0x20, GUSP(gus, REGCNTRLS)); + val = inb(GUSP(gus, REGCNTRLS)); + rev = inb(GUSP(gus, BOARDVERSION)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev); + strcpy(card->driver, "GUS"); + strcpy(card->longname, "Gravis UltraSound Classic (2.4)"); + if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { + if (rev >= 5 && rev <= 9) { + gus->ics_flag = 1; + if (rev == 5) + gus->ics_flipped = 1; + card->longname[27] = '3'; + card->longname[29] = rev == 5 ? '5' : '7'; + } + if (rev >= 10 && rev != 255) { + if (rev >= 10 && rev <= 11) { + strcpy(card->driver, "GUS MAX"); + strcpy(card->longname, "Gravis UltraSound MAX"); + gus->max_flag = 1; + } else if (rev == 0x30) { + strcpy(card->driver, "GUS ACE"); + strcpy(card->longname, "Gravis UltraSound Ace"); + gus->ace_flag = 1; + } else if (rev == 0x50) { + strcpy(card->driver, "GUS Extreme"); + strcpy(card->longname, "Gravis UltraSound Extreme"); + gus->ess_flag = 1; + } else { + snd_printk("unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val); + snd_printk(" please - report to \n"); + } + } + } + strcpy(card->shortname, card->longname); + gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */ + snd_gus_init_control(gus); + return 0; +} + +static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, seq_dev->private_data, return); + gus->seq_dev = NULL; +} + +int snd_gus_initialize(snd_gus_card_t *gus) +{ + int err; + + if (!gus->interwave) { + if ((err = snd_gus_check_version(gus)) < 0) { + snd_printk("version check failed\n"); + return err; + } + if ((err = snd_gus_detect_memory(gus)) < 0) + return err; + } + if ((err = snd_gus_init_dma_irq(gus, 1)) < 0) + return err; +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS, + sizeof(snd_gus_card_t*), &gus->seq_dev) >= 0) { + strcpy(gus->seq_dev->name, "GUS"); + *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus; + gus->seq_dev->private_data = gus; + gus->seq_dev->private_free = snd_gus_seq_dev_free; + } +#endif + snd_gf1_start(gus); + gus->initialized = 1; + return 0; +} + + /* gus_io.c */ +EXPORT_SYMBOL(snd_gf1_delay); +EXPORT_SYMBOL(snd_gf1_write8); +EXPORT_SYMBOL(snd_gf1_look8); +EXPORT_SYMBOL(snd_gf1_write16); +EXPORT_SYMBOL(snd_gf1_look16); +EXPORT_SYMBOL(snd_gf1_i_write8); +EXPORT_SYMBOL(snd_gf1_i_look8); +EXPORT_SYMBOL(snd_gf1_i_write16); +EXPORT_SYMBOL(snd_gf1_i_look16); +EXPORT_SYMBOL(snd_gf1_dram_addr); +EXPORT_SYMBOL(snd_gf1_write_addr); +EXPORT_SYMBOL(snd_gf1_poke); +EXPORT_SYMBOL(snd_gf1_peek); + /* gus_reset.c */ +EXPORT_SYMBOL(snd_gf1_alloc_voice); +EXPORT_SYMBOL(snd_gf1_free_voice); +EXPORT_SYMBOL(snd_gf1_ctrl_stop); +EXPORT_SYMBOL(snd_gf1_stop_voice); +EXPORT_SYMBOL(snd_gf1_start); +EXPORT_SYMBOL(snd_gf1_stop); + /* gus_mixer.c */ +EXPORT_SYMBOL(snd_gf1_new_mixer); + /* gus_pcm.c */ +EXPORT_SYMBOL(snd_gf1_pcm_new); + /* gus.c */ +EXPORT_SYMBOL(snd_gus_use_inc); +EXPORT_SYMBOL(snd_gus_use_dec); +EXPORT_SYMBOL(snd_gus_create); +EXPORT_SYMBOL(snd_gus_initialize); + /* gus_irq.c */ +EXPORT_SYMBOL(snd_gus_interrupt); + /* gus_uart.c */ +EXPORT_SYMBOL(snd_gf1_rawmidi_new); + /* gus_dram.c */ +EXPORT_SYMBOL(snd_gus_dram_write); +EXPORT_SYMBOL(snd_gus_dram_read); + /* gus_volume.c */ +EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw); +EXPORT_SYMBOL(snd_gf1_translate_freq); + /* gus_mem.c */ +EXPORT_SYMBOL(snd_gf1_mem_alloc); +EXPORT_SYMBOL(snd_gf1_mem_xfree); +EXPORT_SYMBOL(snd_gf1_mem_free); +EXPORT_SYMBOL(snd_gf1_mem_lock); + +/* + * INIT part + */ + +static int __init alsa_gus_init(void) +{ + return 0; +} + +static void __exit alsa_gus_exit(void) +{ +} + +module_init(alsa_gus_init) +module_exit(alsa_gus_exit) diff -Nru linux/sound/isa/gus/gus_mem.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem.c --- linux/sound/isa/gus/gus_mem.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,368 @@ +/* + * Copyright (c) by Jaroslav Kysela + * GUS's memory allocation routines / bottom layer + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_DEBUG +static void snd_gf1_mem_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer); +#endif + +void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup) +{ + if (!xup) { + down(&alloc->memory_mutex); + } else { + up(&alloc->memory_mutex); + } +} + +snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc, + snd_gf1_mem_block_t * block) +{ + snd_gf1_mem_block_t *pblock, *nblock; + + nblock = (snd_gf1_mem_block_t *) kmalloc(sizeof(snd_gf1_mem_block_t), GFP_KERNEL); + if (nblock == NULL) + return NULL; + *nblock = *block; + pblock = alloc->first; + while (pblock) { + if (pblock->ptr > nblock->ptr) { + nblock->prev = pblock->prev; + nblock->next = pblock; + pblock->prev = nblock; + if (pblock == alloc->first) + alloc->first = nblock; + else + nblock->prev->next = nblock; + up(&alloc->memory_mutex); + return 0; + } + pblock = pblock->next; + } + nblock->next = NULL; + if (alloc->last == NULL) { + nblock->prev = NULL; + alloc->first = alloc->last = nblock; + } else { + nblock->prev = alloc->last; + alloc->last->next = nblock; + alloc->last = nblock; + } + return nblock; +} + +int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block) +{ + if (block->share) { /* ok.. shared block */ + block->share--; + up(&alloc->memory_mutex); + return 0; + } + if (alloc->first == block) { + alloc->first = block->next; + if (block->next) + block->next->prev = NULL; + } else { + block->prev->next = block->next; + if (block->next) + block->next->prev = block->prev; + } + if (alloc->last == block) { + alloc->last = block->prev; + if (block->prev) + block->prev->next = NULL; + } else { + block->next->prev = block->prev; + if (block->prev) + block->prev->next = block->next; + } + if (block->name) + kfree(block->name); + kfree(block); + return 0; +} + +snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, + unsigned int address) +{ + snd_gf1_mem_block_t *block; + + for (block = alloc->first; block; block = block->next) { + if (block->ptr == address) { + return block; + } + } + return NULL; +} + +snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, + unsigned int *share_id) +{ + snd_gf1_mem_block_t *block; + + if (!share_id[0] && !share_id[1] && + !share_id[2] && !share_id[3]) + return NULL; + for (block = alloc->first; block; block = block->next) + if (!memcmp(share_id, block->share_id, sizeof(share_id))) + return block; + return NULL; +} + +static int snd_gf1_mem_find(snd_gf1_mem_t * alloc, + snd_gf1_mem_block_t * block, + unsigned int size, int w_16, int align) +{ + snd_gf1_bank_info_t *info = w_16 ? alloc->banks_16 : alloc->banks_8; + unsigned int idx, boundary; + int size1; + snd_gf1_mem_block_t *pblock; + unsigned int ptr1, ptr2; + + align--; + if (w_16 && align < 1) + align = 1; + block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0; + block->owner = SNDRV_GF1_MEM_OWNER_DRIVER; + block->share = 0; + block->share_id[0] = block->share_id[1] = + block->share_id[2] = block->share_id[3] = 0; + block->name = NULL; + block->prev = block->next = NULL; + for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) { + while (pblock->ptr >= (boundary = info[idx].address + info[idx].size)) + idx++; + while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size)) + idx++; + ptr2 = boundary; + if (pblock->next) { + if (pblock->ptr + pblock->size == pblock->next->ptr) + continue; + if (pblock->next->ptr < boundary) + ptr2 = pblock->next->ptr; + } + ptr1 = (pblock->ptr + pblock->size + align) & ~align; + if (ptr1 >= ptr2) + continue; + size1 = ptr2 - ptr1; + if (size <= size1) { + block->ptr = ptr1; + block->size = size; + return 0; + } + } + while (++idx < 4) { + if (size <= info[idx].size) { + /* I assume that bank address is already aligned.. */ + block->ptr = info[idx].address; + block->size = size; + return 0; + } + } + return -ENOMEM; +} + +snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, + char *name, int size, int w_16, int align, + unsigned int *share_id) +{ + snd_gf1_mem_block_t block, *nblock; + + snd_gf1_mem_lock(alloc, 0); + if (share_id != NULL) { + nblock = snd_gf1_mem_share(alloc, share_id); + if (nblock != NULL) { + if (size != nblock->size) { + /* TODO: remove in the future */ + snd_printk("snd_gf1_mem_alloc - share: sizes differ\n"); + goto __std; + } + nblock->share++; + snd_gf1_mem_lock(alloc, 1); + return NULL; + } + } + __std: + if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) { + snd_gf1_mem_lock(alloc, 1); + return NULL; + } + if (share_id != NULL) + memcpy(&block.share_id, share_id, sizeof(block.share_id)); + block.owner = owner; + block.name = snd_kmalloc_strdup(name, GFP_KERNEL); + nblock = snd_gf1_mem_xalloc(alloc, &block); + snd_gf1_mem_lock(alloc, 1); + return nblock; +} + +int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address) +{ + int result; + snd_gf1_mem_block_t *block; + + snd_gf1_mem_lock(alloc, 0); + if ((block = snd_gf1_mem_look(alloc, address)) != NULL) { + result = snd_gf1_mem_xfree(alloc, block); + snd_gf1_mem_lock(alloc, 1); + return result; + } + snd_gf1_mem_lock(alloc, 1); + return -EINVAL; +} + +int snd_gf1_mem_init(snd_gus_card_t * gus) +{ + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t block; +#ifdef CONFIG_SND_DEBUG + snd_info_entry_t *entry; +#endif + + alloc = &gus->gf1.mem_alloc; + init_MUTEX(&alloc->memory_mutex); + alloc->first = alloc->last = NULL; + if (!gus->gf1.memory) + return 0; + + memset(&block, 0, sizeof(block)); + block.owner = SNDRV_GF1_MEM_OWNER_DRIVER; + if (gus->gf1.enh_mode) { + block.ptr = 0; + block.size = 1024; + block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL); + if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + return -ENOMEM; + } + block.ptr = gus->gf1.default_voice_address; + block.size = 4; + block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL); + if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + return -ENOMEM; +#ifdef CONFIG_SND_DEBUG + alloc->info_entry = NULL; + entry = snd_info_create_card_entry(gus->card, "gusmem", gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 256 * 1024; + entry->c.text.read = snd_gf1_mem_info_read; + entry->private_data = gus; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + alloc->info_entry = entry; +#endif + return 0; +} + +int snd_gf1_mem_done(snd_gus_card_t * gus) +{ + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t *block, *nblock; + + alloc = &gus->gf1.mem_alloc; + block = alloc->first; + while (block) { + nblock = block->next; + snd_gf1_mem_xfree(alloc, block); + block = nblock; + } +#ifdef CONFIG_SND_DEBUG + if (alloc->info_entry) + snd_info_unregister(alloc->info_entry); +#endif + return 0; +} + +#ifdef CONFIG_SND_DEBUG +static void snd_gf1_mem_info_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + snd_gus_card_t *gus; + snd_gf1_mem_t *alloc; + snd_gf1_mem_block_t *block; + unsigned int total, used; + int i; + + gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + alloc = &gus->gf1.mem_alloc; + down(&alloc->memory_mutex); + snd_iprintf(buffer, "8-bit banks : \n "); + for (i = 0; i < 4; i++) + snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : ""); + snd_iprintf(buffer, "\n" + "16-bit banks : \n "); + for (i = total = 0; i < 4; i++) { + snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : ""); + total += alloc->banks_16[i].size; + } + snd_iprintf(buffer, "\n"); + used = 0; + for (block = alloc->first, i = 0; block; block = block->next, i++) { + used += block->size; + snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size); + if (block->share || + block->share_id[0] || block->share_id[1] || + block->share_id[2] || block->share_id[3]) + snd_iprintf(buffer, " Share : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n", + block->share, + block->share_id[0], block->share_id[1], + block->share_id[2], block->share_id[3]); + snd_iprintf(buffer, " Flags :%s\n", + block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : ""); + snd_iprintf(buffer, " Owner : "); + switch (block->owner) { + case SNDRV_GF1_MEM_OWNER_DRIVER: + snd_iprintf(buffer, "driver - %s\n", block->name); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE: + snd_iprintf(buffer, "SIMPLE wave\n"); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_GF1: + snd_iprintf(buffer, "GF1 wave\n"); + break; + case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF: + snd_iprintf(buffer, "IWFFFF wave\n"); + break; + default: + snd_iprintf(buffer, "unknown\n"); + } + } + snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n", + total, used, total - used); + up(&alloc->memory_mutex); +#if 0 + ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n", + ultra_memory_free_size(card, &card->gf1.mem_alloc), + ultra_memory_free_block(card, &card->gf1.mem_alloc, 0), + ultra_memory_free_block(card, &card->gf1.mem_alloc, 1)); +#endif +} +#endif diff -Nru linux/sound/isa/gus/gus_mem_proc.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem_proc.c --- linux/sound/isa/gus/gus_mem_proc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mem_proc.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,169 @@ +/* + * Copyright (c) by Jaroslav Kysela + * GUS's memory access via proc filesystem + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +typedef struct gus_proc_private { + int rom; /* data are in ROM */ + unsigned int address; + unsigned int size; + snd_gus_card_t * gus; +} gus_proc_private_t; + +static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + snd_gus_card_t *gus = priv->gus; + int err; + + size = count; + if (file->f_pos + size > priv->size) + size = (long)priv->size - file->f_pos; + if (size > 0) { + if ((err = snd_gus_dram_read(gus, buf, file->f_pos, size, priv->rom)) < 0) + return err; + file->f_pos += size; + return size; + } + return 0; +} + +static long long snd_gf1_mem_proc_llseek(snd_info_entry_t *entry, + void *private_file_data, + struct file *file, + long long offset, + int orig) +{ + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + + switch (orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: /* SEEK_END */ + file->f_pos = priv->size - offset; + break; + default: + return -EINVAL; + } + if (file->f_pos > priv->size) + file->f_pos = priv->size; + return file->f_pos; +} + +static void snd_gf1_mem_proc_free(snd_info_entry_t *entry) +{ + gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return); + snd_magic_kfree(priv); +} + +static struct snd_info_entry_ops snd_gf1_mem_proc_ops = { + read: snd_gf1_mem_proc_dump, + llseek: snd_gf1_mem_proc_llseek, +}; + +int snd_gf1_mem_proc_init(snd_gus_card_t * gus) +{ + int idx; + char name[16]; + gus_proc_private_t *priv; + snd_info_entry_t *entry; + + memset(&gus->gf1.rom_entries, 0, sizeof(gus->gf1.rom_entries)); + memset(&gus->gf1.ram_entries, 0, sizeof(gus->gf1.ram_entries)); + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { + priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + if (priv == NULL) { + snd_gf1_mem_proc_done(gus); + return -ENOMEM; + } + priv->gus = gus; + sprintf(name, "gus-ram-%i", idx); + entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = priv; + entry->private_free = snd_gf1_mem_proc_free; + entry->c.ops = &snd_gf1_mem_proc_ops; + priv->address = gus->gf1.mem_alloc.banks_8[idx].address; + priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + gus->gf1.ram_entries[idx] = entry; + } + } + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.rom_present & (1 << idx)) { + priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + if (priv == NULL) { + snd_gf1_mem_proc_done(gus); + return -ENOMEM; + } + priv->rom = 1; + priv->gus = gus; + sprintf(name, "gus-rom-%i", idx); + entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = priv; + entry->private_free = snd_gf1_mem_proc_free; + entry->c.ops = &snd_gf1_mem_proc_ops; + priv->address = idx * 4096 * 1024; + priv->size = entry->size = gus->gf1.rom_memory; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + gus->gf1.rom_entries[idx] = entry; + } + } + return 0; +} + +int snd_gf1_mem_proc_done(snd_gus_card_t * gus) +{ + int idx; + + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.ram_entries[idx]) + snd_info_unregister(gus->gf1.ram_entries[idx]); + } + for (idx = 0; idx < 4; idx++) { + if (gus->gf1.rom_entries[idx]) + snd_info_unregister(gus->gf1.rom_entries[idx]); + } + return 0; +} diff -Nru linux/sound/isa/gus/gus_mixer.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mixer.c --- linux/sound/isa/gus/gus_mixer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_mixer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,205 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of ICS 2101 chip and "mixer" in GF1 chip + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#define chip_t snd_gus_card_t + +/* + * + */ + +#define GF1_SINGLE(xname, xindex, shift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_gf1_info_single, \ + get: snd_gf1_get_single, put: snd_gf1_put_single, \ + private_value: shift | (invert << 8) } + +static int snd_gf1_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_gf1_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + + ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1; + if (invert) + ucontrol->value.integer.value[0] ^= 1; + return 0; +} + +static int snd_gf1_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int change; + unsigned char oval, nval; + + nval = ucontrol->value.integer.value[0] & 1; + if (invert) + nval ^= 1; + nval <<= shift; + spin_lock_irqsave(&gus->reg_lock, flags); + oval = gus->mix_cntrl_reg; + nval = (oval & ~(1 << shift)) | nval; + change = nval != oval; + outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG)); + outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +#define ICS_DOUBLE(xname, xindex, addr) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ics_info_double, \ + get: snd_ics_get_double, put: snd_ics_put_double, \ + private_value: addr } + +static int snd_ics_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_ics_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value & 0xff; + unsigned char left, right; + + spin_lock_irqsave(&gus->reg_lock, flags); + left = gus->gf1.ics_regs[addr][0]; + right = gus->gf1.ics_regs[addr][1]; + spin_unlock_irqrestore(&gus->reg_lock, flags); + ucontrol->value.integer.value[0] = left & 127; + ucontrol->value.integer.value[1] = right & 127; + return 0; +} + +static int snd_ics_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int addr = kcontrol->private_value & 0xff; + int change; + unsigned char val1, val2, oval1, oval2, tmp; + + val1 = ucontrol->value.integer.value[0] & 127; + val2 = ucontrol->value.integer.value[1] & 127; + spin_lock_irqsave(&gus->reg_lock, flags); + oval1 = gus->gf1.ics_regs[addr][0]; + oval2 = gus->gf1.ics_regs[addr][1]; + change = val1 != oval1 || val2 != oval2; + gus->gf1.ics_regs[addr][0] = val1; + gus->gf1.ics_regs[addr][1] = val2; + if (gus->ics_flag && gus->ics_flipped && + (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) { + tmp = val1; + val1 = val2; + val2 = tmp; + } + addr <<= 3; + outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); + outb(1, GUSP(gus, MIXDATAPORT)); + outb(addr | 2, GUSP(gus, MIXCNTRLPORT)); + outb((unsigned char) val1, GUSP(gus, MIXDATAPORT)); + outb(addr | 1, GUSP(gus, MIXCNTRLPORT)); + outb(2, GUSP(gus, MIXDATAPORT)); + outb(addr | 3, GUSP(gus, MIXCNTRLPORT)); + outb((unsigned char) val2, GUSP(gus, MIXDATAPORT)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return change; +} + +#define GF1_CONTROLS (sizeof(snd_gf1_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_gf1_controls[] = { +GF1_SINGLE("Master Playback Switch", 0, 1, 1), +GF1_SINGLE("Line Switch", 0, 0, 1), +GF1_SINGLE("Mic Switch", 0, 2, 0) +}; + +#define ICS_CONTROLS (sizeof(snd_ics_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ics_controls[] = { +GF1_SINGLE("Master Playback Switch", 0, 1, 1), +ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), +ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV), +GF1_SINGLE("Line Switch", 0, 0, 1), +ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV), +GF1_SINGLE("Mic Switch", 0, 2, 0), +ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV), +ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV) +}; + +int snd_gf1_new_mixer(snd_gus_card_t * gus) +{ + snd_card_t *card; + int idx, err, max; + + snd_assert(gus != NULL, return -EINVAL); + card = gus->card; + snd_assert(card != NULL, return -EINVAL); + + if (gus->ics_flag) + snd_component_add(card, "ICS2101"); + if (card->mixername[0] == '\0') { + strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); + } else { + if (gus->ics_flag) + strcat(card->mixername, ",ICS2101"); + strcat(card->mixername, ",GF1"); + } + + if (!gus->ics_flag) { + max = gus->ess_flag ? 1 : GF1_CONTROLS; + for (idx = 0; idx < max; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0) + return err; + } + } else { + for (idx = 0; idx < ICS_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0) + return err; + } + } + return 0; +} diff -Nru linux/sound/isa/gus/gus_pcm.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_pcm.c --- linux/sound/isa/gus/gus_pcm.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_pcm.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,890 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of GF1 chip (PCM things) + * + * InterWave chips supports interleaved DMA, but this feature isn't used in + * this code. + * + * This code emulates autoinit DMA transfer for playback, recording by GF1 + * chip doesn't support autoinit DMA. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include "gus_tables.h" + +#define chip_t snd_gus_card_t + +/* maximum rate */ + +#define SNDRV_GF1_PCM_RATE 48000 + +#define SNDRV_GF1_PCM_PFLG_NONE 0 +#define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) +#define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) + +typedef struct { + snd_gus_card_t * gus; + snd_pcm_substream_t * substream; + spinlock_t lock; + int voices; + snd_gus_voice_t *pvoices[2]; + unsigned int memory; + unsigned short flags; + unsigned char voice_ctrl, ramp_ctrl; + unsigned int bpos; + unsigned int blocks; + unsigned int block_size; + unsigned int dma_size; + wait_queue_head_t sleep; + atomic_t dma_count; + int final_volume; +} gus_pcm_private_t; + +static int snd_gf1_pcm_use_dma = 1; + +static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data) +{ + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, private_data, return); + + if (pcmp) { + atomic_dec(&pcmp->dma_count); + wake_up(&pcmp->sleep); + } +} + +static int snd_gf1_pcm_block_change(snd_pcm_substream_t * substream, + unsigned int offset, + unsigned int addr, + unsigned int count) +{ + snd_gf1_dma_block_t block; + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + count += offset & 31; + offset &= ~31; + // snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count); + memset(&block, 0, sizeof(block)); + block.cmd = SNDRV_GF1_DMA_IRQ; + if (snd_pcm_format_unsigned(runtime->format)) + block.cmd |= SNDRV_GF1_DMA_UNSIGNED; + if (snd_pcm_format_width(runtime->format) == 16) + block.cmd |= SNDRV_GF1_DMA_16BIT; + block.addr = addr & ~31; + block.buffer = runtime->dma_area + offset; + block.buf_addr = runtime->dma_addr + offset; + block.count = count; + block.private_data = pcmp; + block.ack = snd_gf1_pcm_block_change_ack; + if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0)) + atomic_inc(&pcmp->dma_count); + return 0; +} + +static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); + snd_gus_card_t * gus = pcmp->gus; + unsigned long flags; + unsigned char voice_ctrl, ramp_ctrl; + unsigned short rate; + unsigned int curr, begin, end; + unsigned short vol; + unsigned char pan; + unsigned int voice; + + if (substream == NULL) + return; + spin_lock_irqsave(&pcmp->lock, flags); + if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { + spin_unlock_irqrestore(&pcmp->lock, flags); + return; + } + pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; + pcmp->final_volume = 0; + spin_unlock_irqrestore(&pcmp->lock, flags); + rate = snd_gf1_translate_freq(gus, runtime->rate << 4); + /* enable WAVE IRQ */ + voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20; + /* enable RAMP IRQ + rollover */ + ramp_ctrl = 0x24; + if (pcmp->blocks == 1) { + voice_ctrl |= 0x08; /* loop enable */ + ramp_ctrl &= ~0x04; /* disable rollover */ + } + for (voice = 0; voice < pcmp->voices; voice++) { + begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); + curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; + end = curr + (pcmp->block_size / runtime->channels); + end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1; + // snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate); + pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; + vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + spin_lock_irqsave(&gus->reg_lock, flags); + for (voice = 0; voice < pcmp->voices; voice++) { + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */ + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + voice_ctrl &= ~0x20; + } + voice_ctrl |= 0x20; + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + for (voice = 0; voice < pcmp->voices; voice++) { + snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + voice_ctrl &= ~0x20; /* disable IRQ for next voice */ + } + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void snd_gf1_pcm_interrupt_wave(snd_gus_card_t * gus, snd_gus_voice_t *pvoice) +{ + gus_pcm_private_t * pcmp; + snd_pcm_runtime_t * runtime; + unsigned char voice_ctrl, ramp_ctrl; + int idx; + unsigned int end, step; + + if (!pvoice->private_data) { + snd_printd("snd_gf1_pcm: unknown wave irq?\n"); + snd_gf1_smart_stop_voice(gus, pvoice->number); + return; + } + pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + if (pcmp == NULL) { + snd_printd("snd_gf1_pcm: unknown wave irq?\n"); + snd_gf1_smart_stop_voice(gus, pvoice->number); + return; + } + gus = pcmp->gus; + runtime = pcmp->substream->runtime; + + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; + ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; +#if 0 + snd_gf1_select_voice(gus, pvoice->number); + printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); + snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); + printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); + snd_gf1_select_voice(gus, pvoice->number); +#endif + pcmp->bpos++; + pcmp->bpos %= pcmp->blocks; + if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ + voice_ctrl |= 0x08; /* enable loop */ + } else { + ramp_ctrl |= 0x04; /* enable rollover */ + } + end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); + end -= voice_ctrl & 4 ? 2 : 1; + step = pcmp->dma_size / runtime->channels; + voice_ctrl |= 0x20; + if (!pcmp->final_volume) { + ramp_ctrl |= 0x20; + ramp_ctrl &= ~0x03; + } + for (idx = 0; idx < pcmp->voices; idx++, end += step) { + snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + voice_ctrl &= ~0x20; + } + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + voice_ctrl |= 0x20; + for (idx = 0; idx < pcmp->voices; idx++) { + snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); + voice_ctrl &= ~0x20; + } + } + spin_unlock(&gus->reg_lock); + + snd_pcm_period_elapsed(pcmp->substream); +#if 0 + if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && + *runtime->state == SNDRV_PCM_STATE_RUNNING) { + end = pcmp->bpos * pcmp->block_size; + if (runtime->channels > 1) { + snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); + snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); + } else { + snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); + } + } +#endif +} + +static void snd_gf1_pcm_interrupt_volume(snd_gus_card_t * gus, snd_gus_voice_t * pvoice) +{ + unsigned short vol; + int cvoice; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + + /* stop ramp, but leave rollover bit untouched */ + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + spin_unlock(&gus->reg_lock); + if (pcmp == NULL) + return; + /* are we active? */ + if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) + return; + /* load real volume - better precision */ + cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; + if (pcmp->substream == NULL) + return; + vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); + pcmp->final_volume = 1; + spin_unlock(&gus->reg_lock); +} + +static void snd_gf1_pcm_volume_change(snd_gus_card_t * gus) +{ +} + +static int snd_gf1_pcm_poke_block(snd_gus_card_t *gus, unsigned char *buf, + unsigned int pos, unsigned int count, + int w16, int invert) +{ + unsigned int len; + unsigned long flags; + + // printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port); + while (count > 0) { + len = count; + if (len > 512) /* limit, to allow IRQ */ + len = 512; + count -= len; + if (gus->interwave) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00)); + snd_gf1_dram_addr(gus, pos); + if (w16) { + outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); + outsw(GUSP(gus, GF1DATALOW), buf, len >> 1); + } else { + outsb(GUSP(gus, DRAM), buf, len); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + buf += 512; + pos += 512; + } else { + invert = invert ? 0x80 : 0x00; + if (w16) { + len >>= 1; + while (len--) { + snd_gf1_poke(gus, pos++, *buf++); + snd_gf1_poke(gus, pos++, *buf++ ^ invert); + } + } else { + while (len--) + snd_gf1_poke(gus, pos++, *buf++ ^ invert); + } + } + schedule_timeout(1); + if (signal_pending(current)) + return -EAGAIN; + } + return 0; +} + +static int snd_gf1_pcm_playback_copy(snd_pcm_substream_t *substream, + int voice, + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int bpos, len; + + bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); + len = samples_to_bytes(runtime, count); + snd_assert(bpos <= pcmp->dma_size, return -EIO); + snd_assert(bpos + len <= pcmp->dma_size, return -EIO); + if (copy_from_user(runtime->dma_area + bpos, src, len)) + return -EFAULT; + if (snd_gf1_pcm_use_dma && len > 32) { + return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); + } else { + snd_gus_card_t *gus = pcmp->gus; + int err, w16, invert; + + w16 = (snd_pcm_format_width(runtime->format) == 16); + invert = snd_pcm_format_unsigned(runtime->format); + if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) + return err; + } + return 0; +} + +static int snd_gf1_pcm_playback_silence(snd_pcm_substream_t *substream, + int voice, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int bpos, len; + + bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); + len = samples_to_bytes(runtime, count); + snd_assert(bpos <= pcmp->dma_size, return -EIO); + snd_assert(bpos + len <= pcmp->dma_size, return -EIO); + snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count); + if (snd_gf1_pcm_use_dma && len > 32) { + return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); + } else { + snd_gus_card_t *gus = pcmp->gus; + int err, w16, invert; + + w16 = (snd_pcm_format_width(runtime->format) == 16); + invert = snd_pcm_format_unsigned(runtime->format); + if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) + return err; + } + return 0; +} + +static int snd_gf1_pcm_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + snd_gf1_mem_block_t *block; + if (pcmp->memory > 0) { + snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory); + pcmp->memory = 0; + } + if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, + SNDRV_GF1_MEM_OWNER_DRIVER, + "GF1 PCM", + runtime->dma_bytes, 1, 32, + NULL)) == NULL) + return -ENOMEM; + pcmp->memory = block->ptr; + } + pcmp->voices = params_channels(hw_params); + if (pcmp->pvoices[0] == NULL) { + if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) + return -ENOMEM; + pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave; + pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume; + pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change; + pcmp->pvoices[0]->private_data = pcmp; + } + if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) { + if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) + return -ENOMEM; + pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; + pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; + pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; + pcmp->pvoices[1]->private_data = pcmp; + } else if (pcmp->voices == 1) { + if (pcmp->pvoices[1]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); + pcmp->pvoices[1] = NULL; + } + } + return 0; +} + +static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + snd_pcm_lib_free_pages(substream); + if (pcmp->pvoices[0]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]); + pcmp->pvoices[0] = NULL; + } + if (pcmp->pvoices[1]) { + snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); + pcmp->pvoices[1] = NULL; + } + if (pcmp->memory > 0) { + snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory); + pcmp->memory = 0; + } + return 0; +} + +static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + + pcmp->bpos = 0; + pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); + pcmp->block_size = snd_pcm_lib_period_bytes(substream); + pcmp->blocks = pcmp->dma_size / pcmp->block_size; + return 0; +} + +static int snd_gf1_pcm_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + int voice; + + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_gf1_pcm_trigger_up(substream); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + spin_lock(&pcmp->lock); + pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; + spin_unlock(&pcmp->lock); + voice = pcmp->pvoices[0]->number; + snd_gf1_stop_voices(gus, voice, voice); + if (pcmp->pvoices[1]) { + voice = pcmp->pvoices[1]->number; + snd_gf1_stop_voices(gus, voice, voice); + } + } else { + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned int pos; + unsigned char voice_ctrl; + + pos = 0; + spin_lock(&gus->reg_lock); + if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { + snd_gf1_select_voice(gus, pcmp->pvoices[0]->number); + voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory; + if (substream->runtime->channels > 1) + pos <<= 1; + pos = bytes_to_frames(runtime, pos); + } + spin_unlock(&gus->reg_lock); + return pos; +} + +static ratnum_t clock = { + num: 9878400/16, + den_min: 2, + den_max: 257, + den_step: 1, +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: 1, + rats: &clock, +}; + +static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->c_dma_size = params_buffer_bytes(hw_params); + gus->c_period_size = params_period_bytes(hw_params); + gus->c_pos = 0; + gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ + if (params_channels(hw_params) > 1) + gus->gf1.pcm_rcntrl_reg |= 2; + if (gus->gf1.dma2 > 3) + gus->gf1.pcm_rcntrl_reg |= 4; + if (snd_pcm_format_unsigned(params_format(hw_params))) + gus->gf1.pcm_rcntrl_reg |= 0x80; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_gf1_pcm_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_gf1_pcm_capture_prepare(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ + snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ); + return 0; +} + +static int snd_gf1_pcm_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + int val; + + if (cmd == SNDRV_PCM_TRIGGER_START) { + val = gus->gf1.pcm_rcntrl_reg; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + val = 0; + } else { + return -EINVAL; + } + + spin_lock(&gus->reg_lock); + snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val); + snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); + spin_unlock(&gus->reg_lock); + return 0; +} + +static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + int pos = gus->c_period_size - snd_dma_residue(gus->gf1.dma2); + pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size); + return pos; +} + +static void snd_gf1_pcm_interrupt_dma_read(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ + snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ + if (gus->pcm_cap_substream != NULL) { + snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); + snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); + gus->c_pos += gus->c_period_size; + snd_pcm_period_elapsed(gus->pcm_cap_substream); + } +} + +static snd_pcm_hardware_t snd_gf1_pcm_playback = +{ + info: SNDRV_PCM_INFO_NONINTERLEAVED, + formats: (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 5510, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_gf1_pcm_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 5510, + rate_max: 44100, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime) +{ + gus_pcm_private_t * pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); + snd_magic_kfree(pcmp); +} + +static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream) +{ + gus_pcm_private_t *pcmp; + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + pcmp = snd_magic_kcalloc(gus_pcm_private_t, 0, GFP_KERNEL); + if (pcmp == NULL) + return -ENOMEM; + pcmp->gus = gus; + spin_lock_init(&pcmp->lock); + init_waitqueue_head(&pcmp->sleep); + atomic_set(&pcmp->dma_count, 0); + + runtime->private_data = pcmp; + runtime->private_free = snd_gf1_pcm_playback_free; + +#if 0 + printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); +#endif + if ((err = snd_gf1_dma_init(gus)) < 0) + return err; + pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; + pcmp->substream = substream; + runtime->hw = snd_gf1_pcm_playback; + snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); + return 0; +} + +static int snd_gf1_pcm_playback_close(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + unsigned long jiffies_old; + + jiffies_old = jiffies; + while (atomic_read(&pcmp->dma_count) > 0) { + interruptible_sleep_on_timeout(&pcmp->sleep, 1); + if ((signed long)(jiffies - jiffies_old) > 2*HZ) { + snd_printk("gf1 pcm - serious DMA problem\n"); + break; + } + } + snd_gf1_dma_done(gus); + return 0; +} + +static int snd_gf1_pcm_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; + gus->pcm_cap_substream = substream; + substream->runtime->hw = snd_gf1_pcm_capture; + snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + return 0; +} + +static int snd_gf1_pcm_capture_close(snd_pcm_substream_t * substream) +{ + snd_gus_card_t *gus = snd_pcm_substream_chip(substream); + + gus->pcm_cap_substream = NULL; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); + return 0; +} + +static void snd_gf1_pcm_free(snd_pcm_t *pcm) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, pcm->private_data, return); + gus->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int snd_gf1_pcm_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_gf1_pcm_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); + ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; + ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; + spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); + return 0; +} + +static int snd_gf1_pcm_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, idx; + unsigned short val1, val2, vol; + gus_pcm_private_t *pcmp; + snd_gus_voice_t *pvoice; + + val1 = ucontrol->value.integer.value[0] & 127; + val2 = ucontrol->value.integer.value[1] & 127; + spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); + change = val1 != gus->gf1.pcm_volume_level_left1 || + val2 != gus->gf1.pcm_volume_level_right1; + gus->gf1.pcm_volume_level_left1 = val1; + gus->gf1.pcm_volume_level_right1 = val2; + gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4; + gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4; + spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); + /* are we active? */ + spin_lock_irqsave(&gus->voice_alloc, flags); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (!pvoice->pcm) + continue; + pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return -ENXIO); + if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) + continue; + /* load real volume - better precision */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, pvoice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); + pcmp->final_volume = 1; + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return change; +} + +static snd_kcontrol_new_t snd_gf1_pcm_volume_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Playback Volume", + info: snd_gf1_pcm_volume_info, + get: snd_gf1_pcm_volume_get, + put: snd_gf1_pcm_volume_put +}; + +static snd_pcm_ops_t snd_gf1_pcm_playback_ops = { + open: snd_gf1_pcm_playback_open, + close: snd_gf1_pcm_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_gf1_pcm_playback_hw_params, + hw_free: snd_gf1_pcm_playback_hw_free, + prepare: snd_gf1_pcm_playback_prepare, + trigger: snd_gf1_pcm_playback_trigger, + pointer: snd_gf1_pcm_playback_pointer, + copy: snd_gf1_pcm_playback_copy, + silence: snd_gf1_pcm_playback_silence, +}; + +static snd_pcm_ops_t snd_gf1_pcm_capture_ops = { + open: snd_gf1_pcm_capture_open, + close: snd_gf1_pcm_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_gf1_pcm_capture_hw_params, + hw_free: snd_gf1_pcm_capture_hw_free, + prepare: snd_gf1_pcm_capture_prepare, + trigger: snd_gf1_pcm_capture_trigger, + pointer: snd_gf1_pcm_capture_pointer, +}; + +int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm) +{ + snd_card_t *card; + snd_kcontrol_t *kctl; + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + int capture, err; + + if (rpcm) + *rpcm = NULL; + card = gus->card; + capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; + err = snd_pcm_new(card, + gus->interwave ? "AMD InterWave" : "GF1", + pcm_dev, + gus->gf1.pcm_channels / 2, + capture, + &pcm); + if (err < 0) + return err; + pcm->private_data = gus; + pcm->private_free = snd_gf1_pcm_free; + /* playback setup */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); + + for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) + snd_pcm_lib_preallocate_isa_pages(substream, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + if (capture) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); + if (gus->gf1.dma2 == gus->gf1.dma1) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); + } + strcpy(pcm->name, pcm->id); + if (gus->interwave) { + sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); + } + strcat(pcm->name, " (synth)"); + gus->pcm = pcm; + + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus))) < 0) + return err; + kctl->id.index = control_index; + + if (rpcm) + *rpcm = pcm; + return 0; +} + diff -Nru linux/sound/isa/gus/gus_reset.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_reset.c --- linux/sound/isa/gus/gus_reset.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_reset.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,423 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +extern void snd_gf1_timers_init(snd_gus_card_t * gus); +extern void snd_gf1_timers_done(snd_gus_card_t * gus); +extern int snd_gf1_synth_init(snd_gus_card_t * gus); +extern void snd_gf1_synth_done(snd_gus_card_t * gus); + +/* + * ok.. default interrupt handlers... + */ + +static void snd_gf1_default_interrupt_handler_midi_out(snd_gus_card_t * gus) +{ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20); +} + +static void snd_gf1_default_interrupt_handler_midi_in(snd_gus_card_t * gus) +{ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80); +} + +static void snd_gf1_default_interrupt_handler_timer1(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4); +} + +static void snd_gf1_default_interrupt_handler_timer2(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8); +} + +static void snd_gf1_default_interrupt_handler_wave_and_volume(snd_gus_card_t * gus, snd_gus_voice_t * voice) +{ + snd_gf1_i_ctrl_stop(gus, 0x00); + snd_gf1_i_ctrl_stop(gus, 0x0d); +} + +static void snd_gf1_default_interrupt_handler_dma_write(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, 0x41, 0x00); +} + +static void snd_gf1_default_interrupt_handler_dma_read(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, 0x49, 0x00); +} + +void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what) +{ + if (what & SNDRV_GF1_HANDLER_MIDI_OUT) + gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out; + if (what & SNDRV_GF1_HANDLER_MIDI_IN) + gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in; + if (what & SNDRV_GF1_HANDLER_TIMER1) + gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1; + if (what & SNDRV_GF1_HANDLER_TIMER2) + gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2; + if (what & SNDRV_GF1_HANDLER_VOICE) { + snd_gus_voice_t *voice; + + voice = &gus->gf1.voices[what & 0xffff]; + voice->handler_wave = + voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume; + voice->handler_effect = NULL; + voice->volume_change = NULL; + } + if (what & SNDRV_GF1_HANDLER_DMA_WRITE) + gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write; + if (what & SNDRV_GF1_HANDLER_DMA_READ) + gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read; +} + +/* + + */ + +static void snd_gf1_clear_regs(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + inb(GUSP(gus, IRQSTAT)); + snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */ + snd_gf1_write8(gus, 0x45, 0); /* Timer Control */ + snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */ + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void snd_gf1_look_regs(snd_gus_card_t * gus) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */ + snd_gf1_look8(gus, 0x49); /* Sampling Control Register */ + inb(GUSP(gus, IRQSTAT)); + snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */ + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +/* + * put selected GF1 voices to initial stage... + */ + +void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice); +#if 0 + printk(" -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); +#endif + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice); +#if 0 + printk(" -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); +#endif + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO); + snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO); +#endif +} + +void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) +{ + unsigned long flags; + unsigned int daddr; + unsigned short i, w_16; + + daddr = gus->gf1.default_voice_address << 4; + for (i = v_min; i <= v_max; i++) { +#if 0 + if (gus->gf1.syn_voices) + gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC; +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, i); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */ + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */ + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82); /* Deactivate voice */ + w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04; + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7); + if (gus->gf1.enh_mode) { + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO); + snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO); +#endif + } +} + +void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) +{ + unsigned long flags; + short i, ramp_ok; + unsigned short ramp_end; + long time; + + if (!in_interrupt()) { /* this can't be done in interrupt */ + for (i = v_min, ramp_ok = 0; i <= v_max; i++) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, i); + ramp_end = snd_gf1_read16(gus, 9) >> 8; + if (ramp_end > SNDRV_GF1_MIN_OFFSET) { + ramp_ok++; + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20); /* ramp rate */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); /* ramp start */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end); /* ramp end */ + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); /* ramp down */ + if (gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); + } + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + } + time = HZ / 20; + while (time > 0 && !signal_pending(current)) { + set_current_state(TASK_INTERRUPTIBLE); + time = schedule_timeout(time); + } + } + snd_gf1_clear_voices(gus, v_min, v_max); +} + +static void snd_gf1_alloc_voice_use(snd_gus_card_t * gus, + snd_gus_voice_t * pvoice, + int type, int client, int port) +{ + pvoice->use = 1; + switch (type) { + case SNDRV_GF1_VOICE_TYPE_PCM: + gus->gf1.pcm_alloc_voices++; + pvoice->pcm = 1; + break; + case SNDRV_GF1_VOICE_TYPE_SYNTH: + pvoice->synth = 1; + pvoice->client = client; + pvoice->port = port; + break; + case SNDRV_GF1_VOICE_TYPE_MIDI: + pvoice->midi = 1; + pvoice->client = client; + pvoice->port = port; + break; + } +} + +snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port) +{ + snd_gus_voice_t *pvoice; + unsigned long flags; + int idx; + + spin_lock_irqsave(&gus->voice_alloc, flags); + if (type == SNDRV_GF1_VOICE_TYPE_PCM) { + if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) { + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return NULL; + } + } + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (!pvoice->use) { + snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return pvoice; + } + } + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (pvoice->midi && !pvoice->client) { + snd_gf1_clear_voices(gus, pvoice->number, pvoice->number); + snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return pvoice; + } + } + spin_unlock_irqrestore(&gus->voice_alloc, flags); + return NULL; +} + +void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice) +{ + unsigned long flags; + void (*private_free)(snd_gus_voice_t *voice); + void *private_data; + + if (voice == NULL || !voice->use) + return; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number); + snd_gf1_clear_voices(gus, voice->number, voice->number); + spin_lock_irqsave(&gus->voice_alloc, flags); + private_free = voice->private_free; + private_data = voice->private_data; + voice->private_free = NULL; + voice->private_data = NULL; + if (voice->pcm) + gus->gf1.pcm_alloc_voices--; + voice->use = voice->pcm = 0; + voice->sample_ops = NULL; + spin_unlock_irqrestore(&gus->voice_alloc, flags); + if (private_free) + private_free(voice); +} + +/* + * call this function only by start of driver + */ + +int snd_gf1_start(snd_gus_card_t * gus) +{ + unsigned long flags; + unsigned int i; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); + + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); + for (i = 0; i < 32; i++) { + gus->gf1.voices[i].number = i; + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); + } + + snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ + + if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + } + snd_gf1_clear_regs(gus); + snd_gf1_select_active_voices(gus); + snd_gf1_delay(gus); + gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; + /* initialize LFOs & clear LFOs memory */ + if (gus->gf1.enh_mode && gus->gf1.memory) { + gus->gf1.hw_lfo = 1; + gus->gf1.default_voice_address += 1024; + } else { + gus->gf1.sw_lfo = 1; + } +#if 0 + snd_gf1_lfo_init(gus); +#endif + if (gus->gf1.memory > 0) + for (i = 0; i < 4; i++) + snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0); + snd_gf1_clear_regs(gus); + snd_gf1_clear_voices(gus, 0, 31); + snd_gf1_look_regs(gus); + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ + if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); + } + while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0); + + spin_lock_irqsave(&gus->reg_lock, flags); + outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); + outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); + spin_unlock_irqrestore(&gus->reg_lock, flags); + + snd_gf1_timers_init(gus); + snd_gf1_look_regs(gus); + snd_gf1_mem_init(gus); + snd_gf1_mem_proc_init(gus); +#ifdef CONFIG_SND_DEBUG + snd_gus_irq_profile_init(gus); +#endif + +#if 0 + if (gus->pnp_flag) { + if (gus->chip.playback_fifo_size > 0) + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8); + if (gus->chip.record_fifo_size > 0) + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8); + snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg); + } +#endif + + return 0; +} + +/* + * call this function only by shutdown of driver + */ + +int snd_gf1_stop(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */ + snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ + snd_gf1_timers_done(gus); +#ifdef CONFIG_SND_DEBUG + snd_gus_irq_profile_done(gus); +#endif + snd_gf1_mem_proc_done(gus); + snd_gf1_mem_done(gus); +#if 0 + snd_gf1_lfo_done(gus); +#endif + return 0; +} diff -Nru linux/sound/isa/gus/gus_sample.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_sample.c --- linux/sound/isa/gus/gus_sample.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_sample.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,156 @@ +/* + * Routines for Gravis UltraSound soundcards - Sample support + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +/* + * + */ + +static void select_instrument(snd_gus_card_t * gus, snd_gus_voice_t * v) +{ + snd_seq_kinstr_t *instr; + +#if 0 + printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n", + v->instr.cluster, + v->instr.std, + v->instr.bank, + v->instr.prg); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1); + if (instr != NULL) { + if (instr->ops) { + if (instr->ops->instr_type == snd_seq_simple_id) + snd_gf1_simple_init(v); + } + snd_seq_instr_free_use(gus->gf1.ilist, instr); + } +} + +/* + * + */ + +static void event_sample(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.std = ev->data.sample.param.sample.std; + if (v->instr.std & 0xff000000) { /* private instrument */ + v->instr.std &= 0x00ffffff; + v->instr.std |= (unsigned int)ev->source.client << 24; + } + v->instr.bank = ev->data.sample.param.sample.bank; + v->instr.prg = ev->data.sample.param.sample.prg; + select_instrument(p->gus, v); +} + +static void event_cluster(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.cluster = ev->data.sample.param.cluster.cluster; + select_instrument(p->gus, v); +} + +static void event_start(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_start) + v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position); +} + +static void event_stop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode); +} + +static void event_freq(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_freq) + v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency); +} + +static void event_volume(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_volume) + v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume); +} + +static void event_loop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_loop) + v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop); +} + +static void event_position(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_pos) + v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position); +} + +static void event_private1(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) +{ + if (v->sample_ops && v->sample_ops->sample_private1) + v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8); +} + +typedef void (gus_sample_event_handler_t)(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v); + +static gus_sample_event_handler_t *gus_sample_event_handlers[9] = { + event_sample, + event_cluster, + event_start, + event_stop, + event_freq, + event_volume, + event_loop, + event_position, + event_private1 +}; + +void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p) +{ + int idx, voice; + snd_gus_card_t *gus = p->gus; + snd_gus_voice_t *v; + unsigned long flags; + + idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; + if (idx < 0 || idx > 8) + return; + for (voice = 0; voice < 32; voice++) { + v = &gus->gf1.voices[voice]; + if (v->use && v->client == ev->source.client && + v->port == ev->source.port && + v->index == ev->data.sample.channel) { + spin_lock_irqsave(&gus->event_lock, flags); + gus_sample_event_handlers[idx](ev, p, v); + spin_unlock_irqrestore(&gus->event_lock, flags); + return; + } + } +} diff -Nru linux/sound/isa/gus/gus_simple.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_simple.c --- linux/sound/isa/gus/gus_simple.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_simple.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,635 @@ +/* + * Routines for Gravis UltraSound soundcards - Simple instrument handlers + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "gus_tables.h" + +/* + * + */ + +static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice); + +static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); +static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); +static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); +static void sample_volume(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); +static void sample_loop(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); +static void sample_pos(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); +static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); + +static snd_gus_sample_ops_t sample_ops = { + sample_start, + sample_stop, + sample_freq, + sample_volume, + sample_loop, + sample_pos, + sample_private1 +}; + +#if 0 + +static void note_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, int wait); +static void note_wait(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void note_off(snd_gus_card_t *gus, snd_gus_voice_t *voice); +static void note_volume(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_pitchbend(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_vibrato(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void note_tremolo(snd_gus_card_t *card, snd_gus_voice_t *voice); + +static struct snd_gus_note_handlers note_commands = { + note_stop, + note_wait, + note_off, + note_volume, + note_pitchbend, + note_vibrato, + note_tremolo +}; + +static void chn_trigger_down(snd_gus_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ); +static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ); +static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ); + +static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = { + chn_trigger_down, + chn_trigger_up, + chn_control +}; + +#endif + +static void do_volume_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); +static void do_pan_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); + +/* + * + */ + +static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + snd_gf1_stop_voice(gus, voice->number); + spin_lock(&gus->reg_lock); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); + spin_unlock(&gus->reg_lock); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + spin_unlock(&gus->event_lock); +} + +static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + if (voice->flags & SNDRV_GF1_VFLG_RUNNING) + do_volume_envelope(gus, voice); + else + snd_gf1_stop_voice(gus, voice->number); + spin_unlock(&gus->event_lock); +} + +static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + spin_lock(&gus->event_lock); + if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) == + (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) + do_pan_envelope(gus, voice); + spin_unlock(&gus->event_lock); +} + +/* + * + */ + +static void do_volume_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + unsigned short next, rate, old_volume; + int program_next_ramp; + unsigned long flags; + + if (!gus->gf1.volume_ramp) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume); + printk("gf1_volume = 0x%x\n", voice->gf1_volume); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } + program_next_ramp = 0; + rate = next = 0; + while (1) { + program_next_ramp = 0; + rate = next = 0; + switch (voice->venv_state) { + case VENV_BEFORE: + voice->venv_state = VENV_ATTACK; + voice->venv_value_next = 0; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); + spin_unlock_irqrestore(&gus->reg_lock, flags); + break; + case VENV_ATTACK: + voice->venv_state = VENV_SUSTAIN; + program_next_ramp++; + next = 255; + rate = gus->gf1.volume_ramp; + break; + case VENV_SUSTAIN: + voice->venv_state = VENV_RELEASE; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + case VENV_RELEASE: + voice->venv_state = VENV_DONE; + program_next_ramp++; + next = 0; + rate = gus->gf1.volume_ramp; + break; + case VENV_DONE: + snd_gf1_stop_voice(gus, voice->number); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + return; + case VENV_VOLUME: + program_next_ramp++; + next = voice->venv_value_next; + rate = gus->gf1.volume_ramp; + voice->venv_state = voice->venv_state_prev; + break; + } + voice->venv_value_next = next; + if (!program_next_ramp) + continue; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); + old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8; + if (!rate) { + spin_unlock_irqrestore(&gus->reg_lock, flags); + continue; + } + next = (((int)voice->gf1_volume * (int)next) / 255) >> 8; + if (old_volume < SNDRV_GF1_MIN_OFFSET) + old_volume = SNDRV_GF1_MIN_OFFSET; + if (next < SNDRV_GF1_MIN_OFFSET) + next = SNDRV_GF1_MIN_OFFSET; + if (next > SNDRV_GF1_MAX_OFFSET) + next = SNDRV_GF1_MAX_OFFSET; + if (old_volume == next) { + spin_unlock_irqrestore(&gus->reg_lock, flags); + continue; + } + voice->volume_control &= ~0xc3; + voice->volume_control |= 0x20; + if (old_volume > next) { + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume); + voice->volume_control |= 0x40; + } else { + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next); + } + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } +} + +static void do_pan_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) +{ + unsigned long flags; + unsigned char old_pan; + +#if 0 + snd_gf1_select_voice(gus, voice->number); + printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", + voice->number, + voice->flags, + voice->gf1_pan, + snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f); +#endif + if (gus->gf1.enh_mode) { + voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); + return; + } + if (!gus->gf1.smooth_pan) { + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); + spin_unlock_irqrestore(&gus->reg_lock, flags); + return; + } + if (!(voice->flags & SNDRV_GF1_VFLG_PAN)) /* before */ + voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f; + if (old_pan > voice->gf1_pan ) + old_pan--; + if (old_pan < voice->gf1_pan) + old_pan++; + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan); + spin_unlock_irqrestore(&gus->reg_lock, flags); + if (old_pan == voice->gf1_pan) /* the goal was reached */ + voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); +#if 0 + snd_gf1_select_voice(gus, voice->number); + printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", + voice->number, + voice->flags, + voice->gf1_pan, + snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f); +#endif +} + +static void set_enhanced_pan(snd_gus_card_t *gus, snd_gus_voice_t *voice, unsigned short pan) +{ + unsigned long flags; + unsigned short vlo, vro; + + vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan); + vro = SNDRV_GF1_ATTEN(pan); + if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) { + vlo >>= 1; + vro >>= 1; + } + vlo <<= 4; + vro <<= 4; +#if 0 + printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n", + vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT), + vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT)); +#endif + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro); + spin_unlock_irqrestore(&gus->reg_lock, flags); + voice->vlo = vlo; + voice->vro = vro; +} + +/* + * + */ + +static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) +{ + unsigned long flags; + unsigned int begin, addr, addr_end, addr_start; + int w_16; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory << 4; + w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0; + addr_start = simple->loop_start; + if (simple->format & SIMPLE_WAVE_LOOP) { + addr_end = simple->loop_end; + } else { + addr_end = (simple->size << 4) - (w_16 ? 40 : 24); + } + if (simple->format & SIMPLE_WAVE_BACKWARD) { + addr = simple->loop_end; + if (position < simple->loop_end) + addr -= position; + } else { + addr = position; + } + voice->control = 0x00; + voice->mode = 0x20; /* enable offset registers */ + if (simple->format & SIMPLE_WAVE_16BIT) + voice->control |= 0x04; + if (simple->format & SIMPLE_WAVE_BACKWARD) + voice->control |= 0x40; + if (simple->format & SIMPLE_WAVE_LOOP) { + voice->control |= 0x08; + } else { + voice->control |= 0x20; + } + if (simple->format & SIMPLE_WAVE_BIDIR) + voice->control |= 0x10; + if (simple->format & SIMPLE_WAVE_ULAW) + voice->mode |= 0x40; + if (w_16) { + addr = ((addr << 1) & ~0x1f) | (addr & 0x0f); + addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f); + addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f); + } + addr += begin; + addr_start += begin; + addr_end += begin; + snd_gf1_stop_voice(gus, voice->number); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); + voice->venv_state = VENV_BEFORE; + voice->volume_control = 0x03; + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); + if (!gus->gf1.enh_mode) { + snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); + } else { + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro); + snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro); + snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume); + snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); + do_volume_envelope(gus, voice); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + if (gus->gf1.enh_mode) + snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control); + if (!gus->gf1.enh_mode) { + snd_gf1_delay(gus); + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control ); + } + spin_unlock_irqrestore(&gus->reg_lock, flags); +#if 0 + snd_gf1_print_voice_registers(gus); +#endif + voice->flags |= SNDRV_GF1_VFLG_RUNNING; + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode) +{ + unsigned char control; + unsigned long flags; + + if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING)) + return; + switch (mode) { + default: + if (gus->gf1.volume_ramp > 0) { + if (voice->venv_state < VENV_RELEASE) { + voice->venv_state = VENV_RELEASE; + do_volume_envelope(gus, voice); + } + } + if (mode != SAMPLE_STOP_VENVELOPE) { + snd_gf1_stop_voice(gus, voice->number); + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); + spin_unlock_irqrestore(&gus->reg_lock, flags); + voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + } + break; + case SAMPLE_STOP_LOOP: /* disable loop only */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); + control &= ~(0x83 | 0x04); + control |= 0x20; + snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control); + spin_unlock_irqrestore(&gus->reg_lock, flags); + break; + } +} + +static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq) +{ + unsigned long flags; + + spin_lock_irqsave(&gus->reg_lock, flags); + voice->fc_register = snd_gf1_translate_freq(gus, freq); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); + spin_unlock_irqrestore(&gus->reg_lock, flags); +} + +static void sample_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume) +{ + if (volume->volume >= 0) { + volume->volume &= 0x3fff; + voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4; + voice->venv_state_prev = VENV_SUSTAIN; + voice->venv_state = VENV_VOLUME; + do_volume_envelope(gus, voice); + } + if (volume->lr >= 0) { + volume->lr &= 0x3fff; + if (!gus->gf1.enh_mode) { + voice->gf1_pan = (volume->lr >> 10) & 15; + if (!gus->gf1.full_range_pan) { + if (voice->gf1_pan == 0) + voice->gf1_pan++; + if (voice->gf1_pan == 15) + voice->gf1_pan--; + } + voice->flags &= ~SNDRV_GF1_VFLG_PAN; /* before */ + do_pan_envelope(gus, voice); + } else { + set_enhanced_pan(gus, voice, volume->lr >> 7); + } + } +} + +static void sample_loop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop) +{ + unsigned long flags; + int w_16 = voice->control & 0x04; + unsigned int begin, addr_start, addr_end; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + +#if 0 + printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory; + addr_start = loop->start; + addr_end = loop->end; + addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin; + addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +static void sample_pos(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) +{ + unsigned long flags; + int w_16 = voice->control & 0x04; + unsigned int begin, addr; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + +#if 0 + printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); +#endif + instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + begin = simple->address.memory; + addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin; + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_select_voice(gus, voice->number); + snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_seq_instr_free_use(gus->gf1.ilist, instr); +} + +#if 0 + +static unsigned char get_effects_mask( ultra_card_t *card, int value ) +{ + if ( value > 7 ) return 0; + if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE ) + return card -> gf1.effects -> chip.interwave.voice_output[ value ]; + return 0; +} + +#endif + +static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data) +{ +#if 0 + unsigned long flags; + unsigned char uc; + + switch ( *data ) { + case ULTRA_PRIV1_IW_EFFECT: + uc = get_effects_mask( card, ultra_get_byte( data, 4 ) ); + uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 ); + uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) ); + uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 ); + voice -> data.simple.effect_accumulator = uc; + voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4; + if ( !card -> gf1.enh_mode ) return; + if ( voice -> flags & VFLG_WAIT_FOR_START ) return; + if ( voice -> flags & VFLG_RUNNING ) + { + CLI( &flags ); + gf1_select_voice( card, voice -> number ); + ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator ); + ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume ); + STI( &flags ); + } + break; + case ULTRA_PRIV1_IW_LFO: + ultra_lfo_command( card, voice -> number, data ); + } +#endif +} + +#if 0 + +/* + * + */ + +static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait ) +{ +} + +static void note_wait( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_off( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_volume( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice ) +{ +} + +/* + * + */ + +static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ) +{ +} + +static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ) +{ +} + +static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ) +{ +} + +/* + * + */ + +#endif + +void snd_gf1_simple_init(snd_gus_voice_t *voice) +{ + voice->handler_wave = interrupt_wave; + voice->handler_volume = interrupt_volume; + voice->handler_effect = interrupt_effect; + voice->volume_change = NULL; + voice->sample_ops = &sample_ops; +} diff -Nru linux/sound/isa/gus/gus_synth.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_synth.c --- linux/sound/isa/gus/gus_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_synth.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,324 @@ +/* + * Routines for Gravis UltraSound soundcards - Synthesizer + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; + +/* + * + */ + +static void snd_gus_synth_free_voices(snd_gus_card_t * gus, int client, int port) +{ + int idx; + snd_gus_voice_t * voice; + + for (idx = 0; idx < 32; idx++) { + voice = &gus->gf1.voices[idx]; + if (voice->use && voice->client == client && voice->port == port) + snd_gf1_free_voice(gus, voice); + } +} + +static int snd_gus_synth_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_gus_port_t * port = (snd_gus_port_t *)private_data; + snd_gus_card_t * gus = port->gus; + snd_gus_voice_t * voice; + int idx; + + if (info->voices > 32) + return -EINVAL; + down(&gus->register_mutex); + if (!snd_gus_use_inc(gus)) { + up(&gus->register_mutex); + return -EFAULT; + } + for (idx = 0; idx < info->voices; idx++) { + voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); + up(&gus->register_mutex); + return -EBUSY; + } + voice->index = idx; + } + up(&gus->register_mutex); + return 0; +} + +static int snd_gus_synth_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_gus_port_t * port = (snd_gus_port_t *)private_data; + snd_gus_card_t * gus = port->gus; + + down(&gus->register_mutex); + snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); + up(&gus->register_mutex); + return 0; +} + +/* + * + */ + +static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client) +{ + snd_seq_instr_header_t ifree; + + memset(&ifree, 0, sizeof(ifree)); + ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; + snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0); +} + +int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) +{ + snd_gus_port_t * p = (snd_gus_port_t *) private_data; + + snd_assert(p != NULL, return -EINVAL); + if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && + ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { + snd_gus_sample_event(ev, p); + return 0; + } + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && + ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { + if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { + snd_gus_synth_free_private_instruments(p, ev->data.addr.client); + return 0; + } + } + if (direct) { + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { + snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops, + p->gus->gf1.ilist, + ev, + p->gus->gf1.seq_client, + atomic, hop); + return 0; + } + } + return 0; +} + +static void snd_gus_synth_instr_notify(void *private_data, + snd_seq_kinstr_t *instr, + int what) +{ + int idx; + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return); + snd_gus_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&gus->event_lock, flags); + for (idx = 0; idx < 32; idx++) { + pvoice = &gus->gf1.voices[idx]; + if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { + if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { + pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY); + } else { + snd_gf1_stop_voice(gus, pvoice->number); + pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING; + } + } + } + spin_unlock_irqrestore(&gus->event_lock, flags); +} + +/* + * + */ + +static void snd_gus_synth_free_port(void *private_data) +{ + snd_gus_port_t * p = (snd_gus_port_t *)private_data; + + if (p) + snd_midi_channel_free_set(p->chset); +} + +static int snd_gus_synth_create_port(snd_gus_card_t * gus, int idx) +{ + snd_gus_port_t * p; + snd_seq_port_callback_t callbacks; + char name[32]; + int result; + + p = &gus->gf1.seq_ports[idx]; + p->chset = snd_midi_channel_alloc_set(16); + if (p->chset == NULL) + return -ENOMEM; + p->chset->private_data = p; + p->gus = gus; + p->client = gus->gf1.seq_client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_gus_synth_use; + callbacks.unuse = snd_gus_synth_unuse; + callbacks.event_input = snd_gus_synth_event_input; + callbacks.private_free = snd_gus_synth_free_port; + callbacks.private_data = p; + + sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx); + p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client, + &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_MIDI_GS | + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (p->chset->port < 0) { + result = p->chset->port; + snd_gus_synth_free_port(p); + return result; + } + p->port = p->chset->port; + return 0; +} + +/* + * + */ + +static int snd_gus_synth_new_device(snd_seq_device_t *dev) +{ + snd_gus_card_t *gus; + int client, i; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + snd_seq_port_subscribe_t sub; + snd_iwffff_ops_t *iwops; + snd_gf1_ops_t *gf1ops; + snd_simple_ops_t *simpleops; + + gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (gus == NULL) + return -EINVAL; + + init_MUTEX(&gus->register_mutex); + gus->gf1.seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = gus; + callbacks.allow_output = callbacks.allow_input = 1; + client = gus->gf1.seq_client = + snd_seq_create_kernel_client(gus->card, 1, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + sprintf(cinfo.name, gus->interwave ? "AMD InterWave" : "GF1"); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + for (i = 0; i < 4; i++) + snd_gus_synth_create_port(gus, i); + + gus->gf1.ilist = snd_seq_instr_list_new(); + if (gus->gf1.ilist == NULL) { + snd_seq_delete_kernel_client(client); + gus->gf1.seq_client = -1; + return -ENOMEM; + } + gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + + simpleops = &gus->gf1.simple_ops; + snd_seq_simple_init(simpleops, gus, NULL); + simpleops->put_sample = snd_gus_simple_put_sample; + simpleops->get_sample = snd_gus_simple_get_sample; + simpleops->remove_sample = snd_gus_simple_remove_sample; + simpleops->notify = snd_gus_synth_instr_notify; + + gf1ops = &gus->gf1.gf1_ops; + snd_seq_gf1_init(gf1ops, gus, &simpleops->kops); + gf1ops->put_sample = snd_gus_gf1_put_sample; + gf1ops->get_sample = snd_gus_gf1_get_sample; + gf1ops->remove_sample = snd_gus_gf1_remove_sample; + gf1ops->notify = snd_gus_synth_instr_notify; + + iwops = &gus->gf1.iwffff_ops; + snd_seq_iwffff_init(iwops, gus, &gf1ops->kops); + iwops->put_sample = snd_gus_iwffff_put_sample; + iwops->get_sample = snd_gus_iwffff_get_sample; + iwops->remove_sample = snd_gus_iwffff_remove_sample; + iwops->notify = snd_gus_synth_instr_notify; + + memset(&sub, 0, sizeof(sub)); + sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + sub.dest.client = client; + sub.dest.port = 0; + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); + + return 0; +} + +static int snd_gus_synth_delete_device(snd_seq_device_t *dev) +{ + snd_gus_card_t *gus; + + gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (gus == NULL) + return -EINVAL; + + if (gus->gf1.seq_client >= 0) { + snd_seq_delete_kernel_client(gus->gf1.seq_client); + gus->gf1.seq_client = -1; + } + if (gus->gf1.ilist) + snd_seq_instr_list_free(&gus->gf1.ilist); + return 0; +} + +static int __init alsa_gus_synth_init(void) +{ + static snd_seq_dev_ops_t ops = { + snd_gus_synth_new_device, + snd_gus_synth_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops, + sizeof(snd_gus_card_t*)); +} + +static void __exit alsa_gus_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS); +} + +module_init(alsa_gus_synth_init) +module_exit(alsa_gus_synth_exit) diff -Nru linux/sound/isa/gus/gus_tables.h linux-2.4.19-pre5-mjc/sound/isa/gus/gus_tables.h --- linux/sound/isa/gus/gus_tables.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_tables.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,86 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_GF1_SCALE_TABLE_SIZE 128 +#define SNDRV_GF1_ATTEN_TABLE_SIZE 128 + +#ifdef __GUS_TABLES_ALLOC__ + +unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] = +{ + 8372, 8870, 9397, 9956, 10548, 11175, + 11840, 12544, 13290, 14080, 14917, 15804, + 16744, 17740, 18795, 19912, 21096, 22351, + 23680, 25088, 26580, 28160, 29834, 31609, + 33488, 35479, 37589, 39824, 42192, 44701, + 47359, 50175, 53159, 56320, 59669, 63217, + 66976, 70959, 75178, 79649, 84385, 89402, + 94719, 100351, 106318, 112640, 119338, 126434, + 133952, 141918, 150356, 159297, 168769, 178805, + 189437, 200702, 212636, 225280, 238676, 252868, + 267905, 283835, 300713, 318594, 337539, 357610, + 378874, 401403, 425272, 450560, 477352, 505737, + 535809, 567670, 601425, 637188, 675077, 715219, + 757749, 802807, 850544, 901120, 954703, 1011473, + 1071618, 1135340, 1202851, 1274376, 1350154, 1430439, + 1515497, 1605613, 1701088, 1802240, 1909407, 2022946, + 2143237, 2270680, 2405702, 2548752, 2700309, 2860878, + 3030994, 3211227, 3402176, 3604480, 3818814, 4045892, + 4286473, 4541360, 4811404, 5097505, 5400618, 5721755, + 6061989, 6422453, 6804352, 7208960, 7637627, 8091784, + 8572947, 9082720, 9622807, 10195009, 10801236, 11443511, + 12123977, 12844906 +}; + +unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = { + 4095 /* 0 */,1789 /* 1 */,1533 /* 2 */,1383 /* 3 */,1277 /* 4 */, + 1195 /* 5 */,1127 /* 6 */,1070 /* 7 */,1021 /* 8 */,978 /* 9 */, + 939 /* 10 */,903 /* 11 */,871 /* 12 */,842 /* 13 */,814 /* 14 */, + 789 /* 15 */,765 /* 16 */,743 /* 17 */,722 /* 18 */,702 /* 19 */, + 683 /* 20 */,665 /* 21 */,647 /* 22 */,631 /* 23 */,615 /* 24 */, + 600 /* 25 */,586 /* 26 */,572 /* 27 */,558 /* 28 */,545 /* 29 */, + 533 /* 30 */,521 /* 31 */,509 /* 32 */,498 /* 33 */,487 /* 34 */, + 476 /* 35 */,466 /* 36 */,455 /* 37 */,446 /* 38 */,436 /* 39 */, + 427 /* 40 */,418 /* 41 */,409 /* 42 */,400 /* 43 */,391 /* 44 */, + 383 /* 45 */,375 /* 46 */,367 /* 47 */,359 /* 48 */,352 /* 49 */, + 344 /* 50 */,337 /* 51 */,330 /* 52 */,323 /* 53 */,316 /* 54 */, + 309 /* 55 */,302 /* 56 */,296 /* 57 */,289 /* 58 */,283 /* 59 */, + 277 /* 60 */,271 /* 61 */,265 /* 62 */,259 /* 63 */,253 /* 64 */, + 247 /* 65 */,242 /* 66 */,236 /* 67 */,231 /* 68 */,225 /* 69 */, + 220 /* 70 */,215 /* 71 */,210 /* 72 */,205 /* 73 */,199 /* 74 */, + 195 /* 75 */,190 /* 76 */,185 /* 77 */,180 /* 78 */,175 /* 79 */, + 171 /* 80 */,166 /* 81 */,162 /* 82 */,157 /* 83 */,153 /* 84 */, + 148 /* 85 */,144 /* 86 */,140 /* 87 */,135 /* 88 */,131 /* 89 */, + 127 /* 90 */,123 /* 91 */,119 /* 92 */,115 /* 93 */,111 /* 94 */, + 107 /* 95 */,103 /* 96 */,100 /* 97 */,96 /* 98 */,92 /* 99 */, + 88 /* 100 */,85 /* 101 */,81 /* 102 */,77 /* 103 */,74 /* 104 */, + 70 /* 105 */,67 /* 106 */,63 /* 107 */,60 /* 108 */,56 /* 109 */, + 53 /* 110 */,50 /* 111 */,46 /* 112 */,43 /* 113 */,40 /* 114 */, + 37 /* 115 */,33 /* 116 */,30 /* 117 */,27 /* 118 */,24 /* 119 */, + 21 /* 120 */,18 /* 121 */,15 /* 122 */,12 /* 123 */,9 /* 124 */, + 6 /* 125 */,3 /* 126 */,0 /* 127 */, +}; + +#else + +extern unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE]; +extern unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE]; + +#endif diff -Nru linux/sound/isa/gus/gus_timer.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_timer.c --- linux/sound/isa/gus/gus_timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_timer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,207 @@ +/* + * Routines for Gravis UltraSound soundcards - Timers + * Copyright (c) by Jaroslav Kysela + * + * GUS have similar timers as AdLib (OPL2/OPL3 chips). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#define chip_t snd_gus_card_t + +/* + * Timer 1 - 80us + */ + +static int snd_gf1_timer1_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + ticks = timer->sticks; + tmp = (gus->gf1.timer_enabled |= 4); + snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */ + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */ + snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +static int snd_gf1_timer1_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + tmp = (gus->gf1.timer_enabled &= ~4); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +/* + * Timer 2 - 320us + */ + +static int snd_gf1_timer2_start(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + unsigned int ticks; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + ticks = timer->sticks; + tmp = (gus->gf1.timer_enabled |= 8); + snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */ + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */ + snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +static int snd_gf1_timer2_stop(snd_timer_t * timer) +{ + unsigned long flags; + unsigned char tmp; + snd_gus_card_t *gus; + + gus = snd_timer_chip(timer); + spin_lock_irqsave(&gus->reg_lock, flags); + tmp = (gus->gf1.timer_enabled &= ~8); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ + spin_unlock_irqrestore(&gus->reg_lock, flags); + return 0; +} + +/* + + */ + +static void snd_gf1_interrupt_timer1(snd_gus_card_t * gus) +{ + snd_timer_t *timer = gus->gf1.timer1; + + if (timer == NULL) + return; + snd_timer_interrupt(timer, timer->sticks); +} + +static void snd_gf1_interrupt_timer2(snd_gus_card_t * gus) +{ + snd_timer_t *timer = gus->gf1.timer2; + + if (timer == NULL) + return; + snd_timer_interrupt(timer, timer->sticks); +} + +/* + + */ + +static struct _snd_timer_hardware snd_gf1_timer1 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 80000, + ticks: 256, + start: snd_gf1_timer1_start, + stop: snd_gf1_timer1_stop, +}; + +static struct _snd_timer_hardware snd_gf1_timer2 = +{ + flags: SNDRV_TIMER_HW_STOP, + resolution: 320000, + ticks: 256, + start: snd_gf1_timer2_start, + stop: snd_gf1_timer2_stop, +}; + +static void snd_gf1_timer1_free(snd_timer_t *timer) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + gus->gf1.timer1 = NULL; +} + +static void snd_gf1_timer2_free(snd_timer_t *timer) +{ + snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + gus->gf1.timer2 = NULL; +} + +void snd_gf1_timers_init(snd_gus_card_t * gus) +{ + snd_timer_t *timer; + snd_timer_id_t tid; + + if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL) + return; + + gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; + gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = gus->card->number; + tid.device = gus->timer_dev; + tid.subdevice = 0; + + if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { + strcpy(timer->name, "GF1 timer #1"); + timer->private_data = gus; + timer->private_free = snd_gf1_timer1_free; + timer->hw = snd_gf1_timer1; + } + gus->gf1.timer1 = timer; + + tid.device++; + + if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { + strcpy(timer->name, "GF1 timer #2"); + timer->private_data = gus; + timer->private_free = snd_gf1_timer2_free; + timer->hw = snd_gf1_timer2; + } + gus->gf1.timer2 = timer; +} + +void snd_gf1_timers_done(snd_gus_card_t * gus) +{ + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2); + if (gus->gf1.timer1) { + snd_device_free(gus->card, gus->gf1.timer1); + gus->gf1.timer1 = NULL; + } + if (gus->gf1.timer2) { + snd_device_free(gus->card, gus->gf1.timer2); + gus->gf1.timer2 = NULL; + } +} diff -Nru linux/sound/isa/gus/gus_uart.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_uart.c --- linux/sound/isa/gus/gus_uart.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_uart.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,260 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for the GF1 MIDI interface - like UART 6850 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus) +{ + int count; + unsigned char stat, data, byte; + unsigned long flags; + + count = 10; + while (count) { + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + stat = snd_gf1_uart_stat(gus); + if (!(stat & 0x01)) { /* data in Rx FIFO? */ + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + count--; + continue; + } + count = 100; /* arm counter to new value */ + data = snd_gf1_uart_get(gus); + if (!(gus->gf1.uart_cmd & 0x80)) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + continue; + } + if (stat & 0x10) { /* framing error */ + gus->gf1.uart_framing++; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + snd_rawmidi_receive_reset(gus->midi_substream_input); + continue; + } + byte = snd_gf1_uart_get(gus); + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); + if (stat & 0x20) { + gus->gf1.uart_overrun++; + snd_rawmidi_receive_reset(gus->midi_substream_input); + } + } +} + +static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus) +{ + char byte; + unsigned long flags; + + /* try unlock output */ + if (snd_gf1_uart_stat(gus) & 0x01) + snd_gf1_interrupt_midi_in(gus); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */ + if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */ + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */ + } else { + snd_gf1_uart_put(gus, byte); + } + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close) +{ + snd_gf1_uart_cmd(gus, 0x03); /* reset */ + if (!close && gus->uart_enable) { + udelay(160); + snd_gf1_uart_cmd(gus, 0x00); /* normal operations */ + } +} + +static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ + snd_gf1_uart_reset(gus, 0); + } + gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; + gus->midi_substream_output = substream; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +#if 0 + snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); +#endif + return 0; +} + +static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + int i; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { + snd_gf1_uart_reset(gus, 0); + } + gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; + gus->midi_substream_input = substream; + if (gus->uart_enable) { + for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) + snd_gf1_uart_get(gus); /* clean Rx */ + if (i >= 1000) + snd_printk("gus midi uart init read - cleanup error\n"); + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +#if 0 + snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); + snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); +#endif + return 0; +} + +static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) + snd_gf1_uart_reset(gus, 1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); + gus->midi_substream_output = NULL; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return 0; +} + +static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_gus_card_t *gus; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) + snd_gf1_uart_reset(gus, 1); + snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); + gus->midi_substream_input = NULL; + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return 0; +} + +static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + snd_gus_card_t *gus; + unsigned long flags; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (up) { + if ((gus->gf1.uart_cmd & 0x80) == 0) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ + } else { + if (gus->gf1.uart_cmd & 0x80) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_gus_card_t *gus; + char byte; + int timeout; + + gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (up) { + if ((gus->gf1.uart_cmd & 0x20) == 0) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + /* wait for empty Rx - Tx is probably unlocked */ + timeout = 10000; + while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); + /* Tx FIFO free? */ + spin_lock_irqsave(&gus->uart_cmd_lock, flags); + if (gus->gf1.uart_cmd & 0x20) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return; + } + if (snd_gf1_uart_stat(gus) & 0x02) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); + return; + } + snd_gf1_uart_put(gus, byte); + } + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ + } + } else { + if (gus->gf1.uart_cmd & 0x20) + snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); + } + spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); +} + +static snd_rawmidi_ops_t snd_gf1_uart_output = +{ + open: snd_gf1_uart_output_open, + close: snd_gf1_uart_output_close, + trigger: snd_gf1_uart_output_trigger, +}; + +static snd_rawmidi_ops_t snd_gf1_uart_input = +{ + open: snd_gf1_uart_input_open, + close: snd_gf1_uart_input_close, + trigger: snd_gf1_uart_input_trigger, +}; + +int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = gus; + gus->midi_uart = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return err; +} diff -Nru linux/sound/isa/gus/gus_volume.c linux-2.4.19-pre5-mjc/sound/isa/gus/gus_volume.c --- linux/sound/isa/gus/gus_volume.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gus_volume.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,211 @@ +/* + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#define __GUS_TABLES_ALLOC__ +#include "gus_tables.h" + +EXPORT_SYMBOL(snd_gf1_atten_table); /* for snd-gus-synth module */ + +unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol) +{ + unsigned short e, m, tmp; + + if (vol > 65535) + vol = 65535; + tmp = vol; + e = 7; + if (tmp < 128) { + while (e > 0 && tmp < (1 << e)) + e--; + } else { + while (tmp > 255) { + tmp >>= 1; + e++; + } + } + m = vol - (1 << e); + if (m > 0) { + if (e > 8) + m >>= e - 8; + else if (e < 8) + m <<= 8 - e; + m &= 255; + } + return (e << 8) | m; +} + +unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol) +{ + unsigned int rvol; + unsigned short e, m; + + if (!gf1_vol) + return 0; + e = gf1_vol >> 8; + m = (unsigned char) gf1_vol; + rvol = 1 << e; + if (e > 8) + return rvol | (m << (e - 8)); + return rvol | (m >> (8 - e)); +} + +unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, + unsigned short start, + unsigned short end, + unsigned int us) +{ + static unsigned char vol_rates[19] = + { + 23, 24, 26, 28, 29, 31, 32, 34, + 36, 37, 39, 40, 42, 44, 45, 47, + 49, 50, 52 + }; + unsigned short range, increment, value, i; + + start >>= 4; + end >>= 4; + if (start < end) + us /= end - start; + else + us /= start - end; + range = 4; + value = gus->gf1.enh_mode ? + vol_rates[0] : + vol_rates[gus->gf1.active_voices - 14]; + for (i = 0; i < 3; i++) { + if (us < value) { + range = i; + break; + } else + value <<= 3; + } + if (range == 4) { + range = 3; + increment = 1; + } else + increment = (value + (value >> 1)) / us; + return (range << 6) | (increment & 0x3f); +} + +unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16) +{ + freq16 >>= 3; + if (freq16 < 50) + freq16 = 50; + if (freq16 & 0xf8000000) { + freq16 = ~0xf8000000; + snd_printk("snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16); + } + return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq; +} + +short snd_gf1_compute_vibrato(short cents, unsigned short fc_register) +{ + static short vibrato_table[] = + { + 0, 0, 32, 592, 61, 1175, 93, 1808, + 124, 2433, 152, 3007, 182, 3632, 213, 4290, + 241, 4834, 255, 5200 + }; + + long depth; + short *vi1, *vi2, pcents, v1; + + pcents = cents < 0 ? -cents : cents; + for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2); + v1 = *(vi1 + 1); + /* The FC table above is a list of pairs. The first number in the pair */ + /* is the cents index from 0-255 cents, and the second number in the */ + /* pair is the FC adjustment needed to change the pitch by the indexed */ + /* number of cents. The table was created for an FC of 32768. */ + /* The following expression does a linear interpolation against the */ + /* approximated log curve in the table above, and then scales the number */ + /* by the FC before the LFO. This calculation also adjusts the output */ + /* value to produce the appropriate depth for the hardware. The depth */ + /* is 2 * desired FC + 1. */ + depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14; + if (depth) + depth++; + if (depth > 255) + depth = 255; + return cents < 0 ? -(short) depth : (short) depth; +} + +unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens) +{ + static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933}; + int wheel, sensitivity; + unsigned int mantissa, f1, f2; + unsigned short semitones, f1_index, f2_index, f1_power, f2_power; + char bend_down = 0; + int bend; + + if (!sens) + return 1024; + wheel = (int) pitchbend - 8192; + sensitivity = ((int) sens * wheel) / 128; + if (sensitivity < 0) { + bend_down = 1; + sensitivity = -sensitivity; + } + semitones = (unsigned int) (sensitivity >> 13); + mantissa = sensitivity % 8192; + f1_index = semitones % 12; + f2_index = (semitones + 1) % 12; + f1_power = semitones / 12; + f2_power = (semitones + 1) / 12; + f1 = log_table[f1_index] << f1_power; + f2 = log_table[f2_index] << f2_power; + bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1); + if (bend_down) + bend = 1048576L / bend; + return bend; +} + +unsigned short snd_gf1_compute_freq(unsigned int freq, + unsigned int rate, + unsigned short mix_rate) +{ + unsigned int fc; + int scale = 0; + + while (freq >= 4194304L) { + scale++; + freq >>= 1; + } + fc = (freq << 10) / rate; + if (fc > 97391L) { + fc = 97391; + snd_printk("patch: (1) fc frequency overflow - %u\n", fc); + } + fc = (fc * 44100UL) / mix_rate; + while (scale--) + fc <<= 1; + if (fc > 65535L) { + fc = 65535; + snd_printk("patch: (2) fc frequency overflow - %u\n", fc); + } + return (unsigned short) fc; +} diff -Nru linux/sound/isa/gus/gusclassic.c linux-2.4.19-pre5-mjc/sound/isa/gus/gusclassic.c --- linux/sound/isa/gus/gusclassic.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gusclassic.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,302 @@ +/* + * Driver for Gravis UltraSound Classic soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound Classic"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound Classic}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for GUS Classic soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for GUS Classic soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable GUS Classic soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x10}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Classic driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusclassic_detect(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + + return 0; +} + +static void __init snd_gusclassic_init(int dev, snd_gus_card_t * gus) +{ + gus->equal_irq = 0; + gus->codec_flag = 0; + gus->max_flag = 0; + gus->joystick_dac = snd_joystick_dac[dev]; +} + +static int __init snd_gusclassic_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1}; + static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; + int irq, dma1, dma2; + snd_card_t *card; + struct snd_gusclassic *guscard; + snd_gus_card_t *gus = NULL; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + guscard = (struct snd_gusclassic *)card->private_data; + if (snd_pcm_channels[dev] < 2) + snd_pcm_channels[dev] = 2; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + + if ((err = snd_gus_create(card, + snd_port[dev], + irq, dma1, dma2, + 0, snd_channels[dev], snd_pcm_channels[dev], + 0, &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusclassic_detect(gus)) < 0) { + snd_card_free(card); + return err; + } + snd_gusclassic_init(dev, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (gus->max_flag || gus->ess_flag) { + snd_card_free(card); + snd_printdd("GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + if ((err = snd_gf1_new_mixer(gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->ace_flag) { + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, irq, dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_gusclassic_cards[dev] = card; + return 0; +} + +static int __init snd_gusclassic_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_gusclassic_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusclassic_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusclassic_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "GUS Classic soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusclassic_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_gusclassic_cards[idx]); +} + +module_init(alsa_card_gusclassic_init) +module_exit(alsa_card_gusclassic_exit) + +#ifndef MODULE + +/* format is: snd-gusclassic=snd_enable,snd_index,snd_id, + snd_port,snd_irq, + snd_dma1,snd_dma2, + snd_joystick_dac, + snd_channels,snd_pcm_channels */ + +static int __init alsa_card_gusclassic_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_joystick_dac[nr_dev]) == 2 && + get_option(&str,&snd_channels[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusclassic=", alsa_card_gusclassic_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/gus/gusextreme.c linux-2.4.19-pre5-mjc/sound/isa/gus/gusextreme.c --- linux/sound/isa/gus/gusextreme.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gusextreme.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,436 @@ +/* + * Driver for Gravis UltraSound Extreme soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound Extreme"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound Extreme}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long snd_gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable GUS Extreme soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x20}},dialog:list"); +MODULE_PARM(snd_gf1_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_gf1_port, "GF1 port # for GUS Extreme driver (optional)."); +MODULE_PARM_SYNTAX(snd_gf1_port, SNDRV_ENABLED ",allows:{{0x210,0x270,0x10}},dialog:list"); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x320,0x10}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); +MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); +MODULE_PARM(snd_gf1_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_gf1_irq, "GF1 IRQ # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_gf1_irq, SNDRV_ENABLED ",allows:{{2},{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "GF1 DMA # for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Extreme driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusextreme_detect(int dev, + snd_card_t * card, + snd_gus_card_t * gus, + es1688_t *es1688) +{ + unsigned long flags; + + /* + * This is main stuff - enable access to GF1 chip... + * I'm not sure, if this will work for card which have + * ES1688 chip in another place than 0x220. + * + * I used reverse-engineering in DOSEMU. [--jk] + * + * ULTRINIT.EXE: + * 0x230 = 0,2,3 + * 0x240 = 2,0,1 + * 0x250 = 2,0,3 + * 0x260 = 2,2,1 + */ + + spin_lock_irqsave(&es1688->mixer_lock, flags); + snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */ + spin_unlock_irqrestore(&es1688->mixer_lock, flags); + spin_lock_irqsave(&es1688->reg_lock, flags); + outb(snd_gf1_port[dev] & 0x040 ? 2 : 0, ES1688P(es1688, INIT1)); + outb(0, 0x201); + outb(snd_gf1_port[dev] & 0x020 ? 2 : 0, ES1688P(es1688, INIT1)); + outb(0, 0x201); + outb(snd_gf1_port[dev] & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); + spin_unlock_irqrestore(&es1688->reg_lock, flags); + + udelay(100); + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -EIO; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -EIO; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -EIO; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -EIO; +#endif + + return 0; +} + +static void __init snd_gusextreme_init(int dev, snd_gus_card_t * gus) +{ + gus->joystick_dac = snd_joystick_dac[dev]; +} + +static int __init snd_gusextreme_mixer(es1688_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign Master Playback Switch to Synth Playback Switch */ + strcpy(id1.name, "Master Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + return 0; +} + +static int __init snd_gusextreme_probe(int dev) +{ + static int possible_ess_irqs[] = {5, 9, 10, 7, -1}; + static int possible_ess_dmas[] = {1, 3, 0, -1}; + static int possible_gf1_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_gf1_dmas[] = {5, 6, 7, 1, 3, -1}; + int gf1_irq, gf1_dma, ess_irq, mpu_irq, ess_dma; + snd_card_t *card; + struct snd_gusextreme *acard; + snd_gus_card_t *gus; + es1688_t *es1688; + opl3_t *opl3; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_gusextreme *)card->private_data; + + gf1_irq = snd_gf1_irq[dev]; + if (gf1_irq == SNDRV_AUTO_IRQ) { + if ((gf1_irq = snd_legacy_find_free_irq(possible_gf1_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ for GF1\n"); + return -EBUSY; + } + } + ess_irq = snd_irq[dev]; + if (ess_irq == SNDRV_AUTO_IRQ) { + if ((ess_irq = snd_legacy_find_free_irq(possible_ess_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ for ES1688\n"); + return -EBUSY; + } + } + if (snd_mpu_port[dev] == SNDRV_AUTO_PORT) + snd_mpu_port[dev] = 0; + mpu_irq = snd_mpu_irq[dev]; + if (mpu_irq > 15) + mpu_irq = -1; + gf1_dma = snd_dma1[dev]; + if (gf1_dma == SNDRV_AUTO_DMA) { + if ((gf1_dma = snd_legacy_find_free_dma(possible_gf1_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA for GF1\n"); + return -EBUSY; + } + } + ess_dma = snd_dma8[dev]; + if (ess_dma == SNDRV_AUTO_DMA) { + if ((ess_dma = snd_legacy_find_free_dma(possible_ess_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA for ES1688\n"); + return -EBUSY; + } + } + + if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev], + ess_irq, mpu_irq, ess_dma, + ES1688_HW_1688, &es1688)) < 0) { + snd_card_free(card); + return err; + } + if (snd_gf1_port[dev] < 0) + snd_gf1_port[dev] = snd_port[dev] + 0x20; + if ((err = snd_gus_create(card, + snd_gf1_port[dev], + gf1_irq, + gf1_dma, + -1, + 0, snd_channels[dev], + snd_pcm_channels[dev], 0, + &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusextreme_detect(dev, card, gus, es1688)) < 0) { + snd_card_free(card); + return err; + } + snd_gusextreme_init(dev, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->ess_flag) { + snd_card_free(card); + snd_printdd("GUS Extreme soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + if ((err = snd_es1688_pcm(es1688, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1688_mixer(es1688)) < 0) { + snd_card_free(card); + return err; + } + snd_component_add(card, "ES1688"); + if (snd_pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_gf1_new_mixer(gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusextreme_mixer(es1688)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, es1688->port, es1688->port + 2, + OPL3_HW_OPL3, 0, &opl3) < 0) { + printk(KERN_ERR "gusextreme: opl3 not detected at 0x%lx\n", es1688->port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (es1688->mpu_port >= 0x300) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, + es1688->mpu_port, 0, + mpu_irq, + SA_INTERRUPT, + NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, irq %i&%i, dma %i&%i", + es1688->port, gf1_irq, ess_irq, gf1_dma, ess_dma); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_gusextreme_cards[dev] = card; + return 0; +} + +static int __init snd_gusextreme_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_gusextreme_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusextreme_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusextreme_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "GUS Extreme soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusextreme_exit(void) +{ + int idx; + snd_card_t *card; + struct snd_gusextreme *acard; + + for (idx = 0; idx < SNDRV_CARDS; idx++) { + card = snd_gusextreme_cards[idx]; + if (card == NULL) + continue; + acard = (struct snd_gusextreme *)card->private_data; + snd_card_free(snd_gusextreme_cards[idx]); + } +} + +module_init(alsa_card_gusextreme_init) +module_exit(alsa_card_gusextreme_exit) + +#ifndef MODULE + +/* format is: snd-gusextreme=snd_enable,snd_index,snd_id, + snd_port,snd_gf1_port,snd_mpu_port, + snd_irq,snd_gf1_irq,snd_mpu_irq, + snd_dma8,snd_dma1, + snd_joystick_dac, + snd_channels,snd_pcm_channels */ + +static int __init alsa_card_gusextreme_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_gf1_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_gf1_irq[nr_dev]) == 2 && + get_option(&str,&snd_mpu_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusextreme=", alsa_card_gusextreme_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/gus/gusmax.c linux-2.4.19-pre5-mjc/sound/isa/gus/gusmax.c --- linux/sound/isa/gus/gusmax.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/gusmax.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,438 @@ +/* + * Driver for Gravis UltraSound MAX soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Gravis UltraSound MAX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Gravis,UltraSound MAX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for GUS MAX soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for GUS MAX soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable GUS MAX soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x230},{0x240},{0x250},{0x260}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_channels, "Used GF1 channels for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}"); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS MAX driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); + +struct snd_gusmax { + int irq; + snd_card_t *card; + snd_gus_card_t *gus; + cs4231_t *cs4231; + unsigned short gus_status_reg; + unsigned short pcm_status_reg; +}; + +static snd_card_t *snd_gusmax_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + + +static int __init snd_gusmax_detect(snd_gus_card_t * gus) +{ + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + unsigned char d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + return 0; +} + +static void snd_gusmax_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct snd_gusmax *maxcard = (struct snd_gusmax *) dev_id; + int loop, max = 5; + + do { + loop = 0; + if (inb(maxcard->gus_status_reg)) { + snd_gus_interrupt(irq, maxcard->gus, regs); + loop++; + } + if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ + snd_cs4231_interrupt(irq, maxcard->cs4231, regs); + loop++; + } + } while (loop && --max > 0); +} + +static void __init snd_gusmax_init(int dev, snd_card_t * card, snd_gus_card_t * gus) +{ + gus->equal_irq = 1; + gus->codec_flag = 1; + gus->joystick_dac = snd_joystick_dac[dev]; + /* init control register */ + gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f; + if (gus->gf1.dma1 > 3) + gus->max_cntrl_val |= 0x10; + if (gus->gf1.dma2 > 3) + gus->max_cntrl_val |= 0x20; + gus->max_cntrl_val |= 0x40; + outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT)); +} + +#define CS4231_PRIVATE( left, right, shift, mute ) \ + ((left << 24)|(right << 16)|(shift<<8)|mute) + +static int __init snd_gusmax_mixer(cs4231_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUXA to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUXB to CD */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; +#if 0 + /* reassign Mono Input to MIC */ + if (snd_mixer_group_rename(mixer, + SNDRV_MIXER_IN_MONO, 0, + SNDRV_MIXER_IN_MIC, 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT, + SNDRV_MIXER_IN_MIC, 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + "Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1, + "Mic Capture Volume", 0) < 0) + goto __error; + if (snd_mixer_elem_rename(mixer, + "Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1, + "Mic Capture Switch", 0) < 0) + goto __error; +#endif + return 0; +} + +static void snd_gusmax_free(snd_card_t *card) +{ + struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data; + + if (maxcard == NULL) + return; + if (maxcard->irq >= 0) + free_irq(maxcard->irq, (void *)maxcard); +} + +static int __init snd_gusmax_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; + int irq, dma1, dma2, err; + snd_card_t *card; + snd_gus_card_t *gus = NULL; + cs4231_t *cs4231; + struct snd_gusmax *maxcard; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_gusmax)); + if (card == NULL) + return -ENOMEM; + card->private_free = snd_gusmax_free; + maxcard = (struct snd_gusmax *)card->private_data; + maxcard->card = card; + maxcard->irq = -1; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_gus_create(card, + snd_port[dev], + -irq, dma1, dma2, + 0, snd_channels[dev], + snd_pcm_channels[dev], + 0, &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_gusmax_detect(gus)) < 0) { + snd_card_free(card); + return err; + } + maxcard->gus_status_reg = gus->gf1.reg_irqstat; + maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; + snd_gusmax_init(dev, card, gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + if (!gus->max_flag) { + snd_card_free(card); + printk(KERN_ERR "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); + return -ENODEV; + } + + if (request_irq(irq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) { + snd_card_free(card); + printk(KERN_ERR "gusmax: unable to grab IRQ %d\n", irq); + return -EBUSY; + } + maxcard->irq = irq; + + if ((err = snd_cs4231_create(card, + gus->gf1.port + 0x10c, -1, irq, + dma2 < 0 ? dma1 : dma2, dma1, + CS4231_HW_DETECT, + CS4231_HWSHARE_IRQ | + CS4231_HWSHARE_DMA1 | + CS4231_HWSHARE_DMA2, + &cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (snd_pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_gusmax_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, irq, dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%i", dma2); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + maxcard->gus = gus; + maxcard->cs4231 = cs4231; + snd_gusmax_cards[dev] = card; + return 0; +} + +static int __init snd_gusmax_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_gusmax_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_gusmax_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_gusmax_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "GUS MAX soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_gusmax_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_gusmax_cards[idx]); +} + +module_init(alsa_card_gusmax_init) +module_exit(alsa_card_gusmax_exit) + +#ifndef MODULE + +/* format is: snd-gusmax=snd_enable,snd_index,snd_id, + snd_port,snd_irq, + snd_dma1,snd_dma2, + snd_joystick_dac, + snd_channels,snd_pcm_channels */ + +static int __init alsa_card_gusmax_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_joystick_dac[nr_dev]) == 2 && + get_option(&str,&snd_channels[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-gusmax=", alsa_card_gusmax_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/gus/interwave-stb.c linux-2.4.19-pre5-mjc/sound/isa/gus/interwave-stb.c --- linux/sound/isa/gus/interwave-stb.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/interwave-stb.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,2 @@ +#define SNDRV_STB +#include "interwave.c" diff -Nru linux/sound/isa/gus/interwave.c linux-2.4.19-pre5-mjc/sound/isa/gus/interwave.c --- linux/sound/isa/gus/interwave.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/gus/interwave.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1025 @@ +/* + * Driver for AMD InterWave soundcard + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 1999/07/22 Erik Inge Bolso + * * mixer group handlers + * + */ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#ifdef SNDRV_STB +#include +#endif +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +#ifndef SNDRV_STB +MODULE_DESCRIPTION("AMD InterWave"); +MODULE_DEVICES("{{Gravis,UltraSound Plug & Play}," + "{STB,SoundRage32}," + "{MED,MED3210}," + "{Dynasonix,Dynasonix Pro}," + "{Panasonic,PCA761AW}}"); +#else +MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T"); +MODULE_DEVICES("{{AMD,InterWave STB with TEA6330T}}"); +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */ +#ifdef SNDRV_STB +static long snd_port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x350,0x360,0x370,0x380 */ +#endif +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; + /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ +static int snd_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; +static int snd_effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for InterWave soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for InterWave soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable InterWave soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x210,0x260,0x10}},dialog:list"); +#ifdef SNDRV_STB +MODULE_PARM(snd_port_tc, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_port_tc, SNDRV_ENABLED ",allows:{{0x350,0x380,0x10}},dialog:list"); +#endif +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for InterWave driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver."); +MODULE_PARM_SYNTAX(snd_joystic_dac, SNDRV_ENABLED ",allows:{{0,31}}"); +MODULE_PARM(snd_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_midi, "MIDI UART enable for InterWave driver."); +MODULE_PARM_SYNTAX(snd_midi, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for InterWave driver."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); +MODULE_PARM(snd_effect, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_effect, "Effects enable for InterWave driver."); +MODULE_PARM_SYNTAX(snd_effect, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); + +struct snd_interwave { + int irq; + snd_card_t *card; + snd_gus_card_t *gus; + cs4231_t *cs4231; +#ifdef SNDRV_STB + struct resource *i2c_res; +#endif + unsigned short gus_status_reg; + unsigned short pcm_status_reg; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#ifdef SNDRV_STB + struct isapnp_dev *devtc; +#endif +#endif +}; + +static snd_card_t *snd_interwave_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_interwave_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_interwave_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#define ISAPNP_INTERWAVE(_va, _vb, _vc, _device, _audio) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \ + } +#define ISAPNP_INTERWAVE_STB(_va, _vb, _vc, _device, _audio, _tone) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _tone), } \ + } + +static struct isapnp_card_id snd_interwave_pnpids[] __devinitdata = { +#ifndef SNDRV_STB + /* Gravis UltraSound Plug & Play */ + ISAPNP_INTERWAVE('G','R','V',0x0001,0x0000), + /* STB SoundRage32 */ + ISAPNP_INTERWAVE('S','T','B',0x011a,0x0010), + /* MED3210 */ + ISAPNP_INTERWAVE('D','X','P',0x3201,0x0010), + /* Dynasonic Pro */ + /* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */ + ISAPNP_INTERWAVE('C','D','C',0x1111,0x1112), + /* Panasonic PCA761AW Audio Card */ + ISAPNP_INTERWAVE('A','D','V',0x55ff,0x0010), +#else + /* InterWave STB with TEA6330T */ + ISAPNP_INTERWAVE_STB('A','D','V',0x550a,0x0010,0x0015), +#endif + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_interwave_pnpids); + +#endif /* __ISAPNP__ */ + + +#ifdef SNDRV_STB +static void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data) +{ + unsigned long port = bus->private_value; + +#if 0 + printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data); +#endif + outb((data << 1) | ctrl, port); + udelay(10); +} + +static int snd_interwave_i2c_getclockline(snd_i2c_bus_t *bus) +{ + unsigned long port = bus->private_value; + unsigned char res; + + res = inb(port) & 1; +#if 0 + printk("i2c_getclockline - 0x%lx -> %i\n", port, res); +#endif + return res; +} + +static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus, int ack) +{ + unsigned long port = bus->private_value; + unsigned char res; + + if (ack) + udelay(10); + res = (inb(port) & 2) >> 1; +#if 0 + printk("i2c_getdataline - 0x%lx -> %i\n", port, res); +#endif + return res; +} + +static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = { + setlines: snd_interwave_i2c_setlines, + getclock: snd_interwave_i2c_getclockline, + getdata: snd_interwave_i2c_getdataline, +}; + +static int __init snd_interwave_detect_stb(struct snd_interwave *iwcard, + snd_gus_card_t * gus, int dev, + snd_i2c_bus_t **rbus) +{ + unsigned long port; + snd_i2c_bus_t *bus; + snd_card_t *card = iwcard->card; + char name[32]; + int err; + + *rbus = NULL; + port = snd_port_tc[dev]; + if (port == SNDRV_AUTO_PORT) { + port = 0x350; + if (gus->gf1.port == 0x250) { + port = 0x360; + } + while (port <= 0x380) { + if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) + break; + port += 0x10; + } + if (port > 0x380) + return -ENODEV; + } else { + if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) + return -ENODEV; + } + sprintf(name, "InterWave-%i", card->number); + if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0) + return err; + bus->private_value = port; + bus->hw_ops.bit = &snd_interwave_i2c_bit_ops; + if ((err = snd_tea6330t_detect(bus, 0)) < 0) + return err; + *rbus = bus; + return 0; +} +#endif + +static int __init snd_interwave_detect(struct snd_interwave *iwcard, + snd_gus_card_t * gus, + int dev +#ifdef SNDRV_STB + , snd_i2c_bus_t **rbus +#endif + ) +{ + unsigned long flags; + unsigned char rev1, rev2; + + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ +#ifdef CONFIG_SND_DEBUG_DETECT + { + int d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { + snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) + return -ENODEV; +#endif + udelay(160); + snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ + udelay(160); +#ifdef CONFIG_SND_DEBUG_DETECT + { + int d; + + if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { + snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); + return -ENODEV; + } + } +#else + if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) + return -ENODEV; +#endif + + spin_lock_irqsave(&gus->reg_lock, flags); + rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1); + rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1); + spin_unlock_irqrestore(&gus->reg_lock, flags); + snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2); + if ((rev1 & 0xf0) == (rev2 & 0xf0) && + (rev1 & 0x0f) != (rev2 & 0x0f)) { + snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port); + gus->interwave = 1; + strcpy(gus->card->shortname, "AMD InterWave"); + gus->revision = rev1 >> 4; +#ifndef SNDRV_STB + return 0; /* ok.. We have an InterWave board */ +#else + return snd_interwave_detect_stb(iwcard, gus, dev, rbus); +#endif + } + snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port); + return -ENODEV; +} + +static void snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct snd_interwave *iwcard = (struct snd_interwave *) dev_id; + int loop, max = 5; + + do { + loop = 0; + if (inb(iwcard->gus_status_reg)) { + snd_gus_interrupt(irq, iwcard->gus, regs); + loop++; + } + if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ + snd_cs4231_interrupt(irq, iwcard->cs4231, regs); + loop++; + } + } while (loop && --max > 0); +} + +static void __init snd_interwave_reset(snd_gus_card_t * gus) +{ + snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00); + udelay(160); + snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01); + udelay(160); +} + +static void __init snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes) +{ + unsigned int idx; + unsigned int local; + unsigned char d; + + for (idx = 0; idx < 4; idx++) { + sizes[idx] = 0; + d = 0x55; + for (local = idx << 22; + local < (idx << 22) + 0x400000; + local += 0x40000, d++) { + snd_gf1_poke(gus, local, d); + snd_gf1_poke(gus, local + 1, d + 1); +#if 0 + printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n", + d, + snd_gf1_peek(gus, local), + snd_gf1_peek(gus, local + 1), + snd_gf1_peek(gus, idx << 22)); +#endif + if (snd_gf1_peek(gus, local) != d || + snd_gf1_peek(gus, local + 1) != d + 1 || + snd_gf1_peek(gus, idx << 22) != 0x55) + break; + sizes[idx]++; + } + } +#if 0 + printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]); +#endif +} + +struct rom_hdr { + /* 000 */ unsigned char iwave[8]; + /* 008 */ unsigned char rom_hdr_revision; + /* 009 */ unsigned char series_number; + /* 010 */ unsigned char series_name[16]; + /* 026 */ unsigned char date[10]; + /* 036 */ unsigned short vendor_revision_major; + /* 038 */ unsigned short vendor_revision_minor; + /* 040 */ unsigned int rom_size; + /* 044 */ unsigned char copyright[128]; + /* 172 */ unsigned char vendor_name[64]; + /* 236 */ unsigned char rom_description[128]; + /* 364 */ unsigned char pad[147]; + /* 511 */ unsigned char csum; +}; + +static void __init snd_interwave_detect_memory(snd_gus_card_t * gus) +{ + static unsigned int lmc[13] = + { + 0x00000001, 0x00000101, 0x01010101, 0x00000401, + 0x04040401, 0x00040101, 0x04040101, 0x00000004, + 0x00000404, 0x04040404, 0x00000010, 0x00001010, + 0x10101010 + }; + + int bank_pos, pages; + unsigned int i, lmct; + int psizes[4]; + unsigned char csum; + struct rom_hdr romh; + + snd_interwave_reset(gus); + snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); /* DRAM I/O cycles selected */ + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c); + /* ok.. simple test of memory size */ + pages = 0; + snd_gf1_poke(gus, 0, 0x55); + snd_gf1_poke(gus, 1, 0xaa); +#if 1 + if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa) +#else + if (0) /* ok.. for testing of 0k RAM */ +#endif + { + snd_interwave_bank_sizes(gus, psizes); + lmct = (psizes[3] << 24) | (psizes[2] << 16) | + (psizes[1] << 8) | psizes[0]; +#if 0 + printk("lmct = 0x%08x\n", lmct); +#endif + for (i = 0; i < sizeof(lmc) / sizeof(unsigned int); i++) + if (lmct == lmc[i]) { +#if 0 + printk("found !!! %i\n", i); +#endif + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); + snd_interwave_bank_sizes(gus, psizes); + break; + } + if (i >= sizeof(lmc) / sizeof(unsigned int) && !gus->gf1.enh_mode) + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); + for (i = 0; i < 4; i++) { + gus->gf1.mem_alloc.banks_8[i].address = + gus->gf1.mem_alloc.banks_16[i].address = i << 22; + gus->gf1.mem_alloc.banks_8[i].size = + gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18; + pages += psizes[i]; + } + } + pages <<= 18; + gus->gf1.memory = pages; + + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03); /* select ROM */ + snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5)); + gus->gf1.rom_banks = 0; + gus->gf1.rom_memory = 0; + for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { + for (i = 0; i < sizeof(struct rom_hdr); i++) + *(((unsigned char *) &romh) + i) = snd_gf1_peek(gus, i + bank_pos); +#ifdef CONFIG_SND_DEBUG_ROM + printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos, + romh.iwave[0], romh.iwave[1], romh.iwave[2], romh.iwave[3], + romh.iwave[4], romh.iwave[5], romh.iwave[6], romh.iwave[7]); +#endif + if (strncmp(romh.iwave, "INTRWAVE", 8)) + continue; /* first check */ + csum = 0; + for (i = 0; i < sizeof(struct rom_hdr) - 1; i++) + csum += *(((unsigned char *) &romh) + i); +#ifdef CONFIG_SND_DEBUG_ROM + printk("ROM checksum = 0x%x == 0x%x (computed)\n", romh.csum, (unsigned char) (256 - csum)); +#endif + if (256 - csum != romh.csum) + continue; /* not valid rom */ + gus->gf1.rom_banks++; + gus->gf1.rom_present |= 1 << (bank_pos >> 22); +#ifdef SNDRV_LITTLE_ENDIAN + gus->gf1.rom_memory = romh.rom_size; +#else + gus->gf1.rom_memory = ((romh.rom_size >> 24) & 0x000000ff) | + ((romh.rom_size >> 8) & 0x0000ff00) | + ((romh.rom_size << 8) & 0x00ff0000) | + ((romh.rom_size << 24) & 0xff000000); +#endif + } +#if 0 + if (gus->gf1.rom_memory > 0) { + if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) + gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC; + } +#endif + snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00); /* select RAM */ + + if (!gus->gf1.enh_mode) + snd_interwave_reset(gus); +} + +static void __init snd_interwave_init(int dev, snd_gus_card_t * gus) +{ + unsigned long flags; + + /* ok.. some InterWave specific initialization */ + spin_lock_irqsave(&gus->reg_lock, flags); + snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); + snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); + snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); + snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); + snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); + spin_unlock_irqrestore(&gus->reg_lock, flags); + gus->equal_irq = 1; + gus->codec_flag = 1; + gus->interwave = 1; + gus->max_flag = 1; + gus->joystick_dac = snd_joystick_dac[dev]; + +} + +#define INTERWAVE_CONTROLS (sizeof(snd_interwave_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_interwave_controls[] = { +CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), +CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1) +}; + +static int __init snd_interwave_mixer(cs4231_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int idx, err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; +#if 0 + /* remove mono microphone controls */ + strcpy(id1.name, "Mic Playback Switch"); + if ((err = snd_ctl_remove_id(card, &id1)) < 0) + return err; + strcpy(id1.name, "Mic Playback Volume"); + if ((err = snd_ctl_remove_id(card, &id1)) < 0) + return err; +#endif + /* add new master and mic controls */ + for (idx = 0; idx < INTERWAVE_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0) + return err; + snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); + snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); + snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); + snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); + /* reassign AUXA to SYNTHESIZER */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Synth Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Synth Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUXB to CD */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + return 0; +} + +#ifdef __ISAPNP__ + +static int __init snd_interwave_isapnp(int dev, struct snd_interwave *iwcard) +{ + const struct isapnp_card_id *id = snd_interwave_isapnp_id[dev]; + struct isapnp_card *card = snd_interwave_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + iwcard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (iwcard->dev->active) { + iwcard->dev = NULL; + return -EBUSY; + } +#ifdef SNDRV_STB + iwcard->devtc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (iwcard->devtc->active) { + iwcard->dev = iwcard->devtc = NULL; + return -EBUSY; + } +#endif + /* Synth & Codec initialization */ + pdev = iwcard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + if (snd_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + isapnp_resource_change(&pdev->resource[1], snd_port[dev] + 0x100, 12); + isapnp_resource_change(&pdev->resource[2], snd_port[dev] + 0x10c, 4); + } + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_dma2[dev] < 0) + isapnp_resource_change(&pdev->dma_resource[1], 4, 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + if (pdev->resource[0].start + 0x100 != pdev->resource[1].start || + pdev->resource[0].start + 0x10c != pdev->resource[2].start) { + snd_printk("isapnp configure failure (wrong ports)\n"); + pdev->deactivate(pdev); + return -ENOENT; + } + snd_port[dev] = pdev->resource[0].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + if (snd_dma2[dev] >= 0) + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n", + pdev->resource[0].start, + pdev->resource[1].start, + pdev->resource[2].start); + snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]); +#ifdef SNDRV_STB + /* Tone Control initialization */ + pdev = iwcard->devtc; + if (pdev->prepare(pdev)<0) { + iwcard->dev->deactivate(iwcard->dev); + return -EAGAIN; + } + if (snd_port_tc[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port_tc[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("Tone Control isapnp configure failure (out of resources?)\n"); + iwcard->dev->deactivate(iwcard->dev); + return -EBUSY; + } + snd_port_tc[dev] = pdev->resource[0].start; + snd_printdd("isapnp IW: tone control port=0x%lx\n", snd_port_tc[dev]); +#endif + return 0; +} + +static void snd_interwave_deactivate(struct snd_interwave *iwcard) +{ + if (iwcard->dev) { + iwcard->dev->deactivate(iwcard->dev); + iwcard->dev = NULL; + } +#ifdef SNDRV_STB + if (iwcard->devtc) { + iwcard->devtc->deactivate(iwcard->devtc); + iwcard->devtc = NULL; + } +#endif +} + +#endif /* __ISAPNP__ */ + +static void snd_interwave_free(snd_card_t *card) +{ + struct snd_interwave *iwcard = (struct snd_interwave *)card->private_data; + + if (iwcard == NULL) + return; +#ifdef __ISAPNP__ + snd_interwave_deactivate(iwcard); +#endif +#ifdef SNDRV_STB + if (iwcard->i2c_res) { + release_resource(iwcard->i2c_res); + kfree_nocheck(iwcard->i2c_res); + } +#endif + if (iwcard->irq >= 0) + free_irq(iwcard->irq, (void *)iwcard); +} + +static int __init snd_interwave_probe(int dev) +{ + static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; + static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1}; + int irq, dma1, dma2; + snd_card_t *card; + struct snd_interwave *iwcard; + cs4231_t *cs4231; + snd_gus_card_t *gus; +#ifdef SNDRV_STB + snd_i2c_bus_t *i2c_bus; +#endif + snd_pcm_t *pcm; + char *str; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_interwave)); + if (card == NULL) + return -ENOMEM; + iwcard = (struct snd_interwave *)card->private_data; + iwcard->card = card; + iwcard->irq = -1; + card->private_free = snd_interwave_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && snd_interwave_isapnp(dev, iwcard)) { + snd_card_free(card); + return -ENODEV; + } +#endif + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } + dma2 = snd_dma2[dev]; + if (dma2 == SNDRV_AUTO_DMA) { + if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } + + if ((err = snd_gus_create(card, + snd_port[dev], + -irq, dma1, dma2, + 0, 32, + snd_pcm_channels[dev], snd_effect[dev], &gus)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_interwave_detect(iwcard, gus, dev +#ifdef SNDRV_STB + , &i2c_bus +#endif + )) < 0) { + snd_card_free(card); + return err; + } + iwcard->gus_status_reg = gus->gf1.reg_irqstat; + iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; + + snd_interwave_init(dev, gus); + snd_interwave_detect_memory(gus); + if ((err = snd_gus_initialize(gus)) < 0) { + snd_card_free(card); + return err; + } + + if (request_irq(irq, snd_interwave_interrupt, SA_INTERRUPT, "InterWave", (void *)iwcard)) { + snd_card_free(card); + snd_printk("unable to grab IRQ %d\n", irq); + return -EBUSY; + } + iwcard->irq = irq; + + if ((err = snd_cs4231_create(card, + gus->gf1.port + 0x10c, -1, irq, + dma2 < 0 ? dma1 : dma2, dma1, + CS4231_HW_INTERWAVE, + CS4231_HWSHARE_IRQ | + CS4231_HWSHARE_DMA1 | + CS4231_HWSHARE_DMA2, + &cs4231)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); + strcat(pcm->name, " (chip)"); + if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } + if (snd_pcm_channels[dev] > 0) { + if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_interwave_mixer(cs4231)) < 0) { + snd_card_free(card); + return err; + } +#ifdef SNDRV_STB + { + snd_ctl_elem_id_t id1, id2; + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id1.name, "Master Playback Switch"); + strcpy(id2.name, id1.name); + id2.index = 1; + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { + snd_card_free(card); + return err; + } + strcpy(id1.name, "Master Playback Volume"); + strcpy(id2.name, id1.name); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0) { + snd_card_free(card); + return err; + } + } +#endif + + gus->uart_enable = snd_midi[dev]; + if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + +#ifndef SNDRV_STB + str = "AMD InterWave"; + if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) + str = "Dynasonic 3-D"; +#else + str = "InterWave STB"; +#endif + strcpy(card->driver, str); + strcpy(card->shortname, str); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", + str, + gus->gf1.port, + irq, + dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + iwcard->cs4231 = cs4231; + iwcard->gus = gus; + snd_interwave_cards[dev++] = card; + return 0; +} + +static int __init snd_interwave_probe_legacy_port(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + snd_port[dev] = port; + res = snd_interwave_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +#ifdef __ISAPNP__ + +static int __init snd_interwave_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_interwave_isapnp_cards[dev] = card; + snd_interwave_isapnp_id[dev] = id; + res = snd_interwave_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_interwave_init(void) +{ + int cards = 0; + static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, -1}; + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (!snd_interwave_probe(dev)) { + cards++; + continue; + } +#ifdef MODULE + printk(KERN_ERR "InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); +#endif + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_interwave_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards */ + cards += isapnp_probe_cards(snd_interwave_pnpids, snd_interwave_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "InterWave soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_interwave_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_interwave_cards[dev]); +} + +module_init(alsa_card_interwave_init) +module_exit(alsa_card_interwave_exit) + +#ifndef MODULE + +/* format is: snd-interwave=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port[,snd_port_tc],snd_irq, + snd_dma1,snd_dma2, + snd_joystick_dac,snd_midi, + snd_pcm_channels,snd_effect */ + +static int __init alsa_card_interwave_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && +#ifdef SNDRV_STB + get_option(&str,(int *)&snd_port_tc[nr_dev]) == 2 && +#endif + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_joystick_dac[nr_dev]) == 2 && + get_option(&str,&snd_midi[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2 && + get_option(&str,&snd_effect[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +#ifndef SNDRV_STB +__setup("snd-interwave=", alsa_card_interwave_setup); +#else +__setup("snd-interwave-stb=", alsa_card_interwave_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/opl3sa2.c linux-2.4.19-pre5-mjc/sound/isa/opl3sa2.c --- linux/sound/isa/opl3sa2.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/opl3sa2.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,951 @@ +/* + * Driver for Yamaha OPL3-SA[2,3] soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Yamaha OPL3SA2+"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Yamaha,YMF719E-S}," + "{Genius,Sound Maker 3DX}," + "{Yamaha,OPL3SA3}," + "{Intel,AL440LX sound}," + "{NeoMagic,MagicWave 3DX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0xf86,0x370,0x100 */ +static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x530,0xe80,0xf40,0x604 */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x388 */ +static long snd_midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x330,0x300 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 0,1,3,5,9,11,12,15 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ +static int snd_opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* 0,1,2,3 */ /*SL Added*/ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable OPL3-SA soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0xf86},{0x370},{0x100}},dialog:list"); +MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sb_port, "SB port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260}},dialog:list"); +MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wss_port, "WSS port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388}},dialog:list"); +MODULE_PARM(snd_midi_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_midi_port, "MIDI port # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_midi_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{0},{1},{3},{5},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for OPL3-SA driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); +MODULE_PARM(snd_opl3sa3_ymode, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); /* SL Added */ +MODULE_PARM_DESC(snd_opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi."); +MODULE_PARM_SYNTAX(snd_opl3sa3_ymode, SNDRV_ENABLED ",allows:{{0,3}},dialog:list"); /* SL Added */ + +/* control ports */ +#define OPL3SA2_PM_CTRL 0x01 +#define OPL3SA2_SYS_CTRL 0x02 +#define OPL3SA2_IRQ_CONFIG 0x03 +#define OPL3SA2_IRQ_STATUS 0x04 +#define OPL3SA2_DMA_CONFIG 0x06 +#define OPL3SA2_MASTER_LEFT 0x07 +#define OPL3SA2_MASTER_RIGHT 0x08 +#define OPL3SA2_MIC 0x09 +#define OPL3SA2_MISC 0x0A + +/* opl3sa3 only */ +#define OPL3SA3_DGTL_DOWN 0x12 +#define OPL3SA3_ANLG_DOWN 0x13 +#define OPL3SA3_WIDE 0x14 +#define OPL3SA3_BASS 0x15 +#define OPL3SA3_TREBLE 0x16 + +/* power management bits */ +#define OPL3SA2_PM_ADOWN 0x20 +#define OPL3SA2_PM_PSV 0x04 +#define OPL3SA2_PM_PDN 0x02 +#define OPL3SA2_PM_PDX 0x01 + +#define OPL3SA2_PM_D0 0x00 +#define OPL3SA2_PM_D3 (OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX) + +typedef struct snd_opl3sa2 opl3sa2_t; +#define chip_t opl3sa2_t + +struct snd_opl3sa2 { + snd_card_t *card; + int version; /* 2 or 3 */ + unsigned long port; /* control port */ + struct resource *res_port; /* control port resource */ + int irq; + int single_dma; + spinlock_t reg_lock; + snd_hwdep_t *synth; + snd_rawmidi_t *rmidi; + cs4231_t *cs4231; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#endif + unsigned char ctlregs[0x20]; + int ymode; /* SL added */ + snd_kcontrol_t *master_switch; + snd_kcontrol_t *master_volume; +#ifdef CONFIG_PM + struct pm_dev *pm_dev; + void (*cs4231_suspend)(cs4231_t *); + void (*cs4231_resume)(cs4231_t *); +#endif +}; + +static snd_card_t *snd_opl3sa2_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_opl3sa2_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_opl3sa2_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +#define ISAPNP_OPL3SA2(_va, _vb, _vc, _device, _function) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _function), } \ + } + +static struct isapnp_card_id snd_opl3sa2_pnpids[] __devinitdata = { + /* Yamaha YMF719E-S (Genius Sound Maker 3DX) */ + ISAPNP_OPL3SA2('Y','M','H',0x0020,0x0021), + /* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */ + ISAPNP_OPL3SA2('Y','M','H',0x0030,0x0021), + /* Yamaha OPL3-SA2 */ + ISAPNP_OPL3SA2('Y','M','H',0x0800,0x0021), + /* NeoMagic MagicWave 3DX */ + ISAPNP_OPL3SA2('N','M','X',0x2200,0x2210), + /* --- */ + { ISAPNP_CARD_END, } /* end */ +}; + +ISAPNP_CARD_TABLE(snd_opl3sa2_pnpids); + +#endif /* __ISAPNP__ */ + + +/* read control port (w/o spinlock) */ +static unsigned char __snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) +{ + unsigned char result; +#if 0 + outb(0x1d, port); /* password */ + printk("read [0x%lx] = 0x%x\n", port, inb(port)); +#endif + outb(reg, chip->port); /* register */ + result = inb(chip->port + 1); +#if 0 + printk("read [0x%lx] = 0x%x [0x%x]\n", port, result, inb(port)); +#endif + return result; +} + +/* read control port (with spinlock) */ +static unsigned char snd_opl3sa2_read(opl3sa2_t *chip, unsigned char reg) +{ + unsigned long flags; + unsigned char result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = __snd_opl3sa2_read(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* write control port (w/o spinlock) */ +static void __snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) +{ +#if 0 + outb(0x1d, port); /* password */ +#endif + outb(reg, chip->port); /* register */ + outb(value, chip->port + 1); + chip->ctlregs[reg] = value; +} + +/* write control port (with spinlock) */ +static void snd_opl3sa2_write(opl3sa2_t *chip, unsigned char reg, unsigned char value) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __snd_opl3sa2_write(chip, reg, value); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int __init snd_opl3sa2_detect(opl3sa2_t *chip) +{ + snd_card_t *card; + unsigned long port; + unsigned char tmp, tmp1; + char str[2]; + + card = chip->card; + port = chip->port; + if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) + return -EBUSY; + // snd_printk("REG 0A = 0x%x\n", snd_opl3sa2_read(chip, 0x0a)); + chip->version = 0; + tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC); + if (tmp == 0xff) { + snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp); + return -ENODEV; + } + switch (tmp & 0x07) { + case 0x01: + chip->version = 2; /* YMF711 */ + break; + default: + chip->version = 3; + /* 0x02 - standard */ + /* 0x03 - YM715B */ + /* 0x04 - YM719 - OPL-SA4? */ + /* 0x05 - OPL3-SA3 - Libretto 100 */ + break; + } + str[0] = chip->version + '0'; + str[1] = 0; + strcat(card->shortname, str); + snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7); + if ((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC)) != tmp) { + snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1); + return -ENODEV; + } + /* try if the MIC register is accesible */ + tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC); + snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a); + if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) { + snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1); + return -ENODEV; + } + snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f); + /* initialization */ + /* Power Management - full on */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); + if (chip->version > 2) { + /* ymode is bits 4&5 (of 0 to 7) on all but opl3sa2 versions */ + snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, (chip->ymode << 4)); + } else { + /* default for opl3sa2 versions */ + snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, 0x00); + } + snd_opl3sa2_write(chip, OPL3SA2_IRQ_CONFIG, 0x0d); /* Interrupt Channel Configuration - IRQ A = OPL3 + MPU + WSS */ + if (chip->single_dma) { + snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x03); /* DMA Configuration - DMA A = WSS-R + WSS-P */ + } else { + snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x21); /* DMA Configuration - DMA B = WSS-R, DMA A = WSS-P */ + } + snd_opl3sa2_write(chip, OPL3SA2_MISC, 0x80 | (tmp & 7)); /* Miscellaneous - default */ + if (chip->version > 2) { + snd_opl3sa2_write(chip, OPL3SA3_DGTL_DOWN, 0x00); /* Digital Block Partial Power Down - default */ + snd_opl3sa2_write(chip, OPL3SA3_ANLG_DOWN, 0x00); /* Analog Block Partial Power Down - default */ + } + return 0; +} + +static void snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned short status; + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev_id, return); + + if (chip == NULL || chip->card == NULL) + return; + + status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS); + + if (status & 0x20) + snd_opl3_interrupt(chip->synth); + + if ((status & 0x10) && chip->rmidi != NULL) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + + if (status & 0x07) /* TI,CI,PI */ + snd_cs4231_interrupt(irq, chip->cs4231, regs); + + if (status & 0x40) { /* hardware volume change */ + /* reading from Master Lch register at 0x07 clears this bit */ + snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT); + snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT); + if (chip->master_switch && chip->master_volume) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + } +} + +#define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opl3sa2_info_single, \ + get: snd_opl3sa2_get_single, put: snd_opl3sa2_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_opl3sa2_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_opl3sa2_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +int snd_opl3sa2_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val, oval; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = chip->ctlregs[reg]; + val = (oval & ~(mask << shift)) | val; + change = val != oval; + __snd_opl3sa2_write(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opl3sa2_info_double, \ + get: snd_opl3sa2_get_double, put: snd_opl3sa2_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +int snd_opl3sa2_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +int snd_opl3sa2_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +int snd_opl3sa2_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + if (left_reg != right_reg) { + oval1 = chip->ctlregs[left_reg]; + oval2 = chip->ctlregs[right_reg]; + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + __snd_opl3sa2_write(chip, left_reg, val1); + __snd_opl3sa2_write(chip, right_reg, val2); + } else { + oval1 = chip->ctlregs[left_reg]; + val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval1; + __snd_opl3sa2_write(chip, left_reg, val1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define OPL3SA2_CONTROLS (sizeof(snd_opl3sa2_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opl3sa2_controls[] = { +OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), +OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1), +OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1), +OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1) +}; + +#define OPL3SA2_TONE_CONTROLS (sizeof(snd_opl3sa2_tone_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opl3sa2_tone_controls[] = { +OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0), +OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0), +OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0) +}; + +static void snd_opl3sa2_master_free(snd_kcontrol_t *kcontrol) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_switch = NULL; + chip->master_volume = NULL; +} + +static int __init snd_opl3sa2_mixer(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + snd_kcontrol_t *kctl; + int idx, err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX0 to CD */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "CD Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "CD Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUX1 to FM */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "FM Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "FM Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* add OPL3SA2 controls */ + for (idx = 0; idx < OPL3SA2_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0) + return err; + switch (idx) { + case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break; + case 1: chip->master_volume = kctl; kctl->private_free = snd_opl3sa2_master_free; break; + } + } + if (chip->version > 2) { + for (idx = 0; idx < OPL3SA2_TONE_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +/* Power Management support functions */ +#ifdef CONFIG_PM +static void snd_opl3sa2_suspend(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + /* FIXME: is this order ok? */ + chip->cs4231_suspend(chip->cs4231); + snd_pcm_suspend_all(chip->cs4231->pcm); + + /* power down */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3); + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void snd_opl3sa2_resume(opl3sa2_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* power up */ + snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); + + /* restore registers */ + for (i = 2; i <= 0x0a; i++) { + if (i != OPL3SA2_IRQ_STATUS) + snd_opl3sa2_write(chip, i, chip->ctlregs[i]); + } + if (chip->version > 2) { + for (i = 0x12; i <= 0x16; i++) + snd_opl3sa2_write(chip, i, chip->ctlregs[i]); + } + /* restore cs4231 */ + chip->cs4231_resume(chip->cs4231); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +/* callback for control API */ +static int snd_opl3sa2_set_power_state(snd_card_t *card, unsigned int power_state) +{ + opl3sa2_t *chip = (opl3sa2_t *) card->power_state_private_data; + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_opl3sa2_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_opl3sa2_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_opl3sa2_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev->data, return 0); + + switch (rqst) { + case PM_SUSPEND: + snd_opl3sa2_suspend(chip); + break; + case PM_RESUME: + snd_opl3sa2_resume(chip); + break; + } + return 0; +} + +#endif /* CONFIG_PM */ + +#ifdef __ISAPNP__ +static int __init snd_opl3sa2_isapnp(int dev, opl3sa2_t *chip) +{ + const struct isapnp_card_id *id = snd_opl3sa2_isapnp_id[dev]; + struct isapnp_card *card = snd_opl3sa2_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + chip->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (chip->dev->active) { + chip->dev = NULL; + return -EBUSY; + } + /* PnP initialization */ + pdev = chip->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + if (snd_sb_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_sb_port[dev], 16); + if (snd_wss_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_wss_port[dev], 8); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4); + if (snd_midi_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[3], snd_midi_port[dev], 2); + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[4], snd_port[dev], 2); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev)<0) { + snd_printk("isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + snd_sb_port[dev] = pdev->resource[0].start; + snd_wss_port[dev] = pdev->resource[1].start; + snd_fm_port[dev] = pdev->resource[2].start; + snd_midi_port[dev] = pdev->resource[3].start; + snd_port[dev] = pdev->resource[4].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n", + snd_sb_port[dev], snd_wss_port[dev], snd_fm_port[dev], snd_midi_port[dev]); + snd_printdd("isapnp OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", + snd_port[dev], snd_dma1[dev], snd_dma2[dev], snd_irq[dev]); + return 0; +} + +static void snd_opl3sa2_deactivate(opl3sa2_t *chip) +{ + if (chip->dev) { + chip->dev->deactivate(chip->dev); + chip->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static int snd_opl3sa2_free(opl3sa2_t *chip) +{ +#ifdef __ISAPNP__ + snd_opl3sa2_deactivate(chip); +#endif +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_opl3sa2_dev_free(snd_device_t *device) +{ + opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, device->device_data, return -ENXIO); + return snd_opl3sa2_free(chip); +} + +static int __init snd_opl3sa2_probe(int dev) +{ + int irq, dma1, dma2; + snd_card_t *card; + struct snd_opl3sa2 *chip; + cs4231_t *cs4231; + opl3_t *opl3; + static snd_device_ops_t ops = { + dev_free: snd_opl3sa2_dev_free, + }; + int err; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_port\n"); + return -EINVAL; + } + if (snd_wss_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_wss_port\n"); + return -EINVAL; + } + if (snd_fm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_fm_port\n"); + return -EINVAL; + } + if (snd_midi_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify snd_midi_port\n"); + return -EINVAL; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + strcpy(card->driver, "OPL3SA2"); + strcpy(card->shortname, "Yamaha OPL3-SA2"); + chip = snd_magic_kcalloc(opl3sa2_t, 0, GFP_KERNEL); + if (chip == NULL) { + err = -ENOMEM; + goto __error; + } + chip->irq = -1; + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && (err = snd_opl3sa2_isapnp(dev, chip)) < 0) + goto __error; +#endif + chip->ymode = snd_opl3sa3_ymode[dev] & 0x03 ; /* initialise this card from supplied (or default) parameter*/ + chip->card = card; + chip->port = snd_port[dev]; + irq = snd_irq[dev]; + dma1 = snd_dma1[dev]; + dma2 = snd_dma2[dev]; + if (dma2 < 0) + chip->single_dma = 1; + if ((err = snd_opl3sa2_detect(chip)) < 0) + goto __error; + if (request_irq(irq, snd_opl3sa2_interrupt, SA_INTERRUPT, "OPL3-SA2/3", (void *)chip)) { + err = -ENODEV; + goto __error; + } + chip->irq = irq; + if ((err = snd_cs4231_create(card, + snd_wss_port[dev] + 4, -1, + irq, dma1, dma2, + CS4231_HW_OPL3SA2, + CS4231_HWSHARE_IRQ, + &cs4231)) < 0) { + snd_printd("Oops, WSS not detected at 0x%lx\n", snd_wss_port[dev] + 4); + goto __error; + } + chip->cs4231 = cs4231; + if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) + goto __error; + if ((err = snd_cs4231_mixer(cs4231)) < 0) + goto __error; + if ((err = snd_opl3sa2_mixer(chip)) < 0) + goto __error; + if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0) + goto __error; + if (snd_fm_port[dev] >= 0x340 && snd_fm_port[dev] < 0x400) { + if ((err = snd_opl3_create(card, snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_OPL3, 0, &opl3)) < 0) + goto __error; + if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0) + goto __error; + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth)) < 0) + goto __error; + } + if (snd_midi_port[dev] >= 0x300 && snd_midi_port[dev] < 0x340) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2, + snd_midi_port[dev], 0, + irq, 0, &chip->rmidi)) < 0) + goto __error; + } +#ifdef CONFIG_PM + /* Power Management */ + chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_opl3sa2_pm_callback); + if (chip->pm_dev) { + chip->pm_dev->data = chip; + /* remember callbacks for cs4231 - they are called inside + * opl3sa2 pm callback + */ + chip->cs4231_suspend = chip->cs4231->suspend; + chip->cs4231_resume = chip->cs4231->resume; + /* now clear callbacks for cs4231 */ + chip->cs4231->suspend = NULL; + chip->cs4231->resume = NULL; + /* set control api callback */ + card->set_power_state = snd_opl3sa2_set_power_state; + card->power_state_private_data = chip; + } +#endif + + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + card->shortname, chip->port, irq, dma1); + if (dma2 >= 0) + sprintf(card->longname + strlen(card->longname), "&%d", dma2); + + if ((err = snd_card_register(card)) < 0) + goto __error; + + snd_opl3sa2_cards[dev] = card; + return 0; + + __error: + snd_card_free(card); + return err; +} + +#ifdef __ISAPNP__ +static int __init snd_opl3sa2_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_opl3sa2_isapnp_cards[dev] = card; + snd_opl3sa2_isapnp_id[dev] = id; + res = snd_opl3sa2_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_opl3sa2_init(void) +{ + int dev, cards = 0; + + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_opl3sa2_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_opl3sa2_pnpids, snd_opl3sa2_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Yamaha OPL3-SA soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_opl3sa2_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_opl3sa2_cards[idx]); +} + +module_init(alsa_card_opl3sa2_init) +module_exit(alsa_card_opl3sa2_exit) + +#ifndef MODULE + +/* format is: snd-opl3sa2=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_sb_port,snd_wss_port,snd_fm_port, + snd_midi_port,snd_irq,snd_dma1,snd_dma2, + snd_opl3sa3_ymode */ + +static int __init alsa_card_opl3sa2_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_midi_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_opl3sa3_ymode[nr_dev]) == 2); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif + nr_dev++; + return 1; +} + +__setup("snd-opl3sa2=", alsa_card_opl3sa2_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/opti9xx/Makefile linux-2.4.19-pre5-mjc/sound/isa/opti9xx/Makefile --- linux/sound/isa/opti9xx/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,28 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _opti9xx.o + +list-multi := snd-opti92x-ad1848.o snd-opti92x-cs4231.o snd-opti93x.o + +snd-opti92x-ad1848-objs := opti92x-ad1848.o +snd-opti92x-cs4231-objs := opti92x-cs4231.o +snd-opti93x-objs := opti93x.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o +obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o +obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o + +include $(TOPDIR)/Rules.make + +snd-opti92x-ad1848.o: $(snd-opti92x-ad1848-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-ad1848-objs) + +snd-opti92x-cs4231.o: $(snd-opti92x-cs4231-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti92x-cs4231-objs) + +snd-opti93x.o: $(snd-opti93x-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-opti93x-objs) diff -Nru linux/sound/isa/opti9xx/opti92x-ad1848.c linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-ad1848.c --- linux/sound/isa/opti9xx/opti92x-ad1848.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-ad1848.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2218 @@ +/* + card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards. + Copyright (C) 1998-2000 by Massimo Piccioni + + Part of this code was developed at the Italian Ministry of Air Defence, + Sixth Division (oh, che pace ...), Rome. + + Thanks to Maria Grazia Pollarini, Salvatore Vassallo. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include +#include +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#ifdef CS4231 +#include +#else +#ifndef OPTi93X +#include +#else +#include +#include +#endif /* OPTi93X */ +#endif /* CS4231 */ +#include +#include +#define SNDRV_LEGACY_FIND_FREE_IOPORT +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +#ifdef OPTi93X +MODULE_DESCRIPTION("OPTi93X"); +MODULE_DEVICES("{{OPTi,82C931/3}}"); +#else /* OPTi93X */ +#ifdef CS4231 +MODULE_DESCRIPTION("OPTi92X - CS4231"); +MODULE_DEVICES("{{OPTi,82C924 (CS4231)}," + "{OPTi,82C925 (CS4231)}}"); +#else /* CS4231 */ +MODULE_DESCRIPTION("OPTi92X - AD1848"); +MODULE_DEVICES("{{OPTi,82C924 (AD1848)}," + "{OPTi,82C925 (AD1848)}," + "{OAK,Mozart}}"); +#endif /* CS4231 */ +#endif /* OPTi93X */ + +static int snd_index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *snd_id = SNDRV_DEFAULT_STR1; /* ID for this card */ +//static int snd_enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ +static int snd_isapnp = 1; /* Enable ISA PnP detection */ +static long snd_port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ +static long snd_mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ +static long snd_fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ +static int snd_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ +static int snd_mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ +static int snd_dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ +#if defined(CS4231) || defined(OPTi93X) +static int snd_dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ +#endif /* CS4231 || OPTi93X */ + +MODULE_PARM(snd_index, "i"); +MODULE_PARM_DESC(snd_index, "Index value for opti9xx based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "s"); +MODULE_PARM_DESC(snd_id, "ID string for opti9xx based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +//MODULE_PARM(snd_enable, "i"); +//MODULE_PARM_DESC(snd_enable, "Enable opti9xx soundcard."); +//MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_isapnp, "i"); +MODULE_PARM_DESC(snd_isapnp, "Enable ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +MODULE_PARM(snd_port, "l"); +MODULE_PARM_DESC(snd_port, "WSS port # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_mpu_port, "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_fm_port, "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_irq, "i"); +MODULE_PARM_DESC(snd_irq, "WSS irq # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_mpu_irq, "i"); +MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 irq # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma1, "i"); +MODULE_PARM_DESC(snd_dma1, "1st dma # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +#if defined(CS4231) || defined(OPTi93X) +MODULE_PARM(snd_dma2, "i"); +MODULE_PARM_DESC(snd_dma2, "2nd dma # for opti9xx driver."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +#endif /* CS4231 || OPTi93X */ + +#define OPTi9XX_HW_DETECT 0 +#define OPTi9XX_HW_82C928 1 +#define OPTi9XX_HW_82C929 2 +#define OPTi9XX_HW_82C924 3 +#define OPTi9XX_HW_82C925 4 +#define OPTi9XX_HW_82C930 5 +#define OPTi9XX_HW_82C931 6 +#define OPTi9XX_HW_82C933 7 +#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 + +#define OPTi9XX_MC_REG(n) n + +typedef struct _snd_opti9xx opti9xx_t; + +#ifdef OPTi93X + +#define OPTi93X_INDEX 0x00 +#define OPTi93X_DATA 0x01 +#define OPTi93X_STATUS 0x02 +#define OPTi93X_DDATA 0x03 +#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) + +#define OPTi93X_MIXOUT_LEFT 0x00 +#define OPTi93X_MIXOUT_RIGHT 0x01 +#define OPTi93X_CD_LEFT_INPUT 0x02 +#define OPTi93X_CD_RIGHT_INPUT 0x03 +#define OPTi930_AUX_LEFT_INPUT 0x04 +#define OPTi930_AUX_RIGHT_INPUT 0x05 +#define OPTi931_FM_LEFT_INPUT 0x04 +#define OPTi931_FM_RIGHT_INPUT 0x05 +#define OPTi93X_DAC_LEFT 0x06 +#define OPTi93X_DAC_RIGHT 0x07 +#define OPTi93X_PLAY_FORMAT 0x08 +#define OPTi93X_IFACE_CONF 0x09 +#define OPTi93X_PIN_CTRL 0x0a +#define OPTi93X_ERR_INIT 0x0b +#define OPTi93X_ID 0x0c +#define OPTi93X_PLAY_UPR_CNT 0x0e +#define OPTi93X_PLAY_LWR_CNT 0x0f +#define OPTi931_AUX_LEFT_INPUT 0x10 +#define OPTi931_AUX_RIGHT_INPUT 0x11 +#define OPTi93X_LINE_LEFT_INPUT 0x12 +#define OPTi93X_LINE_RIGHT_INPUT 0x13 +#define OPTi93X_MIC_LEFT_INPUT 0x14 +#define OPTi93X_MIC_RIGHT_INPUT 0x15 +#define OPTi93X_OUT_LEFT 0x16 +#define OPTi93X_OUT_RIGHT 0x17 +#define OPTi93X_CAPT_FORMAT 0x1c +#define OPTi93X_CAPT_UPR_CNT 0x1e +#define OPTi93X_CAPT_LWR_CNT 0x1f + +#define OPTi93X_TRD 0x20 +#define OPTi93X_MCE 0x40 +#define OPTi93X_INIT 0x80 + +#define OPTi93X_MIXOUT_MIC_GAIN 0x20 +#define OPTi93X_MIXOUT_LINE 0x00 +#define OPTi93X_MIXOUT_CD 0x40 +#define OPTi93X_MIXOUT_MIC 0x80 +#define OPTi93X_MIXOUT_MIXER 0xc0 + +#define OPTi93X_STEREO 0x10 +#define OPTi93X_LINEAR_8 0x00 +#define OPTi93X_ULAW_8 0x20 +#define OPTi93X_LINEAR_16_LIT 0x40 +#define OPTi93X_ALAW_8 0x60 +#define OPTi93X_ADPCM_16 0xa0 +#define OPTi93X_LINEAR_16_BIG 0xc0 + +#define OPTi93X_CAPTURE_PIO 0x80 +#define OPTi93X_PLAYBACK_PIO 0x40 +#define OPTi93X_AUTOCALIB 0x08 +#define OPTi93X_SINGLE_DMA 0x04 +#define OPTi93X_CAPTURE_ENABLE 0x02 +#define OPTi93X_PLAYBACK_ENABLE 0x01 + +#define OPTi93X_IRQ_ENABLE 0x02 + +#define OPTi93X_DMA_REQUEST 0x10 +#define OPTi93X_CALIB_IN_PROGRESS 0x20 + +#define OPTi93X_IRQ_PLAYBACK 0x04 +#define OPTi93X_IRQ_CAPTURE 0x08 + + +typedef struct _snd_opti93x opti93x_t; + +struct _snd_opti93x { + unsigned long port; + struct resource *res_port; + int irq; + int dma1; + int dma2; + + opti9xx_t *chip; + unsigned short hardware; + unsigned char image[32]; + + unsigned char mce_bit; + unsigned short mode; + int mute; + + spinlock_t lock; + + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; +}; + +#define OPTi93X_MODE_NONE 0x00 +#define OPTi93X_MODE_PLAY 0x01 +#define OPTi93X_MODE_CAPTURE 0x02 +#define OPTi93X_MODE_OPEN (OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE) + +#endif /* OPTi93X */ + +struct _snd_opti9xx { + unsigned short hardware; + unsigned char password; + char name[7]; + + unsigned long mc_base; + struct resource *res_mc_base; + unsigned long mc_base_size; +#ifdef OPTi93X + unsigned long mc_indir_index; +#endif /* OPTi93X */ + unsigned long pwd_reg; + + spinlock_t lock; + + long wss_base; + int irq; + int dma1; +#if defined(CS4231) || defined(OPTi93X) + int dma2; +#endif /* CS4231 || OPTi93X */ + + long fm_port; + + long mpu_port; + int mpu_irq; + +#if defined(OPTi93X) + opti93x_t *opti93x; +#elif defined(CS4231) + cs4231_t *cs4231; +#else + ad1848_t *ad1848; +#endif /* AD1848 */ + snd_rawmidi_t *rmidi; +#ifdef __ISAPNP__ + struct isapnp_dev *dev; + struct isapnp_dev *devmpu; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_opti9xx_card = SNDRV_DEFAULT_PTR1; + +#ifdef __ISAPNP__ + +#define ISAPNP_OPTI9XX(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs: { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \ + ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \ + } + +static struct isapnp_card_id snd_card_opti9xx_pnpids[] = { +#ifndef OPTi93X + /* OPTi 82C924 */ + ISAPNP_OPTI9XX('O','P','T',0x0924,'O','P','T',0x0000,0x0002), + /* OPTi 82C925 */ + ISAPNP_OPTI9XX('O','P','T',0x0925,'O','P','T',0x9250,0x0002), +#else + /* OPTi 82C931/3 */ + ISAPNP_OPTI9XX('O','P','T',0x0931,'O','P','T',0x9310,0x0002), +#endif /* OPTi93X */ + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_card_opti9xx_pnpids); + +#endif /* __ISAPNP__ */ + +#ifdef OPTi93X +#define DRIVER_NAME "snd-card-opti93x" +#else +#define DRIVER_NAME "snd-card-opti92x" +#endif /* OPTi93X */ + +static char * snd_opti9xx_names[] = { + "unkown", + "82C928", "82C929", + "82C924", "82C925", + "82C930", "82C931", "82C933" +}; + + +static int __init snd_opti9xx_init(opti9xx_t *chip, unsigned short hardware) +{ + chip->hardware = hardware; + strcpy(chip->name, snd_opti9xx_names[hardware]); + + spin_lock_init(&chip->lock); + + chip->wss_base = -1; + chip->irq = -1; + chip->dma1 = -1; +#if defined(CS4231) || defined (OPTi93X) + chip->dma2 = -1; +#endif /* CS4231 || OPTi93X */ + chip->fm_port = -1; + chip->mpu_port = -1; + chip->mpu_irq = -1; + + switch (hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + chip->mc_base = 0xf8c; + chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; + chip->pwd_reg = 3; + break; + + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + chip->mc_base = 0xf8c; + chip->password = 0xe5; + chip->pwd_reg = 3; + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; + chip->mc_indir_index = 0xe0e; + chip->password = 0xe4; + chip->pwd_reg = 0; + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", hardware); + return -ENODEV; + } + return 0; +} + +static unsigned char snd_opti9xx_read(opti9xx_t *chip, + unsigned char reg) +{ + unsigned long flags; + unsigned char retval = 0xff; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + retval = inb(chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + retval = inb(chip->mc_base + reg); + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + outb(reg, chip->mc_indir_index); + outb(chip->password, chip->mc_base + chip->pwd_reg); + retval = inb(chip->mc_indir_index + 1); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); + return retval; +} + +static void snd_opti9xx_write(opti9xx_t *chip, unsigned char reg, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + case OPTi9XX_HW_82C925: + if (reg > 7) { + outb(reg, chip->mc_base + 8); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(value, chip->mc_base + 9); + break; + } + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + outb(value, chip->mc_base + reg); + break; +#else /* OPTi93X */ + + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + outb(reg, chip->mc_indir_index); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(value, chip->mc_indir_index + 1); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + } + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +#define snd_opti9xx_write_mask(chip, reg, value, mask) \ + snd_opti9xx_write(chip, reg, \ + (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) + + +static int __init snd_opti9xx_configure(opti9xx_t *chip) +{ + unsigned char wss_base_bits; + unsigned char irq_bits; + unsigned char dma_bits; + unsigned char mpu_port_bits = 0; + unsigned char mpu_irq_bits; + unsigned long flags; + + switch (chip->hardware) { +#ifndef OPTi93X + case OPTi9XX_HW_82C924: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); + + case OPTi9XX_HW_82C925: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); +#ifdef CS4231 + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); +#else + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); +#endif /* CS4231 */ + break; + + case OPTi9XX_HW_82C928: + case OPTi9XX_HW_82C929: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); + /* + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); + */ + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); +#ifdef CS4231 + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); +#else + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); +#endif /* CS4231 */ + break; + +#else /* OPTi93X */ + case OPTi9XX_HW_82C930: + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | + (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), + 0x34); + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); + break; +#endif /* OPTi93X */ + + default: + snd_printk("chip %d not supported\n", chip->hardware); + return -EINVAL; + } + + switch (chip->wss_base) { + case 0x530: + wss_base_bits = 0x00; + break; + case 0x604: + wss_base_bits = 0x03; + break; + case 0xe80: + wss_base_bits = 0x01; + break; + case 0xf40: + wss_base_bits = 0x02; + break; + default: + snd_printk("WSS port 0x%lx not valid\n", chip->wss_base); + goto __skip_base; + } + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); + +__skip_base: + switch (chip->irq) { +#ifdef OPTi93X + case 5: + irq_bits = 0x05; + break; +#endif /* OPTi93X */ + case 7: + irq_bits = 0x01; + break; + case 9: + irq_bits = 0x02; + break; + case 10: + irq_bits = 0x03; + break; + case 11: + irq_bits = 0x04; + break; + default: + snd_printk("WSS irq # %d not valid\n", chip->irq); + goto __skip_resources; + } + + switch (chip->dma1) { + case 0: + dma_bits = 0x01; + break; + case 1: + dma_bits = 0x02; + break; + case 3: + dma_bits = 0x03; + break; + default: + snd_printk("WSS dma1 # %d not valid\n", chip->dma1); + goto __skip_resources; + } + +#if defined(CS4231) || defined(OPTi93X) + if (chip->dma1 == chip->dma2) { + snd_printk("don't want to share dmas\n"); + return -EBUSY; + } + + switch (chip->dma2) { + case 0: + case 1: + break; + default: + snd_printk("WSS dma2 # %d not valid\n", chip->dma2); + goto __skip_resources; + } + dma_bits |= 0x04; +#endif /* CS4231 || OPTi93X */ + + spin_lock_irqsave(&chip->lock, flags); + outb(irq_bits << 3 | dma_bits, chip->wss_base); + spin_unlock_irqrestore(&chip->lock, flags); + +__skip_resources: + if (chip->hardware > OPTi9XX_HW_82C928) { + switch (chip->mpu_port) { + case -1: + break; + case 0x300: + mpu_port_bits = 0x03; + break; + case 0x310: + mpu_port_bits = 0x02; + break; + case 0x320: + mpu_port_bits = 0x01; + break; + case 0x330: + mpu_port_bits = 0x00; + break; + default: + snd_printk("MPU-401 port 0x%lx not valid\n", + chip->mpu_port); + goto __skip_mpu; + } + + switch (chip->mpu_irq) { + case 5: + mpu_irq_bits = 0x02; + break; + case 7: + mpu_irq_bits = 0x03; + break; + case 9: + mpu_irq_bits = 0x00; + break; + case 10: + mpu_irq_bits = 0x01; + break; + default: + snd_printk("MPU-401 irq # %d not valid\n", + chip->mpu_irq); + goto __skip_mpu; + } + + snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), + (chip->mpu_port == -1) ? 0x00 : + 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, + 0xf8); + } +__skip_mpu: + + return 0; +} + +#ifdef OPTi93X + +#define chip_t opti93x_t + +static unsigned char snd_opti93x_default_image[32] = +{ + 0x00, /* 00/00 - l_mixout_outctrl */ + 0x00, /* 01/01 - r_mixout_outctrl */ + 0x88, /* 02/02 - l_cd_inctrl */ + 0x88, /* 03/03 - r_cd_inctrl */ + 0x88, /* 04/04 - l_a1/fm_inctrl */ + 0x88, /* 05/05 - r_a1/fm_inctrl */ + 0x80, /* 06/06 - l_dac_inctrl */ + 0x80, /* 07/07 - r_dac_inctrl */ + 0x00, /* 08/08 - ply_dataform_reg */ + 0x00, /* 09/09 - if_conf */ + 0x00, /* 0a/10 - pin_ctrl */ + 0x00, /* 0b/11 - err_init_reg */ + 0x0a, /* 0c/12 - id_reg */ + 0x00, /* 0d/13 - reserved */ + 0x00, /* 0e/14 - ply_upcount_reg */ + 0x00, /* 0f/15 - ply_lowcount_reg */ + 0x88, /* 10/16 - reserved/l_a1_inctrl */ + 0x88, /* 11/17 - reserved/r_a1_inctrl */ + 0x88, /* 12/18 - l_line_inctrl */ + 0x88, /* 13/19 - r_line_inctrl */ + 0x88, /* 14/20 - l_mic_inctrl */ + 0x88, /* 15/21 - r_mic_inctrl */ + 0x80, /* 16/22 - l_out_outctrl */ + 0x80, /* 17/23 - r_out_outctrl */ + 0x00, /* 18/24 - reserved */ + 0x00, /* 19/25 - reserved */ + 0x00, /* 1a/26 - reserved */ + 0x00, /* 1b/27 - reserved */ + 0x00, /* 1c/28 - cap_dataform_reg */ + 0x00, /* 1d/29 - reserved */ + 0x00, /* 1e/30 - cap_upcount_reg */ + 0x00 /* 1f/31 - cap_lowcount_reg */ +}; + + +static int snd_opti93x_busy_wait(opti93x_t *chip) +{ + int timeout; + + for (timeout = 250; timeout-- > 0; udelay(10)) + if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT)) + return 0; + + snd_printk("chip still busy.\n"); + return -EBUSY; +} + +static unsigned char snd_opti93x_in(opti93x_t *chip, unsigned char reg) +{ + snd_opti93x_busy_wait(chip); + outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); + return inb(OPTi93X_PORT(chip, DATA)); +} + +static void snd_opti93x_out(opti93x_t *chip, unsigned char reg, + unsigned char value) +{ + snd_opti93x_busy_wait(chip); + outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX)); + outb(value, OPTi93X_PORT(chip, DATA)); +} + +static void snd_opti93x_out_image(opti93x_t *chip, unsigned char reg, + unsigned char value) +{ + snd_opti93x_out(chip, reg, chip->image[reg] = value); +} + +static void snd_opti93x_out_mask(opti93x_t *chip, unsigned char reg, + unsigned char mask, unsigned char value) +{ + snd_opti93x_out_image(chip, reg, + (chip->image[reg] & ~mask) | (value & mask)); +} + + +static void snd_opti93x_mce_up(opti93x_t *chip) +{ + snd_opti93x_busy_wait(chip); + + chip->mce_bit = OPTi93X_MCE; + if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)) + outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); +} + +static void snd_opti93x_mce_down(opti93x_t *chip) +{ + snd_opti93x_busy_wait(chip); + + chip->mce_bit = 0; + if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE) + outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX)); +} + +#define snd_opti93x_mute_reg(chip, reg, mute) \ + snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]); + +static void snd_opti93x_mute(opti93x_t *chip, int mute) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + mute = mute ? 1 : 0; + if (chip->mute == mute) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + chip->mute = mute; + + snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute); + switch (chip->hardware) { + case OPTi9XX_HW_82C930: + snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute); + break; + case OPTi9XX_HW_82C931: + case OPTi9XX_HW_82C933: + snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute); + } + snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute); + snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute); + + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static unsigned int snd_opti93x_get_count(unsigned char format, + unsigned int size) +{ + switch (format & 0xe0) { + case OPTi93X_LINEAR_16_LIT: + case OPTi93X_LINEAR_16_BIG: + size >>= 1; + break; + case OPTi93X_ADPCM_16: + return size >> 2; + } + return (format & OPTi93X_STEREO) ? (size >> 1) : size; +} + +unsigned int rates[] = { 5512, 6615, 8000, 9600, 11025, 16000, 18900, + 22050, 27428, 32000, 33075, 37800, 44100, 48000 }; +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: RATES, + list: rates, + mask: 0, +}; + +unsigned char bits[] = { 0x01, 0x0f, 0x00, 0x0e, 0x03, 0x02, 0x05, + 0x07, 0x04, 0x06, 0x0d, 0x09, 0x0b, 0x0c}; + +static unsigned char snd_opti93x_get_freq(unsigned int rate) +{ + int i; + + for (i = 0; i < RATES; i++) { + if (rate == rates[i]) + return bits[i]; + } + snd_BUG(); + return bits[RATES-1]; +} + +static unsigned char snd_opti93x_get_format(opti93x_t *chip, + unsigned int format, int channels) +{ + unsigned char retval = OPTi93X_LINEAR_8; + + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + retval = OPTi93X_ULAW_8; + break; + case SNDRV_PCM_FORMAT_A_LAW: + retval = OPTi93X_ALAW_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + retval = OPTi93X_LINEAR_16_LIT; + break; + case SNDRV_PCM_FORMAT_S16_BE: + retval = OPTi93X_LINEAR_16_BIG; + break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + retval = OPTi93X_ADPCM_16; + } + return (channels > 1) ? (retval | OPTi93X_STEREO) : retval; +} + + +static void snd_opti93x_playback_format(opti93x_t *chip, unsigned char fmt) +{ + unsigned long flags; + unsigned char mask; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mute(chip, 1); + + snd_opti93x_mce_up(chip); + mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff; + snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt); + snd_opti93x_mce_down(chip); + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void snd_opti93x_capture_format(opti93x_t *chip, unsigned char fmt) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mute(chip, 1); + + snd_opti93x_mce_up(chip); + if (!(chip->mode & OPTi93X_MODE_PLAY)) + snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt); + else + fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0; + snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt); + snd_opti93x_mce_down(chip); + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + + +static int snd_opti93x_open(opti93x_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (chip->mode & mode) { + spin_unlock_irqrestore(&chip->lock, flags); + return -EAGAIN; + } + + if (!(chip->mode & OPTi93X_MODE_OPEN)) { + outb(0x00, OPTi93X_PORT(chip, STATUS)); + snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, + OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE); + chip->mode = mode; + } + else + chip->mode |= mode; + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static void snd_opti93x_close(opti93x_t *chip, unsigned int mode) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + chip->mode &= ~mode; + if (chip->mode & OPTi93X_MODE_OPEN) { + spin_unlock_irqrestore(&chip->lock, flags); + return; + } + + snd_opti93x_mute(chip, 1); + + outb(0, OPTi93X_PORT(chip, STATUS)); + snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE, + ~OPTi93X_IRQ_ENABLE); + + snd_opti93x_mce_up(chip); + snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00); + snd_opti93x_mce_down(chip); + chip->mode = 0; + + snd_opti93x_mute(chip, 0); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_opti93x_trigger(snd_pcm_substream_t *substream, + unsigned char what, int cmd) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == chip->playback_substream) { + what |= OPTi93X_PLAYBACK_ENABLE; + snd_pcm_trigger_done(s, substream); + } else if (s == chip->capture_substream) { + what |= OPTi93X_CAPTURE_ENABLE; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&chip->lock); + if (SNDRV_PCM_TRIGGER_START) + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what); + else + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00); + spin_unlock(&chip->lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int snd_opti93x_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + return snd_opti93x_trigger(substream, + OPTi93X_PLAYBACK_ENABLE, cmd); +} + +static int snd_opti93x_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + return snd_opti93x_trigger(substream, + OPTi93X_CAPTURE_ENABLE, cmd); +} + +static int snd_opti93x_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + + +static int snd_opti93x_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + + +static int snd_opti93x_playback_prepare(snd_pcm_substream_t * substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned char format; + unsigned int count = snd_pcm_lib_period_bytes(substream); + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&chip->lock, flags); + + chip->p_dma_size = size; + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, + OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO, + ~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO)); + + snd_dma_program(chip->dma1, runtime->dma_addr, size, + DMA_MODE_WRITE | DMA_AUTOINIT); + + format = snd_opti93x_get_freq(runtime->rate); + format |= snd_opti93x_get_format(chip, runtime->format, + runtime->channels); + snd_opti93x_playback_format(chip, format); + format = chip->image[OPTi93X_PLAY_FORMAT]; + + count = snd_opti93x_get_count(format, count) - 1; + snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count); + snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_opti93x_capture_prepare(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned char format; + unsigned int count = snd_pcm_lib_period_bytes(substream); + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&chip->lock, flags); + + chip->c_dma_size = size; + snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, + OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, + (unsigned char)~(OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO)); + + snd_dma_program(chip->dma2, runtime->dma_addr, size, + DMA_MODE_READ | DMA_AUTOINIT); + + format = snd_opti93x_get_freq(runtime->rate); + format |= snd_opti93x_get_format(chip, runtime->format, + runtime->channels); + snd_opti93x_capture_format(chip, format); + format = chip->image[OPTi93X_CAPT_FORMAT]; + + count = snd_opti93x_get_count(format, count) - 1; + snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count); + snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8); + + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_opti93x_playback_pointer(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE)) + return 0; + + ptr = chip->p_dma_size - snd_dma_residue(chip->dma1); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_opti93x_capture_pointer(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE)) + return 0; + + ptr = chip->c_dma_size - snd_dma_residue(chip->dma2); + return bytes_to_frames(substream->runtime, ptr); +} + + +static void snd_opti93x_overrange(opti93x_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02)) + chip->capture_substream->runtime->overrange++; + + spin_unlock_irqrestore(&chip->lock, flags); +} + +void snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + opti93x_t *codec = snd_magic_cast(opti93x_t, dev_id, return); + unsigned char status; + + status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11)); + if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) + snd_pcm_period_elapsed(codec->playback_substream); + if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { + snd_opti93x_overrange(codec); + snd_pcm_period_elapsed(codec->capture_substream); + } + outb(0x00, OPTi93X_PORT(codec, STATUS)); +} + + +static snd_pcm_hardware_t snd_opti93x_playback = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_opti93x_capture = { + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), + rates: SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_opti93x_playback_open(snd_pcm_substream_t *substream) +{ + int error; + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0) + return error; + snd_pcm_set_sync(substream); + chip->playback_substream = substream; + runtime->hw = snd_opti93x_playback; + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return error; +} + +static int snd_opti93x_capture_open(snd_pcm_substream_t *substream) +{ + int error; + opti93x_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0) + return error; + runtime->hw = snd_opti93x_capture; + snd_pcm_set_sync(substream); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return error; +} + +static int snd_opti93x_playback_close(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_opti93x_close(chip, OPTi93X_MODE_PLAY); + return 0; +} + +static int snd_opti93x_capture_close(snd_pcm_substream_t *substream) +{ + opti93x_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE); + return 0; +} + + +static void snd_opti93x_init(opti93x_t *chip) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->lock, flags); + snd_opti93x_mce_up(chip); + + for (i = 0; i < 32; i++) + snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]); + + snd_opti93x_mce_down(chip); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int snd_opti93x_probe(opti93x_t *chip) +{ + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&chip->lock, flags); + val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f; + spin_unlock_irqrestore(&chip->lock, flags); + + return (val == 0x0a) ? 0 : -ENODEV; +} + +static int snd_opti93x_free(opti93x_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->dma1 >= 0) { + disable_dma(chip->dma1); + free_dma(chip->dma1); + } + if (chip->dma2 >= 0) { + disable_dma(chip->dma2); + free_dma(chip->dma2); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_opti93x_dev_free(snd_device_t *device) +{ + opti93x_t *chip = snd_magic_cast(opti93x_t, device->device_data, return -ENXIO); + return snd_opti93x_free(chip); +} + +static const char *snd_opti93x_chip_id(opti93x_t *codec) +{ + switch (codec->hardware) { + case OPTi9XX_HW_82C930: return "82C930"; + case OPTi9XX_HW_82C931: return "82C931"; + case OPTi9XX_HW_82C933: return "82C933"; + default: return "???"; + } +} + +int snd_opti93x_create(snd_card_t *card, opti9xx_t *chip, + int dma1, int dma2, + opti93x_t **rcodec) +{ + static snd_device_ops_t ops = { + dev_free: snd_opti93x_dev_free, + }; + int error; + opti93x_t *codec; + + *rcodec = NULL; + codec = snd_magic_kcalloc(opti93x_t, 0, GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + codec->irq = -1; + codec->dma1 = -1; + codec->dma2 = -1; + + if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) { + snd_opti93x_free(codec); + return -EBUSY; + } + if (request_dma(dma1, "OPTI93x - 1")) { + snd_opti93x_free(codec); + return -EBUSY; + } + codec->dma1 = chip->dma1; + if (request_dma(dma2, "OPTI93x - 2")) { + snd_opti93x_free(codec); + return -EBUSY; + } + codec->dma2 = chip->dma2; + + codec->card = card; + codec->port = chip->wss_base + 4; + codec->irq = chip->irq; + + spin_lock_init(&codec->lock); + codec->hardware = chip->hardware; + codec->chip = chip; + + if ((error = snd_opti93x_probe(codec))) { + snd_opti93x_free(codec); + return error; + } + + snd_opti93x_init(codec); + + /* Register device */ + if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_opti93x_free(codec); + return error; + } + + *rcodec = codec; + return 0; +} + +static snd_pcm_ops_t snd_opti93x_playback_ops = { + open: snd_opti93x_playback_open, + close: snd_opti93x_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_opti93x_hw_params, + hw_free: snd_opti93x_hw_free, + prepare: snd_opti93x_playback_prepare, + trigger: snd_opti93x_playback_trigger, + pointer: snd_opti93x_playback_pointer, +}; + +static snd_pcm_ops_t snd_opti93x_capture_ops = { + open: snd_opti93x_capture_open, + close: snd_opti93x_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_opti93x_hw_params, + hw_free: snd_opti93x_hw_free, + prepare: snd_opti93x_capture_prepare, + trigger: snd_opti93x_capture_trigger, + pointer: snd_opti93x_capture_pointer, +}; + +static void snd_opti93x_pcm_free(snd_pcm_t *pcm) +{ + opti93x_t *codec = snd_magic_cast(opti93x_t, pcm->private_data, return); + codec->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_opti93x_pcm(opti93x_t *codec, int device, snd_pcm_t **rpcm) +{ + int error; + snd_pcm_t *pcm; + + if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm))) + return error; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops); + + pcm->private_data = codec; + pcm->private_free = snd_opti93x_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + strcpy(pcm->name, snd_opti93x_chip_id(codec)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); + + codec->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * MIXER part + */ + +static int snd_opti93x_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { + "Line1", "Aux", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_opti93x_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6; + ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6; + spin_unlock_irqrestore(&chip->lock, flags); + return 0; +} + +static int snd_opti93x_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right; + int change; + + if (ucontrol->value.enumerated.item[0] > 3 || + ucontrol->value.enumerated.item[1] > 3) + return -EINVAL; + left = ucontrol->value.enumerated.item[0] << 6; + right = ucontrol->value.enumerated.item[1] << 6; + spin_lock_irqsave(&chip->lock, flags); + left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left; + right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right; + change = left != chip->image[OPTi93X_MIXOUT_LEFT] || + right != chip->image[OPTi93X_MIXOUT_RIGHT]; + snd_opti93x_out(chip, OPTi93X_MIXOUT_LEFT, left); + snd_opti93x_out(chip, OPTi93X_MIXOUT_RIGHT, right); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#if 0 + +#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opti93x_info_single, \ + get: snd_opti93x_get_single, put: snd_opti93x_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_opti93x_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_opti93x_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_opti93x_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->lock, flags); + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + snd_opti93x_out(chip, reg, val); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#endif /* single */ + +#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_opti93x_info_double, \ + get: snd_opti93x_get_double, put: snd_opti93x_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \ + do { xctl.private_value ^= 22; } while (0) +#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \ + do { xctl.private_value &= ~0x0000ffff; \ + xctl.private_value |= left_reg | (right_reg << 8); } while (0) + +static int snd_opti93x_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_opti93x_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->lock, flags); + ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; + spin_unlock_irqrestore(&chip->lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_opti93x_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + opti93x_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->lock, flags); + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + snd_opti93x_out(chip, left_reg, val1); + snd_opti93x_out(chip, right_reg, val1); + spin_unlock_irqrestore(&chip->lock, flags); + return change; +} + +#define OPTi93X_CONTROLS (sizeof(snd_opti93x_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_opti93x_controls[] = { +OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 0, 0, 31, 1), +OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1), +OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 0), +OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1), +OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), +OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 0, 0, 15, 1), +OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_opti93x_info_mux, + get: snd_opti93x_get_mux, + put: snd_opti93x_put_mux, +} +}; + +int snd_opti93x_mixer(opti93x_t *chip) +{ + snd_card_t *card; + snd_kcontrol_new_t knew; + int err, idx; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, snd_opti93x_chip_id(chip)); + + for (idx = 0; idx < OPTi93X_CONTROLS; idx++) { + knew = snd_opti93x_controls[idx]; + if (chip->hardware == OPTi9XX_HW_82C930) { + if (strstr(knew.name, "FM")) /* skip FM controls */ + continue; + else if (strcmp(knew.name, "Mic Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + else if (strstr(knew.name, "Aux")) + OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT); + else if (strcmp(knew.name, "PCM Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + else if (strcmp(knew.name, "Master Playback Volume")) + OPTi93X_DOUBLE_INVERT_INVERT(knew); + } + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +#endif /* OPTi93X */ + +static int __init snd_card_opti9xx_detect(snd_card_t *card, opti9xx_t *chip) +{ + int i, err; + static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; + +#ifndef OPTi93X + for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { + unsigned char value; + + if ((err = snd_opti9xx_init(chip, i)) < 0) + return err; + chip->mc_base_size = opti9xx_mc_size[i]; + if (check_region(chip->mc_base, chip->mc_base_size)) + continue; + + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); + if ((value != 0xff) && (value != inb(chip->mc_base + 1))) + if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) + return 1; + } +#else /* OPTi93X */ + for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { + unsigned long flags; + unsigned char value; + + if ((err = snd_opti9xx_init(chip, i)) < 0) + return err; + chip->mc_base_size = opti9xx_mc_size[i]; + if (check_region(chip->mc_base, chip->mc_base_size)) + continue; + + spin_lock_irqsave(&chip->lock, flags); + outb(chip->password, chip->mc_base + chip->pwd_reg); + outb(((chip->mc_indir_index & (1 << 8)) >> 4) | + ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base); + spin_unlock_irqrestore(&chip->lock, flags); + + value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); + snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); + if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) + return 1; + } +#endif /* OPTi93X */ + + return -ENODEV; +} + +#ifdef __ISAPNP__ +static int __init snd_card_opti9xx_isapnp(opti9xx_t *chip) +{ + struct isapnp_dev *pdev = NULL; + const struct isapnp_card_id *pid = snd_card_opti9xx_pnpids-1; + static struct isapnp_card *card = NULL; + + __again: + while (1) { + pid++; + if (pid->card_vendor == 0) + return -ENODEV; + if ((card = isapnp_find_card(pid->card_vendor, pid->card_device, card))) + break; + } + if (card == NULL) + return -ENODEV; + + chip->dev = isapnp_find_dev(card, pid->devs[0].vendor, pid->devs[0].function, NULL); + if (chip->dev == NULL) + goto __again; + + chip->devmpu = isapnp_find_dev(card, pid->devs[1].vendor, pid->devs[1].function, NULL); + + pdev = chip->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + +#ifdef OPTi93X + if (snd_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port + 4, 4); +#else + if ((pid->card_device != ISAPNP_DEVICE(0x0924)) && (snd_port != SNDRV_AUTO_PORT)) + isapnp_resource_change(&pdev->resource[1], snd_port, 4); +#endif /* OPTi93X */ + if (snd_irq != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq, 1); + if (snd_dma1 != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1, 1); +#if defined(CS4231) || defined(OPTi93X) + if (snd_dma2 != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2, 1); +#endif /* CS4231 || OPTi93X */ + if (snd_fm_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port, 4); + + if (pdev->activate(pdev) < 0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + +#ifdef OPTi93X + snd_port = pdev->resource[0].start - 4; + snd_fm_port = pdev->resource[1].start; +#else + if (pid->card_device != ISAPNP_DEVICE(0x0924)) + snd_port = pdev->resource[1].start; + snd_fm_port = pdev->resource[2].start; +#endif /* OPTi93X */ + snd_irq = pdev->irq_resource[0].start; + snd_dma1 = pdev->dma_resource[0].start; +#if defined(CS4231) || defined(OPTi93X) + snd_dma2 = pdev->dma_resource[1].start; +#endif /* CS4231 || OPTi93X */ + + pdev = chip->devmpu; + if (pdev == NULL || pdev->prepare(pdev) < 0) { + snd_mpu_port = -1; + chip->devmpu = NULL; + return pid->card_device; + } + + if (snd_mpu_port != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_mpu_port, 2); + if (snd_mpu_irq != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq, 1); + + if (pdev->activate(pdev) < 0) { + snd_printk("MPU-401 isapnp configure failure\n"); + snd_mpu_port = -1; + chip->devmpu = NULL; + } else { + snd_mpu_port = pdev->resource[0].start; + snd_mpu_irq = pdev->irq_resource[0].start; + } + return pid->card_device; +} + +static void snd_card_opti9xx_deactivate(opti9xx_t *chip) +{ + if (chip->dev) + chip->dev->deactivate(chip->dev); + if (chip->devmpu) + chip->devmpu->deactivate(chip->devmpu); +} +#endif /* __ISAPNP__ */ + +#if 0 +static int __init snd_card_opti9xx_resources(struct snd_card_opti9xx *chip, + snd_card_t *card) +{ + int error, i, pnp = 0; + +#ifdef __ISAPNP__ + pnp = chip->dev != NULL; +#endif /* __ISAPNP__ */ + +#ifndef OPTi93X + if (chip->chip->hardware == OPTi9XX_HW_82C928) + snd_mpu_port = -1; +#endif /* OPTi93X */ + error = 0; + if (!pnp && (snd_mpu_port == SNDRV_DEFAULT_PORT1)) { + for (i = 0; possible_mpu_ports[i] != -1; i++) + if (!snd_register_ioport(card, possible_mpu_ports[i], 2, + DRIVER_NAME" - MPU-401", NULL)) { + snd_mpu_port = possible_mpu_ports[i]; + break; + } + if (snd_mpu_port == SNDRV_DEFAULT_PORT1) + error = -EBUSY; + } + else + error = (snd_mpu_port == -1) ? -ENODEV : + snd_register_ioport(card, snd_mpu_port, 2, + DRIVER_NAME" - MPU-401", NULL); + if (error) + chip->chip->mpu_port = -1; + else if (pnp && (snd_irq == snd_mpu_irq)) + chip->chip->mpu_irq = snd_mpu_irq; + else if (!snd_register_interrupt(card, + DRIVER_NAME" - MPU-401", + snd_mpu_irq, SNDRV_IRQ_TYPE_ISA, + snd_card_opti9xx_mpu_interrupt, chip, + pnp ? no_alternatives : possible_mpu_irqs, + &chip->mpuirqptr)) { + chip->chip->mpu_port = snd_mpu_port; + chip->chip->mpu_irq = chip->mpuirqptr->irq; + } + else + chip->chip->mpu_port = -1; + + if (!pnp && (snd_port == SNDRV_DEFAULT_PORT1)) { + for (i = 0; possible_ports[i] != -1; i++) + if (!snd_register_ioport(card, possible_ports[i], 8, + DRIVER_NAME" - WSS", NULL)) { + snd_port = possible_ports[i]; + break; + } + if (snd_port == SNDRV_DEFAULT_PORT1) + return -EBUSY; + } + else if ((error = snd_register_ioport(card, snd_port, 8, + DRIVER_NAME" - WSS", NULL)) < 0) + return error; + chip->chip->wss_base = snd_port; + if ((error = snd_register_interrupt(card, DRIVER_NAME" - WSS", + snd_irq, SNDRV_IRQ_TYPE_ISA, + snd_card_opti9xx_interrupt, chip, + pnp ? no_alternatives : possible_irqs, + &chip->irqptr)) < 0) + return error; + chip->chip->irq = chip->irqptr->irq; + if ((error = snd_register_dma_channel(card, +#if defined(CS4231) || defined(OPTi93X) + DRIVER_NAME" - WSS playback", +#else + DRIVER_NAME" - WSS", +#endif /* CS4231 || OPTi93X */ + snd_dma1, SNDRV_DMA_TYPE_ISA, snd_dma1_size, + pnp ? no_alternatives : possible_dma1s, + &chip->dma1ptr)) < 0) + return error; + chip->chip->dma1 = chip->dma1ptr->dma; +#if defined(CS4231) || defined(OPTi93X) + if ((error = snd_register_dma_channel(card, DRIVER_NAME" - WSS capture", + snd_dma2, SNDRV_DMA_TYPE_ISA, snd_dma2_size, + pnp ? no_alternatives : + possible_dma2s[chip->dma1ptr->dma], + &chip->dma2ptr)) < 0) + return error; + chip->chip->dma2 = chip->dma2ptr->dma; +#endif /* CS4231 || OPTi93X */ + + if (snd_register_ioport(card, + pnp ? snd_fm_port : snd_fm_port = 0x388, 4, + DRIVER_NAME" - OPL", NULL) < 0) + snd_fm_port = -1; + chip->chip->fm_port = snd_fm_port; + + return 0; +} +#endif + +static void snd_card_opti9xx_free(snd_card_t *card) +{ + opti9xx_t *chip = (opti9xx_t *)card->private_data; + + if (chip) { +#ifdef __ISAPNP__ + snd_card_opti9xx_deactivate(chip); +#endif /* __ISAPNP__ */ + if (chip->res_mc_base) { + release_resource(chip->res_mc_base); + kfree_nocheck(chip->res_mc_base); + } + } +} + +static int __init snd_card_opti9xx_probe(void) +{ + static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; + static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x310, -1}; +#ifdef OPTi93X + static int possible_irqs[] = {5, 9, 10, 11, 7, -1}; +#else + static int possible_irqs[] = {9, 10, 11, 7, -1}; +#endif /* OPTi93X */ + static int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dma1s[] = {3, 1, 0, -1}; +#if defined(CS4231) || defined(OPTi93X) + static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; +#endif /* CS4231 || OPTi93X */ + int error; + opti9xx_t *chip; +#if defined(OPTi93X) + opti93x_t *codec; +#elif defined(CS4231) + cs4231_t *codec; + snd_timer_t *timer; +#else + ad1848_t *codec; +#endif + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_hwdep_t *synth; +#ifdef __ISAPNP__ + int hw; +#endif /* __ISAPNP__ */ + + if (!(card = snd_card_new(snd_index, snd_id, THIS_MODULE, + sizeof(opti9xx_t)))) + return -ENOMEM; + card->private_free = snd_card_opti9xx_free; + chip = (opti9xx_t *)card->private_data; + +#ifdef __ISAPNP__ + if (snd_isapnp && (hw = snd_card_opti9xx_isapnp(chip)) > 0) { + switch (hw) { + case ISAPNP_DEVICE(0x0924): + hw = OPTi9XX_HW_82C924; + break; + case ISAPNP_DEVICE(0x0925): + hw = OPTi9XX_HW_82C925; + break; + case ISAPNP_DEVICE(0x0931): + hw = OPTi9XX_HW_82C931; + break; + default: + snd_card_free(card); + return -ENODEV; + } + + if ((error = snd_opti9xx_init(chip, hw))) { + snd_card_free(card); + return error; + } + if (hw <= OPTi9XX_HW_82C930) + chip->mc_base -= 0x80; + } else { +#endif /* __ISAPNP__ */ + if ((error = snd_card_opti9xx_detect(card, chip)) < 0) { + snd_card_free(card); + return error; + } +#ifdef __ISAPNP__ + } +#endif /* __ISAPNP__ */ + + if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { + snd_card_free(card); + return -ENOMEM; + } + + chip->wss_base = snd_port; + chip->fm_port = snd_fm_port; + chip->mpu_port = snd_mpu_port; + chip->irq = snd_irq; + chip->mpu_irq = snd_mpu_irq; + chip->dma1 = snd_dma1; +#if defined(CS4231) || defined(OPTi93X) + chip->dma2 = snd_dma2; +#endif + +#ifdef __ISAPNP__ + if (!snd_isapnp) { +#endif + if (chip->wss_base == SNDRV_AUTO_PORT) { + if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free WSS port\n"); + return -EBUSY; + } + } + if (chip->mpu_port == SNDRV_AUTO_PORT) { + if ((chip->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free MPU401 port\n"); + return -EBUSY; + } + } + if (chip->irq == SNDRV_AUTO_IRQ) { + if ((chip->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + if (chip->mpu_irq == SNDRV_AUTO_IRQ) { + if ((chip->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free MPU401 IRQ\n"); + return -EBUSY; + } + } + if (chip->dma1 == SNDRV_AUTO_DMA) { + if ((chip->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA1\n"); + return -EBUSY; + } + } +#if defined(CS4231) || defined(OPTi93X) + if (chip->dma2 == SNDRV_AUTO_DMA) { + if ((chip->dma2 = snd_legacy_find_free_dma(possible_dma2s[chip->dma1 % 4])) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA2\n"); + return -EBUSY; + } + } +#endif + +#ifdef __ISAPNP__ + } +#endif + + if ((error = snd_opti9xx_configure(chip))) { + snd_card_free(card); + return error; + } + +#if defined(OPTi93X) + if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec))) { + snd_card_free(card); + return error; + } + if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_opti93x_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } +#elif defined(CS4231) + if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1, + chip->irq, chip->dma1, chip->dma2, + CS4231_HW_DETECT, + 0, + &codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) { + snd_card_free(card); + return error; + } +#else + if ((error = snd_ad1848_create(card, chip->wss_base + 4, + chip->irq, chip->dma1, + AD1848_HW_DETECT, &codec)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) { + snd_card_free(card); + return error; + } + if ((error = snd_ad1848_mixer(codec)) < 0) { + snd_card_free(card); + return error; + } +#endif + + if (chip->mpu_port <= 0) + rmidi = NULL; + else + if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT, + &rmidi))) + snd_printk("no MPU-401 device at 0x%lx?\n", chip->mpu_port); + + if (chip->fm_port > 0) { + opl3_t *opl3; + if (snd_opl3_create(card, + chip->fm_port, + chip->fm_port + 2, + OPL3_HW_AUTO, 0, &opl3) < 0) { + snd_printk("no OPL device at 0x%lx-0x%lx\n", + chip->fm_port, chip->fm_port + 4 - 1); + } else { + if ((error = snd_opl3_timer_new(opl3, +#ifdef CS4231 + 1, 2)) < 0) { +#else + 0, 1)) < 0) { +#endif /* CS4231 */ + snd_card_free(card); + return error; + } + if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0) { + snd_card_free(card); + return error; + } + } + } + + strcpy(card->driver, chip->name); + sprintf(card->shortname, "OPTi %s", card->driver); +#if defined(CS4231) || defined(OPTi93X) + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, pcm->name, chip->wss_base + 4, + chip->irq, chip->dma1, chip->dma2); +#else + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, pcm->name, chip->wss_base + 4, + chip->irq, chip->dma1); +#endif /* CS4231 || OPTi93X */ + if ((error = snd_card_register(card))) { + snd_card_free(card); + return error; + } + snd_opti9xx_card = card; + return 0; +} + +static int __init alsa_card_opti9xx_init(void) +{ + int error; + + if ((error = snd_card_opti9xx_probe())) { +#ifdef MODULE +#ifdef OPTi93X + printk(KERN_ERR "no OPTi 82C93x soundcard found\n"); +#else + printk(KERN_ERR "no OPTi 82C92x soundcard found\n"); +#endif /* OPTi93X */ +#endif + return error; + } + return 0; +} + +static void __exit alsa_card_opti9xx_exit(void) +{ + if (snd_opti9xx_card) + snd_card_free(snd_opti9xx_card); +} + +module_init(alsa_card_opti9xx_init) +module_exit(alsa_card_opti9xx_exit) + +#ifndef MODULE + +/* format is: snd-opti9xx=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_mpu_irq, + snd_dma1,[snd_dma2] */ + +static int __init alsa_card_opti9xx_setup(char *str) +{ + int __attribute__ ((__unused__)) enable = 1; + int __attribute__ ((__unused__)) pnp = INT_MAX; + + (void)(get_option(&str,&enable) == 2 && + get_option(&str,&snd_index) == 2 && + get_id(&str,&snd_id) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port) == 2 && + get_option(&str,(int *)&snd_mpu_port) == 2 && + get_option(&str,(int *)&snd_fm_port) == 2 && + get_option(&str,&snd_irq) == 2 && + get_option(&str,&snd_mpu_irq) == 2 && + get_option(&str,&snd_dma1) == 2 +#if defined(CS4231) || defined(OPTi93X) + && + get_option(&str,&snd_dma2) == 2 +#endif + ); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp = pnp; +#endif + return 1; +} + +#if defined(OPTi93X) +__setup("snd-opti93x=", alsa_card_opti9xx_setup); +#elif defined(CS4231) +__setup("snd-opti92x-cs4231=", alsa_card_opti9xx_setup); +#else +__setup("snd-opti92x-ad1848=", alsa_card_opti9xx_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/opti9xx/opti92x-cs4231.c linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-cs4231.c --- linux/sound/isa/opti9xx/opti92x-cs4231.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti92x-cs4231.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,2 @@ +#define CS4231 +#include "opti92x-ad1848.c" diff -Nru linux/sound/isa/opti9xx/opti93x.c linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti93x.c --- linux/sound/isa/opti9xx/opti93x.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/opti9xx/opti93x.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,3 @@ +#define OPTi93X +#include "opti92x-ad1848.c" + diff -Nru linux/sound/isa/sb/Makefile linux-2.4.19-pre5-mjc/sound/isa/sb/Makefile --- linux/sound/isa/sb/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,67 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _sb.o + +list-multi := snd-sb-common.o snd-sb8-dsp.o snd-sb16-dsp.o snd-sb16-csp.o \ + snd-sb8.o snd-sb16.o snd-sbawe.o snd-emu8000-synth.o snd-es968.o + +export-objs := emu8000.o emu8000_synth.o sb_common.o sb8_main.o sb16_main.o sb16_csp.o + +snd-sb-common-objs := sb_common.o sb_mixer.o +snd-sb8-dsp-objs := sb8_main.o sb8_midi.o +snd-sb16-dsp-objs := sb16_main.o +snd-sb16-csp-objs := sb16_csp.o +snd-sb8-objs := sb8.o +snd-sb16-objs := sb16.o +snd-sbawe-objs := sbawe.o emu8000.o +snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o +snd-es968-objs := es968.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_DT0197H) += snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o +obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o +ifeq ($(CONFIG_SND_SB16_CSP),y) + obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o + obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o +endif +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-sb-common.o: $(snd-sb-common-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb-common-objs) + +snd-sb8-dsp.o: $(snd-sb8-dsp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-dsp-objs) + +snd-sb16-dsp.o: $(snd-sb16-dsp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-dsp-objs) + +snd-sb16-csp.o: $(snd-sb16-csp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-csp-objs) + +snd-sb8.o: $(snd-sb8-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb8-objs) + +snd-sb16.o: $(snd-sb16-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sb16-objs) + +snd-sbawe.o: $(snd-sbawe-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sbawe-objs) + +snd-emu8000-synth.o: $(snd-emu8000-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu8000-synth-objs) + +snd-es968.o: $(snd-es968-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es968-objs) diff -Nru linux/sound/isa/sb/emu8000.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000.c --- linux/sound/isa/sb/emu8000.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1192 @@ +/* + * Copyright (c) by Jaroslav Kysela + * and (c) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * Routines for control of EMU8000 chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe"); +MODULE_DESCRIPTION("Routines for control of EMU8000 chip"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#endif + +/* + * emu8000 register controls + */ + +/* + * The following routines read and write registers on the emu8000. They + * should always be called via the EMU8000*READ/WRITE macros and never + * directly. The macros handle the port number and command word. + */ +/* Write a word */ +void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + outw((unsigned short)val, port); /* Send data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +/* Read a word */ +unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg) +{ + unsigned short res; + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + res = inw(port); /* Read data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); + return res; +} + +/* Write a double word */ +void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + outw((unsigned short)val, port); /* Send low word of data */ + outw((unsigned short)(val>>16), port+2); /* Send high word of data */ + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +/* Read a double word */ +unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg) +{ + unsigned short low; + unsigned int res; + unsigned long flags; + spin_lock_irqsave(&emu->reg_lock, flags); + if (reg != emu->last_reg) { + outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ + emu->last_reg = reg; + } + low = inw(port); /* Read low word of data */ + res = low + (inw(port+2) << 16); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return res; +} + +/* + * Set up / close a channel to be used for DMA. + */ +/*exported*/ void +snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode) +{ + if (mode == EMU8000_RAM_CLOSE) { + EMU8000_CCCA_WRITE(emu, ch, 0); + EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F); + return; + } + EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); + EMU8000_VTFT_WRITE(emu, ch, 0); + EMU8000_CVCF_WRITE(emu, ch, 0); + EMU8000_PTRX_WRITE(emu, ch, 0x40000000); + EMU8000_CPF_WRITE(emu, ch, 0x40000000); + EMU8000_PSST_WRITE(emu, ch, 0); + EMU8000_CSL_WRITE(emu, ch, 0); + if (mode == EMU8000_RAM_WRITE) /* DMA write */ + EMU8000_CCCA_WRITE(emu, ch, 0x06000000); + else /* DMA read */ + EMU8000_CCCA_WRITE(emu, ch, 0x04000000); +} + +/* + */ +static void /*__init*/ +snd_emu8000_read_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + */ +static void /*__init*/ +snd_emu8000_write_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + * detect a card at the given port + */ +static int /*__init*/ +snd_emu8000_detect(emu8000_t *emu) +{ + /* Initialise */ + EMU8000_HWCF1_WRITE(emu, 0x0059); + EMU8000_HWCF2_WRITE(emu, 0x0020); + EMU8000_HWCF3_WRITE(emu, 0x0000); + /* Check for a recognisable emu8000 */ + /* + if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c) + return -ENODEV; + */ + if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058) + return -ENODEV; + if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003) + return -ENODEV; + + snd_printdd("EMU8000 [0x%lx]: Synth chip found\n", + emu->port1); + return 0; +} + + +/* + * intiailize audio channels + */ +static void /*__init*/ +init_audio(emu8000_t *emu) +{ + int ch; + + /* turn off envelope engines */ + for (ch = 0; ch < EMU8000_CHANNELS; ch++) + EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); + + /* reset all other parameters to zero */ + for (ch = 0; ch < EMU8000_CHANNELS; ch++) { + EMU8000_ENVVOL_WRITE(emu, ch, 0); + EMU8000_ENVVAL_WRITE(emu, ch, 0); + EMU8000_DCYSUS_WRITE(emu, ch, 0); + EMU8000_ATKHLDV_WRITE(emu, ch, 0); + EMU8000_LFO1VAL_WRITE(emu, ch, 0); + EMU8000_ATKHLD_WRITE(emu, ch, 0); + EMU8000_LFO2VAL_WRITE(emu, ch, 0); + EMU8000_IP_WRITE(emu, ch, 0); + EMU8000_IFATN_WRITE(emu, ch, 0); + EMU8000_PEFE_WRITE(emu, ch, 0); + EMU8000_FMMOD_WRITE(emu, ch, 0); + EMU8000_TREMFRQ_WRITE(emu, ch, 0); + EMU8000_FM2FRQ2_WRITE(emu, ch, 0); + EMU8000_PTRX_WRITE(emu, ch, 0); + EMU8000_VTFT_WRITE(emu, ch, 0); + EMU8000_PSST_WRITE(emu, ch, 0); + EMU8000_CSL_WRITE(emu, ch, 0); + EMU8000_CCCA_WRITE(emu, ch, 0); + } + + for (ch = 0; ch < EMU8000_CHANNELS; ch++) { + EMU8000_CPF_WRITE(emu, ch, 0); + EMU8000_CVCF_WRITE(emu, ch, 0); + } +} + + +/* + * initialize DMA address + */ +static void /*__init*/ +init_dma(emu8000_t *emu) +{ + EMU8000_SMALR_WRITE(emu, 0); + EMU8000_SMARR_WRITE(emu, 0); + EMU8000_SMALW_WRITE(emu, 0); + EMU8000_SMARW_WRITE(emu, 0); +} + +/* + * initialization arrays; from ADIP + */ +static unsigned short init1[128] /*__devinitdata*/ = { + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, + + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, + + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, + + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +static unsigned short init2[128] /*__devinitdata*/ = { + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, + + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, + + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, + + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +static unsigned short init3[128] /*__devinitdata*/ = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +static unsigned short init4[128] /*__devinitdata*/ = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +/* send an initialization array + * Taken from the oss driver, not obvious from the doc how this + * is meant to work + */ +static void /*__init*/ +send_array(emu8000_t *emu, unsigned short *data, int size) +{ + int i; + unsigned short *p; + + p = data; + for (i = 0; i < size; i++, p++) + EMU8000_INIT1_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT2_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT3_WRITE(emu, i, *p); + for (i = 0; i < size; i++, p++) + EMU8000_INIT4_WRITE(emu, i, *p); +} + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + + +/* + * Send initialization arrays to start up, this just follows the + * initialisation sequence in the adip. + */ +static void /*__init*/ +init_arrays(emu8000_t *emu) +{ + send_array(emu, init1, NELEM(init1)/4); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */ + send_array(emu, init2, NELEM(init2)/4); + send_array(emu, init3, NELEM(init3)/4); + + EMU8000_HWCF4_WRITE(emu, 0); + EMU8000_HWCF5_WRITE(emu, 0x83); + EMU8000_HWCF6_WRITE(emu, 0x8000); + + send_array(emu, init4, NELEM(init4)/4); +} + + +#define UNIQUE_ID1 0xa5b9 +#define UNIQUE_ID2 0x9d53 + +/* + * Size the onboard memory. + * This is written so as not to need arbitary delays after the write. It + * seems that the only way to do this is to use the one channel and keep + * reallocating between read and write. + */ +static void /*__init*/ +size_dram(emu8000_t *emu) +{ + int i, size; + + if (emu->dram_checked) + return; + + size = 0; + + /* write out a magic number */ + snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE); + snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ); + EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET); + EMU8000_SMLD_WRITE(emu, UNIQUE_ID1); + snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */ + + while (size < EMU8000_MAX_DRAM) { + + size += 512 * 1024; /* increment 512kbytes */ + + /* Write a unique data on the test address. + * if the address is out of range, the data is written on + * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is + * changed by this data. + */ + /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/ + EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); + EMU8000_SMLD_WRITE(emu, UNIQUE_ID2); + snd_emu8000_write_wait(emu); + + /* + * read the data on the just written DRAM address + * if not the same then we have reached the end of ram. + */ + /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/ + EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); + /*snd_emu8000_read_wait(emu);*/ + EMU8000_SMLD_READ(emu); /* discard stale data */ + if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2) + break; /* we must have wrapped around */ + + snd_emu8000_read_wait(emu); + + /* + * If it is the same it could be that the address just + * wraps back to the beginning; so check to see if the + * initial value has been overwritten. + */ + EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); + EMU8000_SMLD_READ(emu); /* discard stale data */ + if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) + break; /* we must have wrapped around */ + snd_emu8000_read_wait(emu); + } + + /* wait until FULL bit in SMAxW register is false */ + for (i = 0; i < 10000; i++) { + if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0) + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } + snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE); + snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE); + + snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n", + emu->port1, size/1024); + + emu->mem_size = size; + emu->dram_checked = 1; +} + + +/* + * Initiailise the FM section. You have to do this to use sample RAM + * and therefore lose 2 voices. + */ +/*exported*/ void +snd_emu8000_init_fm(emu8000_t *emu) +{ + unsigned long flags; + + /* Initialize the last two channels for DRAM refresh and producing + the reverb and chorus effects for Yamaha OPL-3 synthesizer */ + + /* 31: FM left channel, 0xffffe0-0xffffe8 */ + EMU8000_DCYSUSV_WRITE(emu, 30, 0x80); + EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */ + EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24)); + EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8)); + EMU8000_CPF_WRITE(emu, 30, 0); + EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3); + + /* 32: FM right channel, 0xfffff0-0xfffff8 */ + EMU8000_DCYSUSV_WRITE(emu, 31, 0x80); + EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */ + EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24)); + EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8)); + EMU8000_CPF_WRITE(emu, 31, 0x8000); + EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3); + + snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0); + + spin_lock_irqsave(&emu->reg_lock, flags); + while (!(inw(EMU8000_PTR(emu)) & 0x1000)) + ; + while ((inw(EMU8000_PTR(emu)) & 0x1000)) + ; + spin_unlock_irqrestore(&emu->reg_lock, flags); + snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828); + /* this is really odd part.. */ + outb(0x3C, EMU8000_PTR(emu)); + outb(0, EMU8000_DATA1(emu)); + + /* skew volume & cutoff */ + EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF); + EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF); +} + + +/* + * The main initialization routine. + */ +static void /*__init*/ +snd_emu8000_init_hw(emu8000_t *emu) +{ + int i; + + emu->last_reg = 0xffff; /* reset the last register index */ + + /* initialize hardware configuration */ + EMU8000_HWCF1_WRITE(emu, 0x0059); + EMU8000_HWCF2_WRITE(emu, 0x0020); + + /* disable audio; this seems to reduce a clicking noise a bit.. */ + EMU8000_HWCF3_WRITE(emu, 0); + + /* initialize audio channels */ + init_audio(emu); + + /* initialize DMA */ + init_dma(emu); + + /* initialize init arrays */ + init_arrays(emu); + + /* + * Initialize the FM section of the AWE32, this is needed + * for DRAM refresh as well + */ + snd_emu8000_init_fm(emu); + + /* terminate all voices */ + for (i = 0; i < EMU8000_DRAM_VOICES; i++) + EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F); + + /* check DRAM memory size */ + size_dram(emu); + + /* enable audio */ + EMU8000_HWCF3_WRITE(emu, 0x4); + + /* set equzlier, chorus and reverb modes */ + snd_emu8000_update_equalizer(emu); + snd_emu8000_update_chorus_mode(emu); + snd_emu8000_update_reverb_mode(emu); +} + + +/*---------------------------------------------------------------- + * Bass/Treble Equalizer + *----------------------------------------------------------------*/ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC384, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +/*exported*/ void +snd_emu8000_update_equalizer(emu8000_t *emu) +{ + unsigned short w; + int bass = emu->bass_level; + int treble = emu->treble_level; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]); + EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]); + EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]); + EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]); + EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]); + EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]); + EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]); + EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]); + EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]); + EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262)); + EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362)); +} + + +/*---------------------------------------------------------------- + * Chorus mode control + *----------------------------------------------------------------*/ + +/* + * chorus mode parameters + */ +#define SNDRV_EMU8000_CHORUS_1 0 +#define SNDRV_EMU8000_CHORUS_2 1 +#define SNDRV_EMU8000_CHORUS_3 2 +#define SNDRV_EMU8000_CHORUS_4 3 +#define SNDRV_EMU8000_CHORUS_FEEDBACK 4 +#define SNDRV_EMU8000_CHORUS_FLANGER 5 +#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6 +#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7 +#define SNDRV_EMU8000_CHORUS_PREDEFINED 8 +/* user can define chorus modes up to 32 */ +#define SNDRV_EMU8000_CHORUS_NUMBERS 32 + +typedef struct soundfont_chorus_fx_t { + unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ + unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ + unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ + unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ + unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ +} soundfont_chorus_fx_t; + +/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ +static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; +static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */ +}; + +/*exported*/ int +snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len) +{ + soundfont_chorus_fx_t rec; + if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) { + snd_printk("illegal chorus mode %d for uploading\n", mode); + return -EINVAL; + } + if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) + return -EFAULT; + chorus_parm[mode] = rec; + chorus_defined[mode] = 1; + return 0; +} + +/*exported*/ void +snd_emu8000_update_chorus_mode(emu8000_t *emu) +{ + int effect = emu->chorus_mode; + if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS || + (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback); + EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset); + EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth); + EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay); + EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq); + EMU8000_HWCF6_WRITE(emu, 0x8000); + EMU8000_HWCF7_WRITE(emu, 0x0000); +} + +/*---------------------------------------------------------------- + * Reverb mode control + *----------------------------------------------------------------*/ + +/* + * reverb mode parameters + */ +#define SNDRV_EMU8000_REVERB_ROOM1 0 +#define SNDRV_EMU8000_REVERB_ROOM2 1 +#define SNDRV_EMU8000_REVERB_ROOM3 2 +#define SNDRV_EMU8000_REVERB_HALL1 3 +#define SNDRV_EMU8000_REVERB_HALL2 4 +#define SNDRV_EMU8000_REVERB_PLATE 5 +#define SNDRV_EMU8000_REVERB_DELAY 6 +#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7 +#define SNDRV_EMU8000_REVERB_PREDEFINED 8 +/* user can define reverb modes up to 32 */ +#define SNDRV_EMU8000_REVERB_NUMBERS 32 + +typedef struct soundfont_reverb_fx_t { + unsigned short parms[28]; +} soundfont_reverb_fx_t; + +/* reverb mode settings; write the following 28 data of 16 bit length + * on the corresponding ports in the reverb_cmds array + */ +static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; +static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = { +{{ /* room 1 */ + 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, + 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 2 */ + 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 3 */ + 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, + 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, +}}, +{{ /* hall 1 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, + 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, +}}, +{{ /* hall 2 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, + 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, + 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* plate */ + 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, + 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* delay */ + 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +{{ /* panning delay */ + 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +}; + +enum { DATA1, DATA2 }; +#define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1 +#define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2 +#define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1 +#define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2 + +static struct reverb_cmd_pair { + unsigned short cmd, port; +} reverb_cmds[28] = { + {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, + {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, + {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, + {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, + {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, + {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, + {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, +}; + +/*exported*/ int +snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len) +{ + soundfont_reverb_fx_t rec; + + if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) { + snd_printk("illegal reverb mode %d for uploading\n", mode); + return -EINVAL; + } + if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) + return -EFAULT; + reverb_parm[mode] = rec; + reverb_defined[mode] = 1; + return 0; +} + +/*exported*/ void +snd_emu8000_update_reverb_mode(emu8000_t *emu) +{ + int effect = emu->reverb_mode; + int i; + + if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS || + (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect])) + return; + for (i = 0; i < 28; i++) { + int port; + if (reverb_cmds[i].port == DATA1) + port = EMU8000_DATA1(emu); + else + port = EMU8000_DATA2(emu); + snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]); + } +} + + +/*---------------------------------------------------------------- + * mixer interface + *----------------------------------------------------------------*/ + +#define chip_t emu8000_t + +/* + * bass/treble + */ +static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 11; + return 0; +} + +static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level; + return 0; +} + +static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + val1 = ucontrol->value.integer.value[0] % 12; + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + change = val1 != emu->treble_level; + emu->treble_level = val1; + } else { + change = val1 != emu->bass_level; + emu->bass_level = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + snd_emu8000_update_equalizer(emu); + return change; +} + +static snd_kcontrol_new_t mixer_bass_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Synth Tone Control - Bass", + info: mixer_bass_treble_info, + get: mixer_bass_treble_get, + put: mixer_bass_treble_put, + private_value: 0, +}; + +static snd_kcontrol_new_t mixer_treble_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Synth Tone Control - Treble", + info: mixer_bass_treble_info, + get: mixer_bass_treble_get, + put: mixer_bass_treble_put, + private_value: 1, +}; + +/* + * chorus/reverb mode + */ +static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1); + return 0; +} + +static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode; + return 0; +} + +static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS; + change = val1 != emu->chorus_mode; + emu->chorus_mode = val1; + } else { + val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS; + change = val1 != emu->reverb_mode; + emu->reverb_mode = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + if (change) { + if (kcontrol->private_value) + snd_emu8000_update_chorus_mode(emu); + else + snd_emu8000_update_reverb_mode(emu); + } + return change; +} + +static snd_kcontrol_new_t mixer_chorus_mode_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Chorus Mode", + info: mixer_chorus_reverb_info, + get: mixer_chorus_reverb_get, + put: mixer_chorus_reverb_put, + private_value: 1, +}; + +static snd_kcontrol_new_t mixer_reverb_mode_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Reverb Mode", + info: mixer_chorus_reverb_info, + get: mixer_chorus_reverb_get, + put: mixer_chorus_reverb_put, + private_value: 0, +}; + +/* + * FM OPL3 chorus/reverb depth + */ +static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth; + return 0; +} + +static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu8000_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned short val1; + + val1 = ucontrol->value.integer.value[0] % 256; + spin_lock_irqsave(&emu->control_lock, flags); + if (kcontrol->private_value) { + change = val1 != emu->fm_chorus_depth; + emu->fm_chorus_depth = val1; + } else { + change = val1 != emu->fm_reverb_depth; + emu->fm_reverb_depth = val1; + } + spin_unlock_irqrestore(&emu->control_lock, flags); + if (change) + snd_emu8000_init_fm(emu); + return change; +} + +static snd_kcontrol_new_t mixer_fm_chorus_depth_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "FM Chorus Depth", + info: mixer_fm_depth_info, + get: mixer_fm_depth_get, + put: mixer_fm_depth_put, + private_value: 1, +}; + +static snd_kcontrol_new_t mixer_fm_reverb_depth_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "FM Reverb Depth", + info: mixer_fm_depth_info, + get: mixer_fm_depth_get, + put: mixer_fm_depth_put, + private_value: 0, +}; + + +static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = { + &mixer_bass_control, + &mixer_treble_control, + &mixer_chorus_mode_control, + &mixer_reverb_mode_control, + &mixer_fm_chorus_depth_control, + &mixer_fm_reverb_depth_control, +}; + +/* + * create and attach mixer elements for WaveTable treble/bass controls + */ +static int /*__init*/ +snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu) +{ + int i, err = 0; + + snd_assert(emu != NULL && card != NULL, return -EINVAL); + + spin_lock_init(&emu->control_lock); + + memset(emu->controls, 0, sizeof(emu->controls)); + for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { + if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0) + goto __error; + } + return 0; + +__error: + for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { + if (emu->controls[i]) + snd_ctl_remove(card, emu->controls[i]); + } + return err; +} + + +/* + * free resources + */ +static int snd_emu8000_free(emu8000_t *hw) +{ + if (hw->res_port1) { + release_resource(hw->res_port1); + kfree_nocheck(hw->res_port1); + } + if (hw->res_port2) { + release_resource(hw->res_port2); + kfree_nocheck(hw->res_port2); + } + if (hw->res_port3) { + release_resource(hw->res_port3); + kfree_nocheck(hw->res_port3); + } + snd_magic_kfree(hw); + return 0; +} + +/* + */ +static int snd_emu8000_dev_free(snd_device_t *device) +{ + emu8000_t *hw = snd_magic_cast(emu8000_t, device->device_data, return -ENXIO); + return snd_emu8000_free(hw); +} + +/* + * initialize and register emu8000 synth device. + */ +/*exported*/ int +snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret) +{ + snd_seq_device_t *awe; + emu8000_t *hw; + int err; + static snd_device_ops_t ops = { + dev_free: snd_emu8000_dev_free, + }; + + if (awe_ret) + *awe_ret = NULL; + + if (seq_ports <= 0) + return 0; + + hw = snd_magic_kcalloc(emu8000_t, 0, GFP_KERNEL); + if (hw == NULL) + return -ENOMEM; + spin_lock_init(&hw->reg_lock); + hw->index = index; + hw->port1 = port; + hw->port2 = port + 0x400; + hw->port3 = port + 0x800; + if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) || + !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) || + !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) { + snd_emu8000_free(hw); + return -EBUSY; + } + hw->mem_size = 0; + hw->card = card; + hw->seq_ports = seq_ports; + hw->bass_level = 5; + hw->treble_level = 9; + hw->chorus_mode = 2; + hw->reverb_mode = 4; + hw->fm_chorus_depth = 0; + hw->fm_reverb_depth = 0; + + if (snd_emu8000_detect(hw) < 0) { + snd_emu8000_free(hw); + return -ENODEV; + } + + snd_emu8000_init_hw(hw); + if ((err = snd_emu8000_create_mixer(card, hw)) < 0) { + snd_emu8000_free(hw); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hw, &ops)) < 0) { + snd_emu8000_free(hw); + return err; + } + if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000, + sizeof(emu8000_t*), &awe) >= 0) { + strcpy(awe->name, "EMU-8000"); + *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw; + } + if (awe_ret) + *awe_ret = awe; + + return 0; +} + + +/* + * exported stuff + */ + +EXPORT_SYMBOL(snd_emu8000_new); +EXPORT_SYMBOL(snd_emu8000_poke); +EXPORT_SYMBOL(snd_emu8000_peek); +EXPORT_SYMBOL(snd_emu8000_poke_dw); +EXPORT_SYMBOL(snd_emu8000_peek_dw); +EXPORT_SYMBOL(snd_emu8000_dma_chan); +EXPORT_SYMBOL(snd_emu8000_init_fm); +EXPORT_SYMBOL(snd_emu8000_load_chorus_fx); +EXPORT_SYMBOL(snd_emu8000_load_reverb_fx); +EXPORT_SYMBOL(snd_emu8000_update_chorus_mode); +EXPORT_SYMBOL(snd_emu8000_update_reverb_mode); +EXPORT_SYMBOL(snd_emu8000_update_equalizer); + +#if 0 +/* + * INIT part + */ + +static int __init alsa_emu8000_init(void) +{ + return 0; +} + +static void __exit alsa_emu8000_exit(void) +{ +} + +module_init(alsa_emu8000_init) +module_exit(alsa_emu8000_exit) +#endif diff -Nru linux/sound/isa/sb/emu8000_callback.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_callback.c --- linux/sound/isa/sb/emu8000_callback.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_callback.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,540 @@ +/* + * synth callback routines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emu8000_local.h" +#include + +/* + * prototypes + */ +static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port); +static int start_voice(snd_emux_voice_t *vp); +static void trigger_voice(snd_emux_voice_t *vp); +static void release_voice(snd_emux_voice_t *vp); +static void update_voice(snd_emux_voice_t *vp, int update); +static void reset_voice(snd_emux_t *emu, int ch); +static void terminate_voice(snd_emux_voice_t *vp); +static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +#ifdef CONFIG_SND_OSSEMUL +static int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2); +#endif +static int load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len); + +static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp); +static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp); +static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch); + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + + +/* + * set up operators + */ +static snd_emux_operators_t emu8000_ops = { + owner: THIS_MODULE, + get_voice: get_voice, + prepare: start_voice, + trigger: trigger_voice, + release: release_voice, + update: update_voice, + terminate: terminate_voice, + reset: reset_voice, + sample_new: snd_emu8000_sample_new, + sample_free: snd_emu8000_sample_free, + sample_reset: snd_emu8000_sample_reset, + load_fx: load_fx, + sysex: sysex, +#ifdef CONFIG_SND_OSSEMUL + oss_ioctl: oss_ioctl, +#endif +}; + +void +snd_emu8000_ops_setup(emu8000_t *hw) +{ + hw->emu->ops = emu8000_ops; +} + + + +/* + * Terminate a voice + */ +static void +release_voice(snd_emux_voice_t *vp) +{ + int dcysusv; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; + EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease; + EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv); +} + + +/* + */ +static void +terminate_voice(snd_emux_voice_t *vp) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F); +} + + +/* + */ +static void +update_voice(snd_emux_voice_t *vp, int update) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + if (update & SNDRV_EMUX_UPDATE_VOLUME) + set_volume(hw, vp); + if (update & SNDRV_EMUX_UPDATE_PITCH) + set_pitch(hw, vp); + if ((update & SNDRV_EMUX_UPDATE_PAN) && + vp->port->ctrls[EMUX_MD_REALTIME_PAN]) + set_pan(hw, vp); + if (update & SNDRV_EMUX_UPDATE_FMMOD) + set_fmmod(hw, vp); + if (update & SNDRV_EMUX_UPDATE_TREMFREQ) + set_tremfreq(hw, vp); + if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) + set_fm2frq2(hw, vp); + if (update & SNDRV_EMUX_UPDATE_Q) + set_filterQ(hw, vp); +} + + +/* + * Find a channel (voice) within the EMU that is not in use or at least + * less in use than other channels. Always returns a valid pointer + * no matter what. If there is a real shortage of voices then one + * will be cut. Such is life. + * + * The channel index (vp->ch) must be initialized in this routine. + * In Emu8k, it is identical with the array index. + */ +static snd_emux_voice_t * +get_voice(snd_emux_t *emu, snd_emux_port_t *port) +{ + int i; + snd_emux_voice_t *vp; + emu8000_t *hw; + + /* what we are looking for, in order of preference */ + enum { + OFF=0, RELEASED, PLAYING, END + }; + + /* Keeps track of what we are finding */ + struct best { + unsigned int time; + int voice; + } best[END]; + struct best *bp; + + hw = snd_magic_cast(emu8000_t, emu->hw, return NULL); + + for (i = 0; i < END; i++) { + best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* + * Go through them all and get a best one to use. + */ + for (i = 0; i < emu->max_voices; i++) { + int state, val; + + vp = &emu->voices[i]; + state = vp->state; + + if (state == SNDRV_EMUX_ST_OFF) + bp = best + OFF; + else if (state == SNDRV_EMUX_ST_RELEASED || + state == SNDRV_EMUX_ST_PENDING) { + bp = best + RELEASED; + val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff; + if (! val) + bp = best + OFF; + } + else if (state & SNDRV_EMUX_ST_ON) + bp = best + PLAYING; + else + continue; + + /* check if sample is finished playing (non-looping only) */ + if (state != SNDRV_EMUX_ST_OFF && + (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { + val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; + if (val >= vp->reg.loopstart) + bp = best + OFF; + } + + if (vp->time < bp->time) { + bp->time = vp->time; + bp->voice = i; + } + } + + for (i = 0; i < END; i++) { + if (best[i].voice >= 0) { + vp = &emu->voices[best[i].voice]; + vp->ch = best[i].voice; + return vp; + } + } + + /* not found */ + return NULL; +} + +/* + */ +static int +start_voice(snd_emux_voice_t *vp) +{ + unsigned int temp; + int ch; + int addr; + snd_midi_channel_t *chan; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return -EINVAL); + ch = vp->ch; + chan = vp->chan; + + /* channel to be silent and idle */ + EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); + EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); + EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); + EMU8000_PTRX_WRITE(hw, ch, 0); + EMU8000_CPF_WRITE(hw, ch, 0); + + /* set pitch offset */ + set_pitch(hw, vp); + + /* set envelope parameters */ + EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay); + EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld); + EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus); + EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay); + EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld); + /* decay/sustain parameter for volume envelope is used + for triggerg the voice */ + + /* cutoff and volume */ + set_volume(hw, vp); + + /* modulation envelope heights */ + EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe); + + /* lfo1/2 delay */ + EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay); + EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay); + + /* lfo1 pitch & cutoff shift */ + set_fmmod(hw, vp); + /* lfo1 volume & freq */ + set_tremfreq(hw, vp); + /* lfo2 pitch & freq */ + set_fm2frq2(hw, vp); + /* pan & loop start */ + set_pan(hw, vp); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->reg.loopend - 1; + temp = vp->reg.parm.chorus; + temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp <<24) | (unsigned int)addr; + EMU8000_CSL_WRITE(hw, ch, temp); + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->reg.start - 1; + temp = vp->reg.parm.filterQ; + temp = (temp<<28) | (unsigned int)addr; + EMU8000_CCCA_WRITE(hw, ch, temp); + + /* clear unknown registers */ + EMU8000_00A0_WRITE(hw, ch, 0); + EMU8000_0080_WRITE(hw, ch, 0); + + /* reset volume */ + temp = vp->vtarget << 16; + EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget); + EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00); + + return 0; +} + +/* + * Start envelope + */ +static void +trigger_voice(snd_emux_voice_t *vp) +{ + int ch = vp->ch; + unsigned int temp; + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, vp->hw, return); + + /* set reverb and pitch target */ + temp = vp->reg.parm.reverb; + temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux; + EMU8000_PTRX_WRITE(hw, ch, temp); + EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16); + EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus); +} + +/* + * reset voice parameters + */ +static void +reset_voice(snd_emux_t *emu, int ch) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return); + EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); + snd_emu8000_tweak_voice(hw, ch); +} + +/* + * Set the pitch of a possibly playing note. + */ +static void +set_pitch(emu8000_t *hw, snd_emux_voice_t *vp) +{ + EMU8000_IP_WRITE(hw, vp->ch, vp->apitch); +} + +/* + * Set the volume of a possibly already playing note + */ +static void +set_volume(emu8000_t *hw, snd_emux_voice_t *vp) +{ + int ifatn; + + ifatn = (unsigned char)vp->acutoff; + ifatn = (ifatn << 8); + ifatn |= (unsigned char)vp->avol; + EMU8000_IFATN_WRITE(hw, vp->ch, ifatn); +} + +/* + * Set pan and loop start address. + */ +static void +set_pan(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned int temp; + + temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1); + EMU8000_PSST_WRITE(hw, vp->ch, temp); +} + +#define MOD_SENSE 18 + +static void +set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fmmod; + short pitch; + unsigned char cutoff; + int modulation; + + pitch = (char)(vp->reg.parm.fmmod>>8); + cutoff = (vp->reg.parm.fmmod & 0xff); + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fmmod = ((unsigned char)pitch<<8) | cutoff; + EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod); +} + +/* set tremolo (lfo1) volume & frequency */ +static void +set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp) +{ + EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq); +} + +/* set lfo2 pitch & frequency */ +static void +set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fm2frq2; + short pitch; + unsigned char freq; + int modulation; + + pitch = (char)(vp->reg.parm.fm2frq2>>8); + freq = vp->reg.parm.fm2frq2 & 0xff; + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fm2frq2 = ((unsigned char)pitch<<8) | freq; + EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2); +} + +/* set filterQ */ +static void +set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp) +{ + unsigned int addr; + addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; + addr |= (vp->reg.parm.filterQ << 28); + EMU8000_CCCA_WRITE(hw, vp->ch, addr); +} + +/* + * set the envelope & LFO parameters to the default values + */ +static void +snd_emu8000_tweak_voice(emu8000_t *emu, int i) +{ + /* set all mod/vol envelope shape to minimum */ + EMU8000_ENVVOL_WRITE(emu, i, 0x8000); + EMU8000_ENVVAL_WRITE(emu, i, 0x8000); + EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F); + EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F); + EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F); + EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */ + EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */ + EMU8000_LFO2VAL_WRITE(emu, i, 0x8000); + EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */ + EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */ + EMU8000_FMMOD_WRITE(emu, i, 0); + EMU8000_TREMFRQ_WRITE(emu, i, 0); + EMU8000_FM2FRQ2_WRITE(emu, i, 0); +} + +/* + * sysex callback + */ +static void +sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return); + + switch (parsed) { + case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE: + hw->chorus_mode = chset->gs_chorus_mode; + snd_emu8000_update_chorus_mode(hw); + break; + + case SNDRV_MIDI_SYSEX_GS_REVERB_MODE: + hw->reverb_mode = chset->gs_reverb_mode; + snd_emu8000_update_reverb_mode(hw); + break; + } +} + + +#ifdef CONFIG_SND_OSSEMUL +/* + * OSS ioctl callback + */ +static int +oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2) +{ + emu8000_t *hw; + + hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + + switch (cmd) { + case _EMUX_OSS_REVERB_MODE: + hw->reverb_mode = p1; + snd_emu8000_update_reverb_mode(hw); + break; + + case _EMUX_OSS_CHORUS_MODE: + hw->chorus_mode = p1; + snd_emu8000_update_chorus_mode(hw); + break; + + case _EMUX_OSS_INITIALIZE_CHIP: + /* snd_emu8000_init(hw); */ /*ignored*/ + break; + + case _EMUX_OSS_EQUALIZER: + hw->bass_level = p1; + hw->treble_level = p2; + snd_emu8000_update_equalizer(hw); + break; + } + return 0; +} +#endif + + +/* + * additional patch keys + */ + +#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */ +#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */ + + +/* + * callback routine + */ + +static int +load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len) +{ + emu8000_t *hw; + hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + + switch (type) { + case SNDRV_EMU8000_LOAD_CHORUS_FX: + return snd_emu8000_load_chorus_fx(hw, mode, buf, len); + case SNDRV_EMU8000_LOAD_REVERB_FX: + return snd_emu8000_load_reverb_fx(hw, mode, buf, len); + } + return -EINVAL; +} + diff -Nru linux/sound/isa/sb/emu8000_local.h linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_local.h --- linux/sound/isa/sb/emu8000_local.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_local.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,42 @@ +#ifndef __EMU8000_LOCAL_H +#define __EMU8000_LOCAL_H +/* + * Local defininitons for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + +/* emu8000_patch.c */ +int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count); +int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); +void snd_emu8000_sample_reset(snd_emux_t *rec); + +/* emu8000_callback.c */ +void snd_emu8000_ops_setup(emu8000_t *emu); + +#endif /* __EMU8000_LOCAL_H */ diff -Nru linux/sound/isa/sb/emu8000_patch.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_patch.c --- linux/sound/isa/sb/emu8000_patch.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_patch.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,308 @@ +/* + * Patch routines for the emu8000 (AWE32/64) + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emu8000_local.h" +#include + +MODULE_PARM(emu8000_reset_addr, "i"); +MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)"); + +int emu8000_reset_addr = 0; + + +/* + * Open up channels. + */ +static int +snd_emu8000_open_dma(emu8000_t *emu, int write) +{ + int i; + + /* reserve all 30 voices for loading */ + for (i = 0; i < EMU8000_DRAM_VOICES; i++) { + snd_emux_lock_voice(emu->emu, i); + snd_emu8000_dma_chan(emu, i, write); + } + + /* assign voice 31 and 32 to ROM */ + EMU8000_VTFT_WRITE(emu, 30, 0); + EMU8000_PSST_WRITE(emu, 30, 0x1d8); + EMU8000_CSL_WRITE(emu, 30, 0x1e0); + EMU8000_CCCA_WRITE(emu, 30, 0x1d8); + EMU8000_VTFT_WRITE(emu, 31, 0); + EMU8000_PSST_WRITE(emu, 31, 0x1d8); + EMU8000_CSL_WRITE(emu, 31, 0x1e0); + EMU8000_CCCA_WRITE(emu, 31, 0x1d8); + + return 0; +} + +/* + * Close all dram channels. + */ +static void +snd_emu8000_close_dma(emu8000_t *emu) +{ + int i; + + for (i = 0; i < EMU8000_DRAM_VOICES; i++) { + snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE); + snd_emux_unlock_voice(emu->emu, i); + } +} + +/* + */ + +#define BLANK_LOOP_START 4 +#define BLANK_LOOP_END 8 +#define BLANK_LOOP_SIZE 12 +#define BLANK_HEAD_SIZE 48 + +/* + * Read a word from userland, taking care of conversions from + * 8bit samples etc. + */ +static unsigned short +read_word(const void *buf, int offset, int mode) +{ + unsigned short c; + if (mode & SNDRV_SFNT_SAMPLE_8BITS) { + unsigned char cc; + get_user(cc, (unsigned char*)buf + offset); + c = cc << 8; /* convert 8bit -> 16bit */ + } else { +#ifdef SNDRV_LITTLE_ENDIAN + get_user(c, (unsigned short*)buf + offset); +#else + unsigned short cc; + get_user(cc, (unsigned short*)buf + offset); + c = swab16(cc); +#endif + } + if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED) + c ^= 0x8000; /* unsigned -> signed */ + return c; +} + +/* + */ +static void +snd_emu8000_write_wait(emu8000_t *emu) +{ + while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + } +} + +/* + * write sample word data + * + * You should not have to keep resetting the address each time + * as the chip is supposed to step on the next address automatically. + * It mostly does, but during writes of some samples at random it + * completely loses words (every one in 16 roughly but with no + * obvious pattern). + * + * This is therefore much slower than need be, but is at least + * working. + */ +inline static void +write_word(emu8000_t *emu, int *offset, unsigned short data) +{ + if (emu8000_reset_addr) { + if (emu8000_reset_addr > 1) + snd_emu8000_write_wait(emu); + EMU8000_SMALW_WRITE(emu, *offset); + } + EMU8000_SMLD_WRITE(emu, data); + *offset += 1; +} + +/* + * Write the sample to EMU800 memory. This routine is invoked out of + * the generic soundfont routines as a callback. + */ +int +snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *data, long count) +{ + int i; + int rc; + int offset; + int truesize; + int dram_offset, dram_start; + emu8000_t *emu; + + emu = snd_magic_cast(emu8000_t, rec->hw, return -EINVAL); + snd_assert(sp != NULL, return -EINVAL); + + if (sp->v.size == 0) + return 0; + + /* be sure loop points start < end */ + if (sp->v.loopstart > sp->v.loopend) { + int tmp = sp->v.loopstart; + sp->v.loopstart = sp->v.loopend; + sp->v.loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->v.size; + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) + truesize += sp->v.loopend - sp->v.loopstart; + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + + sp->block = snd_util_mem_alloc(hdr, truesize * 2); + if (sp->block == NULL) { + /*snd_printd("EMU8000: out of memory\n");*/ + /* not ENOMEM (for compatibility) */ + return -ENOSPC; + } + + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { + if (verify_area(VERIFY_READ, data, sp->v.size)) + return -EFAULT; + } else { + if (verify_area(VERIFY_READ, data, sp->v.size * 2)) + return -EFAULT; + } + + /* recalculate address offset */ + sp->v.end -= sp->v.start; + sp->v.loopstart -= sp->v.start; + sp->v.loopend -= sp->v.start; + sp->v.start = 0; + + /* dram position (in word) -- mem_offset is byte */ + dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1); + dram_start = dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + sp->v.truesize = truesize * 2; /* in bytes */ + + snd_emux_terminate_all(emu->emu); + if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0) + return rc; + + /* Set the address to start writing at */ + snd_emu8000_write_wait(emu); + EMU8000_SMALW_WRITE(emu, dram_offset); + + /*snd_emu8000_init_fm(emu);*/ + +#if 0 + /* first block - write 48 samples for silence */ + if (! sp->block->offset) { + for (i = 0; i < BLANK_HEAD_SIZE; i++) { + write_word(emu, &dram_offset, 0); + } + } +#endif + + offset = 0; + for (i = 0; i < sp->v.size; i++) { + unsigned short s; + + s = read_word(data, offset, sp->v.mode_flags); + offset++; + write_word(emu, &dram_offset, s); + + /* we may take too long time in this loop. + * so give controls back to kernel if needed. + */ + if (current->need_resched) { + if (current->state != TASK_RUNNING) + set_current_state(TASK_RUNNING); + schedule(); + } + + if (i == sp->v.loopend && + (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))) + { + int looplen = sp->v.loopend - sp->v.loopstart; + int k; + + /* copy reverse loop */ + for (k = 1; k <= looplen; k++) { + s = read_word(data, offset - k, sp->v.mode_flags); + write_word(emu, &dram_offset, s); + } + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { + sp->v.loopend += looplen; + } else { + sp->v.loopstart += looplen; + sp->v.loopend += looplen; + } + sp->v.end += looplen; + } + } + + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { + for (i = 0; i < BLANK_LOOP_SIZE; i++) { + write_word(emu, &dram_offset, 0); + } + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + + /* add dram offset */ + sp->v.start += dram_start; + sp->v.end += dram_start; + sp->v.loopstart += dram_start; + sp->v.loopend += dram_start; + + snd_emu8000_close_dma(emu); + snd_emu8000_init_fm(emu); + + return 0; +} + +/* + * free a sample block + */ +int +snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr) +{ + if (sp->block) { + snd_util_mem_free(hdr, sp->block); + sp->block = NULL; + } + return 0; +} + + +/* + * sample_reset callback - terminate voices + */ +void +snd_emu8000_sample_reset(snd_emux_t *rec) +{ + snd_emux_terminate_all(rec); +} diff -Nru linux/sound/isa/sb/emu8000_synth.c linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_synth.c --- linux/sound/isa/sb/emu8000_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/emu8000_synth.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,130 @@ +/* + * Copyright (c) by Jaroslav Kysela + * and (c) 1999 Steve Ratcliffe + * Copyright (C) 1999-2000 Takashi Iwai + * + * Emu8000 synth plug-in routine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu8000_local.h" +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe"); +MODULE_DESCRIPTION("Emu8000 synth plug-in routine"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +/*----------------------------------------------------------------*/ + +/* + * create a new hardware dependent device for Emu8000 + */ +static int snd_emu8000_new_device(snd_seq_device_t *dev) +{ + emu8000_t *hw; + snd_emux_t *emu; + + hw = *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (hw == NULL) + return -EINVAL; + + if (hw->emu) + return -EBUSY; /* already exists..? */ + + if (snd_emux_new(&emu) < 0) + return -ENOMEM; + + hw->emu = emu; + snd_emu8000_ops_setup(hw); + + emu->hw = hw; + emu->max_voices = EMU8000_DRAM_VOICES; + emu->num_ports = hw->seq_ports; + + if (hw->memhdr) { + snd_printk("memhdr is already initialized!?\n"); + snd_util_memhdr_free(hw->memhdr); + } + hw->memhdr = snd_util_memhdr_new(hw->mem_size); + if (hw->memhdr == NULL) { + snd_emux_free(emu); + hw->emu = NULL; + return -ENOMEM; + } + + emu->memhdr = hw->memhdr; + emu->midi_ports = hw->seq_ports < 2 ? hw->seq_ports : 2; /* number of virmidi ports */ + emu->midi_devidx = 1; + + if (snd_emux_register(emu, dev->card, hw->index, "Emu8000") < 0) { + snd_emux_free(emu); + snd_util_memhdr_free(hw->memhdr); + hw->emu = NULL; + hw->memhdr = NULL; + return -ENOMEM; + } + + dev->driver_data = hw; + + return 0; +} + + +/* + * free all resources + */ +static int snd_emu8000_delete_device(snd_seq_device_t *dev) +{ + emu8000_t *hw; + + if (dev->driver_data == NULL) + return 0; /* no synth was allocated actually */ + + hw = dev->driver_data; + if (hw->emu) + snd_emux_free(hw->emu); + if (hw->memhdr) + snd_util_memhdr_free(hw->memhdr); + hw->emu = NULL; + hw->memhdr = NULL; + return 0; +} + +/* + * INIT part + */ + +static int __init alsa_emu8000_init(void) +{ + + static snd_seq_dev_ops_t ops = { + snd_emu8000_new_device, + snd_emu8000_delete_device, + }; + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, sizeof(emu8000_t*)); +} + +static void __exit alsa_emu8000_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); +} + +module_init(alsa_emu8000_init) +module_exit(alsa_emu8000_exit) diff -Nru linux/sound/isa/sb/es968.c linux-2.4.19-pre5-mjc/sound/isa/sb/es968.c --- linux/sound/isa/sb/es968.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/es968.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,302 @@ + +/* + card-es968.c - driver for ESS AudioDrive ES968 based soundcards. + Copyright (C) 1999 by Massimo Piccioni + + Thanks to Pierfrancesco 'qM2' Passerini. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include + +#define chip_t sb_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Massimo Piccioni "); +MODULE_DESCRIPTION("ESS AudioDrive ES968"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,AudioDrive ES968}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for es968 based soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for es968 based soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable es968 based soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for es968 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for es968 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for es968 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +struct snd_card_es968 { +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#endif /* __ISAPNP__ */ +}; + +static snd_card_t *snd_es968_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ +static struct isapnp_card *snd_es968_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_es968_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_es968_pnpids[] __devinitdata = { + { + ISAPNP_CARD_ID('E','S','S',0x0968), + devs: { ISAPNP_DEVICE_ID('E','S','S',0x0968), } + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_es968_pnpids); + +#endif /* __ISAPNP__ */ + +#define DRIVER_NAME "snd-card-es968" + + +static void snd_card_es968_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + + if (chip->open & SB_OPEN_PCM) { + snd_sb8dsp_interrupt(chip); + } else { + snd_sb8dsp_midi_interrupt(chip); + } +} + +#ifdef __ISAPNP__ +static int __init snd_card_es968_isapnp(int dev, struct snd_card_es968 *acard) +{ + const struct isapnp_card_id *id = snd_es968_isapnp_id[dev]; + struct isapnp_card *card = snd_es968_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } + + pdev = acard->dev; + if (pdev->prepare(pdev)<0) + return -EAGAIN; + + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], + 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("AUDIO isapnp configure failure\n"); + return -EBUSY; + } + + snd_port[dev] = pdev->resource[0].start; + snd_dma8[dev] = pdev->dma_resource[0].start; + snd_irq[dev] = pdev->irq_resource[0].start; + + return 0; +} + +static void snd_card_es968_deactivate(struct snd_card_es968 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +} +#endif /* __ISAPNP__ */ + +static void __exit snd_card_es968_free(snd_card_t *card) +{ + struct snd_card_es968 *acard = (struct snd_card_es968 *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_card_es968_deactivate(acard); +#endif /* __ISAPNP__ */ + } +} + +static int __init snd_card_es968_probe(int dev) +{ + int error; + sb_t *chip; + snd_card_t *card; + struct snd_card_es968 *acard; + + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_card_es968))) == NULL) + return -ENOMEM; + acard = (struct snd_card_es968 *)card->private_data; + card->private_free = snd_card_es968_free; + +#ifdef __ISAPNP__ + if ((error = snd_card_es968_isapnp(dev, acard))) { + snd_card_free(card); + return error; + } +#else + snd_printk("you have to enable PnP support ...\n"); + snd_card_free(card); + return -ENOSYS; +#endif /* __ISAPNP__ */ + + if ((error = snd_sbdsp_create(card, snd_port[dev], + snd_irq[dev], + snd_card_es968_interrupt, + snd_dma8[dev], + -1, + SB_HW_AUTO, &chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return error; + } + + if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return error; + } + + strcpy(card->driver, "ES968"); + strcpy(card->shortname, "ESS ES968"); + sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d", + card->shortname, chip->name, chip->port, snd_irq[dev], snd_dma8[dev]); + + if ((error = snd_card_register(card)) < 0) { + snd_card_free(card); + return error; + } + snd_es968_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ +static int __init snd_es968_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; + snd_es968_isapnp_cards[dev] = card; + snd_es968_isapnp_id[dev] = id; + res = snd_card_es968_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} +#endif /* __ISAPNP__ */ + +static int __init alsa_card_es968_init(void) +{ + int cards = 0; + +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_es968_pnpids, snd_es968_isapnp_detect); +#else + snd_printk("you have to enable ISA PnP support.\n"); +#endif +#ifdef MODULE + if (!cards) + snd_printk("no ES968 based soundcards found\n"); +#endif + return cards ? 0 : -ENODEV; +} + +static void __exit alsa_card_es968_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_es968_cards[dev]); +} + +module_init(alsa_card_es968_init) +module_exit(alsa_card_es968_exit) + +#ifndef MODULE + +/* format is: snd-es968=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_dma1 */ + +static int __init alsa_card_es968_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es968=", alsa_card_es968_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/sb/sb16.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb16.c --- linux/sound/isa/sb/sb16.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,711 @@ +/* + * Driver for SoundBlaster 16/AWE32/AWE64 soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +#define chip_t sb_t + +#ifdef SNDRV_SBAWE +#define PFX "sbawe: " +#else +#define PFX "sb16: " +#endif + +#ifndef SNDRV_SBAWE +EXPORT_NO_SYMBOLS; +#endif + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifndef SNDRV_SBAWE +MODULE_DESCRIPTION("Sound Blaster 16"); +MODULE_DEVICES("{{Creative Labs,SB 16}," + "{Creative Labs,SB Vibra16S}," + "{Creative Labs,SB Vibra16C}," + "{Creative Labs,SB Vibra16CL}," + "{Creative Labs,SB Vibra16X}}"); +#else +MODULE_DESCRIPTION("Sound Blaster AWE"); +MODULE_DEVICES("{{Creative Labs,SB AWE 32}," + "{Creative Labs,SB AWE 64}," + "{Creative Labs,SB AWE 64 Gold}}"); +#endif + +#if 0 +#define SNDRV_DEBUG_IRQ +#endif + +#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)) +#define SNDRV_SBAWE_EMU8000 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ +#ifdef __ISAPNP__ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#endif +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */ +static long snd_mpu_port[SNDRV_CARDS] = {0x330, 0x300,[2 ... (SNDRV_CARDS - 1)] = -1}; +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#ifdef SNDRV_SBAWE_EMU8000 +static long snd_awe_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +#endif +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ +static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */ +static int snd_mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +#ifdef CONFIG_SND_SB16_CSP +static int snd_csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +#endif +#ifdef SNDRV_SBAWE_EMU8000 +static int snd_seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; +#endif + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable SoundBlaster 16 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260},{0x280}},dialog:list"); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port # for SB16 PnP driver."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x38c},{0x390},{0x394}},dialog:list"); +#ifdef SNDRV_SBAWE_EMU8000 +MODULE_PARM(snd_awe_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_awe_port, "AWE port # for SB16 PnP driver."); +MODULE_PARM_SYNTAX(snd_awe_port, SNDRV_ENABLED ",allows:{{0x620},{0x640},{0x660},{0x680}},dialog:list"); +#endif +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); +MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for SB16 driver."); +MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC); +MODULE_PARM(snd_mic_agc, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mic_agc, "Mic Auto-Gain-Control switch."); +MODULE_PARM_SYNTAX(snd_mic_agc, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +#ifdef CONFIG_SND_SB16_CSP +MODULE_PARM(snd_csp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_csp, "ASP/CSP chip support."); +MODULE_PARM_SYNTAX(snd_csp, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +#endif +#ifdef SNDRV_SBAWE_EMU8000 +MODULE_PARM(snd_seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_seq_ports, "Number of sequencer ports for WaveTable synth."); +MODULE_PARM_SYNTAX(snd_seq_ports, SNDRV_ENABLED ",allows:{{0,8}},skill:advanced"); +#endif + +struct snd_sb16 { + struct resource *fm_res; /* used to block FM i/o region for legacy cards */ +#ifdef __ISAPNP__ + struct isapnp_dev *dev; +#ifdef SNDRV_SBAWE_EMU8000 + struct isapnp_dev *devwt; +#endif +#endif +}; + +static snd_card_t *snd_sb16_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_sb16_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_sb16_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#define ISAPNP_SB16(_va, _vb, _vc, _device, _audio) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \ + } +#define ISAPNP_SBAWE(_va, _vb, _vc, _device, _audio, _awe) \ + { \ + ISAPNP_CARD_ID(_va, _vb, _vc, _device), \ + devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \ + ISAPNP_DEVICE_ID(_va, _vb, _vc, _awe), } \ + } + +static struct isapnp_card_id snd_sb16_pnpids[] __devinitdata = { +#ifndef SNDRV_SBAWE + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0024,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0026,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0027,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0028,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x0029,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x002a,0x0031), + /* Sound Blaster 16 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SB16('C','T','L',0x002b,0x0031), + /* Sound Blaster 16 PnP */ + ISAPNP_SB16('C','T','L',0x002c,0x0031), + /* Sound Blaster Vibra16S */ + ISAPNP_SB16('C','T','L',0x0051,0x0001), + /* Sound Blaster Vibra16C */ + ISAPNP_SB16('C','T','L',0x0070,0x0001), + /* Sound Blaster Vibra16CL - added by ctm@ardi.com */ + ISAPNP_SB16('C','T','L',0x0080,0x0041), + /* Sound Blaster Vibra16X */ + ISAPNP_SB16('C','T','L',0x00f0,0x0043), +#else /* SNDRV_SBAWE defined */ + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0035,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0039,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0042,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0043,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SBAWE('C','T','L',0x0044,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + /* Note: This card has also a CTL0051:StereoEnhance device!!! */ + ISAPNP_SBAWE('C','T','L',0x0045,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0047,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0048,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x0054,0x0031,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009a,0x0041,0x0021), + /* Sound Blaster AWE 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009c,0x0041,0x0021), + /* Sound Blaster 32 PnP */ + ISAPNP_SBAWE('C','T','L',0x009f,0x0041,0x0021), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x009d,0x0042,0x0022), + /* Sound Blaster AWE 64 PnP Gold */ + ISAPNP_SBAWE('C','T','L',0x009e,0x0044,0x0023), + /* Sound Blaster AWE 64 PnP Gold */ + ISAPNP_SBAWE('C','T','L',0x00b2,0x0044,0x0023), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c1,0x0042,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c3,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c5,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00c7,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00e4,0x0045,0x0022), + /* Sound Blaster AWE 64 PnP */ + ISAPNP_SBAWE('C','T','L',0x00e9,0x0045,0x0022), + /* Sound Blaster 16 PnP (AWE) */ + ISAPNP_SBAWE('C','T','L',0x00ed,0x0041,0x0070), + /* Generic entries */ + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0031,0x0021), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0041,0x0021), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0042,0x0022), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0044,0x0023), + ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0045,0x0022), +#endif /* SNDRV_SBAWE */ + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_sb16_pnpids); + +static int __init snd_sb16_isapnp(int dev, struct snd_sb16 *acard) +{ + const struct isapnp_card_id *id = snd_sb16_isapnp_id[dev]; + struct isapnp_card *card = snd_sb16_isapnp_cards[dev]; + struct isapnp_dev *pdev; + + acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->dev->active) { + acard->dev = NULL; + return -EBUSY; + } +#ifdef SNDRV_SBAWE_EMU8000 + acard->devwt = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL); + if (acard->devwt->active) { + acard->dev = acard->devwt = NULL; + return -EBUSY; + } +#endif + /* Audio initialization */ + pdev = acard->dev; + if (pdev->prepare(pdev) < 0) + return -EAGAIN; + if (snd_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16); + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_mpu_port[dev], 2); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4); + if (snd_dma8[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], 1); + if (snd_dma16[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev], 1); + if (snd_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1); + if (pdev->activate(pdev) < 0) { + printk(KERN_ERR PFX "isapnp configure failure (out of resources?)\n"); + return -EBUSY; + } + snd_port[dev] = pdev->resource[0].start; + snd_mpu_port[dev] = pdev->resource[1].start; + snd_fm_port[dev] = pdev->resource[2].start; + snd_dma8[dev] = pdev->dma_resource[0].start; + snd_dma16[dev] = pdev->dma_resource[1].start; + snd_irq[dev] = pdev->irq_resource[0].start; + snd_printdd("isapnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n", + snd_port[dev], snd_mpu_port[dev], snd_fm_port[dev]); + snd_printdd("isapnp SB16: dma1=%i, dma2=%i, irq=%i\n", + snd_dma8[dev], snd_dma16[dev], snd_irq[dev]); +#ifdef SNDRV_SBAWE_EMU8000 + /* WaveTable initialization */ + pdev = acard->devwt; + if (pdev->prepare(pdev)<0) { + acard->dev->deactivate(acard->dev); + return -EAGAIN; + } + if (snd_awe_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], snd_awe_port[dev], 4); + isapnp_resource_change(&pdev->resource[1], snd_awe_port[dev] + 0x400, 4); + isapnp_resource_change(&pdev->resource[2], snd_awe_port[dev] + 0x800, 4); + } + if (pdev->activate(pdev)<0) { + printk(KERN_ERR PFX "WaveTable isapnp configure failure (out of resources?)\n"); + acard->dev->deactivate(acard->dev); + return -EBUSY; + } + snd_awe_port[dev] = pdev->resource[0].start; + snd_printdd("isapnp SB16: wavetable port=0x%lx\n", pdev->resource[0].start); +#endif + return 0; +} + +static void snd_sb16_deactivate(struct snd_sb16 *acard) +{ + if (acard->dev) { + acard->dev->deactivate(acard->dev); + acard->dev = NULL; + } +#ifdef SNDRV_SBAWE_EMU8000 + if (acard->devwt) { + acard->devwt->deactivate(acard->devwt); + acard->devwt = NULL; + } +#endif +} + +#endif /* __ISAPNP__ */ + +static void snd_sb16_free(snd_card_t *card) +{ + struct snd_sb16 *acard = (struct snd_sb16 *)card->private_data; + + if (acard == NULL) + return; + if (acard->fm_res) { + release_resource(acard->fm_res); + kfree_nocheck(acard->fm_res); + } +#ifdef __ISAPNP__ + snd_sb16_deactivate(acard); +#endif +} + +static int __init snd_sb16_probe(int dev) +{ + static int possible_irqs[] = {5, 9, 10, 7, -1}; + static int possible_dmas8[] = {1, 3, 0, -1}; + static int possible_dmas16[] = {5, 6, 7, -1}; + int irq, dma8, dma16; + sb_t *chip; + snd_card_t *card; + struct snd_sb16 *acard; + opl3_t *opl3; + snd_hwdep_t *synth = NULL; +#ifdef CONFIG_SND_SB16_CSP + snd_hwdep_t *csp = NULL; +#endif + unsigned long flags; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_sb16)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_sb16 *) card->private_data; + card->private_free = snd_sb16_free; +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && snd_sb16_isapnp(dev, acard) < 0) { + snd_card_free(card); + return -EBUSY; + } +#endif + + irq = snd_irq[dev]; + dma8 = snd_dma8[dev]; + dma16 = snd_dma16[dev]; +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + printk(KERN_ERR PFX "unable to find a free IRQ\n"); + return -EBUSY; + } + } + if (dma8 == SNDRV_AUTO_DMA) { + if ((dma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) { + snd_card_free(card); + printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n"); + return -EBUSY; + } + } + if (dma16 == SNDRV_AUTO_DMA) { + if ((dma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) { + snd_card_free(card); + printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n"); + return -EBUSY; + } + } + /* non-PnP FM port address is hardwired with base port address */ + snd_fm_port[dev] = snd_port[dev]; + /* block the 0x388 port to avoid PnP conflicts */ + acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); +#ifdef SNDRV_SBAWE_EMU8000 + /* non-PnP AWE port address is hardwired with base port address */ + snd_awe_port[dev] = snd_port[dev] + 0x400; +#endif +#ifdef __ISAPNP__ + } +#endif + + if ((err = snd_sbdsp_create(card, + snd_port[dev], + irq, + snd_sb16dsp_interrupt, + dma8, + dma16, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware != SB_HW_16) { + snd_card_free(card); + snd_printdd("SB 16 chip was not detected at 0x%lx\n", snd_port[dev]); + return -ENODEV; + } + chip->mpu_port = snd_mpu_port[dev]; +#ifdef __ISAPNP__ + if (!snd_isapnp[dev] && (err = snd_sb16dsp_configure(chip)) < 0) { +#else + if ((err = snd_sb16dsp_configure(chip)) < 0) { +#endif + snd_card_free(card); + return -ENXIO; + } + if ((err = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return -ENXIO; + } + + if (chip->mpu_port) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB, + chip->mpu_port, 0, + irq, 0, &chip->rmidi)) < 0) { + snd_card_free(card); + return -ENXIO; + } + } + + if (snd_fm_port[dev] > 0) { + if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2, + OPL3_HW_OPL3, snd_fm_port[dev] == snd_port[dev], + &opl3) < 0) { + printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n", + snd_fm_port[dev], snd_fm_port[dev] + 2); + } else { +#ifdef SNDRV_SBAWE_EMU8000 + int seqdev = snd_awe_port[dev] > 0 ? 2 : 1; +#else + int seqdev = 1; +#endif + if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0) { + snd_card_free(card); + return -ENXIO; + } + } + } + + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return -ENXIO; + } + +#ifdef CONFIG_SND_SB16_CSP + /* CSP chip on SB16ASP/AWE32 */ + if ((chip->hardware == SB_HW_16) && snd_csp[dev]) { + snd_sb_csp_new(chip, synth != NULL ? 1 : 0, &csp); + if (csp) { + chip->csp = csp->private_data; + chip->hardware = SB_HW_16CSP; + } else { + printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1); + } + } +#endif +#ifdef SNDRV_SBAWE_EMU8000 + if (snd_awe_port[dev] > 0) { + if (snd_emu8000_new(card, 1, snd_awe_port[dev], + snd_seq_ports[dev], NULL) < 0) { + printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", snd_awe_port[dev]); + snd_card_free(card); + return -ENXIO; + } + } +#endif + + /* setup Mic AGC */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, SB_DSP4_MIC_AGC, + (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) | + (snd_mic_agc[dev] ? 0x00 : 0x01)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + strcpy(card->driver, +#ifdef SNDRV_SBAWE_EMU8000 + snd_awe_port[dev] > 0 ? "SB AWE" : +#endif + "SB16"); + strcpy(card->shortname, chip->name); + sprintf(card->longname, "%s at 0x%lx, irq %i, dma ", + chip->name, + chip->port, + irq); + if (dma8 >= 0) + sprintf(card->longname + strlen(card->longname), "%d", dma8); + if (dma16 >= 0) + sprintf(card->longname + strlen(card->longname), "%s%d", + dma8 >= 0 ? "&" : "", dma16); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sb16_cards[dev] = card; + return 0; +} + +static int __init snd_sb16_probe_legacy_port(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + snd_port[dev] = port; + res = snd_sb16_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +#ifdef __ISAPNP__ + +static int __init snd_sb16_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_sb16_isapnp_cards[dev] = card; + snd_sb16_isapnp_id[dev] = id; + res = snd_sb16_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_sb16_init(void) +{ + int dev, cards = 0; + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1}; + + /* legacy non-auto cards at first */ + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (!snd_sb16_probe(dev)) { + cards++; + continue; + } +#ifdef MODULE + printk(KERN_ERR "Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]); +#endif + } + /* legacy auto configured cards */ + cards += snd_legacy_auto_probe(possible_ports, snd_sb16_probe_legacy_port); +#ifdef __ISAPNP__ + /* ISA PnP cards at last */ + cards += isapnp_probe_cards(snd_sb16_pnpids, snd_sb16_isapnp_detect); +#endif + + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Sound Blaster 16 soundcard not found or device busy\n"); +#ifdef SNDRV_SBAWE_EMU8000 + printk(KERN_ERR "In case, if you have non-AWE card, try snd-card-sb16 module\n"); +#else + printk(KERN_ERR "In case, if you have AWE card, try snd-card-sbawe module\n"); +#endif +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_sb16_exit(void) +{ + int dev; + + for (dev = 0; dev < SNDRV_CARDS; dev++) + snd_card_free(snd_sb16_cards[dev]); +} + +module_init(alsa_card_sb16_init) +module_exit(alsa_card_sb16_exit) + +#ifndef MODULE + +/* format is: snd-sb16=snd_enable,snd_index,snd_id,snd_isapnp, + snd_port,snd_mpu_port,snd_fm_port, + snd_irq,snd_dma8,snd_dma16, + snd_mic_agc,snd_csp, + [snd_awe_port,snd_seq_ports] */ + +static int __init alsa_card_sb16_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + int __attribute__ ((__unused__)) pnp = INT_MAX; + int __attribute__ ((__unused__)) csp = INT_MAX; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&pnp) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2 && + get_option(&str,&snd_dma16[nr_dev]) == 2 && + get_option(&str,&snd_mic_agc[nr_dev]) == 2 +#ifdef CONFIG_SND_SB16_CSP + && + get_option(&str,&snd_csp[nr_dev]) == 2 +#endif +#ifdef SNDRV_SBAWE_EMU8000 + && + get_option(&str,(int *)&snd_awe_port[nr_dev]) == 2 && + get_option(&str,&snd_seq_ports[nr_dev]) == 2 +#endif + ); +#ifdef __ISAPNP__ + if (pnp != INT_MAX) + snd_isapnp[nr_dev] = pnp; +#endif +#ifdef CONFIG_SND_SB16_CSP + if (csp != INT_MAX) + snd_csp[nr_dev] = csp; +#endif + nr_dev++; + return 1; +} + +#ifndef SNDRV_SBAWE_EMU8000 +__setup("snd-sb16=", alsa_card_sb16_setup); +#else +__setup("snd-sbawe=", alsa_card_sb16_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/sb/sb16_csp.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp.c --- linux/sound/isa/sb/sb16_csp.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1196 @@ +/* + * Copyright (c) 1999 by Uros Bizjak + * Takashi Iwai + * + * SB16ASP/AWE32 CSP control + * + * CSP microcode loader: + * alsa-tools/sb16_csp/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t snd_sb_csp_t + +MODULE_AUTHOR("Uros Bizjak "); +MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#ifdef SNDRV_LITTLE_ENDIAN +#define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) +#else +#define CSP_HDR_VALUE(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) +#endif +#define LE_SHORT(v) le16_to_cpu(v) +#define LE_INT(v) le32_to_cpu(v) + +#define RIFF_HEADER CSP_HDR_VALUE('R', 'I', 'F', 'F') +#define CSP__HEADER CSP_HDR_VALUE('C', 'S', 'P', ' ') +#define LIST_HEADER CSP_HDR_VALUE('L', 'I', 'S', 'T') +#define FUNC_HEADER CSP_HDR_VALUE('f', 'u', 'n', 'c') +#define CODE_HEADER CSP_HDR_VALUE('c', 'o', 'd', 'e') +#define INIT_HEADER CSP_HDR_VALUE('i', 'n', 'i', 't') +#define MAIN_HEADER CSP_HDR_VALUE('m', 'a', 'i', 'n') + +/* + * RIFF data format + */ +typedef struct riff_header { + __u32 name; + __u32 len; +} riff_header_t; + +typedef struct desc_header { + riff_header_t info; + __u16 func_nr; + __u16 VOC_type; + __u16 flags_play_rec; + __u16 flags_16bit_8bit; + __u16 flags_stereo_mono; + __u16 flags_rates; +} desc_header_t; + +/* + * prototypes + */ +static void snd_sb_csp_free(snd_hwdep_t *hw); +static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file); +static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg); +static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file); + +static int csp_detect(sb_t *chip, int *version); +static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val); +static int set_register(sb_t *chip, unsigned char reg, unsigned char val); +static int read_register(sb_t *chip, unsigned char reg); +static int set_mode_register(sb_t *chip, unsigned char mode); +static int get_version(sb_t *chip); + +static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * code); +static int snd_sb_csp_unload(snd_sb_csp_t * p); +static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags); +static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode); +static int snd_sb_csp_check_version(snd_sb_csp_t * p); + +static int snd_sb_csp_use(snd_sb_csp_t * p); +static int snd_sb_csp_unuse(snd_sb_csp_t * p); +static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels); +static int snd_sb_csp_stop(snd_sb_csp_t * p); +static int snd_sb_csp_pause(snd_sb_csp_t * p); +static int snd_sb_csp_restart(snd_sb_csp_t * p); + +static int snd_sb_qsound_build(snd_sb_csp_t * p); +static void snd_sb_qsound_destroy(snd_sb_csp_t * p); +static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p); + +static int init_proc_entry(snd_sb_csp_t * p, int device); +static void delete_proc_entry(snd_sb_csp_t * p); +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer); + +/* + * Detect CSP chip and create a new instance + */ +int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep) +{ + snd_sb_csp_t *p; + int version, err; + snd_hwdep_t *hw; + + if (rhwdep) + *rhwdep = NULL; + + if (csp_detect(chip, &version)) + return -ENODEV; + + if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0) + return err; + + if ((p = snd_magic_kcalloc(snd_sb_csp_t, 0, GFP_KERNEL)) == NULL) { + snd_device_free(chip->card, hw); + return -ENOMEM; + } + p->chip = chip; + p->version = version; + + /* CSP operators */ + p->ops.csp_use = snd_sb_csp_use; + p->ops.csp_unuse = snd_sb_csp_unuse; + p->ops.csp_autoload = snd_sb_csp_autoload; + p->ops.csp_start = snd_sb_csp_start; + p->ops.csp_stop = snd_sb_csp_stop; + p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer; + + init_MUTEX(&p->access_mutex); + sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f)); + hw->iface = SNDRV_HWDEP_IFACE_SB16CSP; + hw->private_data = p; + hw->private_free = snd_sb_csp_free; + + /* operators - only write/ioctl */ + hw->ops.open = snd_sb_csp_open; + hw->ops.ioctl = snd_sb_csp_ioctl; + hw->ops.release = snd_sb_csp_release; + + /* create a proc entry */ + init_proc_entry(p, device); + if (rhwdep) + *rhwdep = hw; + return 0; +} + +/* + * free_private for hwdep instance + */ +static void snd_sb_csp_free(snd_hwdep_t *hwdep) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hwdep->private_data, return); + if (p) { + if (p->running & SNDRV_SB_CSP_ST_RUNNING) + snd_sb_csp_stop(p); + delete_proc_entry(p); + snd_magic_kfree(p); + } +} + +/* ------------------------------ */ + +/* + * open the device exclusively + */ +static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + return (snd_sb_csp_use(p)); +} + +/* + * ioctl for hwdep device: + */ +static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + snd_sb_csp_info_t info; + snd_sb_csp_start_t start_info; + int err; + + snd_assert(p != NULL, return -EINVAL); + + if (snd_sb_csp_check_version(p)) + return -ENODEV; + + switch (cmd) { + /* get information */ + case SNDRV_SB_CSP_IOCTL_INFO: + *info.codec_name = *p->codec_name; + info.func_nr = p->func_nr; + info.acc_format = p->acc_format; + info.acc_channels = p->acc_channels; + info.acc_width = p->acc_width; + info.acc_rates = p->acc_rates; + info.csp_mode = p->mode; + info.run_channels = p->run_channels; + info.run_width = p->run_width; + info.version = p->version; + info.state = p->running; + err = copy_to_user((void *) arg, &info, sizeof(info)); + break; + + /* load CSP microcode */ + case SNDRV_SB_CSP_IOCTL_LOAD_CODE: + err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? + -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t *) arg)); + break; + case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE: + err = (p->running & SNDRV_SB_CSP_ST_RUNNING ? + -EBUSY : snd_sb_csp_unload(p)); + break; + + /* change CSP running state */ + case SNDRV_SB_CSP_IOCTL_START: + if (copy_from_user(&start_info, (void *) arg, sizeof(start_info))) + err = -EFAULT; + else + err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels); + break; + case SNDRV_SB_CSP_IOCTL_STOP: + err = snd_sb_csp_stop(p); + break; + case SNDRV_SB_CSP_IOCTL_PAUSE: + err = snd_sb_csp_pause(p); + break; + case SNDRV_SB_CSP_IOCTL_RESTART: + err = snd_sb_csp_restart(p); + break; + default: + err = -ENOTTY; + break; + } + + return err; +} + +/* + * close the device + */ +static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + return (snd_sb_csp_unuse(p)); +} + +/* ------------------------------ */ + +/* + * acquire device + */ +static int snd_sb_csp_use(snd_sb_csp_t * p) +{ + down(&p->access_mutex); + if (p->used) { + up(&p->access_mutex); + return -EAGAIN; + } + p->used++; + up(&p->access_mutex); + + return 0; + +} + +/* + * release device + */ +static int snd_sb_csp_unuse(snd_sb_csp_t * p) +{ + down(&p->access_mutex); + p->used--; + up(&p->access_mutex); + + return 0; +} + +/* + * load microcode via ioctl: + * code is user-space pointer + */ +static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * mcode) +{ + snd_sb_csp_mc_header_t info; + + unsigned char *data_ptr, *data_end; + unsigned short func_nr = 0; + + riff_header_t file_h, item_h, code_h; + __u32 item_type; + desc_header_t funcdesc_h; + + unsigned long flags; + int err; + + if (copy_from_user(&info, mcode, sizeof(info))) + return -EFAULT; + data_ptr = mcode->data; + + if (copy_from_user(&file_h, data_ptr, sizeof(file_h))) + return -EFAULT; + if ((file_h.name != RIFF_HEADER) || + (LE_INT(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) { + snd_printd("%s: Invalid RIFF header\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof(file_h); + data_end = data_ptr + LE_INT(file_h.len); + + if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) + return -EFAULT; + if (item_type != CSP__HEADER) { + snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof (item_type); + + for (; data_ptr < data_end; data_ptr += LE_INT(item_h.len)) { + if (copy_from_user(&item_h, data_ptr, sizeof(item_h))) + return -EFAULT; + data_ptr += sizeof(item_h); + if (item_h.name != LIST_HEADER) + continue; + + if (copy_from_user(&item_type, data_ptr, sizeof(item_type))) + return -EFAULT; + switch (item_type) { + case FUNC_HEADER: + if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h))) + return -EFAULT; + func_nr = LE_SHORT(funcdesc_h.func_nr); + break; + case CODE_HEADER: + if (func_nr != info.func_req) + break; /* not required function, try next */ + data_ptr += sizeof(item_type); + + /* destroy QSound mixer element */ + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_sb_qsound_destroy(p); + } + /* Clear all flags */ + p->running = 0; + p->mode = 0; + + /* load microcode blocks */ + for (;;) { + if (data_ptr >= data_end) + return -EINVAL; + if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) + return -EFAULT; + + /* init microcode blocks */ + if (code_h.name != INIT_HEADER) + break; + data_ptr += sizeof(code_h); + err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len), + SNDRV_SB_CSP_LOAD_INITBLOCK | SNDRV_SB_CSP_LOAD_FROMUSER); + if (err) + return err; + data_ptr += LE_INT(code_h.len); + } + /* main microcode block */ + if (copy_from_user(&code_h, data_ptr, sizeof(code_h))) + return -EFAULT; + + if (code_h.name != MAIN_HEADER) { + snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__); + return -EINVAL; + } + data_ptr += sizeof(code_h); + err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len), + SNDRV_SB_CSP_LOAD_FROMUSER); + if (err) + return err; + + /* fill in codec header */ + strncpy(p->codec_name, info.codec_name, sizeof(p->codec_name) - 1); + p->codec_name[sizeof(p->codec_name) - 1] = 0; + p->func_nr = func_nr; + p->mode = LE_SHORT(funcdesc_h.flags_play_rec); + switch (LE_SHORT(funcdesc_h.VOC_type)) { + case 0x0001: /* QSound decoder */ + if (LE_SHORT(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) { + if (snd_sb_qsound_build(p) == 0) + /* set QSound flag and clear all other mode flags */ + p->mode = SNDRV_SB_CSP_MODE_QSOUND; + } + p->acc_format = 0; + break; + case 0x0006: /* A Law codec */ + p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; + break; + case 0x0007: /* Mu Law codec */ + p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; + break; + case 0x0011: /* what Creative thinks is IMA ADPCM codec */ + case 0x0200: /* Creative ADPCM codec */ + p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; + break; + case 201: /* Text 2 Speech decoder */ + /* TODO: Text2Speech handling routines */ + p->acc_format = 0; + break; + case 0x0202: /* Fast Speech 8 codec */ + case 0x0203: /* Fast Speech 10 codec */ + p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL; + break; + default: /* other codecs are unsupported */ + p->acc_format = p->acc_width = p->acc_rates = 0; + p->mode = 0; + snd_printd("%s: Unsupported CSP codec type: 0x%04x\n", + __FUNCTION__, + LE_SHORT(funcdesc_h.VOC_type)); + return -EINVAL; + } + p->acc_channels = LE_SHORT(funcdesc_h.flags_stereo_mono); + p->acc_width = LE_SHORT(funcdesc_h.flags_16bit_8bit); + p->acc_rates = LE_SHORT(funcdesc_h.flags_rates); + + /* Decouple CSP from IRQ and DMAREQ lines */ + spin_lock_irqsave(&p->chip->reg_lock, flags); + set_mode_register(p->chip, 0xfc); + set_mode_register(p->chip, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + + /* finished loading successfully */ + p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */ + return 0; + } + } + snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req); + return -EINVAL; +} + +/* + * unload CSP microcode + */ +static int snd_sb_csp_unload(snd_sb_csp_t * p) +{ + if (p->running & SNDRV_SB_CSP_ST_RUNNING) + return -EBUSY; + if (!(p->running & SNDRV_SB_CSP_ST_LOADED)) + return -ENXIO; + + /* clear supported formats */ + p->acc_format = 0; + p->acc_channels = p->acc_width = p->acc_rates = 0; + /* destroy QSound mixer element */ + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_sb_qsound_destroy(p); + } + /* clear all flags */ + p->running = 0; + p->mode = 0; + return 0; +} + +/* + * send command sequence to DSP + */ +static inline int command_seq(sb_t *chip, const unsigned char *seq, int size) +{ + int i; + for (i = 0; i < size; i++) { + if (!snd_sbdsp_command(chip, seq[i])) + return -EIO; + } + return 0; +} + +/* + * set CSP codec parameter + */ +static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val) +{ + unsigned char dsp_cmd[3]; + + dsp_cmd[0] = 0x05; /* CSP set codec parameter */ + dsp_cmd[1] = val; /* Parameter value */ + dsp_cmd[2] = par; /* Parameter */ + command_seq(chip, dsp_cmd, 3); + snd_sbdsp_command(chip, 0x03); /* DSP read? */ + if (snd_sbdsp_get_byte(chip) != par) + return -EIO; + return 0; +} + +/* + * set CSP register + */ +static int set_register(sb_t *chip, unsigned char reg, unsigned char val) +{ + unsigned char dsp_cmd[3]; + + dsp_cmd[0] = 0x0e; /* CSP set register */ + dsp_cmd[1] = reg; /* CSP Register */ + dsp_cmd[2] = val; /* value */ + return command_seq(chip, dsp_cmd, 3); +} + +/* + * read CSP register + * return < 0 -> error + */ +static int read_register(sb_t *chip, unsigned char reg) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x0f; /* CSP read register */ + dsp_cmd[1] = reg; /* CSP Register */ + command_seq(chip, dsp_cmd, 2); + return snd_sbdsp_get_byte(chip); /* Read DSP value */ +} + +/* + * set CSP mode register + */ +static int set_mode_register(sb_t *chip, unsigned char mode) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x04; /* CSP set mode register */ + dsp_cmd[1] = mode; /* mode */ + return command_seq(chip, dsp_cmd, 2); +} + +/* + * Detect CSP + * return 0 if CSP exists. + */ +static int csp_detect(sb_t *chip, int *version) +{ + unsigned char csp_test1, csp_test2; + unsigned long flags; + int result = -ENODEV; + + spin_lock_irqsave(&chip->reg_lock, flags); + + set_codec_parameter(chip, 0x00, 0x00); + set_mode_register(chip, 0xfc); /* 0xfc = ?? */ + + csp_test1 = read_register(chip, 0x83); + set_register(chip, 0x83, ~csp_test1); + csp_test2 = read_register(chip, 0x83); + if (csp_test2 != (csp_test1 ^ 0xff)) + goto __fail; + + set_register(chip, 0x83, csp_test1); + csp_test2 = read_register(chip, 0x83); + if (csp_test2 != csp_test1) + goto __fail; + + set_mode_register(chip, 0x00); /* 0x00 = ? */ + + *version = get_version(chip); + snd_sbdsp_reset(chip); /* reset DSP after getversion! */ + if (*version >= 0x10 && *version <= 0x1f) + result = 0; /* valid version id */ + + __fail: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* + * get CSP version number + */ +static int get_version(sb_t *chip) +{ + unsigned char dsp_cmd[2]; + + dsp_cmd[0] = 0x08; /* SB_DSP_!something! */ + dsp_cmd[1] = 0x03; /* get chip version id? */ + command_seq(chip, dsp_cmd, 2); + + return (snd_sbdsp_get_byte(chip)); +} + +/* + * check if the CSP version is valid + */ +static int snd_sb_csp_check_version(snd_sb_csp_t * p) +{ + if (p->version < 0x10 || p->version > 0x1f) { + snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version); + return 1; + } + return 0; +} + +/* + * download microcode to CSP (microcode should have one "main" block). + */ +static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags) +{ + int status, i; + int err; + int result = -EIO; + unsigned long flags; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + snd_sbdsp_command(p->chip, 0x01); /* CSP download command */ + if (snd_sbdsp_get_byte(p->chip)) { + snd_printd("%s: Download command failed\n", __FUNCTION__); + goto __fail; + } + /* Send CSP low byte (size - 1) */ + snd_sbdsp_command(p->chip, (unsigned char)(size - 1)); + /* Send high byte */ + snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8)); + /* send microcode sequence */ + if (load_flags & SNDRV_SB_CSP_LOAD_FROMUSER) { + /* copy microcode from user space */ + unsigned char *kbuf, *_kbuf; + _kbuf = kbuf = kmalloc (size, GFP_KERNEL); + if (copy_from_user(kbuf, buf, size)) { + result = -EFAULT; + kfree (_kbuf); + goto __fail; + } + while (size--) { + if (!snd_sbdsp_command(p->chip, *kbuf++)) { + kfree (_kbuf); + goto __fail; + } + } + kfree (_kbuf); + } else { + /* load from kernel space */ + while (size--) { + if (!snd_sbdsp_command(p->chip, *buf++)) + goto __fail; + } + } + if (snd_sbdsp_get_byte(p->chip)) + goto __fail; + + if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) { + i = 0; + /* some codecs (FastSpeech) take some time to initialize */ + while (1) { + snd_sbdsp_command(p->chip, 0x03); + status = snd_sbdsp_get_byte(p->chip); + if (status == 0x55 || ++i >= 10) + break; + udelay (10); + } + if (status != 0x55) { + snd_printd("%s: Microcode initialization failed\n", __FUNCTION__); + goto __fail; + } + } else { + /* + * Read mixer register SB_DSP4_DMASETUP after loading 'main' code. + * Start CSP chip if no 16bit DMA channel is set - some kind + * of autorun or perhaps a bugfix? + */ + spin_lock(&p->chip->mixer_lock); + status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP); + spin_unlock(&p->chip->mixer_lock); + if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) { + err = (set_codec_parameter(p->chip, 0xaa, 0x00) || + set_codec_parameter(p->chip, 0xff, 0x00)); + snd_sbdsp_reset(p->chip); /* really! */ + if (err) + goto __fail; + set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + set_mode_register(p->chip, 0x70); /* 70 = RUN */ + } + } + result = 0; + + __fail: + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + return result; +} + +#include "sb16_csp_codecs.h" + +/* + * autoload hardware codec if necessary + * return 0 if CSP is loaded and ready to run (p->running != 0) + */ +static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode) +{ + unsigned long flags; + int err = 0; + + /* if CSP is running or manually loaded then exit */ + if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED)) + return -EBUSY; + + /* autoload microcode only if requested hardware codec is not already loaded */ + if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) { + p->running = SNDRV_SB_CSP_ST_AUTO; + } else { + switch (pcm_sfmt) { + case SNDRV_PCM_FORMAT_MU_LAW: + err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0); + p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW; + p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; + break; + case SNDRV_PCM_FORMAT_A_LAW: + err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0); + p->acc_format = SNDRV_PCM_FMTBIT_A_LAW; + p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE; + break; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init), + SNDRV_SB_CSP_LOAD_INITBLOCK); + if (err) + break; + if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) { + err = snd_sb_csp_load(p, &ima_adpcm_playback[0], + sizeof(ima_adpcm_playback), 0); + p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE; + } else { + err = snd_sb_csp_load(p, &ima_adpcm_capture[0], + sizeof(ima_adpcm_capture), 0); + p->mode = SNDRV_SB_CSP_MODE_DSP_READ; + } + p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM; + break; + default: + /* Decouple CSP from IRQ and DMAREQ lines */ + if (p->running & SNDRV_SB_CSP_ST_AUTO) { + spin_lock_irqsave(&p->chip->reg_lock, flags); + set_mode_register(p->chip, 0xfc); + set_mode_register(p->chip, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + p->running = 0; /* clear autoloaded flag */ + } + return -EINVAL; + } + if (err) { + p->acc_format = 0; + p->acc_channels = p->acc_width = p->acc_rates = 0; + + p->running = 0; /* clear autoloaded flag */ + p->mode = 0; + return (err); + } else { + p->running = SNDRV_SB_CSP_ST_AUTO; /* set autoloaded flag */ + p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT; /* only 16 bit data */ + p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO; + p->acc_rates = SNDRV_SB_CSP_RATE_ALL; /* HW codecs accept all rates */ + } + + } + return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO; +} + +/* + * start CSP + */ +static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels) +{ + unsigned char s_type; /* sample type */ + unsigned char mixL, mixR; + int result = -EIO; + unsigned long flags; + + if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) { + snd_printd("%s: Microcode not loaded\n", __FUNCTION__); + return -ENXIO; + } + if (p->running & SNDRV_SB_CSP_ST_RUNNING) { + snd_printd("%s: CSP already running\n", __FUNCTION__); + return -EBUSY; + } + if (!(sample_width & p->acc_width)) { + snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__); + return -EINVAL; + } + if (!(channels & p->acc_channels)) { + snd_printd("%s: Invalid number of channels\n", __FUNCTION__); + return -EINVAL; + } + + /* Mute PCM volume */ + spin_lock_irqsave(&p->chip->mixer_lock, flags); + mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); + mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); + + spin_lock(&p->chip->reg_lock); + set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + set_mode_register(p->chip, 0x70); /* 70 = RUN */ + + s_type = 0x00; + if (channels == SNDRV_SB_CSP_MONO) + s_type = 0x11; /* 000n 000n (n = 1 if mono) */ + if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT) + s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */ + + if (set_codec_parameter(p->chip, 0x81, s_type)) { + snd_printd("%s: Set sample type command failed\n", __FUNCTION__); + goto __fail; + } + if (set_codec_parameter(p->chip, 0x80, 0x00)) { + snd_printd("%s: Codec start command failed\n", __FUNCTION__); + goto __fail; + } + p->run_width = sample_width; + p->run_channels = channels; + + p->running |= SNDRV_SB_CSP_ST_RUNNING; + + if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* enable QSound decoder */ + set_codec_parameter(p->chip, 0x00, 0xff); + set_codec_parameter(p->chip, 0x01, 0xff); + p->running |= SNDRV_SB_CSP_ST_QSOUND; + /* set QSound startup value */ + snd_sb_csp_qsound_transfer(p); + } + result = 0; + + __fail: + spin_unlock(&p->chip->reg_lock); + + /* restore PCM volume */ + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); + spin_unlock_irqrestore(&p->chip->mixer_lock, flags); + + return result; +} + +/* + * stop CSP + */ +static int snd_sb_csp_stop(snd_sb_csp_t * p) +{ + int result; + unsigned char mixL, mixR; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) + return 0; + + /* Mute PCM volume */ + spin_lock_irqsave(&p->chip->mixer_lock, flags); + mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV); + mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7); + + spin_lock(&p->chip->reg_lock); + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* disable QSound decoder */ + set_codec_parameter(p->chip, 0x00, 0x00); + set_codec_parameter(p->chip, 0x01, 0x00); + + p->running &= ~SNDRV_SB_CSP_ST_QSOUND; + } + result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */ + spin_unlock(&p->chip->reg_lock); + + /* restore PCM volume */ + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL); + snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR); + spin_unlock_irqrestore(&p->chip->mixer_lock, flags); + + if (!(result)) + p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING); + return result; +} + +/* + * pause CSP codec and hold DMA transfer + */ +static int snd_sb_csp_pause(snd_sb_csp_t * p) +{ + int result; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_RUNNING)) + return -EBUSY; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + result = set_codec_parameter(p->chip, 0x80, 0xff); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + if (!(result)) + p->running |= SNDRV_SB_CSP_ST_PAUSED; + + return result; +} + +/* + * restart CSP codec and resume DMA transfer + */ +static int snd_sb_csp_restart(snd_sb_csp_t * p) +{ + int result; + unsigned long flags; + + if (!(p->running & SNDRV_SB_CSP_ST_PAUSED)) + return -EBUSY; + + spin_lock_irqsave(&p->chip->reg_lock, flags); + result = set_codec_parameter(p->chip, 0x80, 0x00); + spin_unlock_irqrestore(&p->chip->reg_lock, flags); + if (!(result)) + p->running &= ~SNDRV_SB_CSP_ST_PAUSED; + + return result; +} + +/* ------------------------------ */ + +/* + * QSound mixer control for PCM + */ + +static int snd_sb_qsound_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_sb_qsound_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0; + return 0; +} + +static int snd_sb_qsound_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval; + + nval = ucontrol->value.integer.value[0] & 0x01; + spin_lock_irqsave(&p->q_lock, flags); + change = p->q_enabled != nval; + p->q_enabled = nval; + spin_unlock_irqrestore(&p->q_lock, flags); + return change; +} + +static int snd_sb_qsound_space_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + return 0; +} + +static int snd_sb_qsound_space_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&p->q_lock, flags); + ucontrol->value.integer.value[0] = p->qpos_left; + ucontrol->value.integer.value[1] = p->qpos_right; + spin_unlock_irqrestore(&p->q_lock, flags); + return 0; +} + +static int snd_sb_qsound_space_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval1, nval2; + + nval1 = ucontrol->value.integer.value[0]; + if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) + nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + nval2 = ucontrol->value.integer.value[1]; + if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT) + nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT; + spin_lock_irqsave(&p->q_lock, flags); + change = p->qpos_left != nval1 || p->qpos_right != nval2; + p->qpos_left = nval1; + p->qpos_right = nval2; + p->qpos_changed = change; + spin_unlock_irqrestore(&p->q_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_sb_qsound_switch = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Switch", + info: snd_sb_qsound_switch_info, + get: snd_sb_qsound_switch_get, + put: snd_sb_qsound_switch_put +}; + +static snd_kcontrol_new_t snd_sb_qsound_space = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Space", + info: snd_sb_qsound_space_info, + get: snd_sb_qsound_space_get, + put: snd_sb_qsound_space_put +}; + +static int snd_sb_qsound_build(snd_sb_csp_t * p) +{ + snd_card_t * card; + int err; + + snd_assert(p != NULL, return -EINVAL); + + card = p->chip->card; + p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2; + p->qpos_changed = 0; + + spin_lock_init(&p->q_lock); + + if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0) + goto __error; + if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0) + goto __error; + + return 0; + + __error: + snd_sb_qsound_destroy(p); + return err; +} + +static void snd_sb_qsound_destroy(snd_sb_csp_t * p) +{ + snd_card_t * card; + unsigned long flags; + + snd_assert(p != NULL, return); + + card = p->chip->card; + + if (p->qsound_switch) + snd_ctl_remove(card, p->qsound_switch); + if (p->qsound_space) + snd_ctl_remove(card, p->qsound_space); + + /* cancel pending transfer of QSound parameters */ + spin_lock_irqsave (&p->q_lock, flags); + p->qpos_changed = 0; + spin_unlock_irqrestore (&p->q_lock, flags); +} + +/* + * Transfer qsound parameters to CSP, + * function should be called from interrupt routine + */ +static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p) +{ + int err = -ENXIO; + + spin_lock(&p->q_lock); + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + set_codec_parameter(p->chip, 0xe0, 0x01); + /* left channel */ + set_codec_parameter(p->chip, 0x00, p->qpos_left); + set_codec_parameter(p->chip, 0x02, 0x00); + /* right channel */ + set_codec_parameter(p->chip, 0x00, p->qpos_right); + set_codec_parameter(p->chip, 0x03, 0x00); + err = 0; + } + p->qpos_changed = 0; + spin_unlock(&p->q_lock); + return err; +} + +/* ------------------------------ */ + +/* + * proc interface + */ +static int init_proc_entry(snd_sb_csp_t * p, int device) +{ + char name[16]; + snd_info_entry_t *entry; + sprintf(name, "cspD%d", device); + entry = p->proc = snd_info_create_card_entry(p->chip->card, name, p->chip->card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->c.text.read_size = 256; + entry->c.text.read = info_read; + entry->private_data = p; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + p->proc = NULL; + } + } + return 0; +} + +static void delete_proc_entry(snd_sb_csp_t * p) +{ + if (p->proc) { + snd_info_unregister(p->proc); + p->proc = NULL; + } +} + +static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, entry->private_data, return); + + snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f)); + snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'), + ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'), + ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'), + ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-')); + if (p->running & SNDRV_SB_CSP_ST_LOADED) { + snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr); + snd_iprintf(buffer, "Sample rates: "); + if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) { + snd_iprintf(buffer, "All\n"); + } else { + snd_iprintf(buffer, "%s%s%s%s\n", + ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""), + ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : "")); + } + if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { + snd_iprintf(buffer, "QSound decoder %sabled\n", + p->q_enabled ? "en" : "dis"); + } else { + snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n", + p->acc_format, + ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"), + ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"), + ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"), + ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"), + ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"), + ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-")); + } + } + if (p->running & SNDRV_SB_CSP_ST_AUTO) { + snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n"); + } + if (p->running & SNDRV_SB_CSP_ST_RUNNING) { + snd_iprintf(buffer, "Processing %dbit %s PCM samples\n", + ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8), + ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo")); + } + if (p->running & SNDRV_SB_CSP_ST_QSOUND) { + snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n", + p->qpos_left, p->qpos_right); + } +} + +/* */ + +EXPORT_SYMBOL(snd_sb_csp_new); + +/* + * INIT part + */ + +static int __init alsa_sb_csp_init(void) +{ + return 0; +} + +static void __exit alsa_sb_csp_exit(void) +{ +} + +module_init(alsa_sb_csp_init) +module_exit(alsa_sb_csp_exit) diff -Nru linux/sound/isa/sb/sb16_csp_codecs.h linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp_codecs.h --- linux/sound/isa/sb/sb16_csp_codecs.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_csp_codecs.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,949 @@ +/* + * Copyright (c) 1994 Creative Technology Ltd. + * Microcode files for SB16 Advanced Signal Processor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +static unsigned char mulaw_main[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, + 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, + 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, + 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, + 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, + 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, + 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, + 0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, + 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, + 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, + 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, + 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, + 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, + 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, + 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, + 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, + 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, + 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, + 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, + 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, + 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, + 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, + 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, + 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, + 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, + 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, + 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, + 0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54, + 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, + 0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, + 0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, + 0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, + 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b, + 0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, + 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, + 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, + 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, + 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, + 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, + 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, + 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, + 0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c, + 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, + 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, + 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, + 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, + 0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80, + 0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39, + 0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, + 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, + 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, + 0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, + 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, + 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, + 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, + 0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, + 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, + 0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, + 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, + 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, + 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, + 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, + 0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0, + 0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44, + 0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c, + 0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e, + 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, + 0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18, + 0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10, + 0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10, + 0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10, + 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, + 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, + 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, + 0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54, + 0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44, + 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54, + 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, + 0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54, + 0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44, + 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54, + 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00 +}; + +static unsigned char alaw_main[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b, + 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80, + 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b, + 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80, + 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80, + 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82, + 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19, + 0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19, + 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef, + 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1, + 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80, + 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54, + 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0, + 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44, + 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4, + 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4, + 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4, + 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4, + 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4, + 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4, + 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4, + 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80, + 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b, + 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4, + 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88, + 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39, + 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44, + 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19, + 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab, + 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0, + 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84, + 0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54, + 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84, + 0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b, + 0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44, + 0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19, + 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b, + 0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80, + 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19, + 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44, + 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19, + 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19, + 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a, + 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f, + 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e, + 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22, + 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80, + 0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c, + 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e, + 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0, + 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b, + 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02, + 0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49, + 0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39, + 0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49, + 0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39, + 0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02, + 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0, + 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e, + 0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22, + 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80, + 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80, + 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a, + 0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80, + 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a, + 0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82, + 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0, + 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02, + 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80, + 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80, + 0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a, + 0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4, + 0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44, + 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0, + 0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44, + 0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c, + 0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e, + 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80, + 0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10, + 0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b, + 0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80, + 0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80, + 0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19, + 0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49, + 0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18, + 0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb, + 0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80, + 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02, + 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19, + 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a, + 0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54, + 0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44, + 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54, + 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a, + 0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54, + 0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44, + 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54, + 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00 +}; + + +static unsigned char ima_adpcm_init[] = { + 0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e, + 0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80, + 0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82, + 0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19, + 0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82, + 0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19, + 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, + 0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49, + 0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00 +}; + +static unsigned char ima_adpcm_playback[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44, + 0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80, + 0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b, + 0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b, + 0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, + 0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14, + 0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8, + 0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49, + 0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39, + 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45, + 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, + 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, + 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, + 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, + 0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4, + 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, + 0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, + 0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, + 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, + 0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, + 0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, + 0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, + 0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, + 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, + 0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, + 0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, + 0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, + 0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, + 0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, + 0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, + 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, + 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, + 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, + 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, + 0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, + 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, + 0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, + 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, + 0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, + 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, + 0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, + 0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84, + 0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39, + 0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19, + 0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19, + 0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19, + 0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b, + 0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54, + 0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44, + 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19, + 0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84, + 0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b, + 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49, + 0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a, + 0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19, + 0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b, + 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19, + 0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39, + 0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a, + 0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, + 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, + 0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11, + 0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10, + 0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18, + 0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44, + 0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4, + 0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19, + 0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, + 0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, + 0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19, + 0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19, + 0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a, + 0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39, + 0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39, + 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19, + 0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e, + 0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84, + 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80, + 0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44, + 0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44, + 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b, + 0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84, + 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b, + 0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80, + 0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e, + 0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf, + 0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b, + 0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b, + 0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b, + 0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80, + 0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80, + 0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b, + 0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80, + 0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb, + 0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19, + 0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a, + 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44, + 0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44, + 0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60, + 0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80, + 0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2, + 0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4, + 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80, + 0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22, + 0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80, + 0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b, + 0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a, + 0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02, + 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19, + 0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60, + 0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82, + 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb, + 0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b, + 0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f, + 0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a, + 0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, + 0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82, + 0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a, + 0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, + 0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4, + 0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39, + 0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a, + 0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19, + 0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a, + 0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a, + 0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54, + 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44, + 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54, + 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a, + 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54, + 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44, + 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54, + 0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44, + 0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf, + 0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80, + 0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22, + 0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82, + 0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82, + 0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, + 0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80, + 0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80, + 0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a, + 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80, + 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, + 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00 +}; + +static unsigned char ima_adpcm_capture[] = { + 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44, + 0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44, + 0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8, + 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39, + 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49, + 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b, + 0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b, + 0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19, + 0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b, + 0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b, + 0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19, + 0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19, + 0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19, + 0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09, + 0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14, + 0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8, + 0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24, + 0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44, + 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45, + 0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18, + 0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19, + 0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8, + 0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8, + 0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, + 0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a, + 0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a, + 0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45, + 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf, + 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80, + 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b, + 0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4, + 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82, + 0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99, + 0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b, + 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b, + 0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b, + 0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b, + 0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b, + 0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b, + 0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b, + 0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b, + 0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b, + 0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b, + 0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b, + 0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b, + 0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45, + 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80, + 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8, + 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02, + 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82, + 0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8, + 0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44, + 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19, + 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10, + 0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, + 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88, + 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19, + 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a, + 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a, + 0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80, + 0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44, + 0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84, + 0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19, + 0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39, + 0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19, + 0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19, + 0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19, + 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19, + 0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19, + 0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b, + 0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f, + 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09, + 0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14, + 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80, + 0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49, + 0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80, + 0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b, + 0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b, + 0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80, + 0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44, + 0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39, + 0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19, + 0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44, + 0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45, + 0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4, + 0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e, + 0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84, + 0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10, + 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10, + 0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80, + 0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4, + 0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4, + 0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4, + 0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44, + 0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18, + 0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8, + 0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19, + 0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19, + 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, + 0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02, + 0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, + 0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f, + 0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44, + 0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44, + 0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f, + 0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b, + 0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b, + 0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80, + 0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84, + 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44, + 0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b, + 0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a, + 0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18, + 0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a, + 0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44, + 0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18, + 0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb, + 0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a, + 0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8, + 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39, + 0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a, + 0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44, + 0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80, + 0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f, + 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a, + 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02, + 0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02, + 0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09, + 0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44, + 0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f, + 0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88, + 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82, + 0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49, + 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a, + 0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a, + 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19, + 0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f, + 0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80, + 0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14, + 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, + 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, + 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, + 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60, + 0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80, + 0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0, + 0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14, + 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b, + 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80, + 0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54, + 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b, + 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82, + 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11, + 0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4, + 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18, + 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82, + 0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a, + 0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e, + 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a, + 0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a, + 0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a, + 0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a, + 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18, + 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82, + 0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80, + 0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80, + 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80, + 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46, + 0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14, + 0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a, + 0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19, + 0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a, + 0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19, + 0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a, + 0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19, + 0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a, + 0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19, + 0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a, + 0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19, + 0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a, + 0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19, + 0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a, + 0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19, + 0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a, + 0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19, + 0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b, + 0xa6, 0xc5, 0x11, 0x00 +}; diff -Nru linux/sound/isa/sb/sb16_main.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_main.c --- linux/sound/isa/sb/sb16_main.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb16_main.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,899 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of 16-bit SoundBlaster cards and clones + * Note: This is very ugly hardware which uses one 8-bit DMA channel and + * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't + * transfer 16-bit samples and 16-bit DMA channels can't transfer + * 8-bit samples. This make full duplex more complicated than + * can be... People, don't buy these soundcards for full 16-bit + * duplex!!! + * Note: 16-bit wide is assigned to first direction which made request. + * With full duplex - playback is preferred with abstract layer. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones"); +MODULE_LICENSE("GPL"); + +#define chip_t sb_t + +#ifdef CONFIG_SND_SB16_CSP +static void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) && + ((1 << runtime->format) == csp->acc_format)) { + /* Supported runtime PCM format for playback */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) { + /* QSound decoder is loaded and enabled */ + if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) { + /* Only for simple PCM formats */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } + } + } else if (csp->ops.csp_use(csp) == 0) { + /* Acquire CSP and try to autoload hardware codec */ + if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) { + /* Unsupported format, release CSP */ + csp->ops.csp_unuse(csp); + } else { + __start_CSP: + /* Try to start CSP */ + if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ? + SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, + (runtime->channels > 1) ? + SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { + /* Failed, release CSP */ + csp->ops.csp_unuse(csp); + } else { + /* Success, CSP acquired and running */ + chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE; + } + } + } + } +} + +static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) && + ((1 << runtime->format) == csp->acc_format)) { + /* Supported runtime PCM format for capture */ + if (csp->ops.csp_use(csp) == 0) { + /* If CSP was successfully acquired */ + goto __start_CSP; + } + } + } else if (csp->ops.csp_use(csp) == 0) { + /* Acquire CSP and try to autoload hardware codec */ + if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) { + /* Unsupported format, release CSP */ + csp->ops.csp_unuse(csp); + } else { + __start_CSP: + /* Try to start CSP */ + if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ? + SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, + (runtime->channels > 1) ? + SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { + /* Failed, release CSP */ + csp->ops.csp_unuse(csp); + } else { + /* Success, CSP acquired and running */ + chip->open = SNDRV_SB_CSP_MODE_DSP_READ; + } + } + } + } +} + +static void snd_sb16_csp_update(sb_t *chip) +{ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->qpos_changed) { + spin_lock(&chip->reg_lock); + csp->ops.csp_qsound_transfer (csp); + spin_unlock(&chip->reg_lock); + } + } +} + +static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + /* CSP decoders (QSound excluded) support only 16bit transfers */ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) { + runtime->hw.formats |= csp->acc_format; + } + } else { + /* autoloaded codecs */ + runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM; + } + } +} + +static void snd_sb16_csp_playback_close(sb_t *chip) +{ + if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->ops.csp_stop(csp) == 0) { + csp->ops.csp_unuse(csp); + chip->open = 0; + } + } +} + +static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime) +{ + /* CSP coders support only 16bit transfers */ + if (chip->hardware == SB_HW_16CSP) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->running & SNDRV_SB_CSP_ST_LOADED) { + /* manually loaded codec */ + if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) { + runtime->hw.formats |= csp->acc_format; + } + } else { + /* autoloaded codecs */ + runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_IMA_ADPCM; + } + } +} + +static void snd_sb16_csp_capture_close(sb_t *chip) +{ + if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { + snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + + if (csp->ops.csp_stop(csp) == 0) { + csp->ops.csp_unuse(csp); + chip->open = 0; + } + } +} +#else +#define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ +#define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ +#define snd_sb16_csp_update(chip) /*nop*/ +#define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ +#define snd_sb16_csp_playback_close(chip) /*nop*/ +#define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ +#define snd_sb16_csp_capture_close(chip) /*nop*/ +#endif + + +static void snd_sb16_setup_rate(sb_t *chip, + unsigned short rate, + int channel) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16)) + snd_sb_ack_16bit(chip); + else + snd_sb_ack_8bit(chip); + if (!(chip->mode & SB_RATE_LOCK)) { + chip->locked_rate = rate; + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN); + snd_sbdsp_command(chip, rate >> 8); + snd_sbdsp_command(chip, rate & 0xff); + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); + snd_sbdsp_command(chip, rate >> 8); + snd_sbdsp_command(chip, rate & 0xff); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int snd_sb16_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sb16_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char format; + unsigned int size, count, dma; + + snd_sb16_csp_playback_prepare(chip, runtime); + if (snd_pcm_format_unsigned(runtime->format) > 0) { + format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; + } else { + format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; + } + + snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK); + size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); + dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; + snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); + + count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & SB_MODE_PLAYBACK_16) { + count >>= 1; + count--; + snd_sbdsp_command(chip, SB_DSP4_OUT16_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + } else { + count--; + snd_sbdsp_command(chip, SB_DSP4_OUT8_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->mode |= SB_RATE_LOCK_PLAYBACK; + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); + /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ + if (chip->mode & SB_RATE_LOCK_CAPTURE) + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + chip->mode &= ~SB_RATE_LOCK_PLAYBACK; + break; + default: + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char format; + unsigned int size, count, dma; + + snd_sb16_csp_capture_prepare(chip, runtime); + if (snd_pcm_format_unsigned(runtime->format) > 0) { + format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; + } else { + format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; + } + snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE); + size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; + snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); + + count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->mode & SB_MODE_CAPTURE_16) { + count >>= 1; + count--; + snd_sbdsp_command(chip, SB_DSP4_IN16_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + } else { + count--; + snd_sbdsp_command(chip, SB_DSP4_IN8_AI); + snd_sbdsp_command(chip, format); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->mode |= SB_RATE_LOCK_CAPTURE; + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); + /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ + if (chip->mode & SB_RATE_LOCK_PLAYBACK) + snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); + chip->mode &= ~SB_RATE_LOCK_CAPTURE; + break; + default: + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + unsigned char status; + int ok; + + spin_lock(&chip->mixer_lock); + status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); + spin_unlock(&chip->mixer_lock); + if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + if (status & SB_IRQTYPE_8BIT) { + ok = 0; + if (chip->mode & SB_MODE_PLAYBACK_8) { + snd_pcm_period_elapsed(chip->playback_substream); + snd_sb16_csp_update(chip); + ok++; + } + if (chip->mode & SB_MODE_CAPTURE_8) { + snd_pcm_period_elapsed(chip->capture_substream); + ok++; + } + spin_lock(&chip->reg_lock); + if (!ok) + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + snd_sb_ack_8bit(chip); + spin_unlock(&chip->reg_lock); + } + if (status & SB_IRQTYPE_16BIT) { + ok = 0; + if (chip->mode & SB_MODE_PLAYBACK_16) { + snd_pcm_period_elapsed(chip->playback_substream); + snd_sb16_csp_update(chip); + ok++; + } + if (chip->mode & SB_MODE_CAPTURE_16) { + snd_pcm_period_elapsed(chip->capture_substream); + ok++; + } + spin_lock(&chip->reg_lock); + if (!ok) + snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); + snd_sb_ack_16bit(chip); + spin_unlock(&chip->reg_lock); + } +} + +/* + + */ + +static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int dma; + size_t ptr; + + dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; + ptr = chip->p_dma_size - snd_dma_residue(dma); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int dma; + size_t ptr; + + dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; + ptr = chip->c_dma_size - snd_dma_residue(dma); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_sb16_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: 0, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 4000, + rate_max: 44100, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_sb16_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: 0, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, + rate_min: 4000, + rate_max: 44100, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * open/close + */ + +int snd_sb16_playback_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->mode & SB_MODE_PLAYBACK) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + runtime->hw = snd_sb16_playback; + + /* skip if 16 bit DMA was reserved for capture */ + if (chip->force_mode16 & SB_MODE_CAPTURE_16) + goto __skip_16bit; + + if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) { + chip->mode |= SB_MODE_PLAYBACK_16; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + /* Vibra16X hack */ + if (chip->dma16 <= 3) { + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + } else { + snd_sb16_csp_playback_open(chip, runtime); + } + goto __open_ok; + } + + __skip_16bit: + if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) { + chip->mode |= SB_MODE_PLAYBACK_8; + /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ + if (chip->dma16 < 0) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + chip->mode |= SB_MODE_PLAYBACK_16; + } else { + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; + } + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + goto __open_ok; + } + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + + __open_ok: + if (chip->hardware == SB_HW_ALS100) + runtime->hw.rate_max = 48000; + if (chip->mode & SB_RATE_LOCK) + runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; + chip->playback_substream = substream; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_playback_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + snd_sb16_csp_playback_close(chip); + spin_lock_irqsave(&chip->open_lock, flags); + chip->playback_substream = NULL; + chip->mode &= ~SB_MODE_PLAYBACK; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_capture_open(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->mode & SB_MODE_CAPTURE) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + runtime->hw = snd_sb16_capture; + + /* skip if 16 bit DMA was reserved for playback */ + if (chip->force_mode16 & SB_MODE_PLAYBACK_16) + goto __skip_16bit; + + if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) { + chip->mode |= SB_MODE_CAPTURE_16; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + /* Vibra16X hack */ + if (chip->dma16 <= 3) { + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + } else { + snd_sb16_csp_capture_open(chip, runtime); + } + goto __open_ok; + } + + __skip_16bit: + if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) { + chip->mode |= SB_MODE_CAPTURE_8; + /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ + if (chip->dma16 < 0) { + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; + chip->mode |= SB_MODE_CAPTURE_16; + } else { + runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; + } + runtime->hw.buffer_bytes_max = + runtime->hw.period_bytes_max = 64 * 1024; + goto __open_ok; + } + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + + __open_ok: + if (chip->hardware == SB_HW_ALS100) + runtime->hw.rate_max = 48000; + if (chip->mode & SB_RATE_LOCK) + runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; + chip->capture_substream = substream; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +int snd_sb16_capture_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + snd_sb16_csp_capture_close(chip); + spin_lock_irqsave(&chip->open_lock, flags); + chip->capture_substream = NULL; + chip->mode &= ~SB_MODE_CAPTURE; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +/* + * DMA control interface + */ + +static int snd_sb16_set_dma_mode(sb_t *chip, int what) +{ + if (chip->dma8 < 0 || chip->dma16 < 0) { + snd_assert(what == 0, return -EINVAL); + return 0; + } + if (what == 0) { + chip->force_mode16 = 0; + } else if (what == 1) { + chip->force_mode16 = SB_MODE_PLAYBACK_16; + } else if (what == 2) { + chip->force_mode16 = SB_MODE_CAPTURE_16; + } else { + return -EINVAL; + } + return 0; +} + +static int snd_sb16_get_dma_mode(sb_t *chip) +{ + if (chip->dma8 < 0 || chip->dma16 < 0) + return 0; + switch (chip->force_mode16) { + case SB_MODE_PLAYBACK_16: + return 1; + case SB_MODE_CAPTURE_16: + return 2; + default: + return 0; + } +} + +static int snd_sb16_dma_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { + "Auto", "Playback", "Capture" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_sb16_dma_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char nval, oval; + int change; + + if ((nval = ucontrol->value.enumerated.item[0]) > 2) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_sb16_get_dma_mode(chip); + change = nval != oval; + snd_sb16_set_dma_mode(chip, nval); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +snd_kcontrol_new_t snd_sb16_dma_control = { + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "16-bit DMA Allocation", + info: snd_sb16_dma_control_info, + get: snd_sb16_dma_control_get, + put: snd_sb16_dma_control_put +}; + +/* + * Initialization part + */ + +int snd_sb16dsp_configure(sb_t * chip) +{ + unsigned long flags; + unsigned char irqreg = 0, dmareg = 0, mpureg; + unsigned char realirq, realdma, realmpureg; + /* note: mpu register should be present only on SB16 Vibra soundcards */ + + // printk("codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16); + spin_lock_irqsave(&chip->mixer_lock, flags); + mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06; + spin_unlock_irqrestore(&chip->mixer_lock, flags); + switch (chip->irq) { + case 2: + case 9: + irqreg |= SB_IRQSETUP_IRQ9; + break; + case 5: + irqreg |= SB_IRQSETUP_IRQ5; + break; + case 7: + irqreg |= SB_IRQSETUP_IRQ7; + break; + case 10: + irqreg |= SB_IRQSETUP_IRQ10; + break; + default: + return -EINVAL; + } + if (chip->dma8 >= 0) { + switch (chip->dma8) { + case 0: + dmareg |= SB_DMASETUP_DMA0; + break; + case 1: + dmareg |= SB_DMASETUP_DMA1; + break; + case 3: + dmareg |= SB_DMASETUP_DMA3; + break; + default: + return -EINVAL; + } + } + if (chip->dma16 >= 0) { + switch (chip->dma16) { + case 5: + dmareg |= SB_DMASETUP_DMA5; + break; + case 6: + dmareg |= SB_DMASETUP_DMA6; + break; + case 7: + dmareg |= SB_DMASETUP_DMA7; + break; + default: + return -EINVAL; + } + } + switch (chip->mpu_port) { + case 0x300: + mpureg |= 0x04; + break; + case 0x330: + mpureg |= 0x00; + break; + default: + mpureg |= 0x02; /* disable MPU */ + } + spin_lock_irqsave(&chip->mixer_lock, flags); + + snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg); + realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP); + + snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg); + realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP); + + snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg); + realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP); + + spin_unlock_irqrestore(&chip->mixer_lock, flags); + if ((~realirq) & irqreg || (~realdma) & dmareg) { + snd_printk("SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port); + snd_printk("SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg); + snd_printk("SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg); + return -ENODEV; + } + return 0; +} + +static snd_pcm_ops_t snd_sb16_playback_ops = { + open: snd_sb16_playback_open, + close: snd_sb16_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb16_hw_params, + hw_free: snd_sb16_hw_free, + prepare: snd_sb16_playback_prepare, + trigger: snd_sb16_playback_trigger, + pointer: snd_sb16_playback_pointer, +}; + +static snd_pcm_ops_t snd_sb16_capture_ops = { + open: snd_sb16_capture_open, + close: snd_sb16_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb16_hw_params, + hw_free: snd_sb16_hw_free, + prepare: snd_sb16_capture_prepare, + trigger: snd_sb16_capture_trigger, + pointer: snd_sb16_capture_pointer, +}; + +static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_card_t *card = chip->card; + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0) + return err; + sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + pcm->private_data = chip; + pcm->private_free = snd_sb16dsp_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); + + snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +EXPORT_SYMBOL(snd_sb16dsp_pcm); +EXPORT_SYMBOL(snd_sb16dsp_configure); +EXPORT_SYMBOL(snd_sb16dsp_interrupt); + +/* + * INIT part + */ + +static int __init alsa_sb16_init(void) +{ + return 0; +} + +static void __exit alsa_sb16_exit(void) +{ +} + +module_init(alsa_sb16_init) +module_exit(alsa_sb16_exit) diff -Nru linux/sound/isa/sb/sb8.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb8.c --- linux/sound/isa/sb/sb8.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb8.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,256 @@ +/* + * Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_AUTO_PROBE +#define SNDRV_GET_ID +#include + +#define chip_t sb_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ +static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Sound Blaster soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_port, "Port # for SB8 driver."); +MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for SB8 driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC); +MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB8 driver."); +MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC); + +struct snd_sb8 { + struct resource *fm_res; /* used to block FM i/o region for legacy cards */ +}; + +static snd_card_t *snd_sb8_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static void snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + + if (chip->open & SB_OPEN_PCM) { + snd_sb8dsp_interrupt(chip); + } else { + snd_sb8dsp_midi_interrupt(chip); + } +} + +static void snd_sb8_free(snd_card_t *card) +{ + struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data; + + if (acard == NULL) + return; + if (acard->fm_res) { + release_resource(acard->fm_res); + kfree_nocheck(acard->fm_res); + } +} + +static int __init snd_sb8_probe(int dev) +{ + sb_t *chip; + snd_card_t *card; + struct snd_sb8 *acard; + opl3_t *opl3; + int err; + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(struct snd_sb8)); + if (card == NULL) + return -ENOMEM; + acard = (struct snd_sb8 *)card->private_data; + card->private_free = snd_sb8_free; + + /* block the 0x388 port to avoid PnP conflicts */ + acard->fm_res = request_region(0x388, 4, "SoundBlaster FM"); + + if ((err = snd_sbdsp_create(card, snd_port[dev], snd_irq[dev], + snd_sb8_interrupt, + snd_dma8[dev], + -1, + SB_HW_AUTO, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware >= SB_HW_16) { + snd_card_free(card); + if (chip->hardware == SB_HW_ALS100) + snd_printdd("ALS100 chip detected at 0x%lx, try snd-card-als100 module\n", + snd_port[dev]); + else + snd_printdd("SB 16 chip detected at 0x%lx, try snd-card-sb16 module\n", + snd_port[dev]); + return -ENODEV; + } + + if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return err; + } + if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) { + if ((err = snd_opl3_create(card, chip->port + 8, 0, + OPL3_HW_AUTO, 1, + &opl3)) < 0) { + printk(KERN_ERR "sb8: no OPL device at 0x%lx\n", chip->port + 8); + } + } else { + if ((err = snd_opl3_create(card, chip->port, chip->port + 2, + OPL3_HW_AUTO, 1, + &opl3)) < 0) { + printk(KERN_ERR "sb8: no OPL device at 0x%lx-0x%lx\n", + chip->port, chip->port + 2); + } + } + if (err >= 0) { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8"); + strcpy(card->shortname, chip->name); + sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", + chip->name, + chip->port, + snd_irq[dev], snd_dma8[dev]); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sb8_cards[dev] = card; + return 0; +} + +static int __init snd_card_sb8_legacy_auto_probe(unsigned long port) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT) + continue; + snd_port[dev] = port; + res = snd_sb8_probe(dev); + if (res < 0) + snd_port[dev] = SNDRV_AUTO_PORT; + return res; + } + return -ENODEV; +} + +static int __init alsa_card_sb8_init(void) +{ + static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_port[dev] == SNDRV_AUTO_PORT) + continue; + if (snd_sb8_probe(dev) >= 0) + cards++; + } + cards += snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe); + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Sound Blaster soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_sb8_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_sb8_cards[idx]); +} + +module_init(alsa_card_sb8_init) +module_exit(alsa_card_sb8_exit) + +#ifndef MODULE + +/* format is: snd-sb8=snd_enable,snd_index,snd_id, + snd_port,snd_irq,snd_dma8 */ + +static int __init alsa_card_sb8_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_port[nr_dev]) == 2 && + get_option(&str,&snd_irq[nr_dev]) == 2 && + get_option(&str,&snd_dma8[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sb8=", alsa_card_sb8_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/sb/sb8_main.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_main.c --- linux/sound/isa/sb/sb8_main.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_main.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,564 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Uros Bizjak + * + * Routines for control of 8-bit SoundBlaster cards and clones + * Please note: I don't have access to old SB8 soundcards. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Thu Apr 29 20:36:17 BST 1999 George David Morrison + * DSP can't respond to commands whilst in "high speed" mode. Caused + * glitching during playback. Fixed. + * + * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak + * Cleaned up and rewrote lowlevel routines. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela , Uros Bizjak "); +MODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones"); +MODULE_LICENSE("GPL"); + +#define chip_t sb_t + +#define SB8_CLOCK 1000000 +#define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v)) +#define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v)) + +static ratnum_t clock = { + num: SB8_CLOCK, + den_min: 1, + den_max: 256, + den_step: 1, +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = { + nrats: 1, + rats: &clock, +}; + +static ratnum_t stereo_clocks[] = { + { + num: SB8_CLOCK, + den_min: SB8_DEN(22050), + den_max: SB8_DEN(22050), + den_step: 1, + }, + { + num: SB8_CLOCK, + den_min: SB8_DEN(11025), + den_max: SB8_DEN(11025), + den_step: 1, + } +}; + +static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (c->min > 1) { + unsigned int num = 0, den = 0; + int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE), + 2, stereo_clocks, &num, &den); + if (err >= 0 && den) { + params->rate_num = num; + params->rate_den = den; + } + return err; + } + return 0; +} + +static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { + snd_interval_t t = { min: 1, max: 1 }; + return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); + } + return 0; +} + +static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mixreg, rate, size, count; + + rate = runtime->rate; + switch (chip->hardware) { + case SB_HW_PRO: + if (runtime->channels > 1) { + snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); + chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_201: + if (rate > 23000) { + chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_20: + chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; + break; + case SB_HW_10: + chip->playback_format = SB_DSP_OUTPUT; + break; + default: + return -EINVAL; + } + size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); + count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); + if (runtime->channels > 1) { + /* set playback stereo mode */ + spin_lock(&chip->mixer_lock); + mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW); + snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02); + spin_unlock(&chip->mixer_lock); + + /* Soundblaster hardware programming reference guide, 3-23 */ + snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT); + runtime->dma_area[0] = 0x80; + snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE); + /* force interrupt */ + chip->mode = SB_MODE_HALT; + snd_sbdsp_command(chip, SB_DSP_OUTPUT); + snd_sbdsp_command(chip, 0); + snd_sbdsp_command(chip, 0); + } + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); + if (runtime->channels > 1) { + snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); + spin_lock(&chip->mixer_lock); + /* save output filter status and turn it off */ + mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT); + snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20); + spin_unlock(&chip->mixer_lock); + /* just use force_mode16 for temporary storate... */ + chip->force_mode16 = mixreg; + } else { + snd_sbdsp_command(chip, 256 - runtime->rate_den); + } + if (chip->playback_format != SB_DSP_OUTPUT) { + count--; + snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_dma_program(chip->dma8, runtime->dma_addr, + size, DMA_MODE_WRITE | DMA_AUTOINIT); + return 0; +} + +static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int count; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_sbdsp_command(chip, chip->playback_format); + if (chip->playback_format == SB_DSP_OUTPUT) { + count = chip->p_period_size - 1; + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) { + snd_pcm_runtime_t *runtime = substream->runtime; + snd_sbdsp_reset(chip); + if (runtime->channels > 1) { + spin_lock(&chip->mixer_lock); + /* restore output filter and set hardware to mono mode */ + snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02); + spin_unlock(&chip->mixer_lock); + } + } else { + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT; + return 0; +} + +static int snd_sb8_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sb8_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mixreg, rate, size, count; + + rate = runtime->rate; + switch (chip->hardware) { + case SB_HW_PRO: + if (runtime->channels > 1) { + snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); + chip->capture_format = SB_DSP_HI_INPUT_AUTO; + break; + } + chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO; + break; + case SB_HW_201: + if (rate > 13000) { + chip->capture_format = SB_DSP_HI_INPUT_AUTO; + break; + } + /* fallthru */ + case SB_HW_20: + chip->capture_format = SB_DSP_LO_INPUT_AUTO; + break; + case SB_HW_10: + chip->capture_format = SB_DSP_INPUT; + break; + default: + return -EINVAL; + } + size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + count = chip->c_period_size = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + if (runtime->channels > 1) + snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT); + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); + if (runtime->channels > 1) { + snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); + spin_lock(&chip->mixer_lock); + /* save input filter status and turn it off */ + mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT); + snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20); + spin_unlock(&chip->mixer_lock); + /* just use force_mode16 for temporary storate... */ + chip->force_mode16 = mixreg; + } else { + snd_sbdsp_command(chip, 256 - runtime->rate_den); + } + if (chip->capture_format != SB_DSP_OUTPUT) { + count--; + snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + snd_dma_program(chip->dma8, runtime->dma_addr, + size, DMA_MODE_READ | DMA_AUTOINIT); + return 0; +} + +static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int count; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_sbdsp_command(chip, chip->capture_format); + if (chip->capture_format == SB_DSP_INPUT) { + count = chip->c_period_size - 1; + snd_sbdsp_command(chip, count & 0xff); + snd_sbdsp_command(chip, count >> 8); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) { + snd_pcm_runtime_t *runtime = substream->runtime; + snd_sbdsp_reset(chip); + if (runtime->channels > 1) { + /* restore input filter status */ + spin_lock(&chip->mixer_lock); + snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16); + spin_unlock(&chip->mixer_lock); + /* set hardware to mono mode */ + snd_sbdsp_command(chip, SB_DSP_MONO_8BIT); + } + } else { + snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); + } + snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT; + return 0; +} + +void snd_sb8dsp_interrupt(sb_t *chip) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + +#if 0 + snd_printk("sb8: interrupt\n"); +#endif + snd_sb_ack_8bit(chip); + switch (chip->mode) { + case SB_MODE_PLAYBACK_8: /* ok.. playback is active */ + substream = chip->playback_substream; + runtime = substream->runtime; + if (chip->playback_format == SB_DSP_OUTPUT) + snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START); + snd_pcm_period_elapsed(substream); + break; + case SB_MODE_CAPTURE_8: + substream = chip->capture_substream; + runtime = substream->runtime; + if (chip->capture_format == SB_DSP_INPUT) + snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START); + snd_pcm_period_elapsed(substream); + break; + } +} + +static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->mode != SB_MODE_PLAYBACK_8) + return 0; + ptr = chip->p_dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + if (chip->mode != SB_MODE_CAPTURE_8) + return 0; + ptr = chip->c_dma_size - snd_dma_residue(chip->dma8); + return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static snd_pcm_hardware_t snd_sb8_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8, + rates: (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050), + rate_min: 4000, + rate_max: 23000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_sb8_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8, + rates: (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_11025), + rate_min: 4000, + rate_max: 13000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * + */ + +int snd_sb8_open(snd_pcm_substream_t *substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_PCM; + spin_unlock_irqrestore(&chip->open_lock, flags); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + chip->playback_substream = substream; + runtime->hw = snd_sb8_playback; + } else { + chip->capture_substream = substream; + runtime->hw = snd_sb8_capture; + } + switch (chip->hardware) { + case SB_HW_PRO: + runtime->hw.rate_max = 44100; + runtime->hw.channels_max = 2; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_sb8_hw_constraint_rate_channels, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_sb8_hw_constraint_channels_rate, 0, + SNDRV_PCM_HW_PARAM_RATE, -1); + break; + case SB_HW_201: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + runtime->hw.rate_max = 44100; + } else { + runtime->hw.rate_max = 15000; + } + default: + break; + } + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clock); + return 0; +} + +int snd_sb8_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + chip->capture_substream = NULL; + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~SB_OPEN_PCM; + spin_unlock_irqrestore(&chip->open_lock, flags); + return 0; +} + +/* + * Initialization part + */ + +static snd_pcm_ops_t snd_sb8_playback_ops = { + open: snd_sb8_open, + close: snd_sb8_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb8_hw_params, + hw_free: snd_sb8_hw_free, + prepare: snd_sb8_playback_prepare, + trigger: snd_sb8_playback_trigger, + pointer: snd_sb8_playback_pointer, +}; + +static snd_pcm_ops_t snd_sb8_capture_ops = { + open: snd_sb8_open, + close: snd_sb8_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sb8_hw_params, + hw_free: snd_sb8_hw_free, + prepare: snd_sb8_capture_prepare, + trigger: snd_sb8_capture_trigger, + pointer: snd_sb8_capture_pointer, +}; + +static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_card_t *card = chip->card; + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) + return err; + sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); + pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + pcm->private_data = chip; + pcm->private_free = snd_sb8dsp_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); + + snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +EXPORT_SYMBOL(snd_sb8dsp_pcm); +EXPORT_SYMBOL(snd_sb8dsp_interrupt); + /* sb8_midi.c */ +EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt); +EXPORT_SYMBOL(snd_sb8dsp_midi); + +/* + * INIT part + */ + +static int __init alsa_sb8_init(void) +{ + return 0; +} + +static void __exit alsa_sb8_exit(void) +{ +} + +module_init(alsa_sb8_init) +module_exit(alsa_sb8_exit) diff -Nru linux/sound/isa/sb/sb8_midi.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_midi.c --- linux/sound/isa/sb/sb8_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb8_midi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,262 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of SoundBlaster cards - MIDI interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -- + * + * Sun May 9 22:54:38 BST 1999 George David Morrison + * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from + * working. + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +/* + + */ + +void snd_sb8dsp_midi_interrupt(sb_t * chip) +{ + snd_rawmidi_t *rmidi; + int max = 64; + char byte; + + if (chip == NULL || (rmidi = chip->rmidi) == NULL) { + inb(SBP(chip, READ)); /* ack interrupt */ + return; + } + while (max-- > 0) { + spin_lock(&chip->midi_input_lock); + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + byte = inb(SBP(chip, READ)); + spin_unlock(&chip->midi_input_lock); + snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); + } else { + spin_unlock(&chip->midi_input_lock); + } + } +} + +/* + + */ + +static int snd_sb8dsp_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_MIDI_INPUT; + chip->midi_substream_input = substream; + if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + if (chip->open) { + spin_unlock_irqrestore(&chip->open_lock, flags); + return -EAGAIN; + } + chip->open |= SB_OPEN_MIDI_OUTPUT; + chip->midi_substream_output = substream; + if (!(chip->open & SB_OPEN_MIDI_INPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_TRIGGER); + chip->midi_substream_input = NULL; + if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static int snd_sb8dsp_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + spin_lock_irqsave(&chip->open_lock, flags); + chip->open &= ~SB_OPEN_MIDI_OUTPUT; + chip->midi_substream_output = NULL; + if (!(chip->open & SB_OPEN_MIDI_INPUT)) { + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sbdsp_reset(chip); /* reset DSP */ + } else { + spin_unlock_irqrestore(&chip->open_lock, flags); + } + return 0; +} + +static void snd_sb8dsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&chip->open_lock, flags); + if (up) { + if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) { + snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); + chip->open |= SB_OPEN_MIDI_TRIGGER; + } + } else { + if (chip->open & SB_OPEN_MIDI_TRIGGER) { + snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + } + } + spin_unlock_irqrestore(&chip->open_lock, flags); +} + +static void snd_sb8dsp_midi_output_write(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + sb_t *chip; + char byte; + int max = 32; + + /* how big is Tx FIFO? */ + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + while (max-- > 0) { + spin_lock_irqsave(&chip->open_lock, flags); + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + del_timer(&chip->midi_timer); + spin_unlock_irqrestore(&chip->open_lock, flags); + return; + } + snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); + snd_sbdsp_command(chip, byte); + spin_unlock_irqrestore(&chip->open_lock, flags); + } +} + +static void snd_sb8dsp_midi_output_timer(unsigned long data) +{ + snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data; + sb_t * chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->open_lock, flags); + chip->midi_timer.expires = 1 + jiffies; + add_timer(&chip->midi_timer); + spin_unlock_irqrestore(&chip->open_lock, flags); + snd_sb8dsp_midi_output_write(substream); +} + +static void snd_sb8dsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + sb_t *chip; + + chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + spin_lock_irqsave(&chip->open_lock, flags); + if (up) { + if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) { + chip->midi_timer.function = snd_sb8dsp_midi_output_timer; + chip->midi_timer.data = (unsigned long) substream; + chip->midi_timer.expires = 1 + jiffies; + add_timer(&chip->midi_timer); + chip->open |= SB_OPEN_MIDI_TRIGGER; + } + } else { + if (chip->open & SB_OPEN_MIDI_TRIGGER) { + chip->open &= ~SB_OPEN_MIDI_TRIGGER; + } + } + spin_unlock_irqrestore(&chip->open_lock, flags); + + if (up) + snd_sb8dsp_midi_output_write(substream); +} + +/* + + */ + +static snd_rawmidi_ops_t snd_sb8dsp_midi_output = +{ + open: snd_sb8dsp_midi_output_open, + close: snd_sb8dsp_midi_output_close, + trigger: snd_sb8dsp_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_sb8dsp_midi_input = +{ + open: snd_sb8dsp_midi_input_open, + close: snd_sb8dsp_midi_input_close, + trigger: snd_sb8dsp_midi_input_trigger, +}; + +int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "SB8 MIDI"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} diff -Nru linux/sound/isa/sb/sb_common.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb_common.c --- linux/sound/isa/sb/sb_common.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb_common.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,305 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Uros Bizjak + * + * Lowlevel routines for control of Sound Blaster cards + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t sb_t + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); + +#define BUSY_LOOPS 100000 + +#undef IO_DEBUG + +int snd_sbdsp_command(sb_t *chip, unsigned char val) +{ + int i; +#ifdef IO_DEBUG + snd_printk("command 0x%x\n", val); +#endif + for (i = BUSY_LOOPS; i; i--) + if ((inb(SBP(chip, STATUS)) & 0x80) == 0) { + outb(val, SBP(chip, COMMAND)); + return 1; + } + snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val); + return 0; +} + +int snd_sbdsp_get_byte(sb_t *chip) +{ + int val; + int i; + for (i = BUSY_LOOPS; i; i--) { + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + val = inb(SBP(chip, READ)); +#ifdef IO_DEBUG + snd_printk("get_byte 0x%x\n", val); +#endif + return val; + } + } + snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port); + return -ENODEV; +} + +int snd_sbdsp_reset(sb_t *chip) +{ + int i; + + outb(1, SBP(chip, RESET)); + udelay(10); + outb(0, SBP(chip, RESET)); + udelay(30); + for (i = BUSY_LOOPS; i; i--) + if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { + if (inb(SBP(chip, READ)) == 0xaa) + return 0; + else + break; + } + snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port); + return -ENODEV; +} + +int snd_sbdsp_version(sb_t * chip) +{ + unsigned int result = -ENODEV; + + snd_sbdsp_command(chip, SB_DSP_GET_VERSION); + result = (short) snd_sbdsp_get_byte(chip) << 8; + result |= (short) snd_sbdsp_get_byte(chip); + return result; +} + +static int snd_sbdsp_probe(sb_t * chip) +{ + int version; + int major, minor; + char *str; + unsigned long flags; + + /* + * initialization sequence + */ + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_sbdsp_reset(chip) < 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + version = snd_sbdsp_version(chip); + if (version < 0) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENODEV; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + major = version >> 8; + minor = version & 0xff; + snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", + chip->port, major, minor); + + switch (chip->hardware) { + case SB_HW_AUTO: + switch (major) { + case 1: + chip->hardware = SB_HW_10; + str = "1.0"; + break; + case 2: + if (minor) { + chip->hardware = SB_HW_201; + str = "2.01+"; + } else { + chip->hardware = SB_HW_20; + str = "2.0"; + } + break; + case 3: + chip->hardware = SB_HW_PRO; + str = "Pro"; + break; + case 4: + chip->hardware = SB_HW_16; + str = "16"; + break; + default: + snd_printk("SB [0x%lx]: unknown DSP chip version %i.%i\n", + chip->port, major, minor); + return -ENODEV; + } + break; + case SB_HW_ALS100: + str = "16 (ALS-100)"; + break; + case SB_HW_ALS4000: + str = "16 (ALS-4000)"; + break; + default: + return -ENODEV; + } + sprintf(chip->name, "Sound Blaster %s", str); + chip->version = (major << 8) | minor; + return 0; +} + +static int snd_sbdsp_free(sb_t *chip) +{ + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_alt_port) { + release_resource(chip->res_alt_port); + kfree_nocheck(chip->res_alt_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *) chip); +#ifdef CONFIG_ISA + if (chip->dma8 >= 0) { + disable_dma(chip->dma8); + free_dma(chip->dma8); + } + if (chip->dma16 >= 0) { + disable_dma(chip->dma16); + free_dma(chip->dma16); + } +#endif + snd_magic_kfree(chip); + return 0; +} + +static int snd_sbdsp_dev_free(snd_device_t *device) +{ + sb_t *chip = snd_magic_cast(sb_t, device->device_data, return -ENXIO); + return snd_sbdsp_free(chip); +} + +int snd_sbdsp_create(snd_card_t *card, + unsigned long port, + int irq, + void (*irq_handler)(int, void *, struct pt_regs *), + int dma8, + int dma16, + unsigned short hardware, + sb_t **r_chip) +{ + sb_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_sbdsp_dev_free, + }; + + snd_assert(r_chip != NULL, return -EINVAL); + *r_chip = NULL; + chip = snd_magic_kcalloc(sb_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->open_lock); + spin_lock_init(&chip->midi_input_lock); + spin_lock_init(&chip->mixer_lock); + chip->irq = -1; + chip->dma8 = -1; + chip->dma16 = -1; + chip->port = port; + + if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ? + SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT, + "SoundBlaster", (void *) chip)) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->irq = irq; + + if (hardware == SB_HW_ALS4000) + goto __skip_allocation; + + if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) { + snd_sbdsp_free(chip); + return -EBUSY; + } + +#ifdef CONFIG_ISA + if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->dma8 = dma8; + if (dma16 >= 0 && request_dma(dma16, "SoundBlaster - 16bit")) { + snd_sbdsp_free(chip); + return -EBUSY; + } + chip->dma16 = dma16; +#endif + + __skip_allocation: + chip->card = card; + chip->hardware = hardware; + if ((err = snd_sbdsp_probe(chip)) < 0) { + snd_sbdsp_free(chip); + return err; + } + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_sbdsp_free(chip); + return err; + } + *r_chip = chip; + return 0; +} + +EXPORT_SYMBOL(snd_sbdsp_command); +EXPORT_SYMBOL(snd_sbdsp_get_byte); +EXPORT_SYMBOL(snd_sbdsp_reset); +EXPORT_SYMBOL(snd_sbdsp_create); +/* sb_mixer.c */ +EXPORT_SYMBOL(snd_sbmixer_write); +EXPORT_SYMBOL(snd_sbmixer_read); +EXPORT_SYMBOL(snd_sbmixer_new); + +/* + * INIT part + */ + +static int __init alsa_sb_common_init(void) +{ + return 0; +} + +static void __exit alsa_sb_common_exit(void) +{ +} + +module_init(alsa_sb_common_init) +module_exit(alsa_sb_common_exit) diff -Nru linux/sound/isa/sb/sb_mixer.c linux-2.4.19-pre5-mjc/sound/isa/sb/sb_mixer.c --- linux/sound/isa/sb/sb_mixer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sb_mixer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,497 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for Sound Blaster mixer control + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +#define chip_t sb_t + +#undef IO_DEBUG + +void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data) +{ + outb(reg, SBP(chip, MIXER_ADDR)); + udelay(10); + outb(data, SBP(chip, MIXER_DATA)); + udelay(10); +#ifdef IO_DEBUG + snd_printk("mixer_write 0x%x 0x%x\n", reg, data); +#endif +} + +unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg) +{ + unsigned char result; + + outb(reg, SBP(chip, MIXER_ADDR)); + udelay(10); + result = inb(SBP(chip, MIXER_DATA)); + udelay(10); +#ifdef IO_DEBUG + snd_printk("mixer_read 0x%x 0x%x\n", reg, result); +#endif + return result; +} + +/* + * Single channel mixer element + */ + +#define SB_SINGLE(xname, reg, shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: xname, \ + info: snd_sbmixer_info_single, \ + get: snd_sbmixer_get_single, put: snd_sbmixer_put_single, \ + private_value: reg | (shift << 16) | (mask << 24) } + +static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sbmixer_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0xff; + int mask = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + spin_lock_irqsave(&sb->mixer_lock, flags); + val = (snd_sbmixer_read(sb, reg) >> shift) & mask; + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char val, oval; + + val = (ucontrol->value.integer.value[0] & mask) << shift; + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + if (change) + snd_sbmixer_write(sb, reg, val); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * Double channel mixer element + */ + +#define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: xname, \ + info: snd_sbmixer_info_double, \ + get: snd_sbmixer_get_double, put: snd_sbmixer_put_double, \ + private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) } + +static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sbmixer_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + unsigned char left, right; + + spin_lock_irqsave(&sb->mixer_lock, flags); + left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask; + right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask; + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + return 0; +} + +static int snd_sbmixer_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned char left, right, oleft, oright; + + left = (ucontrol->value.integer.value[0] & mask) << left_shift; + right = (ucontrol->value.integer.value[1] & mask) << right_shift; + spin_lock_irqsave(&sb->mixer_lock, flags); + if (left_reg == right_reg) { + oleft = snd_sbmixer_read(sb, left_reg); + left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right; + change = left != oleft; + if (change) + snd_sbmixer_write(sb, left_reg, left); + } else { + oleft = snd_sbmixer_read(sb, left_reg); + oright = snd_sbmixer_read(sb, right_reg); + left = (oleft & ~(mask << left_shift)) | left; + right = (oright & ~(mask << right_shift)) | right; + change = left != oleft || right != oright; + if (change) { + snd_sbmixer_write(sb, left_reg, left); + snd_sbmixer_write(sb, right_reg, right); + } + } + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * SBPRO input multiplexer + */ + +static int snd_sb8mixer_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { + "Mic", "CD", "Line" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + + +static int snd_sb8mixer_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char oval; + + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + switch ((oval >> 0x01) & 0x03) { + case SB_DSP_MIXS_CD: + ucontrol->value.enumerated.item[0] = 1; + break; + case SB_DSP_MIXS_LINE: + ucontrol->value.enumerated.item[0] = 2; + break; + default: + ucontrol->value.enumerated.item[0] = 0; + break; + } + return 0; +} + +static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned char nval, oval; + + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + switch (ucontrol->value.enumerated.item[0]) { + case 1: + nval = SB_DSP_MIXS_CD; + break; + case 2: + nval = SB_DSP_MIXS_LINE; + break; + default: + nval = SB_DSP_MIXS_MIC; + } + nval <<= 1; + spin_lock_irqsave(&sb->mixer_lock, flags); + oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE); + nval |= oval & ~0x06; + change = nval != oval; + if (change) + snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +/* + * SB16 input switch + */ + +#define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: xname, \ + info: snd_sb16mixer_info_input_sw, \ + get: snd_sb16mixer_get_input_sw, put: snd_sb16mixer_put_input_sw, \ + private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } + +static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_sb16mixer_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + unsigned char val1, val2; + + spin_lock_irqsave(&sb->mixer_lock, flags); + val1 = snd_sbmixer_read(sb, reg1); + val2 = snd_sbmixer_read(sb, reg2); + spin_unlock_irqrestore(&sb->mixer_lock, flags); + ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01; + ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01; + ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01; + ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01; + return 0; +} + +static int snd_sb16mixer_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sb_t *sb = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + int change; + unsigned char val1, val2, oval1, oval2; + + spin_lock_irqsave(&sb->mixer_lock, flags); + oval1 = snd_sbmixer_read(sb, reg1); + oval2 = snd_sbmixer_read(sb, reg2); + val1 = oval1 & ~((1 << left_shift) | (1 << right_shift)); + val2 = oval2 & ~((1 << left_shift) | (1 << right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; + change = val1 != oval1 || val2 != oval2; + if (change) { + snd_sbmixer_write(sb, reg1, val1); + snd_sbmixer_write(sb, reg2, val2); + } + spin_unlock_irqrestore(&sb->mixer_lock, flags); + return change; +} + +#define SB20_CONTROLS (sizeof(snd_sb20_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sb20_controls[] = { +SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7), +SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3), +SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7), +SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7) +}; + +#define SB20_INIT_VALUES (sizeof(snd_sb20_init_values)/sizeof(unsigned char)/2) + +static unsigned char snd_sb20_init_values[][2] = { + { SB_DSP20_MASTER_DEV, 0 }, + { SB_DSP20_FM_DEV, 0 }, +}; + +#define SBPRO_CONTROLS (sizeof(snd_sbpro_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sbpro_controls[] = { +SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7), +SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7), +SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1), +SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7), +SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7), +SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7), +SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_sb8mixer_info_mux, + get: snd_sb8mixer_get_mux, + put: snd_sb8mixer_put_mux, +}, +SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1), +SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1) +}; + +#define SBPRO_INIT_VALUES (sizeof(snd_sbpro_init_values)/sizeof(unsigned char)/2) + +static unsigned char snd_sbpro_init_values[][2] = { + { SB_DSP_MASTER_DEV, 0 }, + { SB_DSP_PCM_DEV, 0 }, + { SB_DSP_FM_DEV, 0 }, +}; + +#define SB16_CONTROLS (sizeof(snd_sb16_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sb16_controls[] = { +SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), +SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1), +SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), +SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), +SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31), +SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5), +SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31), +SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1), +SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), +SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31), +SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3), +SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), +SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31), +SB_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1), +SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1), +SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), +SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3), +SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3), +SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3), +SB_SINGLE("Auto Mic Gain", SB_DSP4_MIC_AGC, 0, 1) +}; + +#define SB16_INIT_VALUES (sizeof(snd_sb16_init_values)/sizeof(unsigned char)/2) + +static unsigned char snd_sb16_init_values[][2] = { + { SB_DSP4_MASTER_DEV + 0, 0 }, + { SB_DSP4_MASTER_DEV + 1, 0 }, + { SB_DSP4_PCM_DEV + 0, 0 }, + { SB_DSP4_PCM_DEV + 1, 0 }, + { SB_DSP4_SYNTH_DEV + 0, 0 }, + { SB_DSP4_SYNTH_DEV + 1, 0 }, + { SB_DSP4_INPUT_LEFT, 0 }, + { SB_DSP4_INPUT_RIGHT, 0 }, + { SB_DSP4_OUTPUT_SW, 0 }, + { SB_DSP4_SPEAKER_DEV, 0 }, +}; + +static int snd_sbmixer_init(sb_t *chip, + snd_kcontrol_new_t *controls, + int controls_count, + unsigned char map[][2], + int map_count, + char *name) +{ + unsigned long flags; + snd_card_t *card = chip->card; + int idx, err; + + /* mixer reset */ + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, 0x00, 0x00); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + /* mute and zero volume channels */ + for (idx = 0; idx < map_count; idx++) { + spin_lock_irqsave(&chip->mixer_lock, flags); + snd_sbmixer_write(chip, map[idx][0], map[idx][1]); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + } + + for (idx = 0; idx < controls_count; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&controls[idx], chip))) < 0) + return err; + } + snd_component_add(card, name); + strcpy(card->mixername, name); + return 0; +} + +int snd_sbmixer_new(sb_t *chip) +{ + snd_card_t * card; + int err; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + switch (chip->hardware) { + case SB_HW_10: + return 0; /* no mixer chip on SB1.x */ + case SB_HW_20: + case SB_HW_201: + if ((err = snd_sbmixer_init(chip, + snd_sb20_controls, SB20_CONTROLS, + snd_sb20_init_values, SB20_INIT_VALUES, + "CTL1335")) < 0) + return err; + break; + case SB_HW_PRO: + if ((err = snd_sbmixer_init(chip, + snd_sbpro_controls, SBPRO_CONTROLS, + snd_sbpro_init_values, SBPRO_INIT_VALUES, + "CTL1345")) < 0) + return err; + break; + case SB_HW_16: + case SB_HW_ALS100: + case SB_HW_ALS4000: + if ((err = snd_sbmixer_init(chip, + snd_sb16_controls, SB16_CONTROLS, + snd_sb16_init_values, SB16_INIT_VALUES, + "CTL1745")) < 0) + return err; + break; + default: + strcpy(card->mixername, "???"); + } + return 0; +} diff -Nru linux/sound/isa/sb/sbawe.c linux-2.4.19-pre5-mjc/sound/isa/sb/sbawe.c --- linux/sound/isa/sb/sbawe.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sb/sbawe.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,2 @@ +#define SNDRV_SBAWE +#include "sb16.c" diff -Nru linux/sound/isa/sgalaxy.c linux-2.4.19-pre5-mjc/sound/isa/sgalaxy.c --- linux/sound/isa/sgalaxy.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/sgalaxy.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,352 @@ +/* + * Driver for Aztech Sound Galaxy cards + * Copyright (c) by Christopher Butler +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_LEGACY_FIND_FREE_IRQ +#define SNDRV_LEGACY_FIND_FREE_DMA +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Christopher Butler "); +MODULE_DESCRIPTION("Aztech Sound Galaxy"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Aztech Systems,Sound Galaxy}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */ +static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */ +static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Sound Galaxy soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Sound Galaxy soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_sbport, "Port # for Sound Galaxy SB driver."); +MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220},{0x240}},dialog:list"); +MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_wssport, "Port # for Sound Galaxy WSS driver."); +MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); +MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_irq, "IRQ # for Sound Galaxy driver."); +MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{7},{9},{10},{11}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for Sound Galaxy driver."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC); + +#define SGALAXY_AUXC_LEFT 18 +#define SGALAXY_AUXC_RIGHT 19 + +static snd_card_t *snd_sgalaxy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +/* + + */ + +#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x ) + +/* from lowlevel/sb/sb.c - to avoid having to allocate a sb_t for the */ +/* short time we actually need it.. */ + +static int snd_sgalaxy_sbdsp_reset(unsigned long port) +{ + int i; + + outb(1, SBP1(port, RESET)); + udelay(10); + outb(0, SBP1(port, RESET)); + udelay(30); + for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++); + if (inb(SBP1(port, READ)) != 0xaa) { + snd_printd("sb_reset: failed at 0x%lx!!!\n", port); + return -ENODEV; + } + return 0; +} + +static int __init snd_sgalaxy_sbdsp_command(unsigned long port, unsigned char val) +{ + int i; + + for (i = 10000; i; i--) + if ((inb(SBP1(port, STATUS)) & 0x80) == 0) { + outb(val, SBP1(port, COMMAND)); + return 1; + } + + return 0; +} + +static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) +{ + static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, + 0x10, 0x18, 0x20, -1, -1, -1, -1}; + static int dma_bits[] = {1, 2, 0, 3}; + int tmp, tmp1; + + unsigned long flags; + + if ((tmp = inb(port + 3)) == 0xff) + { + snd_printdd("I/O address dead (0x%lx)\n", port); + return 0; + } +#if 0 + snd_printdd("WSS signature = 0x%x\n", tmp); +#endif + + if ((tmp & 0x3f) != 0x04 && + (tmp & 0x3f) != 0x0f && + (tmp & 0x3f) != 0x00) { + snd_printdd("No WSS signature detected on port 0x%lx\n", + port + 3); + return 0; + } + +#if 0 + snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n"); +#endif + + save_flags(flags); + cli(); + + /* initialize IRQ for WSS codec */ + tmp = interrupt_bits[irq % 16]; + if (tmp < 0) { + restore_flags(flags); + return -EINVAL; + } + outb(tmp | 0x40, port); + tmp1 = dma_bits[dma % 4]; + outb(tmp | tmp1, port); + + restore_flags(flags); + return 0; +} + +static int __init snd_sgalaxy_detect(int dev, int irq, int dma) +{ +#if 0 + snd_printdd("sgalaxy - switching to WSS mode\n"); +#endif + + /* switch to WSS mode */ + snd_sgalaxy_sbdsp_reset(snd_sbport[dev]); + + snd_sgalaxy_sbdsp_command(snd_sbport[dev], 9); + snd_sgalaxy_sbdsp_command(snd_sbport[dev], 0); + + udelay(400); + return snd_sgalaxy_setup_wss(snd_wssport[dev], irq, dma); +} + +#define SGALAXY_CONTROLS 2 + +static snd_kcontrol_new_t snd_sgalaxy_controls[2] = { +AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), +AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) +}; + +static int __init snd_sgalaxy_mixer(ad1848_t *chip) +{ + snd_card_t *card = chip->card; + snd_ctl_elem_id_t id1, id2; + int idx, err; + + memset(&id1, 0, sizeof(id1)); + memset(&id2, 0, sizeof(id2)); + id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + /* reassign AUX0 to LINE */ + strcpy(id1.name, "Aux Playback Switch"); + strcpy(id2.name, "Line Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "Line Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* reassign AUX1 to FM */ + strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strcpy(id2.name, "FM Playback Switch"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + strcpy(id1.name, "Aux Playback Volume"); + strcpy(id2.name, "FM Playback Volume"); + if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) + return err; + /* build AUX2 input */ + for (idx = 0; idx < SGALAXY_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sgalaxy_controls[idx], chip))) < 0) + return err; + } + return 0; +} + +static int __init snd_sgalaxy_probe(int dev) +{ + static int possible_irqs[] = {7, 9, 10, 11, -1}; + static int possible_dmas[] = {1, 3, 0, -1}; + int err, irq, dma1; + snd_card_t *card; + ad1848_t *chip; + + if (snd_sbport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify SB port\n"); + return -EINVAL; + } + if (snd_wssport[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify WSS port\n"); + return -EINVAL; + } + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + irq = snd_irq[dev]; + if (irq == SNDRV_AUTO_IRQ) { + if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free IRQ\n"); + return -EBUSY; + } + } + dma1 = snd_dma1[dev]; + if (dma1 == SNDRV_AUTO_DMA) { + if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { + snd_card_free(card); + snd_printk("unable to find a free DMA\n"); + return -EBUSY; + } + } + + if ((err = snd_sgalaxy_detect(dev, irq, dma1)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_create(card, snd_wssport[dev] + 4, + irq, dma1, + AD1848_HW_DETECT, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) { + snd_printdd("sgalaxy - error creating new ad1848 PCM device\n"); + snd_card_free(card); + return err; + } + if ((err = snd_ad1848_mixer(chip)) < 0) { + snd_printdd("sgalaxy - error creating new ad1848 mixer\n"); + snd_card_free(card); + return err; + } + if (snd_sgalaxy_mixer(chip) < 0) { + snd_printdd("sgalaxy - the mixer rewrite failed\n"); + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Sound Galaxy"); + strcpy(card->shortname, "Sound Galaxy"); + sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d", + snd_wssport[dev], irq, dma1); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_sgalaxy_cards[dev] = card; + return 0; +} + +static int __init alsa_card_sgalaxy_init(void) +{ + int dev, cards; + + for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) { + if (snd_sgalaxy_probe(dev) >= 0) + cards++; + } + if (!cards) { +#ifdef MODULE + printk(KERN_ERR "Sound Galaxy soundcard not found or device busy\n"); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit alsa_card_sgalaxy_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_sgalaxy_cards[idx]); +} + +module_init(alsa_card_sgalaxy_init) +module_exit(alsa_card_sgalaxy_exit) + +#ifndef MODULE + +/* format is: snd-sgalaxy=snd_enable,snd_index,snd_id, + snd_sbport,snd_wssport, + snd_irq,snd_dma1 */ + +static int __init alsa_card_sgalaxy_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_sbport[nr_dev]) == 2 && + get_option(&str,(int *)&snd_wssport[nr_dev]) == 2 && + get_option(&str,(int *)&snd_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_dma1[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sgalaxy=", alsa_card_sgalaxy_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/wavefront/Makefile linux-2.4.19-pre5-mjc/sound/isa/wavefront/Makefile --- linux/sound/isa/wavefront/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,30 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _wavefront.o + +#list-multi := snd-wavefront-fx.o snd-wavefront-synth.o snd-wavefront.o +list-multi := snd-wavefront.o + +#export-objs := wavefront_fx.o wavefront_synth.o + +#snd-wavefront-fx-objs := wavefront_fx.o +#snd-wavefront-synth-objs := wavefront_synth.o wavefront_midi.o +#snd-wavefront-objs := wavefront.o +snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o + +include $(TOPDIR)/Rules.make + +snd-wavefront-fx.o: $(snd-wavefront-fx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-fx-objs) + +snd-wavefront-synth.o: $(snd-wavefront-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-synth-objs) + +snd-wavefront.o: $(snd-wavefront-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-wavefront-objs) diff -Nru linux/sound/isa/wavefront/wavefront.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront.c --- linux/sound/isa/wavefront/wavefront.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,797 @@ +/* + * ALSA card-level driver for Turtle Beach Wavefront cards + * (Maui,Tropez,Tropez+) + * + * Copyright (c) 1997-1999 by Paul Barton-Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifndef LINUX_ISAPNP_H +#include +#define isapnp_card pci_bus +#define isapnp_dev pci_dev +#endif +#include +#define SNDRV_GET_ID +#include +#include +#include + +#define chip_t cs4231_t + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Paul Barton-Davis "); +MODULE_DESCRIPTION("Turtle Beach Wavefront"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Turtle Beach,Maui/Tropez/Tropez+}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ +static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static long snd_cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */ +static long snd_cs4232_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_cs4232_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */ +static long snd_ics2115_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,9,11,12,15 */ +static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ +static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ +static int snd_use_cs4232_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_SYNTAX(snd_index, "Index value for WaveFront soundcard."); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for WaveFront soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable WaveFront soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +#ifdef __ISAPNP__ +MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for WaveFront soundcards."); +MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC); +#endif +MODULE_PARM(snd_cs4232_pcm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_4232_port, "Port # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_cs4232_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_cs4232_pcm_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_cs4232_pcm_irq, "IRQ # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_cs4232_pcm_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC); +MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4232 PCM interface."); +MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC); +MODULE_PARM(snd_cs4232_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_cs4232_mpu_port, "port # for CS4232 MPU-401 interface."); +MODULE_PARM_SYNTAX(snd_cs4232_mpu_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_cs4232_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface."); +MODULE_PARM_SYNTAX(snd_cs4232_mpu_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_ics2115_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_ics2115_irq, "IRQ # for ICS2115."); +MODULE_PARM_SYNTAX(snd_ics2115_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); +MODULE_PARM(snd_ics2115_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_ics2115_port, "Port # for ICS2115."); +MODULE_PARM_SYNTAX(snd_ics2115_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port #."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC); +MODULE_PARM(snd_use_cs4232_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)"); +MODULE_PARM_SYNTAX(snd_use_cs4232_midi, SNDRV_ENABLED ",allows use of CS4323 MPU-401 interface"); + +static snd_card_t *snd_wavefront_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +#ifdef __ISAPNP__ + +static struct isapnp_card *snd_wavefront_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static const struct isapnp_card_id *snd_wavefront_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +static struct isapnp_card_id snd_wavefront_pnpids[] __devinitdata = { + { + ISAPNP_CARD_ID('C','S','C',0x7532), /* Tropez */ + devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000), /* WSS */ + ISAPNP_DEVICE_ID('C','S','C',0x0010), /* CTRL */ + ISAPNP_DEVICE_ID('P','n','P',0xb006), /* MPU */ + ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */ + }, + { + ISAPNP_CARD_ID('C','S','C',0x7632), /* Tropez+ */ + devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000), /* WSS */ + ISAPNP_DEVICE_ID('C','S','C',0x0010), /* CTRL */ + ISAPNP_DEVICE_ID('P','n','P',0xb006), /* MPU */ + ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */ + }, + { ISAPNP_CARD_END, } +}; + +ISAPNP_CARD_TABLE(snd_wavefront_pnpids); + +static int __init +snd_wavefront_isapnp (int dev, snd_wavefront_card_t *acard) +{ + const struct isapnp_card_id *id = snd_wavefront_isapnp_id[dev]; + struct isapnp_card *card = snd_wavefront_isapnp_cards[dev]; + struct isapnp_dev *pdev; + int tmp; + + /* Check for each logical device. */ + + /* CS4232 chip (aka "windows sound system") is logical device 0 */ + + acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL); + if (acard->wss->active) { + acard->wss = NULL; + return -EBUSY; + } + + /* there is a game port at logical device 1, but we ignore it completely */ + + /* the control interface is logical device 2, but we ignore it + completely. in fact, nobody even seems to know what it + does. + */ + + /* Only configure the CS4232 MIDI interface if its been + specifically requested. It is logical device 3. + */ + + if (snd_use_cs4232_midi[dev]) { + acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL); + if (acard->mpu->active) { + acard->wss = acard->synth = acard->mpu = NULL; + return -EBUSY; + } + } + + /* The ICS2115 synth is logical device 4 */ + + acard->synth = isapnp_find_dev(card, id->devs[3].vendor, id->devs[3].function, NULL); + if (acard->synth->active) { + acard->wss = acard->synth = NULL; + return -EBUSY; + } + + /* PCM/FM initialization */ + + pdev = acard->wss; + + if ((tmp = pdev->prepare (pdev)) < 0) { + if (tmp == -EBUSY) { + snd_printk ("ISA PnP configuration appears to have " + "been done. Restart the isapnp module.\n"); + return 0; + } + snd_printk ("isapnp WSS preparation failed\n"); + return -EAGAIN; + } + + /* An interesting note from the Tropez+ FAQ: + + Q. [Ports] Why is the base address of the WSS I/O ports off by 4? + + A. WSS I/O requires a block of 8 I/O addresses ("ports"). Of these, the first + 4 are used to identify and configure the board. With the advent of PnP, + these first 4 addresses have become obsolete, and software applications + only use the last 4 addresses to control the codec chip. Therefore, the + base address setting "skips past" the 4 unused addresses. + + */ + + if (snd_cs4232_pcm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_cs4232_pcm_port[dev], 4); + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4); + if (snd_dma1[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1); + if (snd_dma2[dev] != SNDRV_AUTO_DMA) + isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1); + if (snd_cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->irq_resource[0], snd_cs4232_pcm_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk ("isapnp WSS activation failed\n"); + return -EBUSY; + } + + snd_cs4232_pcm_port[dev] = pdev->resource[0].start; + snd_fm_port[dev] = pdev->resource[1].start; + snd_dma1[dev] = pdev->dma_resource[0].start; + snd_dma2[dev] = pdev->dma_resource[1].start; + snd_cs4232_pcm_irq[dev] = pdev->irq_resource[0].start; + + /* Synth initialization */ + + pdev = acard->synth; + + if ((tmp = pdev->prepare(pdev))<0) { + if (tmp == -EBUSY) { + snd_printk ("ISA PnP configuration appears to have " + "been done. Restart the isapnp module.\n"); + } + acard->wss->deactivate(acard->wss); + snd_printk ("ICS2115 synth preparation failed\n"); + return -EAGAIN; + } + if (snd_ics2115_port[dev] != SNDRV_AUTO_PORT) { + isapnp_resource_change(&pdev->resource[0], snd_ics2115_port[dev], 16); + } + + if (snd_ics2115_port[dev] != SNDRV_AUTO_IRQ) { + isapnp_resource_change(&pdev->irq_resource[0], snd_ics2115_irq[dev], 1); + } + + if (pdev->activate(pdev)<0) { + snd_printk("isapnp activation for ICS2115 failed\n"); + acard->wss->deactivate(acard->wss); + return -EBUSY; + } + + snd_ics2115_port[dev] = pdev->resource[0].start; + snd_ics2115_irq[dev] = pdev->irq_resource[0].start; + + /* CS4232 MPU initialization. Configure this only if + explicitly requested, since its physically inaccessible and + consumes another IRQ. + */ + + if (snd_use_cs4232_midi[dev]) { + + pdev = acard->mpu; + + if (pdev->prepare(pdev)<0) { + acard->wss->deactivate(acard->wss); + if (acard->synth) + acard->synth->deactivate(acard->synth); + snd_printk ("CS4232 MPU preparation failed\n"); + return -EAGAIN; + } + + if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) + isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_port[dev], 2); + if (snd_cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ) + isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_irq[dev], 1); + + if (pdev->activate(pdev)<0) { + snd_printk("isapnp CS4232 MPU activation failed\n"); + snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; + } else { + snd_cs4232_mpu_port[dev] = pdev->resource[0].start; + snd_cs4232_mpu_irq[dev] = pdev->irq_resource[0].start; + } + + snd_printk ("CS4232 MPU: port=0x%lx, irq=%i\n", + snd_cs4232_mpu_port[dev], + snd_cs4232_mpu_irq[dev]); + } + + snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n", + snd_cs4232_pcm_port[dev], + snd_fm_port[dev], + snd_dma1[dev], + snd_dma2[dev], + snd_cs4232_pcm_irq[dev], + snd_ics2115_port[dev], + snd_ics2115_irq[dev]); + + return 0; +} + +static void +snd_wavefront_deactivate (snd_wavefront_card_t *acard) +{ + snd_printk ("deactivating PnP devices\n"); + if (acard->wss) { + acard->wss->deactivate(acard->wss); + acard->wss = NULL; + } + if (acard->ctrl) { + acard->ctrl->deactivate(acard->ctrl); + acard->ctrl = NULL; + } + if (acard->mpu) { + acard->mpu->deactivate(acard->mpu); + acard->mpu = NULL; + } + if (acard->synth) { + acard->synth->deactivate(acard->synth); + acard->synth = NULL; + } +} + +#endif /* __ISAPNP__ */ + +static void snd_wavefront_ics2115_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + snd_wavefront_card_t *acard; + + acard = (snd_wavefront_card_t *) dev_id; + + if (acard == NULL) + return; + + if (acard->wavefront.interrupts_are_midi) { + snd_wavefront_midi_interrupt (acard); + } else { + snd_wavefront_internal_interrupt (acard); + } +} + +snd_hwdep_t * __init +snd_wavefront_new_synth (snd_card_t *card, + int hw_dev, + snd_wavefront_card_t *acard) +{ + snd_hwdep_t *wavefront_synth; + + if (snd_wavefront_detect (acard) < 0) { + return NULL; + } + + if (snd_wavefront_start (&acard->wavefront) < 0) { + return NULL; + } + + if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0) + return NULL; + strcpy (wavefront_synth->name, + "WaveFront (ICS2115) wavetable synthesizer"); + wavefront_synth->ops.open = snd_wavefront_synth_open; + wavefront_synth->ops.release = snd_wavefront_synth_release; + wavefront_synth->ops.ioctl = snd_wavefront_synth_ioctl; + + return wavefront_synth; +} + +snd_hwdep_t * __init +snd_wavefront_new_fx (snd_card_t *card, + int hw_dev, + snd_wavefront_card_t *acard, + unsigned long port) + +{ + snd_hwdep_t *fx_processor; + + if (snd_wavefront_fx_start (&acard->wavefront)) { + snd_printk ("cannot initialize YSS225 FX processor"); + return NULL; + } + + if (snd_hwdep_new (card, "YSS225", hw_dev, &fx_processor) < 0) + return NULL; + sprintf (fx_processor->name, "YSS225 FX Processor at 0x%lx", port); + fx_processor->ops.open = snd_wavefront_fx_open; + fx_processor->ops.release = snd_wavefront_fx_release; + fx_processor->ops.ioctl = snd_wavefront_fx_ioctl; + + return fx_processor; +} + +static snd_wavefront_mpu_id internal_id = internal_mpu; +static snd_wavefront_mpu_id external_id = external_mpu; + +snd_rawmidi_t * __init +snd_wavefront_new_midi (snd_card_t *card, + int midi_dev, + snd_wavefront_card_t *acard, + unsigned long port, + snd_wavefront_mpu_id mpu) + +{ + snd_rawmidi_t *rmidi; + static int first = 1; + + if (first) { + first = 0; + acard->wavefront.midi.base = port; + if (snd_wavefront_midi_start (acard)) { + snd_printk ("cannot initialize MIDI interface\n"); + return NULL; + } + } + + if (snd_rawmidi_new (card, "WaveFront MIDI", midi_dev, 1, 1, &rmidi) < 0) + return NULL; + + if (mpu == internal_mpu) { + strcpy(rmidi->name, "WaveFront MIDI (Internal)"); + rmidi->private_data = &internal_id; + } else { + strcpy(rmidi->name, "WaveFront MIDI (External)"); + rmidi->private_data = &external_id; + } + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_wavefront_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input); + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + + return rmidi; +} + +static void +snd_wavefront_free(snd_card_t *card) +{ + snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data; + + if (acard) { +#ifdef __ISAPNP__ + snd_wavefront_deactivate(acard); +#endif + if (acard->wavefront.res_base != NULL) { + release_resource(acard->wavefront.res_base); + kfree_nocheck(acard->wavefront.res_base); + } + if (acard->wavefront.irq > 0) + free_irq(acard->wavefront.irq, (void *)acard); + } +} + +static int __init +snd_wavefront_probe (int dev) +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + cs4231_t *chip; + snd_hwdep_t *wavefront_synth; + snd_rawmidi_t *ics2115_internal_rmidi = NULL; + snd_rawmidi_t *ics2115_external_rmidi = NULL; + snd_hwdep_t *fx_processor; + int hw_dev = 0, midi_dev = 0, err; + + if (snd_cs4232_mpu_port[dev] < 0) + snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT; + if (snd_fm_port[dev] < 0) + snd_fm_port[dev] = SNDRV_AUTO_PORT; + if (snd_ics2115_port[dev] < 0) + snd_ics2115_port[dev] = SNDRV_AUTO_PORT; + +#ifdef __ISAPNP__ + if (!snd_isapnp[dev]) { +#endif + if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify CS4232 port\n"); + return -EINVAL; + } + if (snd_ics2115_port[dev] == SNDRV_AUTO_PORT) { + snd_printk("specify ICS2115 port\n"); + return -ENODEV; + } +#ifdef __ISAPNP__ + } +#endif + card = snd_card_new (snd_index[dev], + snd_id[dev], + THIS_MODULE, + sizeof(snd_wavefront_card_t)); + + if (card == NULL) { + return -ENOMEM; + } + acard = (snd_wavefront_card_t *)card->private_data; + acard->wavefront.irq = -1; + init_waitqueue_head(&acard->wavefront.interrupt_sleeper); + spin_lock_init(&acard->wavefront.midi.open); + spin_lock_init(&acard->wavefront.midi.virtual); + card->private_free = snd_wavefront_free; + +#ifdef __ISAPNP__ + if (snd_isapnp[dev] && snd_wavefront_isapnp (dev, acard) < 0) { + if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) { + snd_printk ("isapnp detection failed\n"); + snd_card_free (card); + return -ENODEV; + } + } +#endif /* __ISAPNP__ */ + + /* --------- PCM --------------- */ + + if ((err = snd_cs4231_create (card, + snd_cs4232_pcm_port[dev], + -1, + snd_cs4232_pcm_irq[dev], + snd_dma1[dev], + snd_dma2[dev], + CS4231_HW_DETECT, 0, &chip)) < 0) { + snd_card_free(card); + snd_printk ("can't allocate CS4231 device\n"); + return err; + } + + if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + /* ---------- OPL3 synth --------- */ + + if (snd_fm_port[dev] != SNDRV_AUTO_PORT) { + opl3_t *opl3; + + if ((err = snd_opl3_create(card, + snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_OPL3_CS, + 0, &opl3)) < 0) { + snd_printk ("can't allocate or detect OPL3 synth\n"); + snd_card_free(card); + return err; + } + + if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + hw_dev++; + } + + /* ------- ICS2115 Wavetable synth ------- */ + + if ((acard->wavefront.res_base = request_region(snd_ics2115_port[dev], 16, "ICS2115")) == NULL) { + snd_printk("unable to grab ICS2115 i/o region 0x%lx-0x%lx\n", snd_ics2115_port[dev], snd_ics2115_port[dev] + 16 - 1); + snd_card_free(card); + return -EBUSY; + } + if (request_irq(snd_ics2115_irq[dev], snd_wavefront_ics2115_interrupt, SA_INTERRUPT, "ICS2115", (void *)acard)) { + snd_printk("unable to use ICS2115 IRQ %d\n", snd_ics2115_irq[dev]); + snd_card_free(card); + return -EBUSY; + } + + acard->wavefront.irq = snd_ics2115_irq[dev]; + acard->wavefront.base = snd_ics2115_port[dev]; + + if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) { + snd_printk ("can't create WaveFront synth device\n"); + snd_card_free(card); + return -ENOMEM; + } + + strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); + wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115; + hw_dev++; + + /* --------- Mixer ------------ */ + + if ((err = snd_cs4231_mixer(chip)) < 0) { + snd_printk ("can't allocate mixer device\n"); + snd_card_free(card); + return err; + } + + /* -------- CS4232 MPU-401 interface -------- */ + + if (snd_cs4232_mpu_port[dev] > 0 && snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { + if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, + snd_cs4232_mpu_port[dev], 0, + snd_cs4232_mpu_irq[dev], + SA_INTERRUPT, + NULL)) < 0) { + snd_printk ("can't allocate CS4232 MPU-401 device\n"); + snd_card_free(card); + return err; + } + midi_dev++; + } + + /* ------ ICS2115 internal MIDI ------------ */ + + if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) { + ics2115_internal_rmidi = + snd_wavefront_new_midi (card, + midi_dev, + acard, + snd_ics2115_port[dev], + internal_mpu); + if (ics2115_internal_rmidi == NULL) { + snd_printk ("can't setup ICS2115 internal MIDI device\n"); + snd_card_free(card); + return -ENOMEM; + } + midi_dev++; + } + + /* ------ ICS2115 external MIDI ------------ */ + + if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) { + ics2115_external_rmidi = + snd_wavefront_new_midi (card, + midi_dev, + acard, + snd_ics2115_port[dev], + external_mpu); + if (ics2115_external_rmidi == NULL) { + snd_printk ("can't setup ICS2115 external MIDI device\n"); + snd_card_free(card); + return -ENOMEM; + } + midi_dev++; + } + + /* FX processor for Tropez+ */ + + if (acard->wavefront.has_fx) { + fx_processor = snd_wavefront_new_fx (card, + hw_dev, + acard, + snd_ics2115_port[dev]); + if (fx_processor == NULL) { + snd_printk ("can't setup FX device\n"); + snd_card_free(card); + return -ENOMEM; + } + + hw_dev++; + + strcpy(card->driver, "Tropez+"); + strcpy(card->shortname, "Turtle Beach Tropez+"); + } else { + /* Need a way to distinguish between Maui and Tropez */ + strcpy(card->driver, "WaveFront"); + strcpy(card->shortname, "Turtle Beach WaveFront"); + } + + /* ----- Register the card --------- */ + + /* Not safe to include "Turtle Beach" in longname, due to + length restrictions + */ + + sprintf(card->longname, "%s PCM 0x%lx irq %d dma %d", + card->driver, + chip->port, + snd_cs4232_pcm_irq[dev], + snd_dma1[dev]); + + if (snd_dma2[dev] >= 0 && snd_dma2[dev] < 8) + sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]); + + if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { + sprintf (card->longname + strlen (card->longname), + " MPU-401 0x%lx irq %d", + snd_cs4232_mpu_port[dev], + snd_cs4232_mpu_irq[dev]); + } + + sprintf (card->longname + strlen (card->longname), + " SYNTH 0x%lx irq %d", + snd_ics2115_port[dev], + snd_ics2115_irq[dev]); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + snd_wavefront_cards[dev] = card; + return 0; +} + +#ifdef __ISAPNP__ + +static int __init snd_wavefront_isapnp_detect(struct isapnp_card *card, + const struct isapnp_card_id *id) +{ + static int dev = 0; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev] || !snd_isapnp[dev]) + continue; + snd_wavefront_isapnp_cards[dev] = card; + snd_wavefront_isapnp_id[dev] = id; + res = snd_wavefront_probe(dev); + if (res < 0) + return res; + dev++; + return 0; + } + + return -ENODEV; +} + +#endif /* __ISAPNP__ */ + +static int __init alsa_card_wavefront_init(void) +{ + int cards = 0; + int dev; + for (dev = 0; dev < SNDRV_CARDS; dev++) { + if (!snd_enable[dev]) + continue; +#ifdef __ISAPNP__ + if (snd_isapnp[dev]) + continue; +#endif + if (snd_wavefront_probe(dev) >= 0) + cards++; + } +#ifdef __ISAPNP__ + cards += isapnp_probe_cards(snd_wavefront_pnpids, snd_wavefront_isapnp_detect); +#endif + if (!cards) { +#ifdef MODULE + printk (KERN_ERR "No WaveFront cards found or devices busy\n"); +#endif + return -ENODEV; + } + return 0; +} + +static void __exit alsa_card_wavefront_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) + snd_card_free(snd_wavefront_cards[idx]); +} + +module_init(alsa_card_wavefront_init) +module_exit(alsa_card_wavefront_exit) + +#ifndef MODULE + +/* format is: snd-wavefront=snd_enable,snd_index,snd_id,snd_isapnp, + snd_cs4232_pcm_port,snd_cs4232_pcm_irq, + snd_cs4232_mpu_port,snd_cs4232_mpu_irq, + snd_ics2115_port,snd_ics2115_irq, + snd_fm_port, + snd_dma1,snd_dma2, + snd_use_cs4232_midi */ + +static int __init alsa_card_wavefront_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_isapnp[nr_dev]) == 2 && + get_option(&str,(int *)&snd_cs4232_pcm_port[nr_dev]) == 2 && + get_option(&str,&snd_cs4232_pcm_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_cs4232_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_cs4232_mpu_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_ics2115_port[nr_dev]) == 2 && + get_option(&str,&snd_ics2115_irq[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,&snd_dma1[nr_dev]) == 2 && + get_option(&str,&snd_dma2[nr_dev]) == 2 && + get_option(&str,&snd_use_cs4232_midi[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-wavefront=", alsa_card_wavefront_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/isa/wavefront/wavefront_fx.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_fx.c --- linux/sound/isa/wavefront/wavefront_fx.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_fx.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1052 @@ +/* + * Copyright (c) 1998-2002 by Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +MODULE_AUTHOR("Paul Davis "); +MODULE_DESCRIPTION("ALSA driver for Turtle Beach Tropez+ YSS225 FX Processor"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#endif + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static inline void +dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static int +wavefront_fx_idle (snd_wavefront_t *dev) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (dev->fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + snd_printk ("FX device never idle.\n"); + return 0; + } + + return (1); +} + +static void +wavefront_fx_mute (snd_wavefront_t *dev, int onoff) + +{ + if (!wavefront_fx_idle(dev)) { + return; + } + + outb (onoff ? 0x02 : 0x00, dev->fx_op); +} + +static int +wavefront_fx_memset (snd_wavefront_t *dev, + int page, + int addr, + int cnt, + unsigned short *data) +{ + if (page < 0 || page > 7) { + snd_printk ("FX memset: " + "page must be >= 0 and <= 7\n"); + return -(EINVAL); + } + + if (addr < 0 || addr > 0x7f) { + snd_printk ("FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -(EINVAL); + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (page, dev->fx_dsp_page); + outb (addr, dev->fx_dsp_addr); + outb ((data[0] >> 8), dev->fx_dsp_msb); + outb ((data[0] & 0xff), dev->fx_dsp_lsb); + + snd_printk ("FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (page, dev->fx_dsp_page); + outb (addr, dev->fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), dev->fx_dsp_msb); + outb ((data[i] & 0xff), dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) { + break; + } + } + + if (i != cnt) { + snd_printk ("FX memset " + "(0x%x, 0x%x, 0x%lx, %d) incomplete\n", + page, addr, (unsigned long) data, cnt); + return -(EIO); + } + } + + return 0; +} + +int +snd_wavefront_fx_detect (snd_wavefront_t *dev) + +{ + /* This is a crude check, but its the best one I have for now. + Certainly on the Maui and the Tropez, wavefront_fx_idle() will + report "never idle", which suggests that this test should + work OK. + */ + + if (inb (dev->fx_status) & 0x80) { + snd_printk ("Hmm, probably a Maui or Tropez.\n"); + return -1; + } + + return 0; +} + +int +snd_wavefront_fx_open (snd_hwdep_t *hw, struct file *file) + +{ + MOD_INC_USE_COUNT; + if (!try_inc_mod_count(hw->card->module)) { + MOD_DEC_USE_COUNT; + return -EFAULT; + } + file->private_data = hw; + return 0; +} + +int +snd_wavefront_fx_release (snd_hwdep_t *hw, struct file *file) + +{ + dec_mod_count(hw->card->module); + MOD_DEC_USE_COUNT; + return 0; +} + +int +snd_wavefront_fx_ioctl (snd_hwdep_t *sdev, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + snd_wavefront_t *dev; + wavefront_fx_info r; + unsigned short page_data[256]; + unsigned short *pd; + + snd_assert(sdev->card != NULL, return -ENODEV); + + card = sdev->card; + + snd_assert(card->private_data != NULL, return -ENODEV); + + acard = card->private_data; + dev = &acard->wavefront; + + if (copy_from_user (&r, (unsigned char *) arg, sizeof (wavefront_fx_info))) + return -EFAULT; + + switch (r.request) { + case WFFX_MUTE: + wavefront_fx_mute (dev, r.data[0]); + return -EIO; + + case WFFX_MEMSET: + if (r.data[2] <= 0) { + snd_printk ("cannot write " + "<= 0 bytes to FX\n"); + return -EIO; + } else if (r.data[2] == 1) { + pd = (unsigned short *) &r.data[3]; + } else { + if (r.data[2] > sizeof (page_data)) { + snd_printk ("cannot write " + "> 255 bytes to FX\n"); + return -EIO; + } + if (copy_from_user (page_data, + (unsigned char *) r.data[3], + r.data[2])) + return -EFAULT; + pd = page_data; + } + + wavefront_fx_memset (dev, + r.data[0], /* page */ + r.data[1], /* addr */ + r.data[2], /* cnt */ + pd); + break; + + default: + snd_printk ("FX: ioctl %d not yet supported\n", + r.request); + return -ENOTTY; + } + return 0; +} + +/* YSS225 initialization. + + This code was developed using DOSEMU. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEMU enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. Its really pretty wierd. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + + +int +snd_wavefront_fx_start (snd_wavefront_t *dev) + +{ + unsigned int i, j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wavefront_fx_idle (dev)) { + return (-1); + } + + outb (i, dev->fx_mod_addr); + outb (0x0, dev->fx_mod_data); + } + } + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x02, dev->fx_op); /* mute on */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x44, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x42, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x43, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x7c, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x7e, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x46, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x49, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x47, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x4a, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wavefront_fx_idle (dev)) { + return (-1); + } + + outb (i, dev->fx_mod_addr); + outb (0x0, dev->fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x00, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], dev->fx_dsp_msb); + outb (page_zero[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x01, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], dev->fx_dsp_msb); + outb (page_one[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x02, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x03, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x04, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x06, dev->fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], dev->fx_dsp_addr); + outb (page_six[i+1], dev->fx_dsp_msb); + outb (page_six[i+2], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x07, dev->fx_dsp_page); + outb (0x00, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], dev->fx_dsp_msb); + outb (page_seven[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev->fx_mod_addr); + outb (i, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x02, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, dev->fx_mod_addr); + outb (0xff, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x1e, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, dev->fx_mod_addr); + outb (0xff, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x2e, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x3f, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x4e, dev->fx_mod_addr); + outb (0x0e, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x4f, dev->fx_mod_addr); + outb (0x0e, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x6c, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6d, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6e, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (0x6f, dev->fx_mod_addr); + outb (0x40, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, dev->fx_mod_addr); + outb (0xc0, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0xde, dev->fx_mod_addr); + outb (0x10, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xdf, dev->fx_mod_addr); + outb (0x10, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, dev->fx_mod_addr); + outb (0x00, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev->fx_mod_addr); + outb (i, dev->fx_mod_data); + outb (0x02, dev->fx_mod_addr); + outb (0x01, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (0x02, dev->fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], dev->fx_dsp_page); + outb (coefficients[i+1], dev->fx_dsp_addr); + outb (coefficients[i+2], dev->fx_dsp_msb); + outb (coefficients[i+3], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x1e, dev->fx_mod_addr); + outb (0x14, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xde, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0xdf, dev->fx_mod_addr); + outb (0x20, dev->fx_mod_data); + + /* some more coefficients */ + + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x06, dev->fx_dsp_page); + outb (0x78, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x40, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x03, dev->fx_dsp_addr); + outb (0x0f, dev->fx_dsp_msb); + outb (0xff, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x0b, dev->fx_dsp_addr); + outb (0x0f, dev->fx_dsp_msb); + outb (0xff, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x02, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x0a, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x46, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + outb (0x07, dev->fx_dsp_page); + outb (0x49, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x00, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], dev->fx_dsp_msb); + outb (page_zero_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x01, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], dev->fx_dsp_msb); + outb (page_one_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + if (!wavefront_fx_idle (dev)) return (-1); + if (!wavefront_fx_idle (dev)) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x02, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x03, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x04, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x06, dev->fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, dev->fx_dsp_addr); + outb (0x00, dev->fx_dsp_msb); + outb (0x00, dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr); + outb (0x07, dev->fx_dsp_page); + outb (0x10, dev->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], dev->fx_dsp_msb); + outb (page_seven_v2[i+1], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], dev->fx_mod_addr); + outb (mod_v2[i+1], dev->fx_mod_data); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], dev->fx_dsp_page); + outb (coefficients2[i+1], dev->fx_dsp_addr); + outb (coefficients2[i+2], dev->fx_dsp_msb); + outb (coefficients2[i+3], dev->fx_dsp_lsb); + if (!wavefront_fx_idle (dev)) return (-1); + } + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, dev->fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, dev->fx_dsp_addr); + outb (coefficients3[i], dev->fx_dsp_msb); + outb (coefficients3[i+1], dev->fx_dsp_lsb); + } + + outb (0x00, dev->fx_op); /* mute off */ + if (!wavefront_fx_idle (dev)) return (-1); + + return (0); +} + +/* wierd stuff, derived from port I/O tracing with dosemu */ + +static unsigned char page_zero[] __initdata = { +0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, +0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, +0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, +0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, +0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, +0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, +0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, +0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, +0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, +0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, +0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, +0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, +0x1d, 0x02, 0xdf +}; + +static unsigned char page_one[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, +0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, +0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, +0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, +0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, +0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, +0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, +0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, +0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, +0x60, 0x00, 0x1b +}; + +static unsigned char page_two[] __initdata = { +0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, +0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, +0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, +0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, +0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, +0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, +0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, +0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 +}; + +static unsigned char page_three[] __initdata = { +0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, +0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, +0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, +0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, +0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 +}; + +static unsigned char page_four[] __initdata = { +0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, +0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, +0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, +0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, +0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 +}; + +static unsigned char page_six[] __initdata = { +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, +0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, +0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, +0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, +0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, +0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, +0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, +0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, +0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, +0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, +0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, +0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, +0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, +0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, +0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, +0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, +0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, +0x80, 0x00, 0x7e, 0x80, 0x80 +}; + +static unsigned char page_seven[] __initdata = { +0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, +0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, +0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, +0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, +0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, +0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, +0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, +0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, +0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, +0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, +0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, +0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x00 +}; + +static unsigned char page_zero_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char page_one_v2[] __initdata = { +0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char page_two_v2[] __initdata = { +0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +static unsigned char page_three_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +static unsigned char page_four_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char page_seven_v2[] __initdata = { +0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char mod_v2[] __initdata = { +0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, +0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, +0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, +0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, +0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, +0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, +0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, +0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, +0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, +0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, +0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, +0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, +0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, +0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, +0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, +0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, +0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, +0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, +0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, +0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, +0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, +0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, +0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, +0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 +}; +static unsigned char coefficients[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, +0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, +0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, +0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, +0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, +0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, +0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, +0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, +0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, +0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, +0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, +0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, +0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, +0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, +0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, +0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, +0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, +0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, +0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, +0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, +0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, +0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, +0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, +0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, +0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, +0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, +0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, +0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, +0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, +0xba +}; +static unsigned char coefficients2[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, +0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, +0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, +0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, +0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 +}; +static unsigned char coefficients3[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, +0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, +0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, +0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, +0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, +0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, +0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, +0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, +0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, +0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, +0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, +0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, +0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, +0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, +0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, +0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, +0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, +0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, +0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, +0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, +0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, +0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, +0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, +0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, +0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, +0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, +0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, +0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, +0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, +0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, +0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, +0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, +0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, +0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, +0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, +0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, +0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff +}; + +#if 0 +EXPORT_SYMBOL(snd_wavefront_fx_start); +EXPORT_SYMBOL(snd_wavefront_fx_detect); +EXPORT_SYMBOL(snd_wavefront_fx_ioctl); +EXPORT_SYMBOL(snd_wavefront_fx_open); +EXPORT_SYMBOL(snd_wavefront_fx_release); + +static int __init alsa_wavefront_fx_init(void) +{ + return 0; +} + +static void __exit alsa_wavefront_fx_exit(void) +{ +} + +module_init(alsa_wavefront_fx_init) +module_exit(alsa_wavefront_fx_exit) +#endif diff -Nru linux/sound/isa/wavefront/wavefront_midi.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_midi.c --- linux/sound/isa/wavefront/wavefront_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_midi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,574 @@ +/* + * Copyright (C) by Paul Barton-Davis 1998-1999 + * + * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this + * software for more info. + */ + +/* The low level driver for the WaveFront ICS2115 MIDI interface(s) + * + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez and Tropez Plus. This code + * has nothing to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct MIDI + * busses to be used completely independently, giving 32 channels of + * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI + * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1, + * where `n' is the card number. Note that the device numbers may be + * something other than 0 and 1 if the CS4232 UART/MPU-401 interface + * is enabled. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +static inline int +wf_mpu_status (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_status_port); +} + +static inline int +input_avail (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & INPUT_AVAIL); +} + +static inline int +output_ready (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & OUTPUT_READY); +} + +static inline int +read_data (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_data_port); +} + +static inline void +write_data (snd_wavefront_midi_t *midi, unsigned char byte) + +{ + outb (byte, midi->mpu_data_port); +} + +static snd_wavefront_midi_t * +get_wavefront_midi (snd_rawmidi_substream_t *substream) + +{ + snd_card_t *card; + snd_wavefront_card_t *acard; + + if (substream == NULL || substream->rmidi == NULL) + return NULL; + + card = substream->rmidi->card; + + if (card == NULL) + return NULL; + + if (card->private_data == NULL) + return NULL; + + acard = card->private_data; + + return &acard->wavefront.midi; +} + +static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card) +{ + snd_wavefront_midi_t *midi = &card->wavefront.midi; + snd_wavefront_mpu_id mpu; + unsigned long flags; + unsigned char midi_byte; + int max = 256, mask = 1; + int timeout; + + /* Its not OK to try to change the status of "virtuality" of + the MIDI interface while we're outputting stuff. See + snd_wavefront_midi_{enable,disable}_virtual () for the + other half of this. + + The first loop attempts to flush any data from the + current output device, and then the second + emits the switch byte (if necessary), and starts + outputting data for the output device currently in use. + */ + + if (midi->substream_output[midi->output_mpu] == NULL) { + goto __second; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + if (output_ready (midi)) { + if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } + + __second: + + if (midi->substream_output[!midi->output_mpu] == NULL) { + return; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (!midi->isvirtual) + mask = 0; + mpu = midi->output_mpu ^ mask; + mask = 0; /* don't invert the value from now */ + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + if (snd_rawmidi_transmit_empty(midi->substream_output[mpu])) + goto __timer; + if (output_ready (midi)) { + if (mpu != midi->output_mpu) { + write_data(midi, mpu == internal_mpu ? + WF_INTERNAL_SWITCH : + WF_EXTERNAL_SWITCH); + midi->output_mpu = mpu; + } else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + __timer: + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } +} + +static int snd_wavefront_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_INPUT; + midi->substream_input[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_OUTPUT; + midi->substream_output[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_INPUT; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); + snd_assert(substream->rmidi->private_data != NULL, return -EIO); + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT; + spin_unlock_irqrestore (&midi->open, flags); + return 0; +} + +static void snd_wavefront_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) { + return; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER; + } else { + midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); +} + +static void snd_wavefront_midi_output_timer(unsigned long data) +{ + snd_wavefront_card_t *card = (snd_wavefront_card_t *)data; + snd_wavefront_midi_t *midi = &card->wavefront.midi; + unsigned long flags; + + spin_lock_irqsave (&midi->virtual, flags); + midi->timer.expires = 1 + jiffies; + add_timer(&midi->timer); + spin_unlock_irqrestore (&midi->virtual, flags); + snd_wavefront_midi_output_write(card); +} + +static void snd_wavefront_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + if ((midi = get_wavefront_midi (substream)) == NULL) { + return; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { + if (!midi->istimer) { + midi->timer.function = snd_wavefront_midi_output_timer; + midi->timer.data = (unsigned long) substream->rmidi->card->private_data; + midi->timer.expires = 1 + jiffies; + add_timer(&midi->timer); + } + midi->istimer++; + midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER; + } + } else { + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); + + if (up) + snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data); +} + +void +snd_wavefront_midi_interrupt (snd_wavefront_card_t *card) + +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + static snd_rawmidi_substream_t *substream = NULL; + static int mpu = external_mpu; + int max = 128; + unsigned char byte; + + midi = &card->wavefront.midi; + + if (!input_avail (midi)) { /* not for us */ + snd_wavefront_midi_output_write(card); + return; + } + + while (--max) { + spin_lock_irqsave (&midi->virtual, flags); + + if (input_avail (midi)) { + byte = read_data (midi); + + if (midi->isvirtual) { + if (byte == WF_EXTERNAL_SWITCH) { + substream = midi->substream_input[external_mpu]; + mpu = external_mpu; + } else if (byte == WF_INTERNAL_SWITCH) { + substream = midi->substream_output[internal_mpu]; + mpu = internal_mpu; + } /* else just leave it as it is */ + } else { + substream = midi->substream_input[internal_mpu]; + mpu = internal_mpu; + } + + if (substream == NULL) { + spin_unlock_irqrestore (&midi->virtual, flags); + continue; + } + + if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) { + spin_unlock_irqrestore (&midi->virtual, flags); + snd_rawmidi_receive(substream, &byte, 1); + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + break; + } + } + + snd_wavefront_midi_output_write(card); +} + +void +snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + card->wavefront.midi.isvirtual = 1; + card->wavefront.midi.output_mpu = internal_mpu; + card->wavefront.midi.input_mpu = internal_mpu; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +void +snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + // snd_wavefront_midi_input_close (card->ics2115_external_rmidi); + // snd_wavefront_midi_output_close (card->ics2115_external_rmidi); + card->wavefront.midi.isvirtual = 0; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +int +snd_wavefront_midi_start (snd_wavefront_card_t *card) + +{ + int ok, i; + unsigned char rbuf[4], wbuf[4]; + snd_wavefront_t *dev; + snd_wavefront_midi_t *midi; + + dev = &card->wavefront; + midi = &dev->midi; + + /* The ICS2115 MPU-401 interface doesn't do anything + until its set into UART mode. + */ + + /* XXX fix me - no hard timing loops allowed! */ + + for (i = 0; i < 30000 && !output_ready (midi); i++); + + if (!output_ready (midi)) { + snd_printk ("MIDI interface not ready for command\n"); + return -1; + } + + /* Any interrupts received from now on + are owned by the MIDI side of things. + */ + + dev->interrupts_are_midi = 1; + + outb (UART_MODE_ON, midi->mpu_command_port); + + for (ok = 0, i = 50000; i > 0 && !ok; i--) { + if (input_avail (midi)) { + if (read_data (midi) == MPU_ACK) { + ok = 1; + break; + } + } + } + + if (!ok) { + snd_printk ("cannot set UART mode for MIDI interface"); + dev->interrupts_are_midi = 0; + return -1; + } + + /* Route external MIDI to WaveFront synth (by default) */ + + if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) { + snd_printk ("can't enable MIDI-IN-2-synth routing.\n"); + /* XXX error ? */ + } + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { + snd_printk ("virtual MIDI mode not disabled\n"); + return 0; /* We're OK, but missing the external MIDI dev */ + } + + snd_wavefront_midi_enable_virtual (card); + + if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) { + snd_printk ("cannot enable virtual MIDI mode.\n"); + snd_wavefront_midi_disable_virtual (card); + } + return 0; +} + +snd_rawmidi_ops_t snd_wavefront_midi_output = +{ + open: snd_wavefront_midi_output_open, + close: snd_wavefront_midi_output_close, + trigger: snd_wavefront_midi_output_trigger, +}; + +snd_rawmidi_ops_t snd_wavefront_midi_input = +{ + open: snd_wavefront_midi_input_open, + close: snd_wavefront_midi_input_close, + trigger: snd_wavefront_midi_input_trigger, +}; + diff -Nru linux/sound/isa/wavefront/wavefront_synth.c linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_synth.c --- linux/sound/isa/wavefront/wavefront_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/isa/wavefront/wavefront_synth.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2244 @@ +/* Copyright (C) by Paul Barton-Davis 1998-1999 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +/* + * An ALSA lowlevel driver for Turtle Beach ICS2115 wavetable synth + * (Maui, Tropez, Tropez Plus) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int wf_raw = 0; /* we normally check for "raw state" to firmware + loading. if non-zero, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +int debug_default = 0; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +int wait_usecs = 150; /* This magic number seems to give pretty optimal + throughput based on my limited experimentation. + If you want to play around with it and find a better + value, be my guest. Remember, the idea is to + get a number that causes us to just busy wait + for as many WaveFront commands as possible, without + coming up with a number so large that we hog the + whole CPU. + + Specifically, with this number, out of about 134,000 + status waits, only about 250 result in a sleep. + */ + +int sleep_interval = 100; /* HZ/sleep_interval seconds per sleep */ +int sleep_tries = 50; /* number of times we'll try to sleep */ + +int reset_time = 2; /* hundreths of a second we wait after a HW + reset for the expected interrupt. + */ + +int ramcheck_time = 20; /* time in seconds to wait while ROM code + checks on-board RAM. + */ + +int osrun_time = 10; /* time in seconds we wait for the OS to + start running. + */ +#if 0 +MODULE_AUTHOR("Paul Barton-Davis "); +MODULE_DESCRIPTION("ALSA driver for Turtle Beach WaveFront ICS2215 Synth"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#endif +MODULE_PARM(wf_raw,"i"); +MODULE_PARM_DESC(wf_raw, "if non-zero, assume that we need to boot the OS"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM_DESC(fx_raw, "if non-zero, assume that the FX process needs help"); +MODULE_PARM(debug_default,"i"); +MODULE_PARM_DESC(debug_default, "debug parameters for card initialization"); +MODULE_PARM(wait_usecs,"i"); +MODULE_PARM_DESC(wait_usecs, "how long to wait without sleeping, usecs"); +MODULE_PARM(sleep_interval,"i"); +MODULE_PARM_DESC(sleep_interval, "how long to sleep when waiting for reply"); +MODULE_PARM(sleep_tries,"i"); +MODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait"); +MODULE_PARM(ospath,"s"); +MODULE_PARM_DESC(ospath, "full pathname to processed ICS2115 OS firmware"); +MODULE_PARM(reset_time,"i"); +MODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect"); +MODULE_PARM(ramcheck_time,"i"); +MODULE_PARM_DESC(ramcheck_time, "how many seconds to wait for the RAM test"); +MODULE_PARM(osrun_time,"i"); +MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS"); + +/* if WF_DEBUG not defined, no run-time debugging messages will + be available via the debug flag setting. Given the current + beta state of the driver, this will remain set until a future + version. +*/ + +#define WF_DEBUG 1 + +#ifdef WF_DEBUG + +#ifdef NEW_MACRO_VARARGS +#define DPRINT(cond, ...) \ + if ((dev->debug & (cond)) == (cond)) { \ + snd_printk (__VA_ARGS__); \ + } +#else +#define DPRINT(cond, args...) \ + if ((dev->debug & (cond)) == (cond)) { \ + snd_printk (##args); \ + } +#endif +#else +#define DPRINT(cond, args...) +#endif /* WF_DEBUG */ + +#define LOGNAME "WaveFront: " + +/* bitmasks for WaveFront status port value */ + +#define STAT_RINTR_ENABLED 0x01 +#define STAT_CAN_READ 0x02 +#define STAT_INTR_READ 0x04 +#define STAT_WINTR_ENABLED 0x10 +#define STAT_CAN_WRITE 0x20 +#define STAT_INTR_WRITE 0x40 + +static int wavefront_delete_sample (snd_wavefront_t *, int sampnum); +static int wavefront_find_free_sample (snd_wavefront_t *); + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0x0, 0x0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in snd_wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, + { 0x00 } +}; + +static inline void +dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static inline int +wavefront_status (snd_wavefront_t *dev) + +{ + return inb (dev->status_port); +} + +static int +wavefront_sleep (int limit) + +{ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(limit); + + return signal_pending(current); +} + +static int +wavefront_wait (snd_wavefront_t *dev, int mask) + +{ + int i; + + /* Spin for a short period of time, because >99% of all + requests to the WaveFront can be serviced inline like this. + */ + + for (i = 0; i < wait_usecs; i += 5) { + if (wavefront_status (dev) & mask) { + return 1; + } + udelay(5); + } + + for (i = 0; i < sleep_tries; i++) { + + if (wavefront_status (dev) & mask) { + return 1; + } + + if (wavefront_sleep (HZ/sleep_interval)) { + return (0); + } + } + + return (0); +} + +static int +wavefront_read (snd_wavefront_t *dev) + +{ + if (wavefront_wait (dev, STAT_CAN_READ)) + return inb (dev->data_port); + + DPRINT (WF_DEBUG_DATA, "read timeout.\n"); + + return -1; +} + +static int +wavefront_write (snd_wavefront_t *dev, unsigned char data) + +{ + if (wavefront_wait (dev, STAT_CAN_WRITE)) { + outb (data, dev->data_port); + return 0; + } + + DPRINT (WF_DEBUG_DATA, "write timeout.\n"); + + return -1; +} + +int +snd_wavefront_cmd (snd_wavefront_t *dev, + int cmd, unsigned char *rbuf, unsigned char *wbuf) + +{ + int ack; + unsigned int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + snd_printk ("command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned long) rbuf; + rbuf = 0; + } + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + + if (wavefront_write (dev, cmd)) { + DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + } + + if (wfcmd->write_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (wavefront_write (dev, wbuf[i])) { + DPRINT (WF_DEBUG_IO, "bad write for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + + if (wfcmd->read_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read (dev)) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read (dev)) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for " + "error byte at " + "read byte %d " + "of 0x%x [%s].\n", + i, cmd, + wfcmd->action); + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return (0); + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + + DPRINT (WF_DEBUG_IO, "error %d (%s) " + "during " + "read for byte " + "%d of 0x%x " + "[%s].\n", + c, + wavefront_errorstr (c), + i, cmd, + wfcmd->action); + return 1; + + } + + } else { + rbuf[i] = c; + } + + DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read (dev)) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + DPRINT (WF_DEBUG_IO, "cannot read ack for " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read (dev)) == -1) { + DPRINT (WF_DEBUG_DATA, + "cannot read err " + "for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + + DPRINT (WF_DEBUG_IO, "0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + + return -err; + } + } + + DPRINT (WF_DEBUG_DATA, "ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } else { + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + } + + return 0; + +} + +/*********************************************************************** +WaveFront data munging + +Things here are wierd. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8-32 bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +wierd casting and worrying about bit-level offsets. + +**********************************************************************/ + +static unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + unsigned int i; + + for (i = 0; i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + unsigned int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (snd_wavefront_t *dev, int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + dev->sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) { + snd_printk ("cannot request sample count.\n"); + return -1; + } + + sc_real = sc_alias = sc_multi = dev->samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + snd_printk("cannot identify sample " + "type of slot %d\n", i); + dev->sample_status[i] = WF_ST_EMPTY; + continue; + } + + dev->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + dev->sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + snd_printk ("unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + dev->samples_used++; + } + } + + snd_printk ("%d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - dev->samples_used); + + + return (0); + +} + +static int +wavefront_get_patch_status (snd_wavefront_t *dev) + +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + dev->patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + dev->sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + dev->patch_status[i] = 0; + } else { + snd_printk ("upload patch " + "error 0x%x\n", x); + dev->patch_status[i] = 0; + return 1; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (dev->patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (dev->patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2); + + return (0); +} + +static int +wavefront_get_program_status (snd_wavefront_t *dev) + +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + dev->prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + dev->patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + dev->prog_status[i] = 0; + } else { + snd_printk ("upload program " + "error 0x%x\n", x); + dev->prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (dev->prog_status[i]) { + cnt++; + } + } + + snd_printk ("%d programs slots in use\n", cnt); + + return (0); +} + +static int +wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", + header->number); + + dev->patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, 0, buf)) { + snd_printk ("download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", + header->number); + + dev->prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + dev->patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, 0, buf)) { + snd_printk ("download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_freemem (snd_wavefront_t *dev) + +{ + char rbuf[8]; + + if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + snd_printk ("can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (snd_wavefront_t *dev, + wavefront_patch_info *header, + u16 *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + u16 sample_short; + u32 length; + u16 *data_end = 0; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + + DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%lx\n", + header->size ? "" : "header ", + header->number, header->subkey, + header->size, + (unsigned long) header->dataptr); + + if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { + int x; + + if ((x = wavefront_find_free_sample (dev)) < 0) { + return -ENOMEM; + } + snd_printk ("unspecified sample => %d\n", x); + header->number = x; + } + + if (header->size) { + + /* XXX its a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little + crazy: we'd need 158K of kernel space just to hold + a copy of the patch/program/sample header data. + */ + + if (dev->rom_samples_rdonly) { + if (dev->sample_status[header->number] & WF_SLOT_ROM) { + snd_printk ("sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (dev, header->number); + } + + if (header->size) { + dev->freemem = wavefront_freemem (dev); + + if (dev->freemem < header->size) { + snd_printk ("insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { + snd_printk ("channel selection only " + "possible on 16-bit samples"); + return -(EINVAL); + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), + initial_skip, skip); + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((u32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly wierd. What kind of wierdo decided that in + a system dominated by 16 and 32 bit integers, they would use + a just 12 bits ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero ? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (snd_wavefront_cmd (dev, + header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + snd_printk ("sample %sdownload refused.\n", + header->size ? "" : "header "); + return -(EIO); + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, 0, 0)) { + snd_printk ("download block " + "request refused.\n"); + return -(EIO); + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + __get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { /* GUS ? */ + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, dev->block_port); + } else { + outw (sample_short, dev->last_block_port); + } + } + + /* Get "DMA page acknowledge", even though its really + nothing to do with DMA at all. + */ + + if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) { + if (dma_ack == -1) { + snd_printk ("upload sample " + "DMA ack timeout\n"); + return -(EIO); + } else { + snd_printk ("upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -(EIO); + } + } + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return (0); +} + +static int +wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + snd_printk ("download alias failed.\n"); + return -(EIO); + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return (0); +} + +static int +wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + + DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", + header->number, + header->hdr.ms.NumberOfSamples, + num_samples); + + for (i = 0; i < num_samples; i++) { + DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) (long) ((num_samples*2)+3), + msample_hdr)) { + snd_printk ("download of multisample failed.\n"); + return -(EIO); + } + + dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return (0); +} + +static int +wavefront_fetch_multisample (snd_wavefront_t *dev, + wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + snd_printk ("upload multisample failed.\n"); + return -(EIO); + } + + DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", + header->number, log_ns[0]); + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + char d[2]; + int val; + + if ((val = wavefront_read (dev)) == -1) { + snd_printk ("upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + d[0] = val; + + if ((val = wavefront_read (dev)) == -1) { + snd_printk ("upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + d[1] = val; + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + + return (0); +} + + +static int +wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + snd_printk ("download drum failed.\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_find_free_sample (snd_wavefront_t *dev) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(dev->sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + snd_printk ("no free sample slots!\n"); + return -1; +} + +#if 0 +static int +wavefront_find_free_patch (snd_wavefront_t *dev) + +{ + int i; + + for (i = 0; i < WF_MAX_PATCH; i++) { + if (!(dev->patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + snd_printk ("no free patch slots!\n"); + return -1; +} +#endif + +static int +wavefront_load_patch (snd_wavefront_t *dev, const char *addr) + +{ + wavefront_patch_info header; + + if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - + sizeof(wavefront_any))) { + snd_printk ("bad address for load patch.\n"); + return -(EFAULT); + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + if (copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample))) + return -EFAULT; + + return wavefront_send_sample (dev, &header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + if (copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample))) + return -EFAULT; + + return wavefront_send_multisample (dev, &header); + + + case WF_ST_ALIAS: + + if (copy_from_user ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias))) + return -EFAULT; + + return wavefront_send_alias (dev, &header); + + case WF_ST_DRUM: + if (copy_from_user ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum))) + return -EFAULT; + + return wavefront_send_drum (dev, &header); + + case WF_ST_PATCH: + if (copy_from_user ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch))) + return -EFAULT; + + return wavefront_send_patch (dev, &header); + + case WF_ST_PROGRAM: + if (copy_from_user ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program))) + return -EFAULT; + + return wavefront_send_program (dev, &header); + + default: + snd_printk ("unknown patch type %d.\n", + header.subkey); + return -(EINVAL); + } + + return 0; +} + +/*********************************************************************** +WaveFront: hardware-dependent interface +***********************************************************************/ + +static void +process_sample_hdr (u8 *buf) + +{ + wavefront_sample s; + u8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((u32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((u32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (snd_wavefront_card_t *acard, + wavefront_control *wc) + +{ + snd_wavefront_t *dev = &acard->wavefront; + unsigned char patchnumbuf[2]; + int i; + + DPRINT (WF_DEBUG_CMD, "synth control with " + "cmd 0x%x\n", wc->cmd); + + /* Pre-handling of or for various commands */ + + switch (wc->cmd) { + + case WFC_DISABLE_INTERRUPTS: + snd_printk ("interrupts disabled.\n"); + outb (0x80|0x20, dev->control_port); + dev->interrupts_are_midi = 1; + return 0; + + case WFC_ENABLE_INTERRUPTS: + snd_printk ("interrupts enabled.\n"); + outb (0x80|0x40|0x20, dev->control_port); + dev->interrupts_are_midi = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc->rbuf[0] = dev->interrupts_are_midi; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + dev->rom_samples_rdonly = wc->wbuf[0]; + wc->status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc->wbuf[0] | (wc->wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + snd_printk ("invalid slot ID %d\n", + i); + wc->status = EINVAL; + return -EINVAL; + } + wc->rbuf[0] = dev->sample_status[i]; + wc->status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + dev->debug = wc->wbuf[0]; + snd_printk ("debug = 0x%x\n", dev->debug); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((u32 *) wc->wbuf), patchnumbuf, 2); + memcpy (wc->wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + /* multisamples have to be handled differently, and + cannot be dealt with properly by snd_wavefront_cmd() alone. + */ + wc->status = wavefront_fetch_multisample + (dev, (wavefront_patch_info *) wc->rbuf); + return 0; + + case WFC_UPLOAD_SAMPLE_ALIAS: + snd_printk ("support for sample alias upload " + "being considered.\n"); + wc->status = EINVAL; + return -EINVAL; + } + + wc->status = snd_wavefront_cmd (dev, wc->cmd, wc->rbuf, wc->wbuf); + + /* Post-handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc->status == 0) { + switch (wc->cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + dev->freemem = demunge_int32 (wc->rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc->rbuf); + break; + + case WFC_UPLOAD_SAMPLE_ALIAS: + snd_printk ("support for " + "sample aliases still " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + snd_wavefront_midi_disable_virtual (acard); + break; + + case WFC_VMIDI_ON: + snd_wavefront_midi_enable_virtual (acard); + break; + } + } + + return 0; +} + +int +snd_wavefront_synth_open (snd_hwdep_t *hw, struct file *file) + +{ + MOD_INC_USE_COUNT; + if (!try_inc_mod_count(hw->card->module)) { + MOD_DEC_USE_COUNT; + return -EFAULT; + } + file->private_data = hw; + return 0; +} + +int +snd_wavefront_synth_release (snd_hwdep_t *hw, struct file *file) + +{ + dec_mod_count(hw->card->module); + MOD_DEC_USE_COUNT; + return 0; +} + +int +snd_wavefront_synth_ioctl (snd_hwdep_t *hw, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + snd_card_t *card; + snd_wavefront_t *dev; + snd_wavefront_card_t *acard; + wavefront_control wc; + + card = (snd_card_t *) hw->card; + + snd_assert(card != NULL, return -ENODEV); + + snd_assert(card->private_data != NULL, return -ENODEV); + + acard = card->private_data; + dev = &acard->wavefront; + + switch (cmd) { + case WFCTL_LOAD_SPP: + if (wavefront_load_patch (dev, (char *) arg) != 0) { + return -EIO; + } + break; + + case WFCTL_WFCMD: + if (copy_from_user (&wc, (void *) arg, sizeof (wc))) + return -EFAULT; + if (wavefront_synth_control (acard, &wc) < 0) { + return -EIO; + } + if (copy_to_user ((void *) arg, &wc, sizeof (wc))) + return -EFAULT; + break; + + default: + return -EINVAL; + } + + return 0; +} + + +/***********************************************************************/ +/* WaveFront: interface for card-level wavefront module */ +/***********************************************************************/ + +void +snd_wavefront_internal_interrupt (snd_wavefront_card_t *card) +{ + snd_wavefront_t *dev = &card->wavefront; + + /* + Some comments on interrupts. I attempted a version of this + driver that used interrupts throughout the code instead of + doing busy and/or sleep-waiting. Alas, it appears that once + the Motorola firmware is downloaded, the card *never* + generates an RX interrupt. These are successfully generated + during firmware loading, and after that wavefront_status() + reports that an interrupt is pending on the card from time + to time, but it never seems to be delivered to this + driver. Note also that wavefront_status() continues to + report that RX interrupts are enabled, suggesting that I + didn't goof up and disable them by mistake. + + Thus, I stepped back to a prior version of + wavefront_wait(), the only place where this really + matters. Its sad, but I've looked through the code to check + on things, and I really feel certain that the Motorola + firmware prevents RX-ready interrupts. + */ + + if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { + return; + } + + dev->irq_ok = 1; + dev->irq_cnt++; + wake_up_interruptible (&dev->interrupt_sleeper); +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused +*/ + +int +snd_wavefront_interrupt_bits (int irq) + +{ + int bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + snd_printk ("invalid IRQ %d\n", irq); + bits = -1; + } + + return bits; +} + +static void +wavefront_should_cause_interrupt (snd_wavefront_t *dev, + int val, int port, int timeout) + +{ + unsigned long flags; + + save_flags (flags); + cli(); + dev->irq_ok = 0; + outb (val,port); + interruptible_sleep_on_timeout (&dev->interrupt_sleeper, timeout); + restore_flags (flags); +} + +static int +wavefront_reset_to_cleanliness (snd_wavefront_t *dev) + +{ + int bits; + int hwv[2]; + + /* IRQ already checked */ + + bits = snd_wavefront_interrupt_bits (dev->irq); + + /* try reset of port */ + + outb (0x0, dev->control_port); + + /* At this point, the board is in reset, and the H/W initialization + register is accessed at the same address as the data port. + + Bit 7 - Enable IRQ Driver + 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs + 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. + + Bit 6 - MIDI Interface Select + + 0 - Use the MIDI Input from the 26-pin WaveBlaster + compatible header as the serial MIDI source + 1 - Use the MIDI Input from the 9-pin D connector as the + serial MIDI source. + + Bits 5:3 - IRQ Selection + 0 0 0 - IRQ 2/9 + 0 0 1 - IRQ 5 + 0 1 0 - IRQ 12 + 0 1 1 - IRQ 15 + 1 0 0 - Reserved + 1 0 1 - Reserved + 1 1 0 - Reserved + 1 1 1 - Reserved + + Bits 2:1 - Reserved + Bit 0 - Disable Boot ROM + 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM + 1 - memory accesses to 03FC30-03FFFFH are directed to external + storage. + + */ + + /* configure hardware: IRQ, enable interrupts, + plus external 9-pin MIDI interface selected + */ + + outb (0x80 | 0x40 | bits, dev->data_port); + + /* CONTROL REGISTER + + 0 Host Rx Interrupt Enable (1=Enabled) 0x1 + 1 Unused 0x2 + 2 Unused 0x4 + 3 Unused 0x8 + 4 Host Tx Interrupt Enable 0x10 + 5 Mute (0=Mute; 1=Play) 0x20 + 6 Master Interrupt Enable (1=Enabled) 0x40 + 7 Master Reset (0=Reset; 1=Run) 0x80 + + Take us out of reset, mute output, master + TX + RX interrupts on. + + We'll get an interrupt presumably to tell us that the TX + register is clear. + */ + + wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1, + dev->control_port, + (reset_time*HZ)/100); + + /* Note: data port is now the data port, not the h/w initialization + port. + */ + + if (!dev->irq_ok) { + snd_printk ("intr not received after h/w un-reset.\n"); + goto gone_bad; + } + + /* Note: data port is now the data port, not the h/w initialization + port. + + At this point, only "HW VERSION" or "DOWNLOAD OS" commands + will work. So, issue one of them, and wait for TX + interrupt. This can take a *long* time after a cold boot, + while the ISC ROM does its RAM test. The SDK says up to 4 + seconds - with 12MB of RAM on a Tropez+, it takes a lot + longer than that (~16secs). Note that the card understands + the difference between a warm and a cold boot, so + subsequent ISC2115 reboots (say, caused by module + reloading) will get through this much faster. + + XXX Interesting question: why is no RX interrupt received first ? + */ + + wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION, + dev->data_port, ramcheck_time*HZ); + + if (!dev->irq_ok) { + snd_printk ("post-RAM-check interrupt not received.\n"); + goto gone_bad; + } + + if (!wavefront_wait (dev, STAT_CAN_READ)) { + snd_printk ("no response to HW version cmd.\n"); + goto gone_bad; + } + + if ((hwv[0] = wavefront_read (dev)) == -1) { + snd_printk ("board not responding correctly.\n"); + goto gone_bad; + } + + if (hwv[0] == 0xFF) { /* NAK */ + + /* Board's RAM test failed. Try to read error code, + and tell us about it either way. + */ + + if ((hwv[0] = wavefront_read (dev)) == -1) { + snd_printk ("on-board RAM test failed " + "(bad error code).\n"); + } else { + snd_printk ("on-board RAM test failed " + "(error code: 0x%x).\n", + hwv[0]); + } + goto gone_bad; + } + + /* We're OK, just get the next byte of the HW version response */ + + if ((hwv[1] = wavefront_read (dev)) == -1) { + snd_printk ("incorrect h/w response.\n"); + goto gone_bad; + } + + snd_printk ("hardware version %d.%d\n", + hwv[0], hwv[1]); + + return 0; + + + gone_bad: + return (1); +} + +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include + +static int errno; + +static int +wavefront_download_firmware (snd_wavefront_t *dev, char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ + int section_cnt_downloaded = 0; + int fd; + int c; + int i; + mm_segment_t fs; + + /* This tries to be a bit cleverer than the stuff Alan Cox did for + the generic sound firmware, in that it actually knows + something about the structure of the Motorola firmware. In + particular, it uses a version that has been stripped of the + 20K of useless header information, and had section lengths + added, making it possible to load the entire OS without any + [kv]malloc() activity, since the longest entity we ever read is + 42 bytes (well, WF_SECTION_MAX) long. + */ + + fs = get_fs(); + set_fs (get_ds()); + + if ((fd = open (path, 0, 0)) < 0) { + snd_printk ("Unable to load \"%s\".\n", + path); + return 1; + } + + while (1) { + int x; + + if ((x = read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + snd_printk ("firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (read (fd, section, section_length) != section_length) { + snd_printk ("firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (wavefront_write (dev, WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (wavefront_write (dev, section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (dev, STAT_CAN_READ)) { + + if ((c = inb (dev->data_port)) != WF_ACK) { + + snd_printk ("download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } + + } else { + snd_printk ("time out for firmware ACK.\n"); + goto failure; + } + + } + + close (fd); + set_fs (fs); + return 0; + + failure: + close (fd); + set_fs (fs); + snd_printk ("firmware download failed!!!\n"); + return 1; +} + + +static int +wavefront_do_reset (snd_wavefront_t *dev) + +{ + char voices[1]; + + if (wavefront_reset_to_cleanliness (dev)) { + snd_printk ("hw reset failed.\n"); + goto gone_bad; + } + + if (dev->israw) { + if (wavefront_download_firmware (dev, ospath)) { + goto gone_bad; + } + + dev->israw = 0; + + /* Wait for the OS to get running. The protocol for + this is non-obvious, and was determined by + using port-IO tracing in DOSemu and some + experimentation here. + + Rather than using timed waits, use interrupts creatively. + */ + + wavefront_should_cause_interrupt (dev, WFC_NOOP, + dev->data_port, + (osrun_time*HZ)); + + if (!dev->irq_ok) { + snd_printk ("no post-OS interrupt.\n"); + goto gone_bad; + } + + /* Now, do it again ! */ + + wavefront_should_cause_interrupt (dev, WFC_NOOP, + dev->data_port, (10*HZ)); + + if (!dev->irq_ok) { + snd_printk ("no post-OS interrupt(2).\n"); + goto gone_bad; + } + + /* OK, no (RX/TX) interrupts any more, but leave mute + in effect. + */ + + outb (0x80|0x40, dev->control_port); + } + + /* SETUPSND.EXE asks for sample memory config here, but since i + have no idea how to interpret the result, we'll forget + about it. + */ + + if ((dev->freemem = wavefront_freemem (dev)) < 0) { + goto gone_bad; + } + + snd_printk ("available DRAM %dk\n", dev->freemem / 1024); + + if (wavefront_write (dev, 0xf0) || + wavefront_write (dev, 1) || + (wavefront_read (dev) < 0)) { + dev->debug = 0; + snd_printk ("MPU emulation mode not set.\n"); + goto gone_bad; + } + + voices[0] = 32; + + if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, 0, voices)) { + snd_printk ("cannot set number of voices to 32.\n"); + goto gone_bad; + } + + + return 0; + + gone_bad: + /* reset that sucker so that it doesn't bother us. */ + + outb (0x0, dev->control_port); + dev->interrupts_are_midi = 0; + return 1; +} + +int +snd_wavefront_start (snd_wavefront_t *dev) + +{ + int samples_are_from_rom; + + /* IMPORTANT: assumes that snd_wavefront_detect() and/or + wavefront_reset_to_cleanliness() has already been called + */ + + if (dev->israw) { + samples_are_from_rom = 1; + } else { + /* XXX is this always true ? */ + samples_are_from_rom = 0; + } + + if (dev->israw || fx_raw) { + if (wavefront_do_reset (dev)) { + return -1; + } + } + /* Check for FX device, present only on Tropez+ */ + + dev->has_fx = (snd_wavefront_fx_detect (dev) == 0); + + if (dev->has_fx && fx_raw) { + snd_wavefront_fx_start (dev); + } + + wavefront_get_sample_status (dev, samples_are_from_rom); + wavefront_get_program_status (dev); + wavefront_get_patch_status (dev); + + /* Start normal operation: unreset, master interrupt enabled, no mute + */ + + outb (0x80|0x40|0x20, dev->control_port); + + return (0); +} + +int +snd_wavefront_detect (snd_wavefront_card_t *card) + +{ + unsigned char rbuf[4], wbuf[4]; + snd_wavefront_t *dev = &card->wavefront; + + /* returns zero if a WaveFront card is successfully detected. + negative otherwise. + */ + + dev->israw = 0; + dev->has_fx = 0; + dev->debug = debug_default; + dev->interrupts_are_midi = 0; + dev->irq_cnt = 0; + dev->rom_samples_rdonly = 1; + + if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + + dev->fw_version[0] = rbuf[0]; + dev->fw_version[1] = rbuf[1]; + + snd_printk ("firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + /* check that a command actually works */ + + if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION, + rbuf, wbuf) == 0) { + dev->hw_version[0] = rbuf[0]; + dev->hw_version[1] = rbuf[1]; + } else { + snd_printk ("not raw, but no " + "hardware version!\n"); + return -1; + } + + if (!wf_raw) { + return 0; + } else { + snd_printk ("reloading firmware as you requested.\n"); + dev->israw = 1; + } + + } else { + + dev->israw = 1; + snd_printk ("no response to firmware probe, assume raw.\n"); + + } + + return 0; +} + +#if 0 +EXPORT_SYMBOL(snd_wavefront_synth_ioctl); +EXPORT_SYMBOL(snd_wavefront_synth_open); +EXPORT_SYMBOL(snd_wavefront_synth_release); +EXPORT_SYMBOL(snd_wavefront_internal_interrupt); +EXPORT_SYMBOL(snd_wavefront_interrupt_bits); +EXPORT_SYMBOL(snd_wavefront_start); +EXPORT_SYMBOL(snd_wavefront_detect); +EXPORT_SYMBOL(snd_wavefront_cmd); + /* wavefront_midi.c */ +EXPORT_SYMBOL(snd_wavefront_midi_interrupt); +EXPORT_SYMBOL(snd_wavefront_midi_enable_virtual); +EXPORT_SYMBOL(snd_wavefront_midi_disable_virtual); +EXPORT_SYMBOL(snd_wavefront_midi_start); +EXPORT_SYMBOL(snd_wavefront_midi_input); +EXPORT_SYMBOL(snd_wavefront_midi_output); + +static int __init alsa_wavefront_init(void) +{ + return 0; +} + +static void __exit alsa_wavefront_exit(void) +{ +} + +module_init(alsa_wavefront_init) +module_exit(alsa_wavefront_exit) +#endif diff -Nru linux/sound/last.c linux-2.4.19-pre5-mjc/sound/last.c --- linux/sound/last.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/last.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,41 @@ +/* + * Advanced Linux Sound Architecture + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define SNDRV_MAIN_OBJECT_FILE +#include +#include + +static int __init alsa_sound_last_init(void) +{ + int idx, ok = 0; + + printk(KERN_INFO "ALSA device list:\n"); + for (idx = 0; idx < SNDRV_CARDS; idx++) + if (snd_cards[idx] != NULL) { + printk(KERN_INFO " #%i: %s\n", idx, snd_cards[idx]->longname); + ok++; + } + if (ok == 0) + printk(KERN_INFO " No soundcards found.\n"); + return 0; +} + +__initcall(alsa_sound_last_init); diff -Nru linux/sound/oss/724hwmcode.h linux-2.4.19-pre5-mjc/sound/oss/724hwmcode.h --- linux/sound/oss/724hwmcode.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/724hwmcode.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1575 @@ +//============================================================================= +// Copyright (c) 1997-1999 Yamaha Corporation. All Rights Reserved. +// +// Title: +// hwmcode.c +// Desc: +// micro-code for CTRL & DSP +//============================================================================= +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static unsigned long int DspInst[] __initdata = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static unsigned long int CntrlInst[] __initdata = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09?@creat +// 04/12 stop nise fix +// 06/21?@WorkingOff timming +static unsigned long int CntrlInst1E[] __initdata = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ + + diff -Nru linux/sound/oss/CHANGELOG linux-2.4.19-pre5-mjc/sound/oss/CHANGELOG --- linux/sound/oss/CHANGELOG Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/CHANGELOG Mon Apr 8 22:31:22 2002 @@ -0,0 +1,369 @@ +Note these changes relate to Hannu's code and don't include the changes +made outside of this for modularising the sound + +Changelog for version 3.8o +-------------------------- + +Since 3.8h +- Included support for OPL3-SA1 and SoftOSS + +Since 3.8 +- Fixed SNDCTL_DSP_GETOSPACE +- Compatibility fixes for Linux 2.1.47 + +Since 3.8-beta21 +- Fixed all known bugs (I think). + +Since 3.8-beta8 +- Lot of fixes to audio playback code in dmabuf.c + +Since 3.8-beta6 +- Fixed the famous Quake delay bug. + +Since 3.8-beta5 +- Fixed many bugs in audio playback. + +Since 3.8-beta4 +- Just minor changes. + +Since 3.8-beta1 +- Major rewrite of audio playback handling. +- Added AWE32 support by Takashi Iwai (in ./lowlevel/). + +Since 3.7-beta# +- Passing of ioctl() parameters between soundcard.c and other modules has been +changed so that arg always points to kernel space. +- Some bugfixes. + +Since 3.7-beta5 +- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant +stream of received 0x00 bytes when the MIDI receiver is enabled. + +Since 3.5 +- Changes almost everywhere. +- Support for OPTi 82C924-based sound cards. + +Since 3.5.4-beta8 +- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode + with GUS. +- Limited minimum fragment size with some audio devices (GUS=512 and + SB=32). These devices require more time to "recover" from processing + of each fragment. + +Since 3.5.4-beta6/7 +- There seems to be problems in the OPTi 82C930 so cards based on this + chip don't necessarily work yet. There are problems in detecting the + MIDI interface. Also mixer volumes may be seriously wrong on some systems. + You can safely use this driver version with C930 if it looks to work. + However please don't complain if you have problems with it. C930 support + should be fixed in future releases. +- Got initialization of GUS PnP to work. With this version GUS PnP should + work in GUS compatible mode after initialization using isapnptools. +- Fixed a bug in handling of full duplex cards in write only mode. This has + been causing "audio device opening" errors with RealAudio player. + +Since 3.5.4.beta5 +- Changes to OPTi 82C930 driver. +- Major changes to the Soundscape driver. The driver requires now just one + DMA channel. The extra audio/dsp device (the "Not functional" one) used + for code download in the earlier versions has been eliminated. There is now + just one /dev/dsp# device which is used both for code download and audio. + +Since 3.5.4.beta4 +- Minor changes. + +Since 3.5.4-beta2 +- Fixed silent playback with ESS 688/1688. +- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one + is required for 8 and 16 bit modes). +- Added the "lowlevel" subdirectory for additional low level drivers that + are not part of USS core. See lowlevel/README for more info. +- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in + miroPCM sound cards. See lowlevel/aci.readme for more info. +- Support for Aztech Washington chipset (AZT2316 ASIC). + +Since 3.5.4-beta1 +- Reduced clicking with AD1848. +- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback + is sometimes just white noise (occurs randomly). + +Since 3.5.2 +- Major changes to the SB/Jazz16/ESS driver (most parts rewritten). + The most noticeable new feature is support for multiple SB cards at the same + time. +- Renamed sb16_midi.c to uart401.c. Also modified it to work also with + other MPU401 UART compatible cards than SB16/ESS/Jazz. +- Some changes which reduce clicking in audio playback. +- Copying policy is now GPL. + +Since 3.5.1 +- TB Maui initialization support +Since 3.5 +- Improved handling of playback underrun situations. + +Since 3.5-beta10 +- Bug fixing + +Since 3.5-beta9 +- Fixed for compatibility with Linux 1.3.70 and later. +- Changed boot time passing of 16 bit DMA channel number to SB driver. + +Since 3.5-beta8 +- Minor changes + +Since 3.5-beta7 +- enhancements to configure program (by Jeff Tranter): + - prompts are in same format as 1.3.x Linux kernel config program + - on-line help for each question + - fixed some compile warnings detected by gcc/g++ -Wall + - minor grammatical changes to prompts + +Since 3.5-beta6 +- Fixed bugs in mmap() support. +- Minor changes to Maui driver. + +Since 3.5-beta5 +- Fixed crash after recording with ESS688. It's generally a good + idea to stop inbound DMA transfers before freeing the memory + buffer. +- Fixed handling of AD1845 codec (for example Shuttle Sound System). +- Few other fixes. + +Since 3.5-beta4 +- Fixed bug in handling of uninitialized instruments with GUS. + +Since 3.5-beta3 +- Few changes which decrease popping at end/beginning of audio playback. + +Since 3.5-beta2 +- Removed MAD16+CS4231 hack made in previous version since it didn't + help. +- Fixed the above bug in proper way and in proper place. Many thanks + to James Hightower. + +Since 3.5-beta1 +- Bug fixes. +- Full duplex audio with MAD16+CS4231 may work now. The driver configures + SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels. + The side effect is that all 8 bit DMA channels (0,1,3) are populated in + duplex mode. + +Since 3.5-alpha9 +- Bug fixes (mostly in Jazz16 and ESS1688/688 supports). +- Temporarily disabled recording with ESS1688/688 since it causes crash. +- Changed audio buffer partitioning algorithm so that it selects + smaller fragment size than earlier. This improves real time capabilities + of the driver and makes recording to disk to work better. Unfortunately + this change breaks some programs which assume that fragments cannot be + shorter than 4096 bytes. + +Since 3.5-alpha8 +- Bug fixes + +Since 3.5-alpha7 +- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable + using command "cd /linux/drivers/sound;make script" and then + just run kernel's make config normally. +- Minor fixes to the SB support. Hopefully the driver works with + all SB models now. +- Added support for ESS ES1688 "AudioDrive" based cards. + +Since 3.5-alpha6 +- SB Pro and SB16 supports are no longer separately selectable options. + Enabling SB enables them too. +- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified +configure to handle this. +- Removed initialization messages from the +modularized version. They can be enabled by using init_trace=1 in +the insmod command line (insmod sound init_trace=1). +- More AIX stuff. +- Added support for synchronizing dsp/audio devices with /dev/sequencer. +- mmap() support for dsp/audio devices. + +Since 3.5-alpha5 +- AIX port. +- Changed some xxx_PATCH macros in soundcard.h to work with + big endian machines. + +Since 3.5-alpha4 +- Removed the 'setfx' stuff from the version distributed with kernel + sources. Running 'setfx' is required again. + +Since 3.5-alpha3 +- Moved stuff from the 'setfx' program to the AudioTrix Pro driver. + +Since 3.5-alpha2 +- Modifications to makefile and configure.c. Unnecessary sources + are no longer compiled. Newly created local.h is also copied to + /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces + new local.h which is compatible with current version of the driver. +- Some fixes to the SB16 support. +- Fixed random protection fault in gus_wave.c + +Since 3.5-alpha1 +- Modified to work with Linux-1.3.33 and later +- Some minor changes + +Since 3.0.2 +- Support for CS4232 based PnP cards (AcerMagic S23 etc). +- Full duplex support for some CS4231, CS4232 and AD1845 based cards +(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards +having a codec mentioned above). +- Almost fully rewritten loadable modules support. +- Fixed some bugs. +- Huge amount of testing (more testing is still required). +- mmap() support (works with some cards). Requires much more testing. +- Sample/patch/program loading for TB Maui/Tropez. No initialization +since TB doesn't allow me to release that code. +- Using CS4231 compatible codecs as timer for /dev/music. + +Since 3.0.1 +- Added allocation of I/O ports, DMA channels and interrupts +to the initialization code. This may break modules support since +the driver may not free some resources on unload. Should be fixed soon. + +Since 3.0 +- Some important bug fixes. +- select() for /dev/dsp and /dev/audio (Linux only). +(To use select() with read, you have to call read() to start +the recording. Calling write() kills recording immediately so +use select() carefully when you are writing a half duplex app. +Full duplex mode is not implemented yet.) Select works also with +/dev/sequencer and /dev/music. Maybe with /dev/midi## too. + +Since 3.0-beta2 +- Minor fixes. +- Added Readme.cards + +Since 3.0-beta1 +- Minor fixes to the modules support. +- Eliminated call to sb_free_irq() in ad1848.c +- Rewritten MAD16&Mozart support (not tested with MAD16 Pro). +- Fix to DMA initialization of PSS cards. +- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.) +- Fixed some bugs in the PSS driver which caused I/O errors with + the MSS mode (/dev/dsp). + +Since 3.0-950506 +- Recording with GUS MAX fixed. It works when the driver is configured + to use two DMA channels with GUS MAX (16 bit ones recommended). + +Since 3.0-94xxxx +- Too many changes + +Since 3.0-940818 +- Fixes for Linux 1.1.4x. +- Disables Disney Sound System with SG NX Pro 16 (less noise). + +Since 2.90-2 +- Fixes to soundcard.h +- Non blocking mode to /dev/sequencer +- Experimental detection code for Ensoniq Soundscape. + +Since 2.90 +- Minor and major bug fixes + +Since pre-3.0-940712 +- GUS MAX support +- Partially working MSS/WSS support (could work with some cards). +- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs + (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and + GUS MAX, but it doesn't work yet. +Since pre-3.0-940426 +- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc). +This codec chip is used in various sound cards. This version is developed +for the 16 bit daughtercard of GUS. It should work with other cards also +if the following requirements are met: + - The I/O, IRQ and DMA settings are jumper selectable or + the card is initialized by booting DOS before booting Linux (etc.). + - You add the IO, IRQ and DMA settings manually to the local.h. + (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that + the base address bust be the base address of the codec chip not the + card itself. For the GUS16 these are the same but most MSS compatible + cards have the codec located at card_base+4. +- Some minor changes + +Since 2.5 (******* MAJOR REWRITE ***********) + +This version is based on v2.3. I have tried to maintain two versions +together so that this one should have the same features than v2.5. +Something may still be missing. If you notice such things, please let me +know. + +The Readme.v30 contains more details. + +- /dev/midi## devices. +- /dev/sequencer2 + +Since 2.5-beta2 +- Some fine tuning to the GUS v3.7 mixer code. +- Fixed speed limits for the plain SB (1.0 to 2.0). + +Since 2.5-beta +- Fixed OPL-3 detection with SB. Caused problems with PAS16. +- GUS v3.7 mixer support. + +Since 2.4 +- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h). +- Fixed truncated sound on /dev/dsp when the device is closed. +- Linear volume mode for GUS +- Pitch bends larger than +/- 2 octaves. +- MIDI recording for SB and SB Pro. (Untested). +- Some other fixes. +- SB16 MIDI and DSP drivers only initialized if SB16 actually installed. +- Implemented better detection for OPL-3. This should be useful if you + have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3. +- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested). + +Since 2.3b +- Fixed bug which made it impossible to make long recordings to disk. + Recording was not restarted after a buffer overflow situation. +- Limited mixer support for GUS. +- Numerous improvements to the GUS driver by Andrew Robinson. Including + some click removal etc. + +Since 2.3 +- Fixed some minor bugs in the SB16 driver. + +Since 2.2b +- Full SB16 DSP support. 8/16 bit, mono/stereo +- The SCO and FreeBSD versions should be in sync now. There are some + problems with SB16 and GUS in the FreeBSD versions. + The DMA buffer allocation of the SCO version has been polished but + there could still be some problems. At least it hogs memory. + The DMA channel + configuration method used in the SCO/System is a hack. +- Support for the MPU emulation of the SB16. +- Some big arrays are now allocated boot time. This makes the BSS segment + smaller which makes it possible to use the full driver with + NetBSD. These arrays are not allocated if no suitable sound card is available. +- Fixed a bug in the compute_and_set_volume in gus_wave.c +- Fixed the too fast mono playback problem of SB Pro and PAS16. + +Since 2.2 +- Stereo recording for SB Pro. Somehow it was missing and nobody + had noticed it earlier. +- Minor polishing. +- Interpreting of boot time arguments (sound=) for Linux. +- Breakup of sb_dsp.c. Parts of the code has been moved to + sb_mixer.c and sb_midi.c + +Since 2.1 +- Preliminary support for SB16. + - The SB16 mixer is supported in its native mode. + - Digitized voice capability up to 44.1 kHz/8 bit/mono + (16 bit and stereo support coming in the next release). +- Fixed some bugs in the digitized voice driver for PAS16. +- Proper initialization of the SB emulation of latest PAS16 models. + +- Significantly improved /dev/dsp and /dev/audio support. + - Now supports half duplex mode. It's now possible to record and + playback without closing and reopening the device. + - It's possible to use smaller buffers than earlier. There is a new + ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4. + This call instructs the driver to use smaller buffers. The default + buffer size (0.5 to 1.0 seconds) is divided by n. Should be called + immediately after opening the device. + +Since 2.0 +Just cosmetic changes. diff -Nru linux/sound/oss/COPYING linux-2.4.19-pre5-mjc/sound/oss/COPYING --- linux/sound/oss/COPYING Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/COPYING Mon Apr 8 22:31:22 2002 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -Nru linux/sound/oss/Config.help linux-2.4.19-pre5-mjc/sound/oss/Config.help --- linux/sound/oss/Config.help Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/Config.help Mon Apr 8 22:31:22 2002 @@ -0,0 +1,723 @@ +CONFIG_INPUT_GAMEPORT + Gameport support is for the standard 15-pin PC gameport. If you + have a joystick, gamepad, gameport card, a soundcard with a gameport + or anything else that uses the gameport, say Y or M here and also to + at least one of the hardware specific drivers. + Please read the file which + contains more information and the location of the joystick package + that you'll need if you use the gameport with a joystick. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called gameport.o. If you want to compile it as + a module, say M here and read . + +CONFIG_SOUND_ALSA_OSS + OSS is the Open Sound System suite of sound card drivers. They make + sound programming easier since they provide a common API. Say Y or + M here (the module will be called sound.o) if you haven't found a + driver for your sound card above, then pick your driver from the + list below. + +CONFIG_SOUND_ALSA_DMAP + Linux can often have problems allocating DMA buffers for ISA sound + cards on machines with more than 16MB of RAM. This is because ISA + DMA buffers must exist below the 16MB boundary and it is quite + possible that a large enough free block in this region cannot be + found after the machine has been running for a while. If you say Y + here the DMA buffers (64Kb) will be allocated at boot time and kept + until the shutdown. This option is only useful if you said Y to + "OSS sound modules", above. If you said M to "OSS sound modules" + then you can get the persistent DMA buffer functionality by passing + the command-line argument "dmabuf=1" to the sound.o module. + + Say Y unless you have 16MB or more RAM or a PCI sound card. + +CONFIG_SOUND_ALSA_SGALAXY + This module initializes the older non Plug and Play sound galaxy + cards from Aztech. It supports the Waverider Pro 32 - 3D and the + Galaxy Washington 16. + + If you compile the driver into the kernel, you have to add + "sgalaxy=,,,," to the kernel command + line. + +CONFIG_SOUND_ALSA_AD1816 + Say M here if you have a sound card based on the Analog Devices + AD1816(A) chip. + + If you compile the driver into the kernel, you have to add + "ad1816=,,," to the kernel command line. + +CONFIG_SOUND_ALSA_OPL3SA1 + Say Y or M if you have a Yamaha OPL3-SA1 sound chip, which is + usually built into motherboards. Read + for details. + + If you compile the driver into the kernel, you have to add + "opl3sa=,,,,," to the kernel + command line. + +CONFIG_SOUND_ALSA_PAS + Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio + 16 or Logitech SoundMan 16 sound card. Answer N if you have some + other card made by Media Vision or Logitech since those are not + PAS16 compatible. Please read . + It is not necessary to add Sound Blaster support separately; it + is included in PAS support. + + If you compile the driver into the kernel, you have to add + "pas2=,,,,,,, + to the kernel command line. + +CONFIG_PAS_JOYSTICK + Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick + port. + +CONFIG_SOUND_ALSA_SB + Answer Y if you have an original Sound Blaster card made by Creative + Labs or a 100% hardware compatible clone (like the Thunderboard or + SM Games). For an unknown card you may answer Y if the card claims + to be Sound Blaster-compatible. + + Please read the file . + + You should also say Y here for cards based on the Avance Logic + ALS-007 and ALS-1X0 chips (read ) and + for cards based on ESS chips (read + and + ). If you have an SB AWE 32 or SB AWE + 64, say Y here and also to "AWE32 synth" below and read + . If you have an IBM Mwave + card, say Y here and read . + + If you compile the driver into the kernel and don't want to use + isapnp, you have to add "sb=,,," to the kernel + command line. + + You can say M here to compile this driver as a module; the module is + called sb.o. + +CONFIG_SOUND_ALSA_GUS + Say Y here for any type of Gravis Ultrasound card, including the GUS + or GUS MAX. See also for more + information on configuring this card with modules. + + If you compile the driver into the kernel, you have to add + "gus=,,," to the kernel command line. + +CONFIG_SOUND_ALSA_MPU401 + Be careful with this question. The MPU401 interface is supported by + all sound cards. However, some natively supported cards have their + own driver for MPU401. Enabling this MPU401 option with these cards + will cause a conflict. Also, enabling MPU401 on a system that + doesn't really have a MPU401 could cause some trouble. If your card + was in the list of supported cards, look at the card specific + instructions in the file. It + is safe to answer Y if you have a true MPU401 MIDI interface card. + + If you compile the driver into the kernel, you have to add + "mpu401=," to the kernel command line. + +CONFIG_SOUND_ALSA_UART6850 + This option enables support for MIDI interfaces based on the 6850 + UART chip. This interface is rarely found on sound cards. It's safe + to answer N to this question. + + If you compile the driver into the kernel, you have to add + "uart6850=," to the kernel command line. + +CONFIG_SOUND_ALSA_PSS + Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven + ADSP-16 or some other card based on the PSS chipset (AD1848 codec + + ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on + how to compile it into the kernel or as a module see the file + . + + If you compile the driver into the kernel, you have to add + "pss=,,,,," to the kernel + command line. + +CONFIG_PSS_MIXER + Answer Y for Beethoven ADSP-16. You may try to say Y also for other + cards if they have master volume, bass, treble, and you can't + control it under Linux. If you answer N for Beethoven ADSP-16, you + can't control master volume, bass, treble and synth volume. + + If you said M to "PSS support" above, you may enable or disable this + PSS mixer with the module parameter pss_mixer. For more information + see the file . + +CONFIG_PSS_HAVE_BOOT + If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y + to include this file. Without this file the synth device (OPL) may + not work. + +CONFIG_PSS_BOOT_FILE + Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file, + starting from /. + +CONFIG_SOUND_ALSA_MSS + Again think carefully before answering Y to this question. It's + safe to answer Y if you have the original Windows Sound System card + made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may + say Y in case your card is NOT among these: + + ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16, + Ensoniq SoundScape (and compatibles made by Reveal and Spea), + Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max, + Gravis Ultrasound with 16 bit option, Logitech Sound Man 16, + Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi + 82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft + Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid + SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro + Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface, + Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound + Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M + notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM + synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface. + + For cards having native support in VoxWare, consult the card + specific instructions in . + Some drivers have their own MSS support and saying Y to this option + will cause a conflict. + + If you compile the driver into the kernel, you have to add + "ad1848=,,,[,]" to the kernel command + line. + +CONFIG_SOUND_ALSA_VWSND + Say Y or M if you have an SGI Visual Workstation and you want to be + able to use its on-board audio. Read + for more info on this driver's + capabilities. + +CONFIG_SOUND_ALSA_SSCAPE + Answer Y if you have a sound card based on the Ensoniq SoundScape + chipset. Such cards are being manufactured at least by Ensoniq, Spea + and Reveal (Reveal makes also other cards). + + If you compile the driver into the kernel, you have to add + "sscape=,,,," to the kernel command + line. + +CONFIG_SOUND_ALSA_TRIX + Answer Y if you have the AudioTriX Pro sound card manufactured + by MediaTrix. + +CONFIG_TRIX_HAVE_BOOT + The MediaTrix AudioTrix Pro has an on-board microcontroller which + needs to be initialized by downloading the code from the file + TRXPRO.HEX in the DOS driver directory. If you don't have the + TRXPRO.HEX file handy you may skip this step. However, the SB and + MPU-401 modes of AudioTrix Pro will not work without this file! + +CONFIG_TRIX_BOOT_FILE + Enter the full pathname of your TRXPRO.HEX file, starting from /. + +CONFIG_SOUND_ALSA_MAD16 + Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi + 82C928 or 82C929 or 82C931) audio interface chip. These chips are + quite common so it's possible that many no-name cards have one of + them. In addition the MAD16 chip is used in some cards made by known + manufacturers such as Turtle Beach (Tropez), Reveal (some models) + and Diamond (latest ones). Note however that the Tropez sound cards + have their own driver; if you have one of those, say N here and Y or + M to "Full support for Turtle Beach WaveFront", below. + + If you compile the driver into the kernel, you have to add + "mad16=,,,,," to the + kernel command line. + + See also and + for more information on setting + these cards up as modules. + +CONFIG_SOUND_ALSA_WAVEFRONT + Answer Y or M if you have a Tropez Plus, Tropez or Maui sound card + and read the files and + . + +CONFIG_MAD16_OLDCARD + Answer Y (or M) if you have an older card based on the C928 or + Mozart chipset and you want to have MIDI support. If you enable this + option you also need to enable support for Sound Blaster. + +CONFIG_SOUND_ALSA_CS4232 + Say Y here if you have a card based on the Crystal CS4232 chip set, + which uses its own Plug and Play protocol. + + If you compile the driver into the kernel, you have to add + "cs4232=,,,,," to the kernel + command line. + + See for more information on + configuring this card. + +CONFIG_SOUND_ALSA_OPL3SA2 + Say Y or M if you have a card based on one of these Yamaha sound + chipsets or the "SAx", which is actually a SA3. Read + for more information on + configuring these cards. + + If you compile the driver into the kernel and do not also + configure in the optional ISA PnP support, you will have to add + "opl3sa2=,,,,," to the kernel + command line. + +CONFIG_SOUND_ALSA_MAUI + Say Y here if you have a Turtle Beach Wave Front, Maui, or Tropez + sound card. + + If you compile the driver into the kernel, you have to add + "maui=," to the kernel command line. + +CONFIG_MAUI_HAVE_BOOT + Turtle Beach Maui and Tropez sound cards have a microcontroller + which needs to be initialized prior to use. OSWF.MOT is a file + distributed with the card's DOS/Windows drivers. Answer Y if you + have this file. + +CONFIG_MAUI_BOOT_FILE + Enter the full pathname of your OSWF.MOT file, starting from /. + +CONFIG_SOUND_ALSA_MSNDCLAS + Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or + Monterey (not for the Pinnacle or Fiji). + + See for important information + about this driver. Note that it has been discontinued, but the + Voyetra Turtle Beach knowledge base entry for it is still available + at . + +CONFIG_MSNDCLAS_IO + I/O port address for the MultiSound Classic and related cards. + +CONFIG_MSNDCLAS_IRQ + Interrupt Request line for the MultiSound Classic and related cards. + +CONFIG_MSNDCLAS_MEM + Memory-mapped I/O base address for the MultiSound Classic and + related cards. + +CONFIG_MSNDCLAS_INIT_FILE + The MultiSound cards have two firmware files which are required for + operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_MSNDCLAS_PERM_FILE + The MultiSound cards have two firmware files which are required for + operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_SOUND_ALSA_MSNDPIN + Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji. + See for important information + about this driver. Note that it has been discontinued, but the + Voyetra Turtle Beach knowledge base entry for it is still available + at . + +CONFIG_MSNDPIN_IDE_IO0 + CD-ROM drive 0 memory-mapped I/O base address for the MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IDE_IO1 + CD-ROM drive 1 memory-mapped I/O base address for the MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IDE_IRQ + Interrupt request number for the IDE CD-ROM interface on the + MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IO + Memory-mapped I/O base address for the primary synthesizer on + MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_MPU_IO + Memory-mapped I/O base address for the Kurzweil daughterboard + synthesizer on MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_MPU_IRQ + Iinterrupt request number for the Kurzweil daughterboard + synthesizer on MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_IRQ + Interrupt request line for the primary synthesizer on MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_JOYSTICK_IO + Memory-mapped I/O base address for the joystick port on MultiSound + Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_MEM + Memory-mapped I/O base address for the primary synthesizer on + MultiSound Pinnacle and Fiji sound cards. + +CONFIG_MSNDPIN_INIT_FILE + The MultiSound cards have two firmware files which are required + for operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_MSNDPIN_PERM_FILE + The MultiSound cards have two firmware files which are required for + operation, and are not currently included. These files can be + obtained from Turtle Beach. See + for information on how to + obtain this. + +CONFIG_MSNDPIN_DIGITAL + If you have the S/PDIF daughter board for the Pinnacle or Fiji, + answer Y here; otherwise, say N. If you have this, you will be able + to play and record from the S/PDIF port (digital signal). See + for information on how to make + use of this capability. + +CONFIG_MSNDPIN_NONPNP + The Pinnacle and Fiji card resources can be configured either with + PnP, or through a configuration port. Say Y here if your card is NOT + in PnP mode. For the Pinnacle, configuration in non-PnP mode allows + use of the IDE and joystick peripherals on the card as well; these + do not show up when the card is in PnP mode. Specifying zero for any + resource of a device will disable the device. If you are running the + card in PnP mode, you must say N here and use isapnptools to + configure the card's resources. + +CONFIG_MSNDPIN_CFG + This is the port which the Pinnacle and Fiji uses to configure the + card's resources when not in PnP mode. If your card is in PnP mode, + then be sure to say N to the previous option, "MSND Pinnacle Non-PnP + Mode". + +CONFIG_MSND_FIFOSIZE + Configures the size of each audio buffer, in kilobytes, for + recording and playing in the MultiSound drivers (both the Classic + and Pinnacle). Larger values reduce the chance of data overruns at + the expense of overall latency. If unsure, use the default. + +CONFIG_SOUND_ALSA_YM3812 + Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering Y is usually a safe and recommended choice, however some + cards may have software (TSR) FM emulation. Enabling FM support with + these cards may cause trouble (I don't currently know of any such + cards, however). Please read the file + if your card has an OPL3 chip. + + If you compile the driver into the kernel, you have to add + "opl3=" to the kernel command line. + + If unsure, say Y. + +CONFIG_SOUND_ALSA_ACI_MIXER + ACI (Audio Command Interface) is a protocol used to communicate with + the microcontroller on some sound cards produced by miro and + Cardinal Technologies. The main function of the ACI is to control + the mixer and to get a product identification. + + This VoxWare ACI driver currently supports the ACI functions on the + miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI + also controls the radio tuner. This is supported in the video4linux + miropcm20 driver (say M or Y here and go back to "Multimedia + devices" -> "Radio Adapters"). + + This driver is also available as a module and will be called aci.o. + +CONFIG_SOUND_ALSA_AWE32_SYNTH + Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or + similar sound card. See , + and the Soundblaster-AWE + mini-HOWTO, available from + for more info. + +CONFIG_SOUND_ALSA_AEDSP16 + Answer Y if you have a Gallant's Audio Excel DSP 16 card. This + driver supports Audio Excel DSP 16 but not the III nor PnP versions + of this card. + + The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or + a Microsoft Sound System card, so you should have said Y to either + "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support" + or "Microsoft Sound System support", above, and you need to answer + the "MSS emulation" and "SBPro emulation" questions below + accordingly. You should say Y to one and only one of these two + questions. + + Read the file and the head of + as well as + to get more information + about this driver and its configuration. + +CONFIG_AEDSP16_SBPRO + Answer Y if you want your audio card to emulate Sound Blaster Pro. + You should then say Y to "100% Sound Blaster compatibles + (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS + emulation)". + + If you compile the driver into the kernel, you have to add + "aedsp16=,,,,," to the kernel + command line. + +CONFIG_AEDSP16_MSS + Answer Y if you want your audio card to emulate Microsoft Sound + System. You should then say Y to "Microsoft Sound System support" + and say N to "Audio Excel DSP 16 (SBPro emulation)". + +CONFIG_SC6600 + The SC6600 is the new version of DSP mounted on the Audio Excel DSP + 16 cards. Find in the manual the FCC ID of your audio card and + answer Y if you have an SC6600 DSP. + +CONFIG_SC6600_JOY + Say Y here in order to use the joystick interface of the Audio Excel + DSP 16 card. + +CONFIG_SC6600_CDROMBASE + Base I/O port address for the CD-ROM interface of the Audio Excel + DSP 16 card. + +CONFIG_AEDSP16_MPU401 + Answer Y if you want your audio card to emulate the MPU-401 midi + interface. You should then also say Y to "MPU-401 support". + + Note that the I/O base for MPU-401 support of aedsp16 is the same + you have selected for "MPU-401 support". If you are using this + driver as a module you have to specify the MPU I/O base address with + the parameter 'mpu_base=0xNNN'. + +CONFIG_SOUND_ALSA_CMPCI + Say Y or M if you have a PCI sound card using the CMI8338 + or the CMI8378 chipset. Data on these chips are available at + . + + A userspace utility to control some internal registers of these + chips is available at + . + +CONFIG_SOUND_ALSA_CMPCI_CM8738 + Say Y or M if you have a PCI sound card using the CMI8338 + or the CMI8378 chipset. Data on this chip is available at + . + + A userspace utility to control some internal registers of these + chips is available at + . + +CONFIG_SOUND_ALSA_CMPCI_JOYSTICK + Say here in order to enable the joystick port on a sound crd using + the CMI8338 or the CMI8738 chipset. Data on these chips are + available at . + +CONFIG_SOUND_ALSA_CMPCI_SPEAKERS + Specify the number of speaker channels you want the card to drive, + as an integer. + +CONFIG_SOUND_ALSA_CMPCI_SPDIFLOOP + Enable loopback from SPDIF in to SPDIF out. For discussion, see + "The 8738 Audio SPDIF In/Out Technical Data" on the technical + support page at . + + A userspace utility to control even more internal registers of these + chips is available at + . + This package will among other things help you enable SPDIF + out/in/loop/monitor. + +CONFIG_SOUND_ALSA_EMU10K1 + Say Y or M if you have a PCI sound card using the EMU10K1 chipset, + such as the Creative SBLive!, SB PCI512 or Emu-APS. + + For more information on this driver and the degree of support for the + different card models please check . + + It is now possible to load dsp microcode patches into the EMU10K1 + chip. These patches are used to implement real time sound + processing effects which include for example: signal routing, + bass/treble control, AC3 passthrough, ... + Userspace tools to create new patches and load/unload them can be + found at . + +CONFIG_MIDI_EMU10K1 + Say Y if you want to be able to use the OSS /dev/sequencer + interface. This code is still experimental. + +CONFIG_SOUND_ALSA_FUSION + This module drives the Crystal SoundFusion devices (CS4280/46xx + series) when wired as native sound drivers with AC97 codecs. If + this driver does not work try the CS4232 driver. + +CONFIG_SOUND_ALSA_ES1370 + Say Y or M if you have a PCI sound card utilizing the Ensoniq + ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find + out if your sound card uses an ES1370 without removing your + computer's cover, use lspci -n and look for the PCI ID + 1274:5000. Since Ensoniq was bought by Creative Labs, + Sound Blaster 64/PCI models are either ES1370 or ES1371 based. + This driver differs slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_ALSA_ES1371 + Say Y or M if you have a PCI sound card utilizing the Ensoniq + ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if + your sound card uses an ES1371 without removing your computer's + cover, use lspci -n and look for the PCI ID 1274:1371. Since + Ensoniq was bought by Creative Labs, Sound Blaster 64/PCI + models are either ES1370 or ES1371 based. This driver differs + slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_ALSA_ESSSOLO1 + Say Y or M if you have a PCI sound card utilizing the ESS Technology + Solo1 chip. To find out if your sound card uses a + Solo1 chip without removing your computer's cover, use + lspci -n and look for the PCI ID 125D:1969. This driver + differs slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_ALSA_SONICVIBES + Say Y or M if you have a PCI sound card utilizing the S3 + SonicVibes chipset. To find out if your sound card uses a + SonicVibes chip without removing your computer's cover, use + lspci -n and look for the PCI ID 5333:CA00. This driver + differs slightly from OSS/Free, so PLEASE READ + . + +CONFIG_SOUND_ALSA_TRIDENT + Say Y or M if you have a PCI sound card utilizing the Trident + 4DWave-DX/NX chipset or your mother board chipset has SiS 7018 + or ALi 5451 built-in. The SiS 7018 PCI Audio Core is embedded + in SiS960 Super South Bridge and SiS540/630 Single Chipset. + The ALi 5451 PCI Audio Core is embedded in ALi M1535, M1535D, + M1535+ or M1535D+ South Bridge. + + Use lspci -n to find out if your sound card or chipset uses + Trident 4DWave or SiS 7018. PCI ID 1023:2000 or 1023:2001 stands + for Trident 4Dwave. PCI ID 1039:7018 stands for SiS7018. PCI ID + 10B9:5451 stands for ALi5451. + + This driver supports S/PDIF in/out (record/playback) for ALi 5451 + embedded in ALi M1535+ and M1535D+. Note that they aren't all + enabled by default; you can enable them by saying Y to "/proc file + system support" and "Sysctl support", and after the /proc file + system has been mounted, executing the command + + command what is enabled + + echo 0>/proc/ALi5451 pcm out is also set to S/PDIF out. (Default). + + echo 1>/proc/ALi5451 use S/PDIF out to output pcm data. + + echo 2>/proc/ALi5451 use S/PDIF out to output non-pcm data. + (AC3...). + + echo 3>/proc/ALi5451 record from Ac97 in(MIC, Line in...). + (Default). + + echo 4>/proc/ALi5451 no matter Ac97 settings, record from S/PDIF + in. + + + This driver differs slightly from OSS/Free, so PLEASE READ the + comments at the top of . + +CONFIG_SOUND_WAVEARTIST + Say Y here to include support for the Rockwell WaveArtist sound + system. This driver is mainly for the NetWinder. + +CONFIG_SOUND_ALSA_VIA82CXXX + Say Y here to include support for the audio codec found on VIA + 82Cxxx-based chips. Typically these are built into a motherboard. + + DO NOT select Sound Blaster or Adlib with this driver, unless + you have a Sound Blaster or Adlib card in addition to your VIA + audio chip. + +CONFIG_MIDI_VIA82CXXX + Answer Y to use the MIDI interface of the Via686. You may need to + enable this in the BIOS before it will work. This is for connection + to external MIDI hardware, and is not required for software playback + of MIDI files. + +CONFIG_SOUND_ALSA_NM256 + Say M here to include audio support for the NeoMagic 256AV/256ZX + chipsets. These are the audio chipsets found in the Sony + Z505S/SX/DX, some Sony F-series, and the Dell Latitude CPi and CPt + laptops. It includes support for an AC97-compatible mixer and an + apparently proprietary sound engine. + + See for further information. + +CONFIG_SOUND_ALSA_MAESTRO + Say Y or M if you have a sound system driven by ESS's Maestro line + of PCI sound chips. These include the Maestro 1, Maestro 2, and + Maestro 2E. See for more + details. + +CONFIG_SOUND_ALSA_MAESTRO3 + Say Y or M if you have a sound system driven by ESS's Maestro 3 + PCI sound chip. + +CONFIG_SOUND_ALSA_ADLIB + Includes ASB 64 4D. Information on programming AdLib cards is + available at . + +CONFIG_SOUND_ALSA_CS4281 + Picture and feature list at + . + +CONFIG_SOUND_ALSA_GUS16 + Support for Gravis Ulstrasound (GUS) cards (other than the GUS), + sampling at 16-bit width. + +CONFIG_SOUND_ALSA_GUSMAX + Support for Gravis Ulstrasound MAX. + +CONFIG_SOUND_ALSA_ICH + Support for integral audio in Intel's I/O Controller Hub (ICH) + chipset, as used on the 810/820/840 motherboards. + +CONFIG_SOUND_ALSA_TRACEINIT + Verbose soundcard initialization -- affects the format of autoprobe + and initialization messages at boot time. + +CONFIG_SOUND_ALSA_TVMIXER + Support for audio mixer facilities on the BT848 TV frame-grabber + card. + +CONFIG_SOUND_ALSA_VIDC + 16-bit support for the VIDC onboard sound hardware found on Acorn + machines. + +CONFIG_SOUND_ALSA_VMIDI + Support for MIDI loopback on port 1 or 2. + +CONFIG_SOUND_ALSA_YMFPCI + Support for Yamaha cards including the YMF711, YMF715, YMF718, + YMF719, YMF724, Waveforce 192XG, and Waveforce 192 Digital. + +CONFIG_SOUND_ALSA_YMFPCI_LEGACY + Support for YMF7xx PCI cards emulating an MP401. + +CONFIG_SOUND_ALSA_RME96XX + Say Y or M if you have a Hammerfall, Hammerfall light or Hammerfall + DSP card from RME. + +CONFIG_SOUND_ALSA_BT878 + Audio DMA support for bt878 based grabber boards. As you might have + already noticed, bt878 is listed with two functions in /proc/pci. + Function 0 does the video stuff (bt848 compatible), function 1 does + the same for audio data. This is a driver for the audio part of + the chip. If you say 'Y' here you get a oss-compatible dsp device + where you can record from. If you want just watch TV you probably + don't need this driver as most TV cards handle sound with a short + cable from the TV card to your sound card's line-in. + + This driver is available as a module called btaudio.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read . + diff -Nru linux/sound/oss/Config.in linux-2.4.19-pre5-mjc/sound/oss/Config.in --- linux/sound/oss/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/Config.in Mon Apr 8 22:31:24 2002 @@ -0,0 +1,218 @@ +# drivers/sound/Config.in +# +# 18 Apr 1998, Michael Elizabeth Chastain, +# More hacking for modularisation. +# + +# Do not probe for modem codec option. This is a workaround to prevent +# hard IrDA lockups on some laptops (Dell Inspiron). -jprante + +bool 'Do not probe for AC97 Modem Codec (prevent IrDA lockup)' CONFIG_SOUND_ALSA_NO_MODEM_PROBE + +# Prompt user for primary drivers. + +dep_tristate ' BT878 audio dma' CONFIG_SOUND_ALSA_BT878 $CONFIG_SOUND_ALSA +dep_tristate ' C-Media PCI (CMI8338/8738)' CONFIG_SOUND_ALSA_CMPCI $CONFIG_SOUND_ALSA $CONFIG_PCI +if [ "$CONFIG_SOUND_ALSA_CMPCI" = "y" -o "$CONFIG_SOUND_ALSA_CMPCI" = "m" ]; then + bool ' Enable legacy FM' CONFIG_SOUND_ALSA_CMPCI_FM + if [ "$CONFIG_SOUND_ALSA_CMPCI_FM" = "y" ]; then + define_hex CONFIG_SOUND_ALSA_CMPCI_FMIO 388 + hex ' FM I/O 388, 3C8, 3E0, 3E8' CONFIG_SOUND_ALSA_CMPCI_FMIO 388 + fi + bool ' Enable legacy MPU-401' CONFIG_SOUND_ALSA_CMPCI_MIDI + if [ "$CONFIG_SOUND_ALSA_CMPCI_MIDI" = "y" ]; then + hex ' MPU-401 I/O 330, 320, 310, 300' CONFIG_SOUND_ALSA_CMPCI_MPUIO 330 + fi + bool ' Enable joystick' CONFIG_SOUND_ALSA_CMPCI_JOYSTICK + bool ' Support CMI8738 based audio cards' CONFIG_SOUND_ALSA_CMPCI_CM8738 + if [ "$CONFIG_SOUND_ALSA_CMPCI_CM8738" = "y" ]; then + bool ' Inverse S/PDIF in for CMI8738' CONFIG_SOUND_ALSA_CMPCI_SPDIFINVERSE + bool ' Enable S/PDIF loop for CMI8738' CONFIG_SOUND_ALSA_CMPCI_SPDIFLOOP + int ' Number of speakers 2, 4, 5, 6' CONFIG_SOUND_ALSA_CMPCI_SPEAKERS 2 + if [ "$CONFIG_SOUND_ALSA_CMPCI_SPEAKERS" != "2" ]; then + bool ' Use Line-in as Read-out' CONFIG_SOUND_ALSA_CMPCI_LINE_REAR + bool ' Use Line-in as Bass' CONFIG_SOUND_ALSA_CMPCI_LINE_BASS + fi + fi +fi +dep_tristate ' Creative SBLive! (EMU10K1)' CONFIG_SOUND_ALSA_EMU10K1 $CONFIG_SOUND_ALSA $CONFIG_PCI +dep_mbool ' Creative SBLive! MIDI' CONFIG_MIDI_EMU10K1 $CONFIG_SOUND_ALSA_EMU10K1 $CONFIG_EXPERIMENTAL +dep_tristate ' Crystal SoundFusion (CS4280/461x)' CONFIG_SOUND_ALSA_FUSION $CONFIG_SOUND_ALSA +dep_tristate ' Crystal Sound CS4281' CONFIG_SOUND_ALSA_CS4281 $CONFIG_SOUND_ALSA +dep_tristate ' Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ALSA_ES1370 $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_SOUND_ALSA_GAMEPORT +dep_tristate ' Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ALSA_ES1371 $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_SOUND_ALSA_GAMEPORT +dep_tristate ' ESS Technology Solo1' CONFIG_SOUND_ALSA_ESSSOLO1 $CONFIG_SOUND_ALSA $CONFIG_SOUND_ALSA_GAMEPORT +dep_tristate ' ESS Maestro, Maestro2, Maestro2E driver' CONFIG_SOUND_ALSA_MAESTRO $CONFIG_SOUND_ALSA +dep_tristate ' ESS Maestro3/Allegro driver (EXPERIMENTAL)' CONFIG_SOUND_ALSA_MAESTRO3 $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' Intel ICH (i8xx) audio support' CONFIG_SOUND_ALSA_ICH $CONFIG_PCI +dep_tristate ' RME Hammerfall (RME96XX) support' CONFIG_SOUND_ALSA_RME96XX $CONFIG_SOUND_ALSA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' S3 SonicVibes' CONFIG_SOUND_ALSA_SONICVIBES $CONFIG_SOUND_ALSA $CONFIG_SOUND_ALSA_GAMEPORT +if [ "$CONFIG_VISWS" = "y" ]; then + dep_tristate ' SGI Visual Workstation Sound' CONFIG_SOUND_ALSA_VWSND $CONFIG_SOUND_ALSA +fi +if [ "$CONFIG_DDB5477" = "y" ]; then + dep_tristate ' NEC Vrc5477 AC97 sound' CONFIG_SOUND_ALSA_VRC5477 $CONFIG_SOUND_ALSA +fi +dep_tristate ' Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core' CONFIG_SOUND_ALSA_TRIDENT $CONFIG_SOUND_ALSA + +dep_tristate ' Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_ALSA_MSNDCLAS $CONFIG_SOUND_ALSA +if [ "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" -o "$CONFIG_SOUND_ALSA_MSNDCLAS" = "m" ]; then + if [ "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" ]; then + comment ' Compiled-in MSND Classic support requires firmware during compilation.' + define_bool CONFIG_MSNDCLAS_HAVE_BOOT y + else + define_bool CONFIG_MSNDCLAS_HAVE_BOOT n + fi + string 'Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin" + string 'Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin" +fi +if [ "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" ]; then + int ' MSND Classic IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDCLAS_IRQ 5 + hex ' MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDCLAS_MEM D0000 + hex ' MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDCLAS_IO 290 +fi + +dep_tristate ' Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_ALSA_MSNDPIN $CONFIG_SOUND_ALSA +if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" -o "$CONFIG_SOUND_ALSA_MSNDPIN" = "m" ]; then + if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" ]; then + comment 'Compiled-in MSND Pinnacle support requires firmware during compilation.' + define_bool CONFIG_MSNDPIN_HAVE_BOOT y + else + define_bool CONFIG_MSNDPIN_HAVE_BOOT n + fi + string ' Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin" + string ' Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin" +fi +if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" ]; then + int ' MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12' CONFIG_MSNDPIN_IRQ 5 + hex ' MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000' CONFIG_MSNDPIN_MEM D0000 + hex 'MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0' CONFIG_MSNDPIN_IO 290 + bool ' MSND Pinnacle has S/PDIF I/O' CONFIG_MSNDPIN_DIGITAL + bool ' MSND Pinnacle non-PnP Mode' CONFIG_MSNDPIN_NONPNP + if [ "$CONFIG_MSNDPIN_NONPNP" = "y" ]; then + comment 'MSND Pinnacle DSP section will be configured to above parameters.' + hex 'MSND Pinnacle config port 250,260,270' CONFIG_MSNDPIN_CFG 250 + comment 'Pinnacle-specific Device Configuration (0 disables)' + hex 'MSND Pinnacle MPU I/O (e.g. 330)' CONFIG_MSNDPIN_MPU_IO 0 + int 'MSND Pinnacle MPU IRQ (e.g. 9)' CONFIG_MSNDPIN_MPU_IRQ 0 + hex 'MSND Pinnacle IDE I/O 0 (e.g. 170)' CONFIG_MSNDPIN_IDE_IO0 0 + hex 'MSND Pinnacle IDE I/O 1 (e.g. 376)' CONFIG_MSNDPIN_IDE_IO1 0 + int 'MSND Pinnacle IDE IRQ (e.g. 15)' CONFIG_MSNDPIN_IDE_IRQ 0 + hex 'MSND Pinnacle joystick I/O (e.g. 200)' CONFIG_MSNDPIN_JOYSTICK_IO 0 + fi +fi +if [ "$CONFIG_SOUND_ALSA_MSNDPIN" = "y" -o "$CONFIG_SOUND_ALSA_MSNDCLAS" = "y" ]; then + int 'MSND buffer size (kB)' CONFIG_MSND_FIFOSIZE 128 +fi + +dep_tristate ' VIA 82C686 Audio Codec' CONFIG_SOUND_ALSA_VIA82CXXX $CONFIG_PCI +dep_mbool ' VIA 82C686 MIDI' CONFIG_MIDI_VIA82CXXX $CONFIG_SOUND_ALSA_VIA82CXXX + +dep_tristate ' OSS sound modules' CONFIG_SOUND_ALSA_OSS $CONFIG_SOUND_ALSA + +if [ "$CONFIG_SOUND_ALSA_OSS" = "y" -o "$CONFIG_SOUND_ALSA_OSS" = "m" ]; then + bool ' Verbose initialisation' CONFIG_SOUND_ALSA_TRACEINIT + bool ' Persistent DMA buffers' CONFIG_SOUND_ALSA_DMAP + + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_ALSA_AD1816 $CONFIG_SOUND_ALSA_OSS + fi + dep_tristate ' Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_ALSA_SGALAXY $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Adlib Cards' CONFIG_SOUND_ALSA_ADLIB $CONFIG_SOUND_ALSA_OSS + dep_tristate ' ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20)' CONFIG_SOUND_ALSA_ACI_MIXER $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Crystal CS4232 based (PnP) cards' CONFIG_SOUND_ALSA_CS4232 $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Ensoniq SoundScape support' CONFIG_SOUND_ALSA_SSCAPE $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Gravis Ultrasound support' CONFIG_SOUND_ALSA_GUS $CONFIG_SOUND_ALSA_OSS + if [ "$CONFIG_SOUND_ALSA_GUS" != "n" ]; then + bool ' 16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_SOUND_ALSA_GUS16 + bool ' GUS MAX support' CONFIG_SOUND_ALSA_GUSMAX + fi + dep_tristate ' Loopback MIDI device support' CONFIG_SOUND_ALSA_VMIDI $CONFIG_SOUND_ALSA_OSS + dep_tristate ' MediaTrix AudioTrix Pro support' CONFIG_SOUND_ALSA_TRIX $CONFIG_SOUND_ALSA_OSS + if [ "$CONFIG_SOUND_ALSA_TRIX" = "y" ]; then + bool ' Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT + if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then + string ' Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE /etc/sound/trxpro.hex + fi + fi + + dep_tristate ' Microsoft Sound System support' CONFIG_SOUND_ALSA_MSS $CONFIG_SOUND_ALSA_OSS + dep_tristate ' MPU-401 support (NOT for SB16)' CONFIG_SOUND_ALSA_MPU401 $CONFIG_SOUND_ALSA_OSS + dep_tristate ' NM256AV/NM256ZX audio support' CONFIG_SOUND_ALSA_NM256 $CONFIG_SOUND_ALSA_OSS + dep_tristate ' OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_ALSA_MAD16 $CONFIG_SOUND_ALSA_OSS + if [ "$CONFIG_SOUND_ALSA_MAD16" = "y" -o "$CONFIG_SOUND_ALSA_MAD16" = "m" ]; then + bool ' Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD + fi + dep_tristate ' ProAudioSpectrum 16 support' CONFIG_SOUND_ALSA_PAS $CONFIG_SOUND_ALSA_OSS + dep_bool ' Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK $CONFIG_SOUND_ALSA_PAS + + dep_tristate ' PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_ALSA_PSS $CONFIG_SOUND_ALSA_OSS + if [ "$CONFIG_SOUND_ALSA_PSS" = "y" -o "$CONFIG_SOUND_ALSA_PSS" = "m" ]; then + bool ' Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER + bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT + if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then + string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE /etc/sound/dsp001.ld + fi + fi + + dep_tristate ' 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_ALSA_SB $CONFIG_SOUND_ALSA_OSS + dep_tristate ' AWE32 synth' CONFIG_SOUND_ALSA_AWE32_SYNTH $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_ALSA_WAVEFRONT $CONFIG_SOUND_ALSA_OSS m + dep_tristate ' Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_ALSA_MAUI $CONFIG_SOUND_ALSA_OSS + if [ "$CONFIG_SOUND_ALSA_MAUI" = "y" ]; then + bool ' Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT + if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then + string ' Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE /etc/sound/oswf.mot + fi + fi + + dep_tristate ' Yamaha FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_ALSA_YM3812 $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_ALSA_OPL3SA1 $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Yamaha OPL3-SA2 and SA3 based PnP cards' CONFIG_SOUND_ALSA_OPL3SA2 $CONFIG_SOUND_ALSA_OSS + dep_tristate ' Yamaha YMF7xx PCI audio (native mode)' CONFIG_SOUND_ALSA_YMFPCI $CONFIG_SOUND_ALSA_OSS $CONFIG_PCI + dep_mbool ' Yamaha PCI legacy ports support' CONFIG_SOUND_ALSA_YMFPCI_LEGACY $CONFIG_SOUND_ALSA_YMFPCI + dep_tristate ' 6850 UART support' CONFIG_SOUND_ALSA_UART6850 $CONFIG_SOUND_ALSA_OSS + + dep_tristate ' Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_SOUND_ALSA_AEDSP16 $CONFIG_SOUND_ALSA_OSS + if [ "$CONFIG_SOUND_ALSA_AEDSP16" = "y" -o "$CONFIG_SOUND_ALSA_AEDSP16" = "m" ]; then + bool ' SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 + if [ "$CONFIG_SC6600" = "y" ]; then + bool ' Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY + int ' SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4 + hex ' SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0 + fi + if [ "$CONFIG_SOUND_ALSA_SB" = "y" -o "$CONFIG_SOUND_ALSA_SB" = "m" ]; then + if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then + bool ' Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO + fi + fi + if [ "$CONFIG_SOUND_ALSA_MSS" = "y" -o "$CONFIG_SOUND_ALSA_MSS" = "m" ]; then + if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then + bool ' Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS + fi + fi + if [ "$CONFIG_SOUND_ALSA_MPU401" = "y" -o "$CONFIG_SOUND_ALSA_MPU401" = "m" ]; then + bool ' Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401 + fi + fi + + if [ "$CONFIG_ARM" = "y" ]; then + if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" ]; then + dep_tristate ' VIDC 16-bit sound' CONFIG_SOUND_ALSA_VIDC $CONFIG_SOUND_ALSA_OSS + fi + dep_tristate ' Netwinder WaveArtist' CONFIG_SOUND_ALSA_WAVEARTIST $CONFIG_SOUND_ALSA_OSS $CONFIG_ARCH_NETWINDER + fi + +fi + +dep_tristate ' TV card (bt848) mixer support' CONFIG_SOUND_ALSA_TVMIXER $CONFIG_SOUND_ALSA $CONFIG_I2C + +# A cross directory dependence. The sound modules will need gameport.o compiled in, +# but it resides in the drivers/char/joystick directory. This define_tristate takes +# care of that. --Vojtech + +if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then + if [ "$CONFIG_SOUND_ALSA_ESSSOLO1" = "y" -o "$CONFIG_SOUND_ALSA_ES1370" = "y" -o "$CONFIG_SOUND_ALSA_ES1371" = "y" -o "$CONFIG_SOUND_ALSA_SONICVIBES" = "y" ]; then + define_tristate CONFIG_INPUT_GAMEPORT y + fi +fi diff -Nru linux/sound/oss/Hwmcode.h linux-2.4.19-pre5-mjc/sound/oss/Hwmcode.h --- linux/sound/oss/Hwmcode.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/Hwmcode.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,804 @@ +//============================================================================= +// Copyright (c) 1997 Yamaha Corporation. All Rights Reserved. +// +// Title: +// hwmcode.c +// Desc: +// micro-code for CTRL & DSP +// HISTORY: +// April 03, 1997: 1st try by M. Mukojima +//============================================================================= +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + + +static unsigned long int gdwDSPCode[YDSXG_DSPLENGTH >> 2] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09?@creat +// 04/12 stop nise fix +// 06/21?@WorkingOff timming +static unsigned long gdwCtrl1eCode[YDSXG_CTRLLENGTH >> 2] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; diff -Nru linux/sound/oss/Makefile linux-2.4.19-pre5-mjc/sound/oss/Makefile --- linux/sound/oss/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,288 @@ +# Makefile for the Linux sound card driver +# +# 18 Apr 1998, Michael Elizabeth Chastain, +# Rewritten to use lists instead of if-statements. + + +# All of the (potential) objects that export symbols. +# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. + +export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ + msnd.o opl3.o sb_common.o sequencer_syms.o \ + sound_syms.o uart401.o \ + nm256_audio.o ac97.o ac97_codec.o aci.o + +# Each configuration option enables a list of files. + +obj-$(CONFIG_SOUND_ALSA_OSS) += sound.o +obj-$(CONFIG_SOUND_ALSA_CS4232) += cs4232.o ad1848.o + +# Please leave it as is, cause the link order is significant ! + +obj-$(CONFIG_SOUND_ALSA_AEDSP16) += aedsp16.o +obj-$(CONFIG_SOUND_ALSA_PSS) += pss.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_ALSA_TRIX) += trix.o ad1848.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_ALSA_OPL3SA1) += opl3sa.o ad1848.o uart401.o +obj-$(CONFIG_SOUND_ALSA_SSCAPE) += sscape.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_ALSA_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_ALSA_CS4232) += cs4232.o uart401.o +obj-$(CONFIG_SOUND_ALSA_OPL3SA2) += opl3sa2.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_ALSA_MSS) += ad1848.o +obj-$(CONFIG_SOUND_ALSA_PAS) += pas2.o sb.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_ALSA_SB) += sb.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_ALSA_WAVEFRONT) += wavefront.o +obj-$(CONFIG_SOUND_ALSA_MAUI) += maui.o mpu401.o +obj-$(CONFIG_SOUND_ALSA_MPU401) += mpu401.o +obj-$(CONFIG_SOUND_ALSA_UART6850) += uart6850.o +obj-$(CONFIG_SOUND_ALSA_GUS) += gus.o ad1848.o +obj-$(CONFIG_SOUND_ALSA_ADLIB) += adlib_card.o opl3.o +obj-$(CONFIG_SOUND_ALSA_YM3812) += opl3.o +obj-$(CONFIG_SOUND_ALSA_VMIDI) += v_midi.o +obj-$(CONFIG_SOUND_ALSA_VIDC) += vidc_mod.o +obj-$(CONFIG_SOUND_ALSA_WAVEARTIST) += waveartist.o +obj-$(CONFIG_SOUND_ALSA_SGALAXY) += sgalaxy.o ad1848.o +obj-$(CONFIG_SOUND_ALSA_AD1816) += ad1816.o +obj-$(CONFIG_SOUND_ALSA_ACI_MIXER) += aci.o +obj-$(CONFIG_SOUND_ALSA_AWE32_SYNTH) += awe_wave.o + +obj-$(CONFIG_SOUND_ALSA_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o +ifeq ($(CONFIG_MIDI_VIA82CXXX),y) + obj-$(CONFIG_SOUND_ALSA_VIA82CXXX) += sound.o uart401.o +endif +obj-$(CONFIG_SOUND_ALSA_YMFPCI) += ymfpci.o ac97_codec.o +ifeq ($(CONFIG_SOUND_ALSA_YMFPCI_LEGACY),y) + obj-$(CONFIG_SOUND_ALSA_YMFPCI) += opl3.o uart401.o +endif +obj-$(CONFIG_SOUND_ALSA_MSNDCLAS) += msnd.o msnd_classic.o +obj-$(CONFIG_SOUND_ALSA_MSNDPIN) += msnd.o msnd_pinnacle.o +obj-$(CONFIG_SOUND_ALSA_VWSND) += vwsnd.o +obj-$(CONFIG_SOUND_ALSA_NM256) += nm256_audio.o ac97.o +obj-$(CONFIG_SOUND_ALSA_ICH) += i810_audio.o ac97_codec.o +obj-$(CONFIG_SOUND_ALSA_SONICVIBES) += sonicvibes.o +obj-$(CONFIG_SOUND_ALSA_CMPCI) += cmpci.o +obj-$(CONFIG_SOUND_ALSA_ES1370) += es1370.o +obj-$(CONFIG_SOUND_ALSA_ES1371) += es1371.o ac97_codec.o +obj-$(CONFIG_SOUND_ALSA_VRC5477) += nec_vrc5477.o ac97_codec.o +obj-$(CONFIG_SOUND_ALSA_ESSSOLO1) += esssolo1.o +obj-$(CONFIG_SOUND_ALSA_FUSION) += cs46xx.o ac97_codec.o +obj-$(CONFIG_SOUND_ALSA_MAESTRO) += maestro.o +obj-$(CONFIG_SOUND_ALSA_MAESTRO3) += maestro3.o ac97_codec.o +obj-$(CONFIG_SOUND_ALSA_TRIDENT) += trident.o ac97_codec.o +obj-$(CONFIG_SOUND_ALSA_EMU10K1) += ac97_codec.o +obj-$(CONFIG_SOUND_ALSA_RME96XX) += rme96xx.o +obj-$(CONFIG_SOUND_ALSA_BT878) += btaudio.o +obj-$(CONFIG_SOUND_ALSA_EMU10K1) += ac97_codec.o + +ifeq ($(CONFIG_MIDI_EMU10K1),y) + obj-$(CONFIG_SOUND_ALSA_EMU10K1) += sound.o +endif + +subdir-$(CONFIG_SOUND_ALSA_EMU10K1) += emu10k1 +subdir-$(CONFIG_SOUND_ALSA_CS4281) += cs4281 + +ifeq ($(CONFIG_SOUND_ALSA_EMU10K1),y) + obj-y += emu10k1/emu10k1.o +endif + +ifeq ($(CONFIG_SOUND_ALSA_CS4281),y) + obj-y += cs4281/cs4281.o +endif + +subdir-$(CONFIG_DMASOUND) += dmasound + +ifeq ($(CONFIG_DMASOUND),y) + obj-y += dmasound/dmasound.o +endif + + +# Declare multi-part drivers. + +list-multi := sound.o gus.o pas2.o sb.o sb_lib.o vidc_mod.o \ + wavefront.o + +sound-objs := \ + dev_table.o soundcard.o sound_syms.o \ + audio.o audio_syms.o dmabuf.o \ + midi_syms.o midi_synth.o midibuf.o \ + sequencer.o sequencer_syms.o sound_timer.o sys_timer.o + +gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o +pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o +sb-objs := sb_card.o +sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o +vidc_mod-objs := vidc.o vidc_fill.o +wavefront-objs := wavfront.o wf_midi.o yss225.o + + +O_TARGET := sounddrivers.o + +include $(TOPDIR)/Rules.make + + + +# Link rules for multi-part drivers. + +sound.o: $(sound-objs) + $(LD) -r -o $@ $(sound-objs) + +gus.o: $(gus-objs) + $(LD) -r -o $@ $(gus-objs) + +pas2.o: $(pas2-objs) + $(LD) -r -o $@ $(pas2-objs) + +sb.o: $(sb-objs) + $(LD) -r -o $@ $(sb-objs) + +sb_lib.o: $(sb_lib-objs) + $(LD) -r -o $@ $(sb_lib-objs) + +vidc_mod.o: $(vidc_mod-objs) + $(LD) -r -o $@ $(vidc_mod-objs) + +wavefront.o: $(wavefront-objs) + $(LD) -r -o $@ $(wavefront-objs) + +# Firmware files that need translation +# +# The translated files are protected by a file that keeps track +# of what name was used to build them. If the name changes, they +# will be forced to be remade. +# +# First make the utilities. + +bin2hex: bin2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o bin2hex bin2hex.c + +hex2hex: hex2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o hex2hex hex2hex.c + + + + +# Turtle Beach Maui / Tropez + +maui.o: maui_boot.h + +ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) + maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) bin2hex + ./bin2hex -i maui_os < $(CONFIG_MAUI_BOOT_FILE) > $@ +else + maui_boot.h: + ( \ + echo 'static unsigned char * maui_os = NULL;'; \ + echo 'static int maui_osLen = 0;'; \ + ) > $@ +endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MAUI_HAVE_BOOT) $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_HAVE_BOOT) $$(CONFIG_MAUI_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + + +# Turtle Beach MultiSound + +ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y) + msnd_classic.o: msndperm.c msndinit.c + + msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) bin2hex + ./bin2hex msndperm < $(CONFIG_MSNDCLAS_PERM_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_PERM_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_PERM_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) bin2hex + ./bin2hex msndinit < $(CONFIG_MSNDCLAS_INIT_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_INIT_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_INIT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot +endif + +ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y) + msnd_pinnacle.o: pndsperm.c pndspini.c + + pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) bin2hex + ./bin2hex pndsperm < $(CONFIG_MSNDPIN_PERM_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_PERM_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_PERM_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) bin2hex + ./bin2hex pndspini < $(CONFIG_MSNDPIN_INIT_FILE) > $@ + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_INIT_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_INIT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot +endif + + + +# PSS (ECHO-ADI2111) + +pss.o: pss_boot.h + +ifeq ($(CONFIG_PSS_HAVE_BOOT),y) + pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) bin2hex + ./bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@ +else + pss_boot.h: + ( \ + echo 'static unsigned char * pss_synth = NULL;'; \ + echo 'static int pss_synthLen = 0;'; \ + ) > $@ +endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_PSS_HAVE_BOOT) $(CONFIG_PSS_BOOT_FILE)),$$(strip $$(CONFIG_PSS_HAVE_BOOT) $$(CONFIG_PSS_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + + +# MediaTrix AudioTrix Pro + +trix.o: trix_boot.h + +ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) + trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) hex2hex + ./hex2hex -i trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@ +else + trix_boot.h: + ( \ + echo 'static unsigned char * trix_boot = NULL;'; \ + echo 'static int trix_boot_len = 0;'; \ + ) > $@ +endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_TRIX_HAVE_BOOT) $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_HAVE_BOOT) $$(CONFIG_TRIX_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot + + + +# Find boot files whose source file names have changed and force rebuild. + +FILES_BOOT_UP_TO_DATE := + +FILES_BOOT_EXIST := $(wildcard .*.boot) +ifneq ($(FILES_BOOT_EXIST),) +include $(FILES_BOOT_EXIST) +endif + +FILES_BOOT_CHANGED := $(strip \ + $(filter-out $(FILES_BOOT_UP_TO_DATE), \ + maui_boot.h pss_boot.h trix_boot.h)) + +ifneq ($(FILES_BOOT_CHANGED),) +$(FILES_BOOT_CHANGED): dummy +endif diff -Nru linux/sound/oss/README.FIRST linux-2.4.19-pre5-mjc/sound/oss/README.FIRST --- linux/sound/oss/README.FIRST Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/README.FIRST Mon Apr 8 22:31:22 2002 @@ -0,0 +1,6 @@ +The modular sound driver patches were funded by Red Hat Software +(www.redhat.com). The sound driver here is thus a modified version of +Hannu's code. Please bear that in mind when considering the appropriate +forums for bug reporting. + +Alan Cox diff -Nru linux/sound/oss/ac97.c linux-2.4.19-pre5-mjc/sound/oss/ac97.c --- linux/sound/oss/ac97.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ac97.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,455 @@ +#include +#include +#include +#include "ac97.h" + +/* Flag for mono controls. */ +#define MO 0 +/* And for stereo. */ +#define ST 1 + +/* Whether or not the bits in the channel are inverted. */ +#define INV 1 +#define NINV 0 + +static struct ac97_chn_desc { + int ac97_regnum; + int oss_channel; + int maxval; + int is_stereo; + int oss_mask; + int recordNum; + u16 regmask; + int is_inverted; +} mixerRegs[] = { + { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV }, + { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV }, + { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV }, + { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV }, + { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV }, + { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV }, + { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV }, + { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV }, + { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV }, + { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV }, + { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV }, + { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV }, + { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV }, + { -1, -1, 0xff, 0, 0, -1, 0x0000, 0 }, +}; + +static struct ac97_chn_desc * +ac97_find_chndesc (struct ac97_hwint *dev, int oss_channel) +{ + int x; + + for (x = 0; mixerRegs[x].oss_channel != -1; x++) { + if (mixerRegs[x].oss_channel == oss_channel) + return mixerRegs + x; + } + + return NULL; +} + +static inline int +ac97_is_valid_channel (struct ac97_hwint *dev, struct ac97_chn_desc *chn) +{ + return (dev->last_written_mixer_values[chn->ac97_regnum / 2] + != AC97_REG_UNSUPPORTED); +} + +int +ac97_init (struct ac97_hwint *dev) +{ + int x; + int reg0; + + /* Clear out the arrays of cached values. */ + for (x = 0; x < AC97_REG_CNT; x++) + dev->last_written_mixer_values[x] = AC97_REGVAL_UNKNOWN; + + for (x = 0; x < SOUND_MIXER_NRDEVICES; x++) + dev->last_written_OSS_values[x] = AC97_REGVAL_UNKNOWN; + + /* Clear the device masks. */ + dev->mixer_devmask = 0; + dev->mixer_stereomask = 0; + dev->mixer_recmask = 0; + + /* ??? Do a "standard reset" via register 0? */ + + /* Hardware-dependent reset. */ + if (dev->reset_device (dev)) + return -1; + + /* Check the mixer device capabilities. */ + reg0 = dev->read_reg (dev, AC97_RESET); + + if (reg0 < 0) + return -1; + + /* Check for support for treble/bass controls. */ + if (! (reg0 & 4)) { + dev->last_written_mixer_values[AC97_MASTER_TONE / 2] + = AC97_REG_UNSUPPORTED; + } + + /* ??? There may be other tests here? */ + + /* Fill in the device masks. */ + for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { + if (ac97_is_valid_channel (dev, mixerRegs + x)) { + dev->mixer_devmask |= mixerRegs[x].oss_mask; + + if (mixerRegs[x].is_stereo) + dev->mixer_stereomask |= mixerRegs[x].oss_mask; + + if (mixerRegs[x].recordNum != -1) + dev->mixer_recmask |= mixerRegs[x].oss_mask; + } + } + + return 0; +} + +/* Reset the mixer to the currently saved settings. */ +int +ac97_reset (struct ac97_hwint *dev) +{ + int x; + + if (dev->reset_device (dev)) + return -1; + + /* Now set the registers back to their last-written values. */ + for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { + int regnum = mixerRegs[x].ac97_regnum; + int value = dev->last_written_mixer_values [regnum / 2]; + if (value >= 0) + ac97_put_register (dev, regnum, value); + } + return 0; +} + +/* Return the contents of register REG; use the cache if the value in it + is valid. Returns a negative error code on failure. */ +int +ac97_get_register (struct ac97_hwint *dev, u8 reg) +{ + if (reg > 127 || (reg & 1)) + return -EINVAL; + + /* See if it's in the cache, or if it's just plain invalid. */ + switch (dev->last_written_mixer_values[reg / 2]) { + case AC97_REG_UNSUPPORTED: + return -EINVAL; + break; + case AC97_REGVAL_UNKNOWN: + dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg); + break; + default: + break; + } + return dev->last_written_mixer_values[reg / 2]; +} + +/* Write VALUE to AC97 register REG, and cache its value in the last-written + cache. Returns a negative error code on failure, or 0 on success. */ +int +ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value) +{ + if (reg > 127 || (reg & 1)) + return -EINVAL; + + if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED) + return -EINVAL; + else { + int res = dev->write_reg (dev, reg, value); + if (res >= 0) { + dev->last_written_mixer_values[reg / 2] = value; + return 0; + } + else + return res; + } +} + +/* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100. If + IS_STEREO is set, VALUE is a stereo value; the left channel value + is in the lower 8 bits, and the right channel value is in the upper + 8 bits. + + A negative error code is returned on failure, or the unsigned + scaled value on success. */ + +static int +ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv) +{ + /* Muted? */ + if (value & AC97_MUTE) + return 0; + + if (is_stereo) + return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8) + | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); + else { + int i; + + /* Inverted. */ + if (inv) + value = maxval - value; + + i = (value * 100 + (maxval / 2)) / maxval; + if (i > 100) + i = 100; + if (i < 0) + i = 0; + return i; + } +} + +static int +ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv) +{ + if (is_stereo) + return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8) + | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); + else { + int i = ((value & 255) * maxval + 50) / 100; + if (inv) + i = maxval - i; + if (i < 0) + i = 0; + if (i > maxval) + i = maxval; + return i; + } +} + +int +ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value) +{ + int scaled_value; + struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); + int result; + + if (channel == NULL) + return -ENODEV; + if (! ac97_is_valid_channel (dev, channel)) + return -ENODEV; + scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval, + channel->is_stereo, + channel->is_inverted); + if (scaled_value < 0) + return scaled_value; + + if (channel->regmask != 0) { + int mv; + + int oldval = ac97_get_register (dev, channel->ac97_regnum); + if (oldval < 0) + return oldval; + + for (mv = channel->regmask; ! (mv & 1); mv >>= 1) + scaled_value <<= 1; + + scaled_value &= channel->regmask; + scaled_value |= (oldval & ~channel->regmask); + } + result = ac97_put_register (dev, channel->ac97_regnum, scaled_value); + if (result == 0) + dev->last_written_OSS_values[oss_channel] = oss_value; + return result; +} + +int +ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel) +{ + struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); + int regval; + + if (channel == NULL) + return -ENODEV; + + if (! ac97_is_valid_channel (dev, channel)) + return -ENODEV; + + regval = ac97_get_register (dev, channel->ac97_regnum); + + if (regval < 0) + return regval; + + if (channel->regmask != 0) { + int mv; + + regval &= channel->regmask; + + for (mv = channel->regmask; ! (mv & 1); mv >>= 1) + regval >>= 1; + } + return ac97_scale_to_oss_val (regval, channel->maxval, + channel->is_stereo, + channel->is_inverted); +} + +int +ac97_get_recmask (struct ac97_hwint *dev) +{ + int recReg = ac97_get_register (dev, AC97_RECORD_SELECT); + + if (recReg < 0) + return recReg; + else { + int x; + for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { + if (mixerRegs[x].recordNum == (recReg & 7)) + return mixerRegs[x].oss_mask; + } + return -ENODEV; + } +} + +int +ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask) +{ + int x; + + if (oss_recmask == 0) + oss_recmask = SOUND_MIXER_MIC; + + for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { + if ((mixerRegs[x].recordNum >= 0) + && (oss_recmask & mixerRegs[x].oss_mask)) + break; + } + if (mixerRegs[x].ac97_regnum < 0) + return -ENODEV; + else { + int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum; + int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval); + if (res == 0) + return ac97_get_recmask (dev); + else + return res; + } +} + +/* Set the mixer DEV to the list of values in VALUE_LIST. Return 0 on + success, or a negative error code. */ +int +ac97_set_values (struct ac97_hwint *dev, + struct ac97_mixer_value_list *value_list) +{ + int x; + + for (x = 0; value_list[x].oss_channel != -1; x++) { + int chnum = value_list[x].oss_channel; + struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum); + if (chent != NULL) { + u16 val; + int res; + + if (chent->is_stereo) + val = (value_list[x].value.stereo.right << 8) + | value_list[x].value.stereo.left; + else { + /* We do this so the returned value looks OK in the + mixer app. It's not necessary otherwise. */ + val = (value_list[x].value.mono << 8) + | value_list[x].value.mono; + } + res = ac97_set_mixer (dev, chnum, val); + if (res < 0) + return res; + } + else + return -ENODEV; + } + return 0; +} + +int +ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, caddr_t arg) +{ + int ret; + + switch (cmd) { + case SOUND_MIXER_READ_RECSRC: + ret = ac97_get_recmask (dev); + break; + + case SOUND_MIXER_WRITE_RECSRC: + { + if (get_user (ret, (int *) arg)) + ret = -EFAULT; + else + ret = ac97_set_recmask (dev, ret); + } + break; + + case SOUND_MIXER_READ_CAPS: + ret = SOUND_CAP_EXCL_INPUT; + break; + + case SOUND_MIXER_READ_DEVMASK: + ret = dev->mixer_devmask; + break; + + case SOUND_MIXER_READ_RECMASK: + ret = dev->mixer_recmask; + break; + + case SOUND_MIXER_READ_STEREODEVS: + ret = dev->mixer_stereomask; + break; + + default: + /* Read or write request. */ + ret = -EINVAL; + if (_IOC_TYPE (cmd) == 'M') { + int dir = _SIOC_DIR (cmd); + int channel = _IOC_NR (cmd); + + if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) { + ret = 0; + if (dir & _SIOC_WRITE) { + int val; + if (get_user (val, (int *) arg) == 0) + ret = ac97_set_mixer (dev, channel, val); + else + ret = -EFAULT; + } + if (ret >= 0 && (dir & _SIOC_READ)) { + if (dev->last_written_OSS_values[channel] + == AC97_REGVAL_UNKNOWN) + dev->last_written_OSS_values[channel] + = ac97_get_mixer_scaled (dev, channel); + ret = dev->last_written_OSS_values[channel]; + } + } + } + break; + } + + if (ret < 0) + return ret; + else + return put_user(ret, (int *) arg); +} + +EXPORT_SYMBOL(ac97_init); +EXPORT_SYMBOL(ac97_set_values); +EXPORT_SYMBOL(ac97_set_mixer); +EXPORT_SYMBOL(ac97_get_register); +EXPORT_SYMBOL(ac97_put_register); +EXPORT_SYMBOL(ac97_get_mixer_scaled); +EXPORT_SYMBOL(ac97_mixer_ioctl); +EXPORT_SYMBOL(ac97_reset); +MODULE_LICENSE("GPL"); + + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru linux/sound/oss/ac97.h linux-2.4.19-pre5-mjc/sound/oss/ac97.h --- linux/sound/oss/ac97.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ac97.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,220 @@ +/* + * ac97.h + * + * definitions for the AC97, Intel's Audio Codec 97 Spec + * also includes support for a generic AC97 interface + */ + +#ifndef _AC97_H_ +#define _AC97_H_ +#include "sound_config.h" +#include "sound_calls.h" + +#define AC97_RESET 0x0000 // +#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out +#define AC97_HEADPHONE_VOL 0x0004 // +#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output +#define AC97_MASTER_TONE 0x0008 // +#define AC97_PCBEEP_VOL 0x000a // none +#define AC97_PHONE_VOL 0x000c // TAD Input (mono) +#define AC97_MIC_VOL 0x000e // MIC Input (mono) +#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) +#define AC97_CD_VOL 0x0012 // CD Input (stereo) +#define AC97_VIDEO_VOL 0x0014 // none +#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) +#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) +#define AC97_RECORD_SELECT 0x001a // +#define AC97_RECORD_GAIN 0x001c +#define AC97_RECORD_GAIN_MIC 0x001e +#define AC97_GENERAL_PURPOSE 0x0020 +#define AC97_3D_CONTROL 0x0022 +#define AC97_MODEM_RATE 0x0024 +#define AC97_POWER_CONTROL 0x0026 + +/* registers 0x0028 - 0x0058 are reserved */ + +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved */ +/* range 0x3c-0x58 - MODEM */ + +/* registers 0x005a - 0x007a are vendor reserved */ + +#define AC97_VENDOR_ID1 0x007c +#define AC97_VENDOR_ID2 0x007e + +/* volume control bit defines */ + +#define AC97_MUTE 0x8000 +#define AC97_MICBOOST 0x0040 +#define AC97_LEFTVOL 0x3f00 +#define AC97_RIGHTVOL 0x003f + +/* record mux defines */ + +#define AC97_RECMUX_MIC 0x0000 +#define AC97_RECMUX_CD 0x0101 +#define AC97_RECMUX_VIDEO 0x0202 /* not used */ +#define AC97_RECMUX_AUX 0x0303 +#define AC97_RECMUX_LINE 0x0404 +#define AC97_RECMUX_STEREO_MIX 0x0505 +#define AC97_RECMUX_MONO_MIX 0x0606 +#define AC97_RECMUX_PHONE 0x0707 + + +/* general purpose register bit defines */ + +#define AC97_GP_LPBK 0x0080 /* Loopback mode */ +#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ +#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ +#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ +#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ +#define AC97_GP_LD 0x1000 /* Loudness 1=on */ +#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ +#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ +#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ + + +/* powerdown control and status bit defines */ + +/* status */ +#define AC97_PWR_MDM 0x0010 /* Modem section ready */ +#define AC97_PWR_REF 0x0008 /* Vref nominal */ +#define AC97_PWR_ANL 0x0004 /* Analog section ready */ +#define AC97_PWR_DAC 0x0002 /* DAC section ready */ +#define AC97_PWR_ADC 0x0001 /* ADC section ready */ + +/* control */ +#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ +#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ +#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ +#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ +#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ +#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ +#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ +#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ + +/* useful power states */ +#define AC97_PWR_D0 0x0000 /* everything on */ +#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 +#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ + +/* Total number of defined registers. */ +#define AC97_REG_CNT 64 + +/* Generic AC97 mixer interface. */ + +/* Structure describing access to the hardware. */ +struct ac97_hwint +{ + /* Perform any hardware-specific reset and initialization. Returns + 0 on success, or a negative error code. */ + int (*reset_device) (struct ac97_hwint *dev); + + /* Returns the contents of the specified register REG. The caller + should check to see if the desired contents are available in + the cache first, if applicable. Returns a positive unsigned value + representing the contents of the register, or a negative error + code. */ + int (*read_reg) (struct ac97_hwint *dev, u8 reg); + + /* Writes VALUE to register REG. Returns 0 on success, or a + negative error code. */ + int (*write_reg) (struct ac97_hwint *dev, u8 reg, u16 value); + + /* Hardware-specific information. */ + void *driver_private; + + /* Three OSS masks. */ + int mixer_devmask; + int mixer_stereomask; + int mixer_recmask; + + /* The mixer cache. The indices correspond to the AC97 hardware register + number / 2, since the register numbers are always an even number. + + Unknown values are set to -1; unsupported registers contain a + -2. */ + int last_written_mixer_values[AC97_REG_CNT]; + + /* A cache of values written via OSS; we need these so we can return + the values originally written by the user. + + Why the original user values? Because the real-world hardware + has less precision, and some existing applications assume that + they will get back the exact value that they wrote (aumix). + + A -1 value indicates that no value has been written to this mixer + channel via OSS. */ + int last_written_OSS_values[SOUND_MIXER_NRDEVICES]; +}; + +/* Values stored in the register cache. */ +#define AC97_REGVAL_UNKNOWN -1 +#define AC97_REG_UNSUPPORTED -2 + +struct ac97_mixer_value_list +{ + /* Mixer channel to set. List is terminated by a value of -1. */ + int oss_channel; + /* The scaled value to set it to; values generally range from 0-100. */ + union { + struct { + u8 left, right; + } stereo; + u8 mono; + } value; +}; + +/* Initialize the ac97 mixer by resetting it. */ +extern int ac97_init (struct ac97_hwint *dev); + +/* Sets the mixer DEV to the values in VALUE_LIST. Returns 0 on success, + or a negative error code. */ +extern int ac97_set_values (struct ac97_hwint *dev, + struct ac97_mixer_value_list *value_list); + +/* Sets one mixer channel OSS_CHANNEL to the scaled value OSS_VALUE. + Returns the resulting (rescaled) value, or a negative value + representing an error code. + + Stereo channels have two values in OSS_VALUE (the left value is in the + lower 8 bits, the right value is in the upper 8 bits). */ +extern int ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, + u16 oss_value); + +/* Return the contents of the specified AC97 register REG; it uses the + last-written value if it is available. */ +extern int ac97_get_register (struct ac97_hwint *dev, u8 reg); + +/* Writes the specified VALUE to the AC97 register REG in the mixer. + Takes care of setting the last-written cache as well. */ +extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value); + +/* Returns the last OSS value written to the OSS_CHANNEL mixer channel. */ +extern int ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel); + +/* Default ioctl. */ +extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, + caddr_t arg); + +/* Do a complete reset on the AC97 mixer, restoring all mixer registers to + the current values. Normally used after an APM resume event. */ +extern int ac97_reset (struct ac97_hwint *dev); +#endif + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru linux/sound/oss/ac97_codec.c linux-2.4.19-pre5-mjc/sound/oss/ac97_codec.c --- linux/sound/oss/ac97_codec.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ac97_codec.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1058 @@ + +/* + * ac97_codec.c: Generic AC97 mixer/modem module + * + * Derived from ac97 mixer in maestro and trident driver. + * + * Copyright 2000 Silicon Integrated System Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + ************************************************************************** + * + * The Intel Audio Codec '97 specification is available at the Intel + * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/ + * + * The specification itself is currently available at: + * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf + * + ************************************************************************** + * + * History + * v0.4a Mar 12 2002 Jörg Prante + * minor fix: do not probe for modem codec + * v0.4 Mar 15 2000 Ollie Lho + * dual codecs support verified with 4 channels output + * v0.3 Feb 22 2000 Ollie Lho + * bug fix for record mask setting + * v0.2 Feb 10 2000 Ollie Lho + * add ac97_read_proc for /proc/driver/{vendor}/ac97 + * v0.1 Jan 14 2000 Ollie Lho + * Isolated from trident.c to support multiple ac97 codec + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel); +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right); +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ); +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask); +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg); + +static int ac97_init_mixer(struct ac97_codec *codec); + +static int wolfson_init(struct ac97_codec * codec); +static int tritech_init(struct ac97_codec * codec); +static int tritech_maestro_init(struct ac97_codec * codec); +static int sigmatel_9708_init(struct ac97_codec *codec); +static int sigmatel_9721_init(struct ac97_codec *codec); +static int sigmatel_9744_init(struct ac97_codec *codec); +static int eapd_control(struct ac97_codec *codec, int); +static int crystal_digital_control(struct ac97_codec *codec, int mode); + + +/* + * AC97 operations. + * + * If you are adding a codec then you should be able to use + * eapd_ops - any codec that supports EAPD amp control (most) + * null_ops - any ancient codec that supports nothing + * + * The three functions are + * init - used for non AC97 standard initialisation + * amplifier - used to do amplifier control (1=on 0=off) + * digital - switch to digital modes (0 = analog) + * + * Not all codecs support all features, not all drivers use all the + * operations yet + */ + +static struct ac97_ops null_ops = { NULL, NULL, NULL }; +static struct ac97_ops default_ops = { NULL, eapd_control, NULL }; +static struct ac97_ops wolfson_ops = { wolfson_init, NULL, NULL }; +static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL }; +static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL }; +static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL }; +static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL }; +static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL }; +static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control }; + +/* sorted by vendor/device id */ +static const struct { + u32 id; + char *name; + struct ac97_ops *ops; +} ac97_codec_ids[] = { + {0x41445303, "Analog Devices AD1819", &null_ops}, + {0x41445340, "Analog Devices AD1881", &null_ops}, + {0x41445348, "Analog Devices AD1881A", &null_ops}, + {0x41445360, "Analog Devices AD1885", &default_ops}, + {0x41445460, "Analog Devices AD1885", &default_ops}, + {0x414B4D00, "Asahi Kasei AK4540", &null_ops}, + {0x414B4D01, "Asahi Kasei AK4542", &null_ops}, + {0x414B4D02, "Asahi Kasei AK4543", &null_ops}, + {0x414C4710, "ALC200/200P", &null_ops}, + {0x43525900, "Cirrus Logic CS4297", &default_ops}, + {0x43525903, "Cirrus Logic CS4297", &default_ops}, + {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops}, + {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops}, + {0x43525923, "Cirrus Logic CS4298", &null_ops}, + {0x4352592B, "Cirrus Logic CS4294", &null_ops}, + {0x4352592D, "Cirrus Logic CS4294", &null_ops}, + {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops}, + {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops}, + {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops}, + {0x45838308, "ESS Allegro ES1988", &null_ops}, + {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */ + {0x4e534331, "National Semiconductor LM4549", &null_ops}, + {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, + {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, + {0x545200FF, "TriTech TR?????", &tritech_m_ops}, + {0x54524102, "TriTech TR28022", &null_ops}, + {0x54524103, "TriTech TR28023", &null_ops}, + {0x54524106, "TriTech TR28026", &null_ops}, + {0x54524108, "TriTech TR28028", &tritech_ops}, + {0x54524123, "TriTech TR A5", &null_ops}, + {0x574D4C00, "Wolfson WM9704", &wolfson_ops}, + {0x574D4C03, "Wolfson WM9703/9704", &wolfson_ops}, + {0x574D4C04, "Wolfson WM9704 (quad)", &wolfson_ops}, + {0x83847600, "SigmaTel STAC????", &null_ops}, + {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops}, + {0x83847605, "SigmaTel STAC9704", &null_ops}, + {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops}, + {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops}, + {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops}, + {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops}, + {0x83847684, "SigmaTel STAC9783/84?", &null_ops}, + {0x57454301, "Winbond 83971D", &null_ops}, +}; + +static const char *ac97_stereo_enhancements[] = +{ + /* 0 */ "No 3D Stereo Enhancement", + /* 1 */ "Analog Devices Phat Stereo", + /* 2 */ "Creative Stereo Enhancement", + /* 3 */ "National Semi 3D Stereo Enhancement", + /* 4 */ "YAMAHA Ymersion", + /* 5 */ "BBE 3D Stereo Enhancement", + /* 6 */ "Crystal Semi 3D Stereo Enhancement", + /* 7 */ "Qsound QXpander", + /* 8 */ "Spatializer 3D Stereo Enhancement", + /* 9 */ "SRS 3D Stereo Enhancement", + /* 10 */ "Platform Tech 3D Stereo Enhancement", + /* 11 */ "AKM 3D Audio", + /* 12 */ "Aureal Stereo Enhancement", + /* 13 */ "Aztech 3D Enhancement", + /* 14 */ "Binaura 3D Audio Enhancement", + /* 15 */ "ESS Technology Stereo Enhancement", + /* 16 */ "Harman International VMAx", + /* 17 */ "Nvidea 3D Stereo Enhancement", + /* 18 */ "Philips Incredible Sound", + /* 19 */ "Texas Instruments 3D Stereo Enhancement", + /* 20 */ "VLSI Technology 3D Stereo Enhancement", + /* 21 */ "TriTech 3D Stereo Enhancement", + /* 22 */ "Realtek 3D Stereo Enhancement", + /* 23 */ "Samsung 3D Stereo Enhancement", + /* 24 */ "Wolfson Microelectronics 3D Enhancement", + /* 25 */ "Delta Integration 3D Enhancement", + /* 26 */ "SigmaTel 3D Enhancement", + /* 27 */ "Winbond 3D Stereo Enhancement", + /* 28 */ "Rockwell 3D Stereo Enhancement", + /* 29 */ "Reserved 29", + /* 30 */ "Reserved 30", + /* 31 */ "Reserved 31" +}; + +/* this table has default mixer values for all OSS mixers. */ +static struct mixer_defaults { + int mixer; + unsigned int value; +} mixer_defaults[SOUND_MIXER_NRDEVICES] = { + /* all values 0 -> 100 in bytes */ + {SOUND_MIXER_VOLUME, 0x4343}, + {SOUND_MIXER_BASS, 0x4343}, + {SOUND_MIXER_TREBLE, 0x4343}, + {SOUND_MIXER_PCM, 0x4343}, + {SOUND_MIXER_SPEAKER, 0x4343}, + {SOUND_MIXER_LINE, 0x4343}, + {SOUND_MIXER_MIC, 0x0000}, + {SOUND_MIXER_CD, 0x4343}, + {SOUND_MIXER_ALTPCM, 0x4343}, + {SOUND_MIXER_IGAIN, 0x4343}, + {SOUND_MIXER_LINE1, 0x4343}, + {SOUND_MIXER_PHONEIN, 0x4343}, + {SOUND_MIXER_PHONEOUT, 0x4343}, + {SOUND_MIXER_VIDEO, 0x4343}, + {-1,0} +}; + +/* table to scale scale from OSS mixer value to AC97 mixer register value */ +static struct ac97_mixer_hw { + unsigned char offset; + int scale; +} ac97_hw[SOUND_MIXER_NRDEVICES]= { + [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64}, + [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16}, + [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16}, + [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32}, + [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16}, + [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32}, + [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32}, + [SOUND_MIXER_CD] = {AC97_CD_VOL, 32}, + [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64}, + [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16}, + [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32}, + [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32}, + [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64}, + [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32}, +}; + +/* the following tables allow us to go from OSS <-> ac97 quickly. */ +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static const unsigned int ac97_rm2oss[] = { + [AC97_REC_MIC] = SOUND_MIXER_MIC, + [AC97_REC_CD] = SOUND_MIXER_CD, + [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, + [AC97_REC_AUX] = SOUND_MIXER_LINE1, + [AC97_REC_LINE] = SOUND_MIXER_LINE, + [AC97_REC_STEREO]= SOUND_MIXER_IGAIN, + [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN +}; + +/* indexed by bit position */ +static const unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_IGAIN] = AC97_REC_STEREO, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows + about that given mixer, and should be holding a spinlock for the card */ +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) +{ + u16 val; + int ret = 0; + int scale; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + + val = codec->codec_read(codec , mh->offset); + + if (val & AC97_MUTE) { + ret = 0; + } else if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* nice stereo mixers .. */ + int left,right; + + left = (val >> 8) & 0x7f; + right = val & 0x7f; + + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + } else { + /* these may have 5 or 6 bit resolution */ + if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM) + scale = (1 << codec->bit_resolution); + else + scale = mh->scale; + + right = 100 - ((right * 100) / scale); + left = 100 - ((left * 100) / scale); + } + ret = left | (right << 8); + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_PHONEIN) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_PHONEOUT) { + scale = (1 << codec->bit_resolution); + ret = 100 - (((val & 0x1f) * 100) / scale); + } else if (oss_channel == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } else if (oss_channel == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + +#ifdef DEBUG + printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), " + "0x%04x -> 0x%04x\n", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, val, ret); +#endif + + return ret; +} + +/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to + make sure all is well in arg land, call with spinlock held */ +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right) +{ + u16 val = 0; + int scale; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + +#ifdef DEBUG + printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), " + "left vol:%2d, right vol:%2d:", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, left, right); +#endif + + if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* stereo mixers */ + if (left == 0 && right == 0) { + val = AC97_MUTE; + } else { + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * mh->scale) / 100; + left = (left * mh->scale) / 100; + if (right >= mh->scale) + right = mh->scale-1; + if (left >= mh->scale) + left = mh->scale-1; + } else { + /* these may have 5 or 6 bit resolution */ + if (oss_channel == SOUND_MIXER_VOLUME || + oss_channel == SOUND_MIXER_ALTPCM) + scale = (1 << codec->bit_resolution); + else + scale = mh->scale; + + right = ((100 - right) * scale) / 100; + left = ((100 - left) * scale) / 100; + if (right >= scale) + right = scale-1; + if (left >= scale) + left = scale-1; + } + val = (left << 8) | right; + } + } else if (oss_channel == SOUND_MIXER_BASS) { + val = codec->codec_read(codec , mh->offset) & ~0x0f00; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= (left << 8) & 0x0e00; + } else if (oss_channel == SOUND_MIXER_TREBLE) { + val = codec->codec_read(codec , mh->offset) & ~0x000f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left & 0x000e; + } else if(left == 0) { + val = AC97_MUTE; + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left << 1; + } else if (oss_channel == SOUND_MIXER_PHONEIN) { + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left; + } else if (oss_channel == SOUND_MIXER_PHONEOUT) { + scale = (1 << codec->bit_resolution); + left = ((100 - left) * scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val = left; + } else if (oss_channel == SOUND_MIXER_MIC) { + val = codec->codec_read(codec , mh->offset) & ~0x801f; + left = ((100 - left) * mh->scale) / 100; + if (left >= mh->scale) + left = mh->scale-1; + val |= left; + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } +#ifdef DEBUG + printk(" 0x%04x", val); +#endif + + codec->codec_write(codec, mh->offset, val); + +#ifdef DEBUG + val = codec->codec_read(codec, mh->offset); + printk(" -> 0x%04x\n", val); +#endif +} + +/* a thin wrapper for write_mixer */ +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) +{ + unsigned int left,right; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if (right > 100) right = 100; + if (left > 100) left = 100; + + codec->mixer_state[oss_mixer] = (right << 8) | left; + codec->write_mixer(codec, oss_mixer, left, right); +} + +/* read or write the recmask, the ac97 can really have left and right recording + inputs independantly set, but OSS doesn't seem to want us to express that to + the user. the caller guarantees that we have a supported bit set, and they + must be holding the card's spinlock */ +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) +{ + unsigned int val; + + if (rw) { + /* read it from the card */ + val = codec->codec_read(codec, AC97_RECORD_SELECT); +#ifdef DEBUG + printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val); +#endif + return (1 << ac97_rm2oss[val & 0x07]); + } + + /* else, write the first set in the mask as the + output */ + /* clear out current set value first (AC97 supports only 1 input!) */ + val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]); + if (mask != val) + mask &= ~val; + + val = ffs(mask); + val = ac97_oss_rm[val-1]; + val |= val << 8; /* set both channels */ + +#ifdef DEBUG + printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val); +#endif + + codec->codec_write(codec, AC97_RECORD_SELECT, val); + + return 0; +}; + +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) +{ + int i, val = 0; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + info.modify_counter = codec->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + if (!codec->recmask_io) { + val = 0; + } else { + val = codec->recmask_io(codec, 1, 0); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = codec->supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = codec->record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = codec->stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ + /* val = codec->read_mixer(codec, i); */ + val = codec->mixer_state[i]; + break; + } + return put_user(val, (int *)arg); + } + + if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) { + codec->modcnt++; + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (!codec->recmask_io) return -EINVAL; + if (!val) return 0; + if (!(val &= codec->record_sources)) return -EINVAL; + + codec->recmask_io(codec, 0, val); + + return 0; + default: /* write a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + ac97_set_mixer(codec, i, val); + + return 0; + } + } + return -EINVAL; +} + +/* entry point for /proc/driver/controller_vendor/ac97/%d */ +int ac97_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0, cap, extid, val, id1, id2; + struct ac97_codec *codec; + int is_ac97_20 = 0; + + if ((codec = data) == NULL) + return -ENODEV; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + len += sprintf (page+len, "Vendor name : %s\n", codec->name); + len += sprintf (page+len, "Vendor id : %04X %04X\n", id1, id2); + + extid = codec->codec_read(codec, AC97_EXTENDED_ID); + extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13)); + len += sprintf (page+len, "AC97 Version : %s\n", + extid ? "2.0 or later" : "1.0"); + if (extid) is_ac97_20 = 1; + + cap = codec->codec_read(codec, AC97_RESET); + len += sprintf (page+len, "Capabilities :%s%s%s%s%s%s\n", + cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", + cap & 0x0002 ? " -reserved1-" : "", + cap & 0x0004 ? " -bass & treble-" : "", + cap & 0x0008 ? " -simulated stereo-" : "", + cap & 0x0010 ? " -headphone out-" : "", + cap & 0x0020 ? " -loudness-" : ""); + val = cap & 0x00c0; + len += sprintf (page+len, "DAC resolutions :%s%s%s\n", + " -16-bit-", + val & 0x0040 ? " -18-bit-" : "", + val & 0x0080 ? " -20-bit-" : ""); + val = cap & 0x0300; + len += sprintf (page+len, "ADC resolutions :%s%s%s\n", + " -16-bit-", + val & 0x0100 ? " -18-bit-" : "", + val & 0x0200 ? " -20-bit-" : ""); + len += sprintf (page+len, "3D enhancement : %s\n", + ac97_stereo_enhancements[(cap >> 10) & 0x1f]); + + val = codec->codec_read(codec, AC97_GENERAL_PURPOSE); + len += sprintf (page+len, "POP path : %s 3D\n" + "Sim. stereo : %s\n" + "3D enhancement : %s\n" + "Loudness : %s\n" + "Mono output : %s\n" + "MIC select : %s\n" + "ADC/DAC loopback : %s\n", + val & 0x8000 ? "post" : "pre", + val & 0x4000 ? "on" : "off", + val & 0x2000 ? "on" : "off", + val & 0x1000 ? "on" : "off", + val & 0x0200 ? "MIC" : "MIX", + val & 0x0100 ? "MIC2" : "MIC1", + val & 0x0080 ? "on" : "off"); + + extid = codec->codec_read(codec, AC97_EXTENDED_ID); + cap = extid; + len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n", + cap & 0x0001 ? " -var rate PCM audio-" : "", + cap & 0x0002 ? " -2x PCM audio out-" : "", + cap & 0x0008 ? " -var rate MIC in-" : "", + cap & 0x0040 ? " -PCM center DAC-" : "", + cap & 0x0080 ? " -PCM surround DAC-" : "", + cap & 0x0100 ? " -PCM LFE DAC-" : "", + cap & 0x0200 ? " -slot/DAC mappings-" : ""); + if (is_ac97_20) { + len += sprintf (page+len, "Front DAC rate : %d\n", + codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)); + } + + return len; +} + +/** + * ac97_probe_codec - Initialize and setup AC97-compatible codec + * @codec: (in/out) Kernel info for a single AC97 codec + * + * Reset the AC97 codec, then initialize the mixer and + * the rest of the @codec structure. + * + * The codec_read and codec_write fields of @codec are + * required to be setup and working when this function + * is called. All other fields are set by this function. + * + * codec_wait field of @codec can optionally be provided + * when calling this function. If codec_wait is not %NULL, + * this function will call codec_wait any time it is + * necessary to wait for the audio chip to reach the + * codec-ready state. If codec_wait is %NULL, then + * the default behavior is to call schedule_timeout. + * Currently codec_wait is used to wait for AC97 codec + * reset to complete. + * + * Returns 1 (true) on success, or 0 (false) on failure. + */ + +int ac97_probe_codec(struct ac97_codec *codec) +{ + u16 id1, id2; + u16 audio, modem; + int i; + + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should + * be read zero. + * + * FIXME: is the following comment outdated? -jgarzik + * Probing of AC97 in this way is not reliable, it is not even SAFE !! + */ + codec->codec_write(codec, AC97_RESET, 0L); + + /* also according to spec, we wait for codec-ready state */ + if (codec->codec_wait) + codec->codec_wait(codec); + else + udelay(10); + + if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { + printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", + codec->id ? "Secondary" : "Primary"); + return 0; + } + + /* probe for Modem Codec */ +#if CONFIG_SOUND_ALSA_NO_MODEM_PROBE + /* Do not probe for Modem Codec. + This prevents IrDA lockups on some laptops (Dell Inspiron) + when sound is initialized after IrDA init. -jprante */ + modem = 0; +#else + codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); + modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID); +#endif + + codec->name = NULL; + codec->codec_ops = &null_ops; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) { + if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) { + codec->type = ac97_codec_ids[i].id; + codec->name = ac97_codec_ids[i].name; + codec->codec_ops = ac97_codec_ids[i].ops; + break; + } + } + if (codec->name == NULL) + codec->name = "Unknown"; + printk(KERN_INFO "ac97_codec: AC97 %s codec, id: 0x%04x:" + "0x%04x (%s)\n", audio ? "Audio" : (modem ? "Modem" : ""), + id1, id2, codec->name); + + return ac97_init_mixer(codec); +} + +static int ac97_init_mixer(struct ac97_codec *codec) +{ + u16 cap; + int i; + + cap = codec->codec_read(codec, AC97_RESET); + + /* mixer masks */ + codec->supported_mixers = AC97_SUPPORTED_MASK; + codec->stereo_mixers = AC97_STEREO_MASK; + codec->record_sources = AC97_RECORD_MASK; + if (!(cap & 0x04)) + codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + if (!(cap & 0x10)) + codec->supported_mixers &= ~SOUND_MASK_ALTPCM; + + /* detect bit resolution */ + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020); + if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x1f1f) + codec->bit_resolution = 5; + else + codec->bit_resolution = 6; + + /* generic OSS to AC97 wrapper */ + codec->read_mixer = ac97_read_mixer; + codec->write_mixer = ac97_write_mixer; + codec->recmask_io = ac97_recmask_io; + codec->mixer_ioctl = ac97_mixer_ioctl; + + /* codec specific initialization for 4-6 channel output or secondary codec stuff */ + if (codec->codec_ops->init != NULL) { + codec->codec_ops->init(codec); + } + + /* initialize mixer channel volumes */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + struct mixer_defaults *md = &mixer_defaults[i]; + if (md->mixer == -1) + break; + if (!supported_mixer(codec, md->mixer)) + continue; + ac97_set_mixer(codec, md->mixer, md->value); + } + + return 1; +} + +#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ +#define AC97_SIGMATEL_DAC2INVERT 0x6e +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */ +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 + + +static int sigmatel_9708_init(struct ac97_codec * codec) +{ + u16 codec72, codec6c; + + codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000; + codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG); + + if ((codec72==0) && (codec6c==0)) { + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000); + codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007); + } else if ((codec72==0x8000) && (codec6c==0)) { + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001); + codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008); + } else if ((codec72==0x8000) && (codec6c==0x0080)) { + /* nothing */ + } + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + + +static int sigmatel_9721_init(struct ac97_codec * codec) +{ + /* Only set up secondary codec */ + if (codec->id == 0) + return 0; + + codec->codec_write(codec, AC97_SURROUND_MASTER, 0L); + + /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link + sloc 3,4 = 0x01, slot 7,8 = 0x00, */ + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00); + + /* we don't have the crystal when we are on an AMR card, so use + BIT_CLK as our clock source. Write the magic word ABBA and read + back to enable register 0x78 */ + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_read(codec, AC97_SIGMATEL_CIC1); + + /* sync all the clocks*/ + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802); + + return 0; +} + + +static int sigmatel_9744_init(struct ac97_codec * codec) +{ + // patch for SigmaTel + codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk + codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); + codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002); + codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + + +static int wolfson_init(struct ac97_codec * codec) +{ + codec->codec_write(codec, 0x72, 0x0808); + codec->codec_write(codec, 0x74, 0x0808); + + // patch for DVD noise + codec->codec_write(codec, 0x5a, 0x0200); + + // init vol as PCM vol + codec->codec_write(codec, 0x70, + codec->codec_read(codec, AC97_PCMOUT_VOL)); + + codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + return 0; +} + + +static int tritech_init(struct ac97_codec * codec) +{ + codec->codec_write(codec, 0x26, 0x0300); + codec->codec_write(codec, 0x26, 0x0000); + codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000); + codec->codec_write(codec, AC97_RESERVED_3A, 0x0000); + return 0; +} + + +/* copied from drivers/sound/maestro.c */ +static int tritech_maestro_init(struct ac97_codec * codec) +{ + /* no idea what this does */ + codec->codec_write(codec, 0x2A, 0x0001); + codec->codec_write(codec, 0x2C, 0x0000); + codec->codec_write(codec, 0x2C, 0XFFFF); + return 0; +} + + +/* + * This is basically standard AC97. It should work as a default for + * almost all modern codecs. Note that some cards wire EAPD *backwards* + * That side of it is up to the card driver not us to cope with. + * + */ + +static int eapd_control(struct ac97_codec * codec, int on) +{ + if(on) + codec->codec_write(codec, AC97_POWER_CONTROL, + codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000); + else + codec->codec_write(codec, AC97_POWER_CONTROL, + codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000); + return 0; +} + +/* + * Crystal digital audio control (CS4299 + */ + +static int crystal_digital_control(struct ac97_codec *codec, int mode) +{ + u16 cv; + + switch(mode) + { + case 0: cv = 0x0; break; /* SPEN off */ + case 1: cv = 0x8004; break; /* 48KHz digital */ + case 2: cv = 0x8104; break; /* 44.1KHz digital */ + default: + return -1; /* Not supported yet(eg AC3) */ + } + codec->codec_write(codec, 0x68, cv); + return 0; +} + +/* copied from drivers/sound/maestro.c */ +#if 0 /* there has been 1 person on the planet with a pt101 that we + know of. If they care, they can put this back in :) */ +static int pt101_init(struct ac97_codec * codec) +{ + printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + codec->codec_write(codec, 0x2A, 0x0001); + codec->codec_write(codec, 0x2C, 0x0000); + codec->codec_write(codec, 0x2C, 0xFFFF); + codec->codec_write(codec, 0x10, 0x9F1F); + codec->codec_write(codec, 0x12, 0x0808); + codec->codec_write(codec, 0x14, 0x9F1F); + codec->codec_write(codec, 0x16, 0x9F1F); + codec->codec_write(codec, 0x18, 0x0404); + codec->codec_write(codec, 0x1A, 0x0000); + codec->codec_write(codec, 0x1C, 0x0000); + codec->codec_write(codec, 0x02, 0x0404); + codec->codec_write(codec, 0x04, 0x0808); + codec->codec_write(codec, 0x0C, 0x801F); + codec->codec_write(codec, 0x0E, 0x801F); + return 0; +} +#endif + + +EXPORT_SYMBOL(ac97_read_proc); +EXPORT_SYMBOL(ac97_probe_codec); + +/* + * AC97 library support routines + */ + +/** + * ac97_set_dac_rate - set codec rate adaption + * @codec: ac97 code + * @rate: rate in hertz + * + * Set the DAC rate. Assumes the codec supports VRA. The caller is + * expected to have checked this little detail. + */ + +unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate) +{ + unsigned int new_rate = rate; + u32 dacp; + u32 mast_vol, phone_vol, mono_vol, pcm_vol; + u32 mute_vol = 0x8000; /* The mute volume? */ + + if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)) + { + /* Mute several registers */ + mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO); + mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO); + phone_vol = codec->codec_read(codec, AC97_HEADPHONE_VOL); + pcm_vol = codec->codec_read(codec, AC97_PCMOUT_VOL); + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mute_vol); + codec->codec_write(codec, AC97_MASTER_VOL_MONO, mute_vol); + codec->codec_write(codec, AC97_HEADPHONE_VOL, mute_vol); + codec->codec_write(codec, AC97_PCMOUT_VOL, mute_vol); + + /* Power down the DAC */ + dacp=codec->codec_read(codec, AC97_POWER_CONTROL); + codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200); + /* Load the rate and read the effective rate */ + codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); + new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE); + /* Power it back up */ + codec->codec_write(codec, AC97_POWER_CONTROL, dacp); + + /* Restore volumes */ + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, mast_vol); + codec->codec_write(codec, AC97_MASTER_VOL_MONO, mono_vol); + codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol); + codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol); + } + return new_rate; +} + +EXPORT_SYMBOL(ac97_set_dac_rate); + +/** + * ac97_set_adc_rate - set codec rate adaption + * @codec: ac97 code + * @rate: rate in hertz + * + * Set the ADC rate. Assumes the codec supports VRA. The caller is + * expected to have checked this little detail. + */ + +unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate) +{ + unsigned int new_rate = rate; + u32 dacp; + + if(rate != codec->codec_read(codec, AC97_PCM_LR_ADC_RATE)) + { + /* Power down the ADC */ + dacp=codec->codec_read(codec, AC97_POWER_CONTROL); + codec->codec_write(codec, AC97_POWER_CONTROL, dacp|0x0100); + /* Load the rate and read the effective rate */ + codec->codec_write(codec, AC97_PCM_LR_ADC_RATE, rate); + new_rate=codec->codec_read(codec, AC97_PCM_LR_ADC_RATE); + /* Power it back up */ + codec->codec_write(codec, AC97_POWER_CONTROL, dacp); + } + return new_rate; +} + +EXPORT_SYMBOL(ac97_set_adc_rate); + +int ac97_save_state(struct ac97_codec *codec) +{ + return 0; +} + +EXPORT_SYMBOL(ac97_save_state); + +int ac97_restore_state(struct ac97_codec *codec) +{ + int i; + unsigned int left, right, val; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!supported_mixer(codec, i)) + continue; + + val = codec->mixer_state[i]; + right = val >> 8; + left = val & 0xff; + codec->write_mixer(codec, i, left, right); + } + return 0; +} + +EXPORT_SYMBOL(ac97_restore_state); + +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/aci.c linux-2.4.19-pre5-mjc/sound/oss/aci.c --- linux/sound/oss/aci.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/aci.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,711 @@ +/* + * Audio Command Interface (ACI) driver (sound/aci.c) + * + * ACI is a protocol used to communicate with the microcontroller on + * some sound cards produced by miro, e.g. the miroSOUND PCM12 and + * PCM20. The ACI has been developed for miro by Norberto Pellicci + * . Special thanks to both him and miro for + * providing the ACI specification. + * + * The main function of the ACI is to control the mixer and to get a + * product identification. On the PCM20, ACI also controls the radio + * tuner on this card, this is supported in the Video for Linux + * miropcm20 driver. + * - + * This is a fullfeatured implementation. Unsupported features + * are bugs... (: + * + * It is not longer necessary to load the mad16 module first. The + * user is currently responsible to set the mad16 mixer correctly. + * + * To toggle the solo mode for full duplex operation just use the OSS + * record switch for the pcm ('wave') controller. Robert + * - + * + * Revision history: + * + * 1995-11-10 Markus Kuhn + * First version written. + * 1995-12-31 Markus Kuhn + * Second revision, general code cleanup. + * 1996-05-16 Hannu Savolainen + * Integrated with other parts of the driver. + * 1996-05-28 Markus Kuhn + * Initialize CS4231A mixer, make ACI first mixer, + * use new private mixer API for solo mode. + * 1998-08-18 Ruurd Reitsma + * Small modification to export ACI functions and + * complete modularisation. + * 2000-06-20 Robert Siemer + * Don't initialize the CS4231A mixer anymore, so the code is + * working again, and other small changes to fit in todays + * kernels. + * 2000-08-26 Robert Siemer + * Clean up and rewrite for 2.4.x. Maybe it's SMP safe now... (: + * ioctl bugfix, and integration of solo-mode into OSS-API, + * added (OSS-limited) equalizer support, return value bugfix, + * changed param aci_reset to reset, new params: ide, wss. + * 2001-04-20 Robert Siemer + * even more cleanups... + * 2001-10-08 Arnaldo Carvalho de Melo + * Get rid of check_region, .bss optimizations, use set_current_state + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +int aci_port; /* as determined by bit 4 in the OPTi 929 MC4 register */ +int aci_idcode[2]; /* manufacturer and product ID */ +int aci_version; /* ACI firmware version */ + +EXPORT_SYMBOL(aci_port); +EXPORT_SYMBOL(aci_idcode); +EXPORT_SYMBOL(aci_version); + +#include "aci.h" + + +static int aci_solo; /* status bit of the card that can't be * + * checked with ACI versions prior to 0xb0 */ +static int aci_amp; /* status bit for power-amp/line-out level + but I have no docs about what is what... */ +static int aci_micpreamp=3; /* microphone preamp-level that can't be * + * checked with ACI versions prior to 0xb0 */ + +static int mixer_device; +static struct semaphore aci_sem; + +#ifdef MODULE +static int reset; +MODULE_PARM(reset,"i"); +MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer."); +#else +static int reset = 1; +#endif + +static int ide=-1; +MODULE_PARM(ide,"i"); +MODULE_PARM_DESC(ide,"1 enable, 0 disable ide-port - untested" + " default: do nothing"); +static int wss=-1; +MODULE_PARM(wss,"i"); +MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested" + " default: do nothing; for PCM1-pro only"); + +#if DEBUG +static void print_bits(unsigned char c) +{ + int j; + printk(KERN_DEBUG "aci: "); + + for (j=7; j>=0; j--) { + printk("%d", (c >> j) & 0x1); + } + + printk("\n"); +} +#endif + +/* + * This busy wait code normally requires less than 15 loops and + * practically always less than 100 loops on my i486/DX2 66 MHz. + * + * Warning: Waiting on the general status flag after reseting the MUTE + * function can take a VERY long time, because the PCM12 does some kind + * of fade-in effect. For this reason, access to the MUTE function has + * not been implemented at all. + * + * - The OSS interface has no mute option. It takes about 3 seconds to + * fade-in on my PCM20. busy_wait() handles it great now... Robert + */ + +static int busy_wait(void) +{ + #define MINTIME 500 + long timeout; + unsigned char byte; + + for (timeout = 1; timeout <= MINTIME+30; timeout++) { + if (((byte=inb(BUSY_REGISTER)) & 1) == 0) { + if (timeout >= MINTIME) + printk(KERN_DEBUG "aci: Got READYFLAG in round %ld.\n", timeout-MINTIME); + return byte; + } + if (timeout >= MINTIME) { + long out=10*HZ; + switch (timeout-MINTIME) { + case 0 ... 9: + out /= 10; + case 10 ... 19: + out /= 10; + case 20 ... 30: + out /= 10; + default: + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(out); + break; + } + } + } + printk(KERN_WARNING "aci: busy_wait() time out.\n"); + return -EBUSY; +} + +/* The four ACI command types are fucked up. [-: + * implied is: 1w - special case for INIT + * write is: 2w1r + * read is: x(1w1r) where x is 1 or 2 (1 CHECK_SIG, 1 CHECK_STER, + * 1 VERSION, 2 IDCODE) + * the command is only in the first write, rest is protocol overhead + * + * indexed is technically a write and used for STATUS + * and the special case for TUNE is: 3w1r + * + * Here the new general sheme: TUNE --> aci_rw_cmd(x, y, z) + * indexed and write --> aci_rw_cmd(x, y, -1) + * implied and read (x=1) --> aci_rw_cmd(x, -1, -1) + * + * Read (x>=2) is not implemented (only used during initialization). + * Use aci_idcode[2] and aci_version... Robert + */ + +/* Some notes for error detection: theoretically it is possible. + * But it doubles the I/O-traffic from ww(r) to wwwrw(r) in the normal + * case and doesn't seem to be designed for that... Robert + */ + +static inline int aci_rawwrite(unsigned char byte) +{ + if (busy_wait() >= 0) { +#if DEBUG + printk(KERN_DEBUG "aci_rawwrite(%d)\n", byte); +#endif + outb(byte, COMMAND_REGISTER); + return 0; + } else + return -EBUSY; +} + +static inline int aci_rawread(void) +{ + unsigned char byte; + + if (busy_wait() >= 0) { + byte=inb(STATUS_REGISTER); +#if DEBUG + printk(KERN_DEBUG "%d = aci_rawread()\n", byte); +#endif + return byte; + } else + return -EBUSY; +} + + +int aci_rw_cmd(int write1, int write2, int write3) +{ + int write[] = {write1, write2, write3}; + int read = -EINTR, i; + + if (down_interruptible(&aci_sem)) + goto out; + + for (i=0; i<3; i++) { + if (write[i]< 0 || write[i] > 255) + break; + else { + read = aci_rawwrite(write[i]); + if (read < 0) + goto out_up; + } + + } + + read = aci_rawread(); +out_up: up(&aci_sem); +out: return read; +} + +EXPORT_SYMBOL(aci_rw_cmd); + +static int setvolume(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int vol, ret, uservol, buf; + + __get_user(uservol, (int *)arg); + + /* left channel */ + vol = uservol & 0xff; + if (vol > 100) + vol = 100; + vol = SCALE(100, 0x20, vol); + if ((buf=aci_write_cmd(left_index, 0x20 - vol))<0) + return buf; + ret = SCALE(0x20, 100, vol); + + + /* right channel */ + vol = (uservol >> 8) & 0xff; + if (vol > 100) + vol = 100; + vol = SCALE(100, 0x20, vol); + if ((buf=aci_write_cmd(right_index, 0x20 - vol))<0) + return buf; + ret |= SCALE(0x20, 100, vol) << 8; + + __put_user(ret, (int *)arg); + + return 0; +} + +static int getvolume(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int vol; + int buf; + + /* left channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) + return buf; + vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0); + + /* right channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) + return buf; + vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8; + + __put_user(vol, (int *)arg); + + return 0; +} + + +/* The equalizer is somewhat strange on the ACI. From -12dB to +12dB + * write: 0xff..down.to..0x80==0x00..up.to..0x7f + */ + +static inline unsigned int eq_oss2aci(unsigned int vol) +{ + int boost=0; + unsigned int ret; + + if (vol > 100) + vol = 100; + if (vol > 50) { + vol -= 51; + boost=1; + } + if (boost) + ret=SCALE(49, 0x7e, vol)+1; + else + ret=0xff - SCALE(50, 0x7f, vol); + return ret; +} + +static inline unsigned int eq_aci2oss(unsigned int vol) +{ + if (vol < 0x80) + return SCALE(0x7f, 50, vol) + 50; + else + return SCALE(0x7f, 50, 0xff-vol); +} + + +static int setequalizer(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int buf; + unsigned int vol; + + __get_user(vol, (int *)arg); + + /* left channel */ + if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0) + return buf; + + /* right channel */ + if ((buf=aci_write_cmd(right_index, eq_oss2aci((vol>>8) & 0xff)))<0) + return buf; + + /* the ACI equalizer is more precise */ + return 0; +} + +static int getequalizer(caddr_t arg, + unsigned char left_index, unsigned char right_index) +{ + int buf; + unsigned int vol; + + /* left channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) + return buf; + vol = eq_aci2oss(buf); + + /* right channel */ + if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) + return buf; + vol |= eq_aci2oss(buf) << 8; + + __put_user(vol, (int *)arg); + + return 0; +} + +static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + int vol, buf; + + switch (cmd) { + case SOUND_MIXER_WRITE_VOLUME: + return setvolume(arg, 0x01, 0x00); + case SOUND_MIXER_WRITE_CD: + return setvolume(arg, 0x3c, 0x34); + case SOUND_MIXER_WRITE_MIC: + return setvolume(arg, 0x38, 0x30); + case SOUND_MIXER_WRITE_LINE: + return setvolume(arg, 0x39, 0x31); + case SOUND_MIXER_WRITE_SYNTH: + return setvolume(arg, 0x3b, 0x33); + case SOUND_MIXER_WRITE_PCM: + return setvolume(arg, 0x3a, 0x32); + case MIXER_WRITE(SOUND_MIXER_RADIO): /* fall through */ + case SOUND_MIXER_WRITE_LINE1: /* AUX1 or radio */ + return setvolume(arg, 0x3d, 0x35); + case SOUND_MIXER_WRITE_LINE2: /* AUX2 */ + return setvolume(arg, 0x3e, 0x36); + case SOUND_MIXER_WRITE_BASS: /* set band one and two */ + if (aci_idcode[1]=='C') { + if ((buf=setequalizer(arg, 0x48, 0x40)) || + (buf=setequalizer(arg, 0x49, 0x41))); + return buf; + } + break; + case SOUND_MIXER_WRITE_TREBLE: /* set band six and seven */ + if (aci_idcode[1]=='C') { + if ((buf=setequalizer(arg, 0x4d, 0x45)) || + (buf=setequalizer(arg, 0x4e, 0x46))); + return buf; + } + break; + case SOUND_MIXER_WRITE_IGAIN: /* MIC pre-amp */ + if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { + __get_user(vol, (int *)arg); + vol = vol & 0xff; + if (vol > 100) + vol = 100; + vol = SCALE(100, 3, vol); + if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0) + return buf; + aci_micpreamp = vol; + vol = SCALE(3, 100, vol); + vol |= (vol << 8); + __put_user(vol, (int *)arg); + return 0; + } + break; + case SOUND_MIXER_WRITE_OGAIN: /* Power-amp/line-out level */ + if (aci_idcode[1]=='A' || aci_idcode[1]=='B') { + __get_user(buf, (int *)arg); + buf = buf & 0xff; + if (buf > 50) + vol = 1; + else + vol = 0; + if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0) + return buf; + aci_amp = vol; + if (aci_amp) + buf = (100 || 100<<8); + else + buf = 0; + __put_user(buf, (int *)arg); + return 0; + } + break; + case SOUND_MIXER_WRITE_RECSRC: + /* handle solo mode control */ + __get_user(buf, (int *)arg); + /* unset solo when RECSRC for PCM is requested */ + if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { + vol = !(buf & SOUND_MASK_PCM); + if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0) + return buf; + aci_solo = vol; + } + buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| + SOUND_MASK_SYNTH| SOUND_MASK_LINE2); + if (aci_idcode[1] == 'C') /* PCM20 radio */ + buf |= SOUND_MASK_RADIO; + else + buf |= SOUND_MASK_LINE1; + if (!aci_solo) + buf |= SOUND_MASK_PCM; + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_DEVMASK: + buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_MIC | SOUND_MASK_LINE | + SOUND_MASK_SYNTH | SOUND_MASK_PCM | + SOUND_MASK_LINE2); + switch (aci_idcode[1]) { + case 'C': /* PCM20 radio */ + buf |= (SOUND_MASK_RADIO | SOUND_MASK_IGAIN | + SOUND_MASK_BASS | SOUND_MASK_TREBLE); + break; + case 'B': /* PCM12 */ + buf |= (SOUND_MASK_LINE1 | SOUND_MASK_IGAIN | + SOUND_MASK_OGAIN); + break; + case 'A': /* PCM1-pro */ + buf |= (SOUND_MASK_LINE1 | SOUND_MASK_OGAIN); + break; + default: + buf |= SOUND_MASK_LINE1; + } + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_STEREODEVS: + buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_MIC | SOUND_MASK_LINE | + SOUND_MASK_SYNTH | SOUND_MASK_PCM | + SOUND_MASK_LINE2); + switch (aci_idcode[1]) { + case 'C': /* PCM20 radio */ + buf |= (SOUND_MASK_RADIO | + SOUND_MASK_BASS | SOUND_MASK_TREBLE); + break; + default: + buf |= SOUND_MASK_LINE1; + } + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_RECMASK: + buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE| + SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM); + if (aci_idcode[1] == 'C') /* PCM20 radio */ + buf |= SOUND_MASK_RADIO; + else + buf |= SOUND_MASK_LINE1; + + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_RECSRC: + buf = (SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | + SOUND_MASK_SYNTH | SOUND_MASK_LINE2); + /* do we need aci_solo or can I get it from the ACI? */ + switch (aci_idcode[1]) { + case 'B': /* PCM12 */ + case 'C': /* PCM20 radio */ + if (aci_version >= 0xb0) { + if ((vol=aci_rw_cmd(ACI_STATUS, + ACI_S_GENERAL, -1))<0) + return vol; + if (vol & 0x20) + buf |= SOUND_MASK_PCM; + } + else + if (!aci_solo) + buf |= SOUND_MASK_PCM; + break; + default: + buf |= SOUND_MASK_PCM; + } + if (aci_idcode[1] == 'C') /* PCM20 radio */ + buf |= SOUND_MASK_RADIO; + else + buf |= SOUND_MASK_LINE1; + + __put_user(buf, (int *)arg); + return 0; + case SOUND_MIXER_READ_CAPS: + __put_user(0, (int *)arg); + return 0; + case SOUND_MIXER_READ_VOLUME: + return getvolume(arg, 0x04, 0x03); + case SOUND_MIXER_READ_CD: + return getvolume(arg, 0x0a, 0x09); + case SOUND_MIXER_READ_MIC: + return getvolume(arg, 0x06, 0x05); + case SOUND_MIXER_READ_LINE: + return getvolume(arg, 0x08, 0x07); + case SOUND_MIXER_READ_SYNTH: + return getvolume(arg, 0x0c, 0x0b); + case SOUND_MIXER_READ_PCM: + return getvolume(arg, 0x0e, 0x0d); + case MIXER_READ(SOUND_MIXER_RADIO): /* fall through */ + case SOUND_MIXER_READ_LINE1: /* AUX1 */ + return getvolume(arg, 0x11, 0x10); + case SOUND_MIXER_READ_LINE2: /* AUX2 */ + return getvolume(arg, 0x13, 0x12); + case SOUND_MIXER_READ_BASS: /* get band one */ + if (aci_idcode[1]=='C') { + return getequalizer(arg, 0x23, 0x22); + } + break; + case SOUND_MIXER_READ_TREBLE: /* get band seven */ + if (aci_idcode[1]=='C') { + return getequalizer(arg, 0x2f, 0x2e); + } + break; + case SOUND_MIXER_READ_IGAIN: /* MIC pre-amp */ + if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { + /* aci_micpreamp or ACI? */ + if (aci_version >= 0xb0) { + if ((buf=aci_indexed_cmd(ACI_STATUS, + ACI_S_READ_IGAIN))<0) + return buf; + } + else + buf=aci_micpreamp; + vol = SCALE(3, 100, buf <= 3 ? buf : 3); + vol |= vol << 8; + __put_user(vol, (int *)arg); + return 0; + } + break; + case SOUND_MIXER_READ_OGAIN: + if (aci_amp) + buf = (100 || 100<<8); + else + buf = 0; + __put_user(buf, (int *)arg); + return 0; + } + return -EINVAL; +} + +static struct mixer_operations aci_mixer_operations = +{ + owner: THIS_MODULE, + id: "ACI", + ioctl: aci_mixer_ioctl +}; + +/* + * There is also an internal mixer in the codec (CS4231A or AD1845), + * that deserves no purpose in an ACI based system which uses an + * external ACI controlled stereo mixer. Make sure that this codec + * mixer has the AUX1 input selected as the recording source, that the + * input gain is set near maximum and that the other channels going + * from the inputs to the codec output are muted. + */ + +static int __init attach_aci(void) +{ + char *boardname; + int i, rc = -EBUSY; + + init_MUTEX(&aci_sem); + + outb(0xE3, 0xf8f); /* Write MAD16 password */ + aci_port = (inb(0xf90) & 0x10) ? + 0x344: 0x354; /* Get aci_port from MC4_PORT */ + + if (!request_region(aci_port, 3, "sound mixer (ACI)")) { + printk(KERN_NOTICE + "aci: I/O area 0x%03x-0x%03x already used.\n", + aci_port, aci_port+2); + goto out; + } + + /* force ACI into a known state */ + rc = -EFAULT; + for (i=0; i<3; i++) + if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0) + goto out_release_region; + + /* official this is one aci read call: */ + rc = -EFAULT; + if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 || + (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) { + printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n", + aci_port); + goto out_release_region; + } + + if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) { + printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n", + aci_port); + goto out_release_region; + } + + if (aci_idcode[0] == 'm') { + /* It looks like a miro sound card. */ + switch (aci_idcode[1]) { + case 'A': + boardname = "PCM1 pro / early PCM12"; + break; + case 'B': + boardname = "PCM12"; + break; + case 'C': + boardname = "PCM20 radio"; + break; + default: + boardname = "unknown miro"; + } + } else { + printk(KERN_WARNING "aci: Warning: unsupported card! - " + "no hardware, no specs...\n"); + boardname = "unknown Cardinal Technologies"; + } + + printk(KERN_INFO " at 0x%03x\n", + aci_version, + aci_idcode[0], aci_idcode[1], + aci_idcode[0], aci_idcode[1], + boardname, aci_port); + + rc = -EBUSY; + if (reset) { + /* first write()s after reset fail with my PCM20 */ + if (aci_rw_cmd(ACI_INIT, -1, -1)<0 || + aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 || + aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0) + goto out_release_region; + } + + /* the PCM20 is muted after reset (and reboot) */ + if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0) + goto out_release_region; + + if (ide>=0) + if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0) + goto out_release_region; + + if (wss>=0 && aci_idcode[1]=='A') + if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0) + goto out_release_region; + + mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname, + &aci_mixer_operations, + sizeof(aci_mixer_operations), NULL); + rc = 0; + if (mixer_device < 0) { + printk(KERN_ERR "aci: Failed to install mixer.\n"); + rc = mixer_device; + goto out_release_region; + } /* else Maybe initialize the CS4231A mixer here... */ +out: return rc; +out_release_region: + release_region(aci_port, 3); + goto out; +} + +static void __exit unload_aci(void) +{ + sound_unload_mixerdev(mixer_device); + release_region(aci_port, 3); +} + +module_init(attach_aci); +module_exit(unload_aci); +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/aci.h linux-2.4.19-pre5-mjc/sound/oss/aci.h --- linux/sound/oss/aci.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/aci.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,58 @@ +#ifndef _ACI_H_ +#define _ACI_H_ + +extern int aci_port; +extern int aci_idcode[2]; /* manufacturer and product ID */ +extern int aci_version; /* ACI firmware version */ +extern int aci_rw_cmd(int write1, int write2, int write3); + +#define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1) +#define aci_write_cmd(a, b) aci_rw_cmd(a, b, -1) +#define aci_read_cmd(a) aci_rw_cmd(a,-1, -1) + +#define COMMAND_REGISTER (aci_port) /* write register */ +#define STATUS_REGISTER (aci_port + 1) /* read register */ +#define BUSY_REGISTER (aci_port + 2) /* also used for rds */ + +#define RDS_REGISTER BUSY_REGISTER + +#define ACI_SET_MUTE 0x0d +#define ACI_SET_POWERAMP 0x0f +#define ACI_SET_TUNERMUTE 0xa3 +#define ACI_SET_TUNERMONO 0xa4 +#define ACI_SET_IDE 0xd0 +#define ACI_SET_WSS 0xd1 +#define ACI_SET_SOLOMODE 0xd2 +#define ACI_WRITE_IGAIN 0x03 +#define ACI_WRITE_TUNE 0xa7 +#define ACI_READ_TUNERSTEREO 0xa8 +#define ACI_READ_TUNERSTATION 0xa9 +#define ACI_READ_VERSION 0xf1 +#define ACI_READ_IDCODE 0xf2 +#define ACI_INIT 0xff +#define ACI_STATUS 0xf0 +#define ACI_S_GENERAL 0x00 +#define ACI_S_READ_IGAIN 0x21 +#define ACI_ERROR_OP 0xdf + +/* + * The following macro SCALE can be used to scale one integer volume + * value into another one using only integer arithmetic. If the input + * value x is in the range 0 <= x <= xmax, then the result will be in + * the range 0 <= SCALE(xmax,ymax,x) <= ymax. + * + * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the + * following nice properties: + * + * - SCALE(xmax,ymax,xmax) = ymax + * - SCALE(xmax,ymax,0) = 0 + * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x) + * + * In addition, the rounding error is minimal and nicely distributed. + * The proofs are left as an exercise to the reader. + */ + +#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax)) + + +#endif /* _ACI_H_ */ diff -Nru linux/sound/oss/ad1816.c linux-2.4.19-pre5-mjc/sound/oss/ad1816.c --- linux/sound/oss/ad1816.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ad1816.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1478 @@ +/* + * + * AD1816 lowlevel sound driver for Linux 2.2.0 and above + * + * Copyright (C) 1998 by Thorsten Knabe + * + * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996 + * + * + * version: 1.3.1 + * status: experimental + * date: 1999/4/18 + * + * Changes: + * Oleg Drokin: Some cleanup of load/unload functions. 1998/11/24 + * + * Thorsten Knabe: attach and unload rewritten, + * some argument checks added 1998/11/30 + * + * Thorsten Knabe: Buggy isa bridge workaround added 1999/01/16 + * + * David Moews/Thorsten Knabe: Introduced options + * parameter. Added slightly modified patch from + * David Moews to disable dsp audio sources by setting + * bit 0 of options parameter. This seems to be + * required by some Aztech/Newcom SC-16 cards. 1999/04/18 + * + * Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03 + * + * Christoph Hellwig: Added isapnp support 2000/03/15 + * + * Arnaldo Carvalho de Melo: get rid of check_region 2001/10/07 + */ + +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#define DEBUGNOISE(x) +#define DEBUGINFO(x) +#define DEBUGLOG(x) +#define DEBUGWARN(x) + +#define CHECK_FOR_POWER { int timeout=100; \ + while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\ + timeout--; \ + } \ + if (timeout==0) {\ + printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ + } \ +} + +/* structure to hold device specific information */ +typedef struct +{ + int base; /* set in attach */ + int irq; + int dma_playback; + int dma_capture; + + int speed; /* open */ + int channels; + int audio_format; + unsigned char format_bits; + int audio_mode; + int opened; + + int recmask; /* setup */ + int supported_devices; + int supported_rec_devices; + unsigned short levels[SOUND_MIXER_NRDEVICES]; + int dev_no; /* this is the # in audio_devs and NOT + in ad1816_info */ + int irq_ok; + int *osp; + +} ad1816_info; + +static int nr_ad1816_devs; +static int ad1816_clockfreq = 33000; +static int options; + +/* for backward mapping of irq to sound device */ + +static volatile char irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + +/* supported audio formats */ +static int ad_format_mask = +AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW; + +/* array of device info structures */ +static ad1816_info dev_info[MAX_AUDIO_DEV]; + + +/* ------------------------------------------------------------------- */ + +/* functions for easier access to inderect registers */ + +static int ad_read (ad1816_info * devc, int reg) +{ + unsigned long flags; + int result; + + CHECK_FOR_POWER; + + save_flags (flags); /* make register access atomic */ + cli (); + outb ((unsigned char) (reg & 0x3f), devc->base+0); + result = inb(devc->base+2); + result+= inb(devc->base+3)<<8; + restore_flags (flags); + + return (result); +} + + +static void ad_write (ad1816_info * devc, int reg, int data) +{ + unsigned long flags; + + CHECK_FOR_POWER; + + save_flags (flags); /* make register access atomic */ + cli (); + outb ((unsigned char) (reg & 0xff), devc->base+0); + outb ((unsigned char) (data & 0xff),devc->base+2); + outb ((unsigned char) ((data>>8)&0xff),devc->base+3); + restore_flags (flags); + +} + +/* ------------------------------------------------------------------- */ + +/* function interface required by struct audio_driver */ + +static void ad1816_halt_input (int dev) +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + unsigned char buffer; + + DEBUGINFO (printk("ad1816: halt_input called\n")); + + save_flags (flags); + cli (); + + if(!isa_dma_bridge_buggy) { + disable_dma(audio_devs[dev]->dmap_in->dma); + } + + buffer=inb(devc->base+9); + if (buffer & 0x01) { + /* disable capture */ + outb(buffer & ~0x01,devc->base+9); + } + + if(!isa_dma_bridge_buggy) { + enable_dma(audio_devs[dev]->dmap_in->dma); + } + + /* Clear interrupt status */ + outb (~0x40, devc->base+1); + + devc->audio_mode &= ~PCM_ENABLE_INPUT; + restore_flags (flags); +} + +static void ad1816_halt_output (int dev) +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + unsigned char buffer; + + DEBUGINFO (printk("ad1816: halt_output called!\n")); + + save_flags (flags); + cli (); + /* Mute pcm output */ + ad_write(devc, 4, ad_read(devc,4)|0x8080); + + if(!isa_dma_bridge_buggy) { + disable_dma(audio_devs[dev]->dmap_out->dma); + } + + buffer=inb(devc->base+8); + if (buffer & 0x01) { + /* disable capture */ + outb(buffer & ~0x01,devc->base+8); + } + + if(!isa_dma_bridge_buggy) { + enable_dma(audio_devs[dev]->dmap_out->dma); + } + + /* Clear interrupt status */ + outb ((unsigned char)~0x80, devc->base+1); + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + restore_flags (flags); +} + +static void ad1816_output_block (int dev, unsigned long buf, + int count, int intrflag) +{ + unsigned long flags; + unsigned long cnt; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + DEBUGINFO(printk("ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); + + cnt = count/4 - 1; + + save_flags (flags); + cli (); + + /* set transfer count */ + ad_write (devc, 8, cnt & 0xffff); + + devc->audio_mode |= PCM_ENABLE_OUTPUT; + restore_flags (flags); +} + + +static void ad1816_start_input (int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + unsigned long cnt; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + DEBUGINFO(printk("ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); + + cnt = count/4 - 1; + + save_flags (flags); /* make register access atomic */ + cli (); + + /* set transfer count */ + ad_write (devc, 10, cnt & 0xffff); + + devc->audio_mode |= PCM_ENABLE_INPUT; + restore_flags (flags); +} + +static int ad1816_prepare_for_input (int dev, int bsize, int bcount) +{ + unsigned long flags; + unsigned int freq; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + unsigned char fmt_bits; + + DEBUGINFO (printk ("ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); + + save_flags (flags); + cli (); + + fmt_bits= (devc->format_bits&0x7)<<3; + + /* set mono/stereo mode */ + if (devc->channels > 1) { + fmt_bits |=0x4; + } + + /* set Mono/Stereo in playback/capture register */ + outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); + outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); + + /* If compiled into kernel, AD1816_CLOCK is defined, so use it */ +#ifdef AD1816_CLOCK + ad1816_clockfreq=AD1816_CLOCK; +#endif + + /* capture/playback frequency correction for soundcards + with clock chips != 33MHz (allowed range 5 - 100 kHz) */ + + if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { + ad1816_clockfreq=33000; + } + + freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; + + /* write playback/capture speeds */ + ad_write (devc, 2, freq & 0xffff); + ad_write (devc, 3, freq & 0xffff); + + restore_flags (flags); + + ad1816_halt_input(dev); + return 0; +} + +static int ad1816_prepare_for_output (int dev, int bsize, int bcount) +{ + unsigned long flags; + unsigned int freq; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + unsigned char fmt_bits; + + DEBUGINFO (printk ("ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); + + save_flags (flags); /* make register access atomic */ + cli (); + + fmt_bits= (devc->format_bits&0x7)<<3; + /* set mono/stereo mode */ + if (devc->channels > 1) { + fmt_bits |=0x4; + } + + /* write format bits to playback/capture registers */ + outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); + outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); + +#ifdef AD1816_CLOCK + ad1816_clockfreq=AD1816_CLOCK; +#endif + + /* capture/playback frequency correction for soundcards + with clock chips != 33MHz (allowed range 5 - 100 kHz)*/ + + if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { + ad1816_clockfreq=33000; + } + + freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; + + /* write playback/capture speeds */ + ad_write (devc, 2, freq & 0xffff); + ad_write (devc, 3, freq & 0xffff); + + restore_flags (flags); + + ad1816_halt_output(dev); + return 0; + +} + +static void ad1816_trigger (int dev, int state) +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); + + /* mode may have changed */ + + save_flags (flags); /* make register access atomic */ + cli (); + + /* mask out modes not specified on open call */ + state &= devc->audio_mode; + + /* setup soundchip to new io-mode */ + if (state & PCM_ENABLE_INPUT) { + /* enable capture */ + outb(inb(devc->base+9)|0x01, devc->base+9); + } else { + /* disable capture */ + outb(inb(devc->base+9)&~0x01, devc->base+9); + } + + if (state & PCM_ENABLE_OUTPUT) { + /* enable playback */ + outb(inb(devc->base+8)|0x01, devc->base+8); + /* unmute pcm output */ + ad_write(devc, 4, ad_read(devc,4)&~0x8080); + } else { + /* mute pcm output */ + ad_write(devc, 4, ad_read(devc,4)|0x8080); + /* disable capture */ + outb(inb(devc->base+8)&~0x01, devc->base+8); + } + restore_flags (flags); +} + + +/* halt input & output */ +static void ad1816_halt (int dev) +{ + ad1816_halt_input(dev); + ad1816_halt_output(dev); +} + +static void ad1816_reset (int dev) +{ + ad1816_halt (dev); +} + +/* set playback speed */ +static int ad1816_set_speed (int dev, int arg) +{ + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + if (arg == 0) { + return devc->speed; + } + /* range checking */ + if (arg < 4000) { + arg = 4000; + } + if (arg > 55000) { + arg = 55000; + } + + devc->speed = arg; + return devc->speed; + +} + +static unsigned int ad1816_set_bits (int dev, unsigned int arg) +{ + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + static struct format_tbl { + int format; + unsigned char bits; + } format2bits[] = { + { 0, 0 }, + { AFMT_MU_LAW, 1 }, + { AFMT_A_LAW, 3 }, + { AFMT_IMA_ADPCM, 0 }, + { AFMT_U8, 0 }, + { AFMT_S16_LE, 2 }, + { AFMT_S16_BE, 6 }, + { AFMT_S8, 0 }, + { AFMT_U16_LE, 0 }, + { AFMT_U16_BE, 0 } + }; + + int i, n = sizeof (format2bits) / sizeof (struct format_tbl); + + /* return current format */ + if (arg == 0) + return devc->audio_format; + + devc->audio_format = arg; + + /* search matching format bits */ + for (i = 0; i < n; i++) + if (format2bits[i].format == arg) { + devc->format_bits = format2bits[i].bits; + devc->audio_format = arg; + return arg; + } + + /* Still hanging here. Something must be terribly wrong */ + devc->format_bits = 0; + return devc->audio_format = AFMT_U8; +} + +static short ad1816_set_channels (int dev, short arg) +{ + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + if (arg != 1 && arg != 2) + return devc->channels; + + devc->channels = arg; + return arg; +} + +/* open device */ +static int ad1816_open (int dev, int mode) +{ + ad1816_info *devc = NULL; + unsigned long flags; + + /* is device number valid ? */ + if (dev < 0 || dev >= num_audiodevs) + return -(ENXIO); + + /* get device info of this dev */ + devc = (ad1816_info *) audio_devs[dev]->devc; + + /* make check if device already open atomic */ + save_flags (flags); + cli (); + + if (devc->opened) { + restore_flags (flags); + return -(EBUSY); + } + + /* mark device as open */ + devc->opened = 1; + + devc->audio_mode = 0; + devc->speed = 8000; + devc->audio_format=AFMT_U8; + devc->channels=1; + + ad1816_reset(devc->dev_no); /* halt all pending output */ + restore_flags (flags); + return 0; +} + +static void ad1816_close (int dev) /* close device */ +{ + unsigned long flags; + ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc; + + save_flags (flags); + cli (); + + /* halt all pending output */ + ad1816_reset(devc->dev_no); + + devc->opened = 0; + devc->audio_mode = 0; + devc->speed = 8000; + devc->audio_format=AFMT_U8; + devc->format_bits = 0; + + + restore_flags (flags); +} + + +/* ------------------------------------------------------------------- */ + +/* Audio driver structure */ + +static struct audio_driver ad1816_audio_driver = +{ + owner: THIS_MODULE, + open: ad1816_open, + close: ad1816_close, + output_block: ad1816_output_block, + start_input: ad1816_start_input, + prepare_for_input: ad1816_prepare_for_input, + prepare_for_output: ad1816_prepare_for_output, + halt_io: ad1816_halt, + halt_input: ad1816_halt_input, + halt_output: ad1816_halt_output, + trigger: ad1816_trigger, + set_speed: ad1816_set_speed, + set_bits: ad1816_set_bits, + set_channels: ad1816_set_channels, +}; + + +/* ------------------------------------------------------------------- */ + +/* Interrupt handler */ + + +static void ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy) +{ + unsigned char status; + ad1816_info *devc; + int dev; + unsigned long flags; + + + if (irq < 0 || irq > 15) { + printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq); + return; + } + + dev = irq2dev[irq]; + + if (dev < 0 || dev >= num_audiodevs) { + printk(KERN_WARNING "ad1816: IRQ2AD1816-mapping failed for " + "irq %d device %d\n", irq,dev); + return; + } + + devc = (ad1816_info *) audio_devs[dev]->devc; + + save_flags(flags); + cli(); + + /* read interrupt register */ + status = inb (devc->base+1); + /* Clear all interrupt */ + outb (~status, devc->base+1); + + DEBUGNOISE (printk("ad1816: Got interrupt subclass %d\n",status)); + + devc->irq_ok=1; + + if (status == 0) + DEBUGWARN(printk ("ad1816: interrupt: Got interrupt, but no reason?\n")); + + if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64)) + DMAbuf_inputintr (dev); + + if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128)) + DMAbuf_outputintr (dev, 1); + + restore_flags(flags); +} + +/* ------------------------------------------------------------------- */ + +/* Mixer stuff */ + +struct mixer_def { + unsigned int regno: 7; + unsigned int polarity:1; /* 0=normal, 1=reversed */ + unsigned int bitpos:4; + unsigned int nbits:4; +}; + +static char mix_cvt[101] = { + 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, + 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, + 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, + 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, + 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, + 100 +}; + +typedef struct mixer_def mixer_ent; + +/* + * Most of the mixer entries work in backwards. Setting the polarity field + * makes them to work correctly. + * + * The channel numbering used by individual soundcards is not fixed. Some + * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. + * The current version doesn't try to compensate this. + */ + +#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r) \ + {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}} + + +mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = { +MIX_ENT(SOUND_MIXER_VOLUME, 14, 1, 8, 5, 14, 1, 0, 5), +MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 5, 1, 8, 6, 5, 1, 0, 6), +MIX_ENT(SOUND_MIXER_PCM, 4, 1, 8, 6, 4, 1, 0, 6), +MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 18, 1, 8, 5, 18, 1, 0, 5), +MIX_ENT(SOUND_MIXER_MIC, 19, 1, 8, 5, 19, 1, 0, 5), +MIX_ENT(SOUND_MIXER_CD, 15, 1, 8, 5, 15, 1, 0, 5), +MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 20, 0, 8, 4, 20, 0, 0, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 17, 1, 8, 5, 17, 1, 0, 5), +MIX_ENT(SOUND_MIXER_LINE2, 16, 1, 8, 5, 16, 1, 0, 5), +MIX_ENT(SOUND_MIXER_LINE3, 39, 0, 9, 4, 39, 1, 0, 5) +}; + + +static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] = +{ + 0x4343, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x0000, /* FM */ + 0x4343, /* PCM */ + 0x0000, /* PC Speaker */ + 0x0000, /* Ext Line */ + 0x0000, /* Mic */ + 0x0000, /* CD */ + 0x0000, /* Recording monitor */ + 0x0000, /* SB PCM */ + 0x0000, /* Recording level */ + 0x0000, /* Input gain */ + 0x0000, /* Output gain */ + 0x0000, /* Line1 */ + 0x0000, /* Line2 */ + 0x0000 /* Line3 (usually line in)*/ +}; + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + + + +static int +ad1816_set_recmask (ad1816_info * devc, int mask) +{ + unsigned char recdev; + int i, n; + + mask &= devc->supported_rec_devices; + + n = 0; + /* Count selected device bits */ + for (i = 0; i < 32; i++) + if (mask & (1 << i)) + n++; + + if (n == 0) + mask = SOUND_MASK_MIC; + else if (n != 1) { /* Too many devices selected */ + /* Filter out active settings */ + mask &= ~devc->recmask; + + n = 0; + /* Count selected device bits */ + for (i = 0; i < 32; i++) + if (mask & (1 << i)) + n++; + + if (n != 1) + mask = SOUND_MASK_MIC; + } + + switch (mask) { + case SOUND_MASK_MIC: + recdev = 5; + break; + + case SOUND_MASK_LINE: + recdev = 0; + break; + + case SOUND_MASK_CD: + recdev = 2; + break; + + case SOUND_MASK_LINE1: + recdev = 4; + break; + + case SOUND_MASK_LINE2: + recdev = 3; + break; + + case SOUND_MASK_VOLUME: + recdev = 1; + break; + + default: + mask = SOUND_MASK_MIC; + recdev = 5; + } + + recdev <<= 4; + ad_write (devc, 20, + (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8)); + + devc->recmask = mask; + return mask; +} + +static void +change_bits (int *regval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + + /* Reverse polarity*/ + + if (mix_devices[dev][chn].polarity == 1) + newval = 100 - newval; + + mask = (1 << mix_devices[dev][chn].nbits) - 1; + shift = mix_devices[dev][chn].bitpos; + /* Scale it */ + newval = (int) ((newval * mask) + 50) / 100; + /* Clear bits */ + *regval &= ~(mask << shift); + /* Set new value */ + *regval |= (newval & mask) << shift; +} + +static int +ad1816_mixer_get (ad1816_info * devc, int dev) +{ + DEBUGINFO(printk("ad1816: mixer_get called!\n")); + + /* range check + supported mixer check */ + if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) + return (-(EINVAL)); + if (!((1 << dev) & devc->supported_devices)) + return -(EINVAL); + + return devc->levels[dev]; +} + +static int +ad1816_mixer_set (ad1816_info * devc, int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retvol; + + int regoffs; + int val; + int valmute; + + DEBUGINFO(printk("ad1816: mixer_set called!\n")); + + if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) + return -(EINVAL); + + if (left > 100) + left = 100; + if (left < 0) + left = 0; + if (right > 100) + right = 100; + if (right < 0) + right = 0; + + /* Mono control */ + if (mix_devices[dev][RIGHT_CHN].nbits == 0) + right = left; + retvol = left | (right << 8); + + /* Scale it */ + + left = mix_cvt[left]; + right = mix_cvt[right]; + + /* reject all mixers that are not supported */ + if (!(devc->supported_devices & (1 << dev))) + return -(EINVAL); + + /* sanity check */ + if (mix_devices[dev][LEFT_CHN].nbits == 0) + return -(EINVAL); + + /* keep precise volume internal */ + devc->levels[dev] = retvol; + + /* Set the left channel */ + regoffs = mix_devices[dev][LEFT_CHN].regno; + val = ad_read (devc, regoffs); + change_bits (&val, dev, LEFT_CHN, left); + + valmute=val; + + /* Mute bit masking on some registers */ + if ( regoffs==5 || regoffs==14 || regoffs==15 || + regoffs==16 || regoffs==17 || regoffs==18 || + regoffs==19 || regoffs==39) { + if (left==0) + valmute |= 0x8000; + else + valmute &= ~0x8000; + } + ad_write (devc, regoffs, valmute); /* mute */ + + /* + * Set the right channel + */ + + /* Was just a mono channel */ + if (mix_devices[dev][RIGHT_CHN].nbits == 0) + return retvol; + + regoffs = mix_devices[dev][RIGHT_CHN].regno; + val = ad_read (devc, regoffs); + change_bits (&val, dev, RIGHT_CHN, right); + + valmute=val; + if ( regoffs==5 || regoffs==14 || regoffs==15 || + regoffs==16 || regoffs==17 || regoffs==18 || + regoffs==19 || regoffs==39) { + if (right==0) + valmute |= 0x80; + else + valmute &= ~0x80; + } + ad_write (devc, regoffs, valmute); /* mute */ + + return retvol; +} + +#define MIXER_DEVICES ( SOUND_MASK_VOLUME | \ + SOUND_MASK_SYNTH | \ + SOUND_MASK_PCM | \ + SOUND_MASK_LINE | \ + SOUND_MASK_LINE1 | \ + SOUND_MASK_LINE2 | \ + SOUND_MASK_LINE3 | \ + SOUND_MASK_MIC | \ + SOUND_MASK_CD | \ + SOUND_MASK_RECLEV \ + ) +#define REC_DEVICES ( SOUND_MASK_LINE2 |\ + SOUND_MASK_LINE |\ + SOUND_MASK_LINE1 |\ + SOUND_MASK_MIC |\ + SOUND_MASK_CD |\ + SOUND_MASK_VOLUME \ + ) + +static void +ad1816_mixer_reset (ad1816_info * devc) +{ + int i; + + devc->supported_devices = MIXER_DEVICES; + + devc->supported_rec_devices = REC_DEVICES; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (devc->supported_devices & (1 << i)) + ad1816_mixer_set (devc, i, default_mixer_levels[i]); + ad1816_set_recmask (devc, SOUND_MASK_MIC); +} + +static int +ad1816_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + ad1816_info *devc = mixer_devs[dev]->devc; + int val; + + DEBUGINFO(printk("ad1816: mixer_ioctl called!\n")); + + /* Mixer ioctl */ + if (((cmd >> 8) & 0xff) == 'M') { + + /* set ioctl */ + if (_SIOC_DIR (cmd) & _SIOC_WRITE) { + switch (cmd & 0xff){ + case SOUND_MIXER_RECSRC: + + if (get_user(val, (int *)arg)) + return -EFAULT; + val=ad1816_set_recmask (devc, val); + return put_user(val, (int *)arg); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0) + return val; + else + return put_user(val, (int *)arg); + } + } else { + /* read ioctl */ + switch (cmd & 0xff) { + + case SOUND_MIXER_RECSRC: + val=devc->recmask; + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_DEVMASK: + val=devc->supported_devices; + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_STEREODEVS: + val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_RECMASK: + val=devc->supported_rec_devices; + return put_user(val, (int *)arg); + break; + + case SOUND_MIXER_CAPS: + val=SOUND_CAP_EXCL_INPUT; + return put_user(val, (int *)arg); + break; + + default: + if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0) + return val; + else + return put_user(val, (int *)arg); + } + } + } else + /* not for mixer */ + return -(EINVAL); +} + +/* ------------------------------------------------------------------- */ + +/* Mixer structure */ + +static struct mixer_operations ad1816_mixer_operations = { + owner: THIS_MODULE, + id: "AD1816", + name: "AD1816 Mixer", + ioctl: ad1816_mixer_ioctl +}; + + +/* ------------------------------------------------------------------- */ + +/* stuff for card recognition, init and unloading */ + + +/* replace with probe routine */ +static int __init probe_ad1816 ( struct address_info *hw_config ) +{ + ad1816_info *devc = &dev_info[nr_ad1816_devs]; + int io_base=hw_config->io_base; + int *osp=hw_config->osp; + int tmp; + + printk(KERN_INFO "ad1816: AD1816 sounddriver " + "Copyright (C) 1998 by Thorsten Knabe\n"); + printk(KERN_INFO "ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, " + "clockfreq=%d, options=%d isadmabug=%d\n", + hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma2, + ad1816_clockfreq, + options, + isa_dma_bridge_buggy); + + if (!request_region(io_base, 16, "AD1816 Sound")) { + printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", + io_base); + goto err; + } + + DEBUGLOG(printk ("ad1816: detect(%x)\n", io_base)); + + if (nr_ad1816_devs >= MAX_AUDIO_DEV) { + printk(KERN_WARNING "ad1816: detect error - step 0\n"); + goto out_release_region; + } + + devc->base = io_base; + devc->irq_ok = 0; + devc->irq = 0; + devc->opened = 0; + devc->osp = osp; + + /* base+0: bit 1 must be set but not 255 */ + tmp=inb(devc->base); + if ( (tmp&0x80)==0 || tmp==255 ) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 or chip is not active (Test 0)\n")); + goto out_release_region; + } + + + /* writes to ireg 8 are copied to ireg 9 */ + ad_write(devc,8,12345); + if (ad_read(devc,9)!=12345) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 1)\n")); + goto out_release_region; + } + + /* writes to ireg 8 are copied to ireg 9 */ + ad_write(devc,8,54321); + if (ad_read(devc,9)!=54321) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n")); + goto out_release_region; + } + + + /* writes to ireg 10 are copied to ireg 11 */ + ad_write(devc,10,54321); + if (ad_read(devc,11)!=54321) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 3)\n")); + goto out_release_region; + } + + /* writes to ireg 10 are copied to ireg 11 */ + ad_write(devc,10,12345); + if (ad_read(devc,11)!=12345) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n")); + goto out_release_region; + } + + /* bit in base +1 cannot be set to 1 */ + tmp=inb(devc->base+1); + outb(0xff,devc->base+1); + if (inb(devc->base+1)!=tmp) { + DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 5)\n")); + goto out_release_region; + } + + + DEBUGLOG (printk ("ad1816: detect() - Detected OK\n")); + DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45))); + + /* detection was successful */ + return 1; +out_release_region: + release_region(io_base, 16); + /* detection was NOT successful */ +err: return 0; +} + + +/* allocate resources from the kernel. If any allocation fails, free + all allocated resources and exit attach. + + */ + +static void __init attach_ad1816 (struct address_info *hw_config) +{ + int my_dev; + char dev_name[100]; + ad1816_info *devc = &dev_info[nr_ad1816_devs]; + + devc->base = hw_config->io_base; + + /* disable all interrupts */ + ad_write(devc,1,0); + + /* Clear pending interrupts */ + outb (0, devc->base+1); + + /* allocate irq */ + if (hw_config->irq < 0 || hw_config->irq > 15) + goto out_release_region; + if (request_irq(hw_config->irq, ad1816_interrupt,0, + "SoundPort", hw_config->osp) < 0) { + printk(KERN_WARNING "ad1816: IRQ in use\n"); + goto out_release_region; + } + devc->irq=hw_config->irq; + + /* DMA stuff */ + if (sound_alloc_dma (hw_config->dma, "Sound System")) { + printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", + hw_config->dma); + goto out_free_irq; + } + devc->dma_playback=hw_config->dma; + + if ( hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) { + if (sound_alloc_dma(hw_config->dma2, + "Sound System (capture)")) { + printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", + hw_config->dma2); + goto out_free_dma; + } + devc->dma_capture=hw_config->dma2; + devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX; + } else { + devc->dma_capture=-1; + devc->audio_mode=DMA_AUTOMODE; + } + + sprintf (dev_name,"AD1816 audio driver"); + + conf_printf2 (dev_name, + devc->base, devc->irq, hw_config->dma, hw_config->dma2); + + /* register device */ + if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION, + dev_name, + &ad1816_audio_driver, + sizeof (struct audio_driver), + devc->audio_mode, + ad_format_mask, + devc, + hw_config->dma, + hw_config->dma2)) < 0) { + printk(KERN_WARNING "ad1816: Can't install sound driver\n"); + goto out_free_dma_2; + } + + /* fill rest of structure with reasonable default values */ + irq2dev[hw_config->irq] = devc->dev_no = my_dev; + devc->opened = 0; + devc->irq_ok = 0; + devc->osp = hw_config->osp; + nr_ad1816_devs++; + + ad_write(devc,32,0x80f0); /* sound system mode */ + if (options&1) { + ad_write(devc,33,0); /* disable all audiosources for dsp */ + } else { + ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */ + } + ad_write(devc,4,0x8080); /* default values for volumes (muted)*/ + ad_write(devc,5,0x8080); + ad_write(devc,6,0x8080); + ad_write(devc,7,0x8080); + ad_write(devc,15,0x8888); + ad_write(devc,16,0x8888); + ad_write(devc,17,0x8888); + ad_write(devc,18,0x8888); + ad_write(devc,19,0xc888); /* +20db mic active */ + ad_write(devc,14,0x0000); /* Master volume unmuted */ + ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */ + ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */ + outb(0x10,devc->base+8); /* set dma mode */ + outb(0x10,devc->base+9); + + /* enable capture + playback interrupt */ + ad_write(devc,1,0xc000); + + /* set mixer defaults */ + ad1816_mixer_reset (devc); + + /* register mixer */ + if ((audio_devs[my_dev]->mixer_dev=sound_install_mixer( + MIXER_DRIVER_VERSION, + dev_name, + &ad1816_mixer_operations, + sizeof (struct mixer_operations), + devc)) >= 0) { + audio_devs[my_dev]->min_fragment = 0; + } +out: return; +out_free_dma_2: + if (devc->dma_capture >= 0) + sound_free_dma(hw_config->dma2); +out_free_dma: + sound_free_dma(hw_config->dma); +out_free_irq: + free_irq(hw_config->irq,hw_config->osp); +out_release_region: + release_region(hw_config->io_base, 16); + goto out; +} + +static void __exit unload_card(ad1816_info *devc) +{ + int mixer, dev = 0; + + if (devc != NULL) { + DEBUGLOG (printk("ad1816: Unloading card at base=%x\n",devc->base)); + + dev = devc->dev_no; + mixer = audio_devs[dev]->mixer_dev; + + /* unreg mixer*/ + if(mixer>=0) { + sound_unload_mixerdev(mixer); + } + sound_unload_audiodev(dev); + + /* free dma channels */ + if (devc->dma_capture>=0) { + sound_free_dma(devc->dma_capture); + } + + /* card wont get added if resources could not be allocated + thus we need not ckeck if allocation was successful */ + sound_free_dma (devc->dma_playback); + free_irq(devc->irq, devc->osp); + release_region (devc->base, 16); + + DEBUGLOG (printk("ad1816: Unloading card at base=%x was successful\n",devc->base)); + + } else + printk(KERN_WARNING "ad1816: no device/card specified\n"); +} + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; + +#ifdef __ISAPNP__ +struct pci_dev *ad1816_dev = NULL; + +static int activated = 1; + +static int isapnp = 1; +static int isapnpjump = 0; + +MODULE_PARM(isapnp, "i"); +MODULE_PARM(isapnpjump, "i"); + +#else +static int isapnp = 0; +#endif + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(ad1816_clockfreq,"i"); +MODULE_PARM(options,"i"); + +#ifdef __ISAPNP__ + +static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +{ + int err; + + if(dev->active) { + activated = 0; + return(dev); + } + + if((err = dev->activate(dev)) < 0) { + printk(KERN_ERR "ad1816: %s %s config failed (out of resources?)[%d]\n", + devname, resname, err); + dev->deactivate(dev); + return(NULL); + } + + return(dev); +} + +static struct pci_dev *ad1816_init_generic(struct pci_bus *bus, struct pci_dev *card, + struct address_info *hw_config) +{ + if((ad1816_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) { + ad1816_dev->prepare(ad1816_dev); + + if((ad1816_dev = activate_dev("Analog Devices 1816(A)", "ad1816", ad1816_dev))) { + hw_config->io_base = ad1816_dev->resource[2].start; + hw_config->irq = ad1816_dev->irq_resource[0].start; + hw_config->dma = ad1816_dev->dma_resource[0].start; + hw_config->dma2 = ad1816_dev->dma_resource[1].start; + } + } + + return(ad1816_dev); +} + +static struct ad1816_data { + struct pci_dev * (*initfunc)(struct pci_bus*, struct pci_dev *, struct address_info *); + char *name; +} ad1816_pnp_data[] __initdata = { + { &ad1816_init_generic, "Analog Devices 1815" }, + { &ad1816_init_generic, "Analog Devices 1816A" } +}; + +static struct { + unsigned short card_vendor, card_device; + unsigned short vendor; + unsigned short function; + struct ad1816_data *data; +} isapnp_ad1816_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), + &ad1816_pnp_data[0] }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), + &ad1816_pnp_data[1] }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list); + +static int __init ad1816_init_isapnp(struct address_info *hw_config, + struct pci_bus *bus, struct pci_dev *card, int slot) +{ + struct pci_dev *idev = NULL; + + /* You missed the init func? That's bad. */ + if(isapnp_ad1816_list[slot].data->initfunc) { + char *busname = bus->name[0] ? bus->name : isapnp_ad1816_list[slot].data->name; + + printk(KERN_INFO "ad1816: %s detected\n", busname); + + /* Initialize this baby. */ + if((idev = isapnp_ad1816_list[slot].data->initfunc(bus, card, hw_config))) { + /* We got it. */ + + printk(KERN_NOTICE "ad1816: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", + busname, + hw_config->io_base, hw_config->irq, hw_config->dma, + hw_config->dma2); + return 1; + } else + printk(KERN_INFO "ad1816: Failed to initialize %s\n", busname); + } else + printk(KERN_ERR "ad1816: Bad entry in ad1816.c PnP table\n"); + + return 0; +} + +/* + * Actually this routine will detect and configure only the first card with successful + * initialization. isapnpjump could be used to jump to a specific entry. + * Please always add entries at the end of the array. + * Should this be fixed? - azummo + */ + +int __init ad1816_probe_isapnp(struct address_info *hw_config) +{ + int i; + + /* Count entries in isapnp_ad1816_list */ + for (i = 0; isapnp_ad1816_list[i].vendor != 0; i++) + ; + /* Check and adjust isapnpjump */ + if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) { + printk(KERN_ERR "ad1816: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1); + isapnpjump = 0; + } + + for (i = isapnpjump; isapnp_ad1816_list[i].vendor != 0; i++) { + struct pci_dev *card = NULL; + + while ((card = isapnp_find_dev(NULL, isapnp_ad1816_list[i].vendor, + isapnp_ad1816_list[i].function, card))) + if(ad1816_init_isapnp(hw_config, card->bus, card, i)) + return 0; + } + + return -ENODEV; +} +#endif + +static int __init init_ad1816(void) +{ + +#ifdef __ISAPNP__ + if(isapnp && (ad1816_probe_isapnp(&cfg) < 0) ) { + printk(KERN_NOTICE "ad1816: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + } +#endif + + if( isapnp == 0) { + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + } + + if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.dma2 == -1) { + printk(KERN_INFO "ad1816: dma, dma2, irq and io must be set.\n"); + return -EINVAL; + } + + if (probe_ad1816(&cfg) == 0) { + return -ENODEV; + } + + attach_ad1816(&cfg); + + return 0; +} + +static void __exit cleanup_ad1816 (void) +{ + int i; + ad1816_info *devc = NULL; + + /* remove any soundcard */ + for (i = 0; i < nr_ad1816_devs; i++) { + devc = &dev_info[i]; + unload_card(devc); + } + nr_ad1816_devs=0; + +#ifdef __ISAPNP__ + if(activated) + if(ad1816_dev) + ad1816_dev->deactivate(ad1816_dev); +#endif +} + +module_init(init_ad1816); +module_exit(cleanup_ad1816); + +#ifndef MODULE +static int __init setup_ad1816(char *str) +{ + /* io, irq, dma, dma2 */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + + return 1; +} + +__setup("ad1816=", setup_ad1816); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/ad1848.c linux-2.4.19-pre5-mjc/sound/oss/ad1848.c --- linux/sound/oss/ad1848.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ad1848.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,3177 @@ +/* + * sound/ad1848.c + * + * The low level driver for the AD1848/CS4248 codec chip which + * is used for example in the MS Sound System. + * + * The CS4231 which is used in the GUS MAX and some other cards is + * upwards compatible with AD1848 and this driver is able to drive it. + * + * CS4231A and AD1845 are upward compatible with CS4231. However + * the new features of these chips are different. + * + * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). + * CS4232A is an improved version of CS4232. + * + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * general sleep/wakeup clean up. + * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free + * of irqs. Use dev_id. + * Christoph Hellwig : adapted to module_init/module_exit + * Aki Laukkanen : added power management support + * Arnaldo C. de Melo : added missing restore_flags in ad1848_resume + * Miguel Freitas : added ISA PnP support + * Alan Cox : Added CS4236->4239 identification + * Daniel T. Cobra : Alernate config/mixer for later chips + * Alan Cox : Merged chip idents and config code + * + * TODO + * APM save restore assist code on IBM thinkpad + * + * Status: + * Tested. Believed fully functional. + */ + +#include +#include +#include +#include +#include +#include + +#define DEB(x) +#define DEB1(x) +#include "sound_config.h" + +#include "ad1848.h" +#include "ad1848_mixer.h" + +typedef struct +{ + int base; + int irq; + int dma1, dma2; + int dual_dma; /* 1, when two DMA channels allocated */ + int subtype; + unsigned char MCE_bit; + unsigned char saved_regs[64]; /* Includes extended register space */ + int debug_flag; + + int audio_flags; + int record_dev, playback_dev; + + int xfer_count; + int audio_mode; + int open_mode; + int intr_active; + char *chip_name, *name; + int model; +#define MD_1848 1 +#define MD_4231 2 +#define MD_4231A 3 +#define MD_1845 4 +#define MD_4232 5 +#define MD_C930 6 +#define MD_IWAVE 7 +#define MD_4235 8 /* Crystal Audio CS4235 */ +#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/ +#define MD_4236 10 /* 4236 and higher */ +#define MD_42xB 11 /* CS 42xB */ +#define MD_4239 12 /* CS4239 */ + + /* Mixer parameters */ + int recmask; + int supported_devices, orig_devices; + int supported_rec_devices, orig_rec_devices; + int *levels; + short mixer_reroute[32]; + int dev_no; + volatile unsigned long timer_ticks; + int timer_running; + int irq_ok; + mixer_ents *mix_devices; + int mixer_output_port; + + /* Power management */ + struct pm_dev *pmdev; +} ad1848_info; + +typedef struct ad1848_port_info +{ + int open_mode; + int speed; + unsigned char speed_bits; + int channels; + int audio_format; + unsigned char format_bits; +} +ad1848_port_info; + +static struct address_info cfg; +static int nr_ad1848_devs; + +int deskpro_xl; +int deskpro_m; +int soundpro; + +static volatile signed char irq2dev[17] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#ifndef EXCLUDE_TIMERS +static int timer_installed = -1; +#endif + +static int loaded; + +static int ad_format_mask[13 /*devc->model */ ] = +{ + 0, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */ + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE /* CS4235 */, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM +}; + +static ad1848_info adev_info[MAX_AUDIO_DEV]; + +#define io_Index_Addr(d) ((d)->base) +#define io_Indexed_Data(d) ((d)->base+1) +#define io_Status(d) ((d)->base+2) +#define io_Polled_IO(d) ((d)->base+3) + +static struct { + unsigned char flags; +#define CAP_F_TIMER 0x01 +} capabilities [10 /*devc->model */ ] = { + {0} + ,{0} /* MD_1848 */ + ,{CAP_F_TIMER} /* MD_4231 */ + ,{CAP_F_TIMER} /* MD_4231A */ + ,{CAP_F_TIMER} /* MD_1845 */ + ,{CAP_F_TIMER} /* MD_4232 */ + ,{0} /* MD_C930 */ + ,{CAP_F_TIMER} /* MD_IWAVE */ + ,{0} /* MD_4235 */ + ,{CAP_F_TIMER} /* MD_1845_SSCAPE */ +}; + +#ifdef __ISAPNP__ +static int isapnp = 1; +static int isapnpjump = 0; +static int reverse = 0; + +static int audio_activated = 0; +#else +static int isapnp = 0; +#endif + + + +static int ad1848_open(int dev, int mode); +static void ad1848_close(int dev); +static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag); +static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag); +static int ad1848_prepare_for_output(int dev, int bsize, int bcount); +static int ad1848_prepare_for_input(int dev, int bsize, int bcount); +static void ad1848_halt(int dev); +static void ad1848_halt_input(int dev); +static void ad1848_halt_output(int dev); +static void ad1848_trigger(int dev, int bits); +static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); + +#ifndef EXCLUDE_TIMERS +static int ad1848_tmr_install(int dev); +static void ad1848_tmr_reprogram(int dev); +#endif + +static int ad_read(ad1848_info * devc, int reg) +{ + unsigned long flags; + int x; + int timeout = 900000; + + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + if(reg < 32) + { + outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + x = inb(io_Indexed_Data(devc)); + } + else + { + int xreg, xra; + + xreg = (reg & 0xff) - 32; + xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); + outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); + x = inb(io_Indexed_Data(devc)); + } + restore_flags(flags); + + return x; +} + +static void ad_write(ad1848_info * devc, int reg, int data) +{ + unsigned long flags; + int timeout = 900000; + + while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + if(reg < 32) + { + outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc)); + } + else + { + int xreg, xra; + + xreg = (reg & 0xff) - 32; + xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); + outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); + outb((unsigned char) (data & 0xff), io_Indexed_Data(devc)); + } + restore_flags(flags); +} + +static void wait_for_calibration(ad1848_info * devc) +{ + int timeout = 0; + + /* + * Wait until the auto calibration process has finished. + * + * 1) Wait until the chip becomes ready (reads don't return 0x80). + * 2) Wait until the ACI bit of I11 gets on and then off. + */ + + timeout = 100000; + while (timeout > 0 && inb(devc->base) == 0x80) + timeout--; + if (inb(devc->base) & 0x80) + printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n"); + + timeout = 100; + while (timeout > 0 && !(ad_read(devc, 11) & 0x20)) + timeout--; + if (!(ad_read(devc, 11) & 0x20)) + return; + + timeout = 80000; + while (timeout > 0 && (ad_read(devc, 11) & 0x20)) + timeout--; + if (ad_read(devc, 11) & 0x20) + if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE)) + printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n"); +} + +static void ad_mute(ad1848_info * devc) +{ + int i; + unsigned char prev; + + /* + * Save old register settings and mute output channels + */ + + for (i = 6; i < 8; i++) + { + prev = devc->saved_regs[i] = ad_read(devc, i); + } + +} + +static void ad_unmute(ad1848_info * devc) +{ +} + +static void ad_enter_MCE(ad1848_info * devc) +{ + unsigned long flags; + int timeout = 1000; + unsigned short prev; + + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + devc->MCE_bit = 0x40; + prev = inb(io_Index_Addr(devc)); + if (prev & 0x40) + { + restore_flags(flags); + return; + } + outb((devc->MCE_bit), io_Index_Addr(devc)); + restore_flags(flags); +} + +static void ad_leave_MCE(ad1848_info * devc) +{ + unsigned long flags; + unsigned char prev, acal; + int timeout = 1000; + + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + acal = ad_read(devc, 9); + + devc->MCE_bit = 0x00; + prev = inb(io_Index_Addr(devc)); + outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ + + if ((prev & 0x40) == 0) /* Not in MCE mode */ + { + restore_flags(flags); + return; + } + outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ + if (acal & 0x08) /* Auto calibration is enabled */ + wait_for_calibration(devc); + restore_flags(flags); +} + +static int ad1848_set_recmask(ad1848_info * devc, int mask) +{ + unsigned char recdev; + int i, n; + + mask &= devc->supported_rec_devices; + + /* Rename the mixer bits if necessary */ + for (i = 0; i < 32; i++) + { + if (devc->mixer_reroute[i] != i) + { + if (mask & (1 << i)) + { + mask &= ~(1 << i); + mask |= (1 << devc->mixer_reroute[i]); + } + } + } + + n = 0; + for (i = 0; i < 32; i++) /* Count selected device bits */ + if (mask & (1 << i)) + n++; + + if (!soundpro) { + if (n == 0) + mask = SOUND_MASK_MIC; + else if (n != 1) { /* Too many devices selected */ + mask &= ~devc->recmask; /* Filter out active settings */ + + n = 0; + for (i = 0; i < 32; i++) /* Count selected device bits */ + if (mask & (1 << i)) + n++; + + if (n != 1) + mask = SOUND_MASK_MIC; + } + switch (mask) { + case SOUND_MASK_MIC: + recdev = 2; + break; + + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + recdev = 0; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + recdev = 1; + break; + + case SOUND_MASK_IMIX: + recdev = 3; + break; + + default: + mask = SOUND_MASK_MIC; + recdev = 2; + } + + recdev <<= 6; + ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev); + ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev); + } else { /* soundpro */ + unsigned char val; + int set_rec_bit; + int j; + + for (i = 0; i < 32; i++) { /* For each bit */ + if ((devc->supported_rec_devices & (1 << i)) == 0) + continue; /* Device not supported */ + + for (j = LEFT_CHN; j <= RIGHT_CHN; j++) { + if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */ + continue; + + /* + * This is tricky: + * set_rec_bit becomes 1 if the corresponding bit in mask is set + * then it gets flipped if the polarity is inverse + */ + set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol; + + val = ad_read(devc, devc->mix_devices[i][j].recreg); + val &= ~(1 << devc->mix_devices[i][j].recpos); + val |= (set_rec_bit << devc->mix_devices[i][j].recpos); + ad_write(devc, devc->mix_devices[i][j].recreg, val); + } + } + } + + /* Rename the mixer bits back if necessary */ + for (i = 0; i < 32; i++) + { + if (devc->mixer_reroute[i] != i) + { + if (mask & (1 << devc->mixer_reroute[i])) + { + mask &= ~(1 << devc->mixer_reroute[i]); + mask |= (1 << i); + } + } + } + devc->recmask = mask; + return mask; +} + +static void change_bits(ad1848_info * devc, unsigned char *regval, + unsigned char *muteval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + int mute; + int mutemask; + int set_mute_bit; + + set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol; + + if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */ + newval = 100 - newval; + + mask = (1 << devc->mix_devices[dev][chn].nbits) - 1; + shift = devc->mix_devices[dev][chn].bitpos; + + if (devc->mix_devices[dev][chn].mutepos == 8) + { /* if there is no mute bit */ + mute = 0; /* No mute bit; do nothing special */ + mutemask = ~0; /* No mute bit; do nothing special */ + } + else + { + mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos); + mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos); + } + + newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ + *regval &= ~(mask << shift); /* Clear bits */ + *regval |= (newval & mask) << shift; /* Set new value */ + + *muteval &= mutemask; + *muteval |= mute; +} + +static int ad1848_mixer_get(ad1848_info * devc, int dev) +{ + if (!((1 << dev) & devc->supported_devices)) + return -EINVAL; + + dev = devc->mixer_reroute[dev]; + + return devc->levels[dev]; +} + +static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel) +{ + int regoffs, muteregoffs; + unsigned char val, muteval; + + regoffs = devc->mix_devices[dev][channel].regno; + muteregoffs = devc->mix_devices[dev][channel].mutereg; + val = ad_read(devc, regoffs); + + if (muteregoffs != regoffs) { + muteval = ad_read(devc, muteregoffs); + change_bits(devc, &val, &muteval, dev, channel, value); + } + else + change_bits(devc, &val, &val, dev, channel, value); + + ad_write(devc, regoffs, val); + devc->saved_regs[regoffs] = val; + if (muteregoffs != regoffs) { + ad_write(devc, muteregoffs, muteval); + devc->saved_regs[muteregoffs] = muteval; + } +} + +static int ad1848_mixer_set(ad1848_info * devc, int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retvol; + + if (dev > 31) + return -EINVAL; + + if (!(devc->supported_devices & (1 << dev))) + return -EINVAL; + + dev = devc->mixer_reroute[dev]; + + if (devc->mix_devices[dev][LEFT_CHN].nbits == 0) + return -EINVAL; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ + right = left; + + retvol = left | (right << 8); + + /* Scale volumes */ + left = mix_cvt[left]; + right = mix_cvt[right]; + + devc->levels[dev] = retvol; + + /* + * Set the left channel + */ + ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN); + + /* + * Set the right channel + */ + if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) + goto out; + ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN); + + out: + return retvol; +} + +static void ad1848_mixer_reset(ad1848_info * devc) +{ + int i; + char name[32]; + + devc->mix_devices = &(ad1848_mix_devices[0]); + + sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs); + + for (i = 0; i < 32; i++) + devc->mixer_reroute[i] = i; + + devc->supported_rec_devices = MODE1_REC_DEVICES; + + switch (devc->model) + { + case MD_4231: + case MD_4231A: + case MD_1845: + case MD_1845_SSCAPE: + devc->supported_devices = MODE2_MIXER_DEVICES; + break; + + case MD_C930: + devc->supported_devices = C930_MIXER_DEVICES; + devc->mix_devices = &(c930_mix_devices[0]); + break; + + case MD_IWAVE: + devc->supported_devices = MODE3_MIXER_DEVICES; + devc->mix_devices = &(iwave_mix_devices[0]); + break; + + case MD_42xB: + case MD_4239: + devc->mix_devices = &(cs42xb_mix_devices[0]); + devc->supported_devices = MODE3_MIXER_DEVICES; + break; + case MD_4232: + case MD_4236: + devc->supported_devices = MODE3_MIXER_DEVICES; + break; + + case MD_1848: + if (soundpro) { + devc->supported_devices = SPRO_MIXER_DEVICES; + devc->supported_rec_devices = SPRO_REC_DEVICES; + devc->mix_devices = &(spro_mix_devices[0]); + break; + } + + default: + devc->supported_devices = MODE1_MIXER_DEVICES; + } + + devc->orig_devices = devc->supported_devices; + devc->orig_rec_devices = devc->supported_rec_devices; + + devc->levels = load_mixer_volumes(name, default_mixer_levels, 1); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + { + if (devc->supported_devices & (1 << i)) + ad1848_mixer_set(devc, i, devc->levels[i]); + } + + ad1848_set_recmask(devc, SOUND_MASK_MIC); + + devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT; + + if (!soundpro) { + if (devc->mixer_output_port & AUDIO_SPEAKER) + ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ + else + ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ + } else { + /* + * From the "wouldn't it be nice if the mixer API had (better) + * support for custom stuff" category + */ + /* Enable surround mode and SB16 mixer */ + ad_write(devc, 16, 0x60); + } +} + +static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + ad1848_info *devc = mixer_devs[dev]->devc; + int val; + + if (cmd == SOUND_MIXER_PRIVATE1) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val != 0xffff) + { + val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT); + devc->mixer_output_port = val; + val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */ + devc->mixer_output_port = val; + if (val & AUDIO_SPEAKER) + ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ + else + ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ + } + val = devc->mixer_output_port; + return put_user(val, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + return(ad1848_control(AD1848_MIXER_REROUTE, val)); + } + if (((cmd >> 8) & 0xff) == 'M') + { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = ad1848_set_recmask(devc, val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = ad1848_mixer_set(devc, cmd & 0xff, val); + break; + } + return put_user(val, (int *)arg); + } + else + { + switch (cmd & 0xff) + { + /* + * Return parameters + */ + + case SOUND_MIXER_RECSRC: + val = devc->recmask; + break; + + case SOUND_MIXER_DEVMASK: + val = devc->supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + val = devc->supported_devices; + if (devc->model != MD_C930) + val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + break; + + case SOUND_MIXER_RECMASK: + val = devc->supported_rec_devices; + break; + + case SOUND_MIXER_CAPS: + val=SOUND_CAP_EXCL_INPUT; + break; + + default: + val = ad1848_mixer_get(devc, cmd & 0xff); + break; + } + return put_user(val, (int *)arg); + } + } + else + return -EINVAL; +} + +static int ad1848_set_speed(int dev, int arg) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + /* + * The sampling speed is encoded in the least significant nibble of I8. The + * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other + * three bits select the divisor (indirectly): + * + * The available speeds are in the following table. Keep the speeds in + * the increasing order. + */ + typedef struct + { + int speed; + unsigned char bits; + } + speed_struct; + + static speed_struct speed_table[] = + { + {5510, (0 << 1) | 1}, + {5510, (0 << 1) | 1}, + {6620, (7 << 1) | 1}, + {8000, (0 << 1) | 0}, + {9600, (7 << 1) | 0}, + {11025, (1 << 1) | 1}, + {16000, (1 << 1) | 0}, + {18900, (2 << 1) | 1}, + {22050, (3 << 1) | 1}, + {27420, (2 << 1) | 0}, + {32000, (3 << 1) | 0}, + {33075, (6 << 1) | 1}, + {37800, (4 << 1) | 1}, + {44100, (5 << 1) | 1}, + {48000, (6 << 1) | 0} + }; + + int i, n, selected = -1; + + n = sizeof(speed_table) / sizeof(speed_struct); + + if (arg <= 0) + return portc->speed; + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */ + { + if (arg < 4000) + arg = 4000; + if (arg > 50000) + arg = 50000; + + portc->speed = arg; + portc->speed_bits = speed_table[3].bits; + return portc->speed; + } + if (arg < speed_table[0].speed) + selected = 0; + if (arg > speed_table[n - 1].speed) + selected = n - 1; + + for (i = 1 /*really */ ; selected == -1 && i < n; i++) + { + if (speed_table[i].speed == arg) + selected = i; + else if (speed_table[i].speed > arg) + { + int diff1, diff2; + + diff1 = arg - speed_table[i - 1].speed; + diff2 = speed_table[i].speed - arg; + + if (diff1 < diff2) + selected = i - 1; + else + selected = i; + } + } + if (selected == -1) + { + printk(KERN_WARNING "ad1848: Can't find speed???\n"); + selected = 3; + } + portc->speed = speed_table[selected].speed; + portc->speed_bits = speed_table[selected].bits; + return portc->speed; +} + +static short ad1848_set_channels(int dev, short arg) +{ + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + if (arg != 1 && arg != 2) + return portc->channels; + + portc->channels = arg; + return arg; +} + +static unsigned int ad1848_set_bits(int dev, unsigned int arg) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + static struct format_tbl + { + int format; + unsigned char bits; + } + format2bits[] = + { + { + 0, 0 + } + , + { + AFMT_MU_LAW, 1 + } + , + { + AFMT_A_LAW, 3 + } + , + { + AFMT_IMA_ADPCM, 5 + } + , + { + AFMT_U8, 0 + } + , + { + AFMT_S16_LE, 2 + } + , + { + AFMT_S16_BE, 6 + } + , + { + AFMT_S8, 0 + } + , + { + AFMT_U16_LE, 0 + } + , + { + AFMT_U16_BE, 0 + } + }; + int i, n = sizeof(format2bits) / sizeof(struct format_tbl); + + if (arg == 0) + return portc->audio_format; + + if (!(arg & ad_format_mask[devc->model])) + arg = AFMT_U8; + + portc->audio_format = arg; + + for (i = 0; i < n; i++) + if (format2bits[i].format == arg) + { + if ((portc->format_bits = format2bits[i].bits) == 0) + return portc->audio_format = AFMT_U8; /* Was not supported */ + + return arg; + } + /* Still hanging here. Something must be terribly wrong */ + portc->format_bits = 0; + return portc->audio_format = AFMT_U8; +} + +static struct audio_driver ad1848_audio_driver = +{ + owner: THIS_MODULE, + open: ad1848_open, + close: ad1848_close, + output_block: ad1848_output_block, + start_input: ad1848_start_input, + prepare_for_input: ad1848_prepare_for_input, + prepare_for_output: ad1848_prepare_for_output, + halt_io: ad1848_halt, + halt_input: ad1848_halt_input, + halt_output: ad1848_halt_output, + trigger: ad1848_trigger, + set_speed: ad1848_set_speed, + set_bits: ad1848_set_bits, + set_channels: ad1848_set_channels +}; + +static struct mixer_operations ad1848_mixer_operations = +{ + owner: THIS_MODULE, + id: "SOUNDPORT", + name: "AD1848/CS4248/CS4231", + ioctl: ad1848_mixer_ioctl +}; + +static int ad1848_open(int dev, int mode) +{ + ad1848_info *devc = NULL; + ad1848_port_info *portc; + unsigned long flags; + + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; + + devc = (ad1848_info *) audio_devs[dev]->devc; + portc = (ad1848_port_info *) audio_devs[dev]->portc; + + save_flags(flags); + cli(); + if (portc->open_mode || (devc->open_mode & mode)) + { + restore_flags(flags); + return -EBUSY; + } + devc->dual_dma = 0; + + if (audio_devs[dev]->flags & DMA_DUPLEX) + { + devc->dual_dma = 1; + } + devc->intr_active = 0; + devc->audio_mode = 0; + devc->open_mode |= mode; + portc->open_mode = mode; + ad1848_trigger(dev, 0); + + if (mode & OPEN_READ) + devc->record_dev = dev; + if (mode & OPEN_WRITE) + devc->playback_dev = dev; + restore_flags(flags); +/* + * Mute output until the playback really starts. This decreases clicking (hope so). + */ + ad_mute(devc); + + return 0; +} + +static void ad1848_close(int dev) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + DEB(printk("ad1848_close(void)\n")); + + save_flags(flags); + cli(); + + devc->intr_active = 0; + ad1848_halt(dev); + + devc->audio_mode = 0; + devc->open_mode &= ~portc->open_mode; + portc->open_mode = 0; + + ad_unmute(devc); + restore_flags(flags); +} + +static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + cnt = count; + + if (portc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (portc->channels > 1) + cnt >>= 1; + cnt--; + + if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && + intrflag && + cnt == devc->xfer_count) + { + devc->audio_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + save_flags(flags); + cli(); + + ad_write(devc, 15, (unsigned char) (cnt & 0xff)); + ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + + devc->xfer_count = cnt; + devc->audio_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + restore_flags(flags); +} + +static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + cnt = count; + if (portc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (portc->channels > 1) + cnt >>= 1; + cnt--; + + if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && + intrflag && + cnt == devc->xfer_count) + { + devc->audio_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + save_flags(flags); + cli(); + + if (devc->model == MD_1848) + { + ad_write(devc, 15, (unsigned char) (cnt & 0xff)); + ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + } + else + { + ad_write(devc, 31, (unsigned char) (cnt & 0xff)); + ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff)); + } + + ad_unmute(devc); + + devc->xfer_count = cnt; + devc->audio_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + restore_flags(flags); +} + +static int ad1848_prepare_for_output(int dev, int bsize, int bcount) +{ + int timeout; + unsigned char fs, old_fs, tmp = 0; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + ad_mute(devc); + + save_flags(flags); + cli(); + fs = portc->speed_bits | (portc->format_bits << 5); + + if (portc->channels > 1) + fs |= 0x10; + + ad_enter_MCE(devc); /* Enables changes to the format select reg */ + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */ + { + fs &= 0xf0; /* Mask off the rate select bits */ + + ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ + ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ + } + old_fs = ad_read(devc, 8); + + if (devc->model == MD_4232 || devc->model >= MD_4236) + { + tmp = ad_read(devc, 16); + ad_write(devc, 16, tmp | 0x30); + } + if (devc->model == MD_IWAVE) + ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ + + ad_write(devc, 8, fs); + + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + + if (devc->model >= MD_4232) + ad_write(devc, 16, tmp & ~0x30); + + ad_leave_MCE(devc); /* + * Starts the calibration process. + */ + restore_flags(flags); + devc->xfer_count = 0; + +#ifndef EXCLUDE_TIMERS + if (dev == timer_installed && devc->timer_running) + if ((fs & 0x01) != (old_fs & 0x01)) + { + ad1848_tmr_reprogram(dev); + } +#endif + ad1848_halt_output(dev); + return 0; +} + +static int ad1848_prepare_for_input(int dev, int bsize, int bcount) +{ + int timeout; + unsigned char fs, old_fs, tmp = 0; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + if (devc->audio_mode) + return 0; + + save_flags(flags); + cli(); + fs = portc->speed_bits | (portc->format_bits << 5); + + if (portc->channels > 1) + fs |= 0x10; + + ad_enter_MCE(devc); /* Enables changes to the format select reg */ + + if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */ + { + fs &= 0xf0; /* Mask off the rate select bits */ + + ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ + ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ + } + if (devc->model == MD_4232) + { + tmp = ad_read(devc, 16); + ad_write(devc, 16, tmp | 0x30); + } + if (devc->model == MD_IWAVE) + ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ + + /* + * If mode >= 2 (CS4231), set I28. It's the capture format register. + */ + + if (devc->model != MD_1848) + { + old_fs = ad_read(devc, 28); + ad_write(devc, 28, fs); + + /* + * Write to I28 starts resynchronization. Wait until it completes. + */ + + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + + if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE) + { + /* + * CS4231 compatible devices don't have separate sampling rate selection + * register for recording an playback. The I8 register is shared so we have to + * set the speed encoding bits of it too. + */ + unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0); + + ad_write(devc, 8, tmp); + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + } + } + else + { /* For AD1848 set I8. */ + + old_fs = ad_read(devc, 8); + ad_write(devc, 8, fs); + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + } + + if (devc->model == MD_4232) + ad_write(devc, 16, tmp & ~0x30); + + ad_leave_MCE(devc); /* + * Starts the calibration process. + */ + restore_flags(flags); + devc->xfer_count = 0; + +#ifndef EXCLUDE_TIMERS + if (dev == timer_installed && devc->timer_running) + { + if ((fs & 0x01) != (old_fs & 0x01)) + { + ad1848_tmr_reprogram(dev); + } + } +#endif + ad1848_halt_input(dev); + return 0; +} + +static void ad1848_halt(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + unsigned char bits = ad_read(devc, 9); + + if (bits & 0x01 && (portc->open_mode & OPEN_WRITE)) + ad1848_halt_output(dev); + + if (bits & 0x02 && (portc->open_mode & OPEN_READ)) + ad1848_halt_input(dev); + devc->audio_mode = 0; +} + +static void ad1848_halt_input(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long flags; + + if (!(ad_read(devc, 9) & 0x02)) + return; /* Capture not enabled */ + + save_flags(flags); + cli(); + + ad_mute(devc); + + { + int tmout; + + if(!isa_dma_bridge_buggy) + disable_dma(audio_devs[dev]->dmap_in->dma); + + for (tmout = 0; tmout < 100000; tmout++) + if (ad_read(devc, 11) & 0x10) + break; + ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */ + + if(!isa_dma_bridge_buggy) + enable_dma(audio_devs[dev]->dmap_in->dma); + devc->audio_mode &= ~PCM_ENABLE_INPUT; + } + + outb(0, io_Status(devc)); /* Clear interrupt status */ + outb(0, io_Status(devc)); /* Clear interrupt status */ + + devc->audio_mode &= ~PCM_ENABLE_INPUT; + + restore_flags(flags); +} + +static void ad1848_halt_output(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long flags; + + if (!(ad_read(devc, 9) & 0x01)) + return; /* Playback not enabled */ + + save_flags(flags); + cli(); + + ad_mute(devc); + { + int tmout; + + if(!isa_dma_bridge_buggy) + disable_dma(audio_devs[dev]->dmap_out->dma); + + for (tmout = 0; tmout < 100000; tmout++) + if (ad_read(devc, 11) & 0x10) + break; + ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */ + + if(!isa_dma_bridge_buggy) + enable_dma(audio_devs[dev]->dmap_out->dma); + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + } + + outb((0), io_Status(devc)); /* Clear interrupt status */ + outb((0), io_Status(devc)); /* Clear interrupt status */ + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + + restore_flags(flags); +} + +static void ad1848_trigger(int dev, int state) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + unsigned long flags; + unsigned char tmp, old; + + save_flags(flags); + cli(); + state &= devc->audio_mode; + + tmp = old = ad_read(devc, 9); + + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + tmp |= 0x02; + else + tmp &= ~0x02; + } + if (portc->open_mode & OPEN_WRITE) + { + if (state & PCM_ENABLE_OUTPUT) + tmp |= 0x01; + else + tmp &= ~0x01; + } + /* ad_mute(devc); */ + if (tmp != old) + { + ad_write(devc, 9, tmp); + ad_unmute(devc); + } + restore_flags(flags); +} + +static void ad1848_init_hw(ad1848_info * devc) +{ + int i; + int *init_values; + + /* + * Initial values for the indirect registers of CS4248/AD1848. + */ + static int init_values_a[] = + { + 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, + + /* Positions 16 to 31 just for CS4231/2 and ad1845 */ + 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static int init_values_b[] = + { + /* + Values for the newer chips + Some of the register initialization values were changed. In + order to get rid of the click that preceded PCM playback, + calibration was disabled on the 10th byte. On that same byte, + dual DMA was enabled; on the 11th byte, ADC dithering was + enabled, since that is theoretically desirable; on the 13th + byte, Mode 3 was selected, to enable access to extended + registers. + */ + 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /* + * Select initialisation data + */ + + init_values = init_values_a; + if(devc->model >= MD_4236) + init_values = init_values_b; + + for (i = 0; i < 16; i++) + ad_write(devc, i, init_values[i]); + + + ad_mute(devc); /* Initialize some variables */ + ad_unmute(devc); /* Leave it unmuted now */ + + if (devc->model > MD_1848) + { + if (devc->model == MD_1845_SSCAPE) + ad_write(devc, 12, ad_read(devc, 12) | 0x50); + else + ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ + + if (devc->model == MD_IWAVE) + ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ + + if (devc->model != MD_1845_SSCAPE) + for (i = 16; i < 32; i++) + ad_write(devc, i, init_values[i]); + + if (devc->model == MD_IWAVE) + ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ + } + if (devc->model > MD_1848) + { + if (devc->audio_flags & DMA_DUPLEX) + ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */ + else + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) + ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */ + + if (devc->model == MD_IWAVE) + { /* Some magic Interwave specific initialization */ + ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ + ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ + ad_write(devc, 17, 0xc2); /* Alternate feature enable */ + } + } + else + { + devc->audio_flags &= ~DMA_DUPLEX; + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + if (soundpro) + ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ + } + + outb((0), io_Status(devc)); /* Clear pending interrupts */ + + /* + * Toggle the MCE bit. It completes the initialization phase. + */ + + ad_enter_MCE(devc); /* In case the bit was off */ + ad_leave_MCE(devc); + + ad1848_mixer_reset(devc); +} + +int ad1848_detect(int io_base, int *ad_flags, int *osp) +{ + unsigned char tmp; + ad1848_info *devc = &adev_info[nr_ad1848_devs]; + unsigned char tmp1 = 0xff, tmp2 = 0xff; + int optiC930 = 0; /* OPTi 82C930 flag */ + int interwave = 0; + int ad1847_flag = 0; + int cs4248_flag = 0; + int sscape_flag = 0; + + int i; + + DDB(printk("ad1848_detect(%x)\n", io_base)); + + if (ad_flags) + { + if (*ad_flags == 0x12345678) + { + interwave = 1; + *ad_flags = 0; + } + + if (*ad_flags == 0x87654321) + { + sscape_flag = 1; + *ad_flags = 0; + } + + if (*ad_flags == 0x12345677) + { + cs4248_flag = 1; + *ad_flags = 0; + } + } + if (nr_ad1848_devs >= MAX_AUDIO_DEV) + { + printk(KERN_ERR "ad1848 - Too many audio devices\n"); + return 0; + } + if (check_region(io_base, 4)) + { + printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base); + return 0; + } + devc->base = io_base; + devc->irq_ok = 0; + devc->timer_running = 0; + devc->MCE_bit = 0x40; + devc->irq = 0; + devc->open_mode = 0; + devc->chip_name = devc->name = "AD1848"; + devc->model = MD_1848; /* AD1848 or CS4248 */ + devc->levels = NULL; + devc->debug_flag = 0; + + /* + * Check that the I/O address is in use. + * + * The bit 0x80 of the base I/O port is known to be 0 after the + * chip has performed its power on initialization. Just assume + * this has happened before the OS is starting. + * + * If the I/O address is unused, it typically returns 0xff. + */ + + if (inb(devc->base) == 0xff) + { + DDB(printk("ad1848_detect: The base I/O address appears to be dead\n")); + } + + /* + * Wait for the device to stop initialization + */ + + DDB(printk("ad1848_detect() - step 0\n")); + + for (i = 0; i < 10000000; i++) + { + unsigned char x = inb(devc->base); + + if (x == 0xff || !(x & 0x80)) + break; + } + + DDB(printk("ad1848_detect() - step A\n")); + + if (inb(devc->base) == 0x80) /* Not ready. Let's wait */ + ad_leave_MCE(devc); + + if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */ + { + DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base))); + return 0; + } + + /* + * Test if it's possible to change contents of the indirect registers. + * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only + * so try to avoid using it. + */ + + DDB(printk("ad1848_detect() - step B\n")); + ad_write(devc, 0, 0xaa); + ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ + + if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45) + { + if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */ + ad1847_flag = 1; + else + { + DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); + return 0; + } + } + DDB(printk("ad1848_detect() - step C\n")); + ad_write(devc, 0, 0x45); + ad_write(devc, 1, 0xaa); + + if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa) + { + if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */ + ad1847_flag = 1; + else + { + DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); + return 0; + } + } + + /* + * The indirect register I12 has some read only bits. Let's + * try to change them. + */ + + DDB(printk("ad1848_detect() - step D\n")); + tmp = ad_read(devc, 12); + ad_write(devc, 12, (~tmp) & 0x0f); + + if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f)) + { + DDB(printk("ad1848 detect error - step D (%x)\n", tmp1)); + return 0; + } + + /* + * NOTE! Last 4 bits of the reg I12 tell the chip revision. + * 0x01=RevB and 0x0A=RevC. + */ + + /* + * The original AD1848/CS4248 has just 15 indirect registers. This means + * that I0 and I16 should return the same value (etc.). + * However this doesn't work with CS4248. Actually it seems to be impossible + * to detect if the chip is a CS4231 or CS4248. + * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails + * with CS4231. + */ + + /* + * OPTi 82C930 has mode2 control bit in another place. This test will fail + * with it. Accept this situation as a possible indication of this chip. + */ + + DDB(printk("ad1848_detect() - step F\n")); + ad_write(devc, 12, 0); /* Mode2=disabled */ + + for (i = 0; i < 16; i++) + { + if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) + { + DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2)); + if (!ad1847_flag) + optiC930 = 1; + break; + } + } + + /* + * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). + * The bit 0x80 is always 1 in CS4248 and CS4231. + */ + + DDB(printk("ad1848_detect() - step G\n")); + + if (ad_flags && *ad_flags == 400) + *ad_flags = 0; + else + ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */ + + + if (ad_flags) + *ad_flags = 0; + + tmp1 = ad_read(devc, 12); + if (tmp1 & 0x80) + { + if (ad_flags) + *ad_flags |= AD_F_CS4248; + + devc->chip_name = "CS4248"; /* Our best knowledge just now */ + } + if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40)) + { + /* + * CS4231 detected - is it? + * + * Verify that setting I0 doesn't change I16. + */ + + DDB(printk("ad1848_detect() - step H\n")); + ad_write(devc, 16, 0); /* Set I16 to known value */ + + ad_write(devc, 0, 0x45); + if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */ + { + ad_write(devc, 0, 0xaa); + if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */ + { + DDB(printk("ad1848 detect error - step H(%x)\n", tmp1)); + return 0; + } + + /* + * Verify that some bits of I25 are read only. + */ + + DDB(printk("ad1848_detect() - step I\n")); + tmp1 = ad_read(devc, 25); /* Original bits */ + ad_write(devc, 25, ~tmp1); /* Invert all bits */ + if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7)) + { + int id; + + /* + * It's at least CS4231 + */ + + devc->chip_name = "CS4231"; + devc->model = MD_4231; + + /* + * It could be an AD1845 or CS4231A as well. + * CS4231 and AD1845 report the same revision info in I25 + * while the CS4231A reports different. + */ + + id = ad_read(devc, 25); + if ((id & 0xe7) == 0x80) /* Device busy??? */ + id = ad_read(devc, 25); + if ((id & 0xe7) == 0x80) /* Device still busy??? */ + id = ad_read(devc, 25); + DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25))); + + if ((id & 0xe7) == 0x80) { + /* + * It must be a CS4231 or AD1845. The register I23 of + * CS4231 is undefined and it appears to be read only. + * AD1845 uses I23 for setting sample rate. Assume + * the chip is AD1845 if I23 is changeable. + */ + + unsigned char tmp = ad_read(devc, 23); + ad_write(devc, 23, ~tmp); + + if (interwave) + { + devc->model = MD_IWAVE; + devc->chip_name = "IWave"; + } + else if (ad_read(devc, 23) != tmp) /* AD1845 ? */ + { + devc->chip_name = "AD1845"; + devc->model = MD_1845; + } + else if (cs4248_flag) + { + if (ad_flags) + *ad_flags |= AD_F_CS4248; + devc->chip_name = "CS4248"; + devc->model = MD_1848; + ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */ + } + ad_write(devc, 23, tmp); /* Restore */ + } + else + { + switch (id & 0x1f) { + case 3: /* CS4236/CS4235/CS42xB/CS4239 */ + { + int xid; + ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */ + ad_write(devc, 23, 0x9c); /* select extended register 25 */ + xid = inb(io_Indexed_Data(devc)); + ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */ + switch (xid & 0x1f) + { + case 0x00: + devc->chip_name = "CS4237B(B)"; + devc->model = MD_42xB; + break; + case 0x08: + /* Seems to be a 4238 ?? */ + devc->chip_name = "CS4238"; + devc->model = MD_42xB; + break; + case 0x09: + devc->chip_name = "CS4238B"; + devc->model = MD_42xB; + break; + case 0x0b: + devc->chip_name = "CS4236B"; + devc->model = MD_4236; + break; + case 0x10: + devc->chip_name = "CS4237B"; + devc->model = MD_42xB; + break; + case 0x1d: + devc->chip_name = "CS4235"; + devc->model = MD_4235; + break; + case 0x1e: + devc->chip_name = "CS4239"; + devc->model = MD_4239; + break; + default: + printk("Chip ident is %X.\n", xid&0x1F); + devc->chip_name = "CS42xx"; + devc->model = MD_4232; + break; + } + } + break; + + case 2: /* CS4232/CS4232A */ + devc->chip_name = "CS4232"; + devc->model = MD_4232; + break; + + case 0: + if ((id & 0xe0) == 0xa0) + { + devc->chip_name = "CS4231A"; + devc->model = MD_4231A; + } + else + { + devc->chip_name = "CS4321"; + devc->model = MD_4231; + } + break; + + default: /* maybe */ + DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7)); + if (optiC930) + { + devc->chip_name = "82C930"; + devc->model = MD_C930; + } + else + { + devc->chip_name = "CS4231"; + devc->model = MD_4231; + } + } + } + } + ad_write(devc, 25, tmp1); /* Restore bits */ + + DDB(printk("ad1848_detect() - step K\n")); + } + } else if (tmp1 == 0x0a) { + /* + * Is it perhaps a SoundPro CMI8330? + * If so, then we should be able to change indirect registers + * greater than I15 after activating MODE2, even though reading + * back I12 does not show it. + */ + + /* + * Let's try comparing register values + */ + for (i = 0; i < 16; i++) { + if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) { + DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2)); + soundpro = 1; + devc->chip_name = "SoundPro CMI 8330"; + break; + } + } + } + + DDB(printk("ad1848_detect() - step L\n")); + if (ad_flags) + { + if (devc->model != MD_1848) + *ad_flags |= AD_F_CS4231; + } + DDB(printk("ad1848_detect() - Detected OK\n")); + + if (devc->model == MD_1848 && ad1847_flag) + devc->chip_name = "AD1847"; + + + if (sscape_flag == 1) + devc->model = MD_1845_SSCAPE; + + return 1; +} + +int ad1848_init (char *name, int io_base, int irq, int dma_playback, + int dma_capture, int share_dma, int *osp, struct module *owner) +{ + /* + * NOTE! If irq < 0, there is another driver which has allocated the IRQ + * so that this driver doesn't need to allocate/deallocate it. + * The actually used IRQ is ABS(irq). + */ + + int my_dev; + char dev_name[100]; + int e; + + ad1848_info *devc = &adev_info[nr_ad1848_devs]; + + ad1848_port_info *portc = NULL; + + devc->irq = (irq > 0) ? irq : 0; + devc->open_mode = 0; + devc->timer_ticks = 0; + devc->dma1 = dma_playback; + devc->dma2 = dma_capture; + devc->subtype = cfg.card_subtype; + devc->audio_flags = DMA_AUTOMODE; + devc->playback_dev = devc->record_dev = 0; + if (name != NULL) + devc->name = name; + + if (name != NULL && name[0] != 0) + sprintf(dev_name, + "%s (%s)", name, devc->chip_name); + else + sprintf(dev_name, + "Generic audio codec (%s)", devc->chip_name); + + request_region(devc->base, 4, devc->name); + + conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture); + + if (devc->model == MD_1848 || devc->model == MD_C930) + devc->audio_flags |= DMA_HARDSTOP; + + if (devc->model > MD_1848) + { + if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1) + devc->audio_flags &= ~DMA_DUPLEX; + else + devc->audio_flags |= DMA_DUPLEX; + } + + portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL); + if(portc==NULL) + return -1; + + if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + dev_name, + &ad1848_audio_driver, + sizeof(struct audio_driver), + devc->audio_flags, + ad_format_mask[devc->model], + devc, + dma_playback, + dma_capture)) < 0) + { + kfree(portc); + portc=NULL; + return -1; + } + + audio_devs[my_dev]->portc = portc; + audio_devs[my_dev]->mixer_dev = -1; + if (owner) + audio_devs[my_dev]->d->owner = owner; + memset((char *) portc, 0, sizeof(*portc)); + + nr_ad1848_devs++; + + devc->pmdev = pm_register(PM_ISA_DEV, my_dev, ad1848_pm_callback); + if (devc->pmdev) + devc->pmdev->data = devc; + + ad1848_init_hw(devc); + + if (irq > 0) + { + devc->dev_no = my_dev; + if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0) + { + printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n"); + /* Don't free it either then.. */ + devc->irq = 0; + } + if (capabilities[devc->model].flags & CAP_F_TIMER) + { +#ifndef CONFIG_SMP + int x; + unsigned char tmp = ad_read(devc, 16); +#endif + + devc->timer_ticks = 0; + + ad_write(devc, 21, 0x00); /* Timer MSB */ + ad_write(devc, 20, 0x10); /* Timer LSB */ +#ifndef CONFIG_SMP + ad_write(devc, 16, tmp | 0x40); /* Enable timer */ + for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); + ad_write(devc, 16, tmp & ~0x40); /* Disable timer */ + + if (devc->timer_ticks == 0) + printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq); + else + { + DDB(printk("Interrupt test OK\n")); + devc->irq_ok = 1; + } +#else + devc->irq_ok = 1; +#endif + } + else + devc->irq_ok = 1; /* Couldn't test. assume it's OK */ + } else if (irq < 0) + irq2dev[-irq] = devc->dev_no = my_dev; + +#ifndef EXCLUDE_TIMERS + if ((capabilities[devc->model].flags & CAP_F_TIMER) && + devc->irq_ok) + ad1848_tmr_install(my_dev); +#endif + + if (!share_dma) + { + if (sound_alloc_dma(dma_playback, devc->name)) + printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback); + + if (dma_capture != dma_playback) + if (sound_alloc_dma(dma_capture, devc->name)) + printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture); + } + + if ((e = sound_install_mixer(MIXER_DRIVER_VERSION, + dev_name, + &ad1848_mixer_operations, + sizeof(struct mixer_operations), + devc)) >= 0) + { + audio_devs[my_dev]->mixer_dev = e; + if (owner) + mixer_devs[e]->owner = owner; + } + return my_dev; +} + +int ad1848_control(int cmd, int arg) +{ + ad1848_info *devc; + + if (nr_ad1848_devs < 1) + return -ENODEV; + + devc = &adev_info[nr_ad1848_devs - 1]; + + switch (cmd) + { + case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ + if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE) + return -EINVAL; + ad_enter_MCE(devc); + ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); + ad_leave_MCE(devc); + break; + + case AD1848_MIXER_REROUTE: + { + int o = (arg >> 8) & 0xff; + int n = arg & 0xff; + + if (o < 0 || o >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + + if (!(devc->supported_devices & (1 << o)) && + !(devc->supported_rec_devices & (1 << o))) + return -EINVAL; + + if (n == SOUND_MIXER_NONE) + { /* Just hide this control */ + ad1848_mixer_set(devc, o, 0); /* Shut up it */ + devc->supported_devices &= ~(1 << o); + devc->supported_rec_devices &= ~(1 << o); + break; + } + + /* Make the mixer control identified by o to appear as n */ + if (n < 0 || n >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + + devc->mixer_reroute[n] = o; /* Rename the control */ + if (devc->supported_devices & (1 << o)) + devc->supported_devices |= (1 << n); + if (devc->supported_rec_devices & (1 << o)) + devc->supported_rec_devices |= (1 << n); + + devc->supported_devices &= ~(1 << o); + devc->supported_rec_devices &= ~(1 << o); + } + break; + } + return 0; +} + +void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma) +{ + int i, mixer, dev = 0; + ad1848_info *devc = NULL; + + for (i = 0; devc == NULL && i < nr_ad1848_devs; i++) + { + if (adev_info[i].base == io_base) + { + devc = &adev_info[i]; + dev = devc->dev_no; + } + } + + if (devc != NULL) + { + if(audio_devs[dev]->portc!=NULL) + kfree(audio_devs[dev]->portc); + release_region(devc->base, 4); + + if (!share_dma) + { + if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */ + free_irq(devc->irq, (void *)devc->dev_no); + + sound_free_dma(dma_playback); + + if (dma_playback != dma_capture) + sound_free_dma(dma_capture); + + } + mixer = audio_devs[devc->dev_no]->mixer_dev; + if(mixer>=0) + sound_unload_mixerdev(mixer); + + if (devc->pmdev) + pm_unregister(devc->pmdev); + + nr_ad1848_devs--; + for ( ; i < nr_ad1848_devs ; i++) + adev_info[i] = adev_info[i+1]; + } + else + printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); +} + +void adintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + unsigned char status; + ad1848_info *devc; + int dev; + int alt_stat = 0xff; + unsigned char c930_stat = 0; + int cnt = 0; + + dev = (int)dev_id; + devc = (ad1848_info *) audio_devs[dev]->devc; + +interrupt_again: /* Jump back here if int status doesn't reset */ + + status = inb(io_Status(devc)); + + if (status == 0x80) + printk(KERN_DEBUG "adintr: Why?\n"); + if (devc->model == MD_1848) + outb((0), io_Status(devc)); /* Clear interrupt status */ + + if (status & 0x01) + { + if (devc->model == MD_C930) + { /* 82C930 has interrupt status register in MAD16 register MC11 */ + unsigned long flags; + + save_flags(flags); + cli(); + + /* 0xe0e is C930 address port + * 0xe0f is C930 data port + */ + outb(11, 0xe0e); + c930_stat = inb(0xe0f); + outb((~c930_stat), 0xe0f); + + restore_flags(flags); + + alt_stat = (c930_stat << 2) & 0x30; + } + else if (devc->model != MD_1848) + { + alt_stat = ad_read(devc, 24); + ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */ + } + + if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20)) + { + DMAbuf_inputintr(devc->record_dev); + } + if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) && + (alt_stat & 0x10)) + { + DMAbuf_outputintr(devc->playback_dev, 1); + } + if (devc->model != MD_1848 && (alt_stat & 0x40)) /* Timer interrupt */ + { + devc->timer_ticks++; +#ifndef EXCLUDE_TIMERS + if (timer_installed == dev && devc->timer_running) + sound_timer_interrupt(); +#endif + } + } +/* + * Sometimes playback or capture interrupts occur while a timer interrupt + * is being handled. The interrupt will not be retriggered if we don't + * handle it now. Check if an interrupt is still pending and restart + * the handler in this case. + */ + if (inb(io_Status(devc)) & 0x01 && cnt++ < 4) + { + goto interrupt_again; + } +} + +/* + * Experimental initialization sequence for the integrated sound system + * of the Compaq Deskpro M. + */ + +static int init_deskpro_m(struct address_info *hw_config) +{ + unsigned char tmp; + + if ((tmp = inb(0xc44)) == 0xff) + { + DDB(printk("init_deskpro_m: Dead port 0xc44\n")); + return 0; + } + + outb(0x10, 0xc44); + outb(0x40, 0xc45); + outb(0x00, 0xc46); + outb(0xe8, 0xc47); + outb(0x14, 0xc44); + outb(0x40, 0xc45); + outb(0x00, 0xc46); + outb(0xe8, 0xc47); + outb(0x10, 0xc44); + + return 1; +} + +/* + * Experimental initialization sequence for the integrated sound system + * of Compaq Deskpro XL. + */ + +static int init_deskpro(struct address_info *hw_config) +{ + unsigned char tmp; + + if ((tmp = inb(0xc44)) == 0xff) + { + DDB(printk("init_deskpro: Dead port 0xc44\n")); + return 0; + } + outb((tmp | 0x04), 0xc44); /* Select bank 1 */ + if (inb(0xc44) != 0x04) + { + DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n")); + return 0; + } + /* + * OK. It looks like a Deskpro so let's proceed. + */ + + /* + * I/O port 0xc44 Audio configuration register. + * + * bits 0xc0: Audio revision bits + * 0x00 = Compaq Business Audio + * 0x40 = MS Sound System Compatible (reset default) + * 0x80 = Reserved + * 0xc0 = Reserved + * bit 0x20: No Wait State Enable + * 0x00 = Disabled (reset default, DMA mode) + * 0x20 = Enabled (programmed I/O mode) + * bit 0x10: MS Sound System Decode Enable + * 0x00 = Decoding disabled (reset default) + * 0x10 = Decoding enabled + * bit 0x08: FM Synthesis Decode Enable + * 0x00 = Decoding Disabled (reset default) + * 0x08 = Decoding enabled + * bit 0x04 Bank select + * 0x00 = Bank 0 + * 0x04 = Bank 1 + * bits 0x03 MSS Base address + * 0x00 = 0x530 (reset default) + * 0x01 = 0x604 + * 0x02 = 0xf40 + * 0x03 = 0xe80 + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc44 (before): "); + outb((tmp & ~0x04), 0xc44); + printk("%02x ", inb(0xc44)); + outb((tmp | 0x04), 0xc44); + printk("%02x\n", inb(0xc44)); +#endif + + /* Set bank 1 of the register */ + tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */ + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0x604: + tmp |= 0x01; + break; + case 0xf40: + tmp |= 0x02; + break; + case 0xe80: + tmp |= 0x03; + break; + default: + DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base)); + return 0; + } + outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc44 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc44)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc44)); +#endif + + /* + * I/O port 0xc45 FM Address Decode/MSS ID Register. + * + * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88) + * bank=0, bit 0x01: SBIC Power Control Bit + * 0x00 = Powered up + * 0x01 = Powered down + * bank=1, bits 0xfc: MSS ID (default=0x40) + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc45 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc45)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc45)); +#endif + + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc45 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc45)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc45)); +#endif + + + /* + * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register. + * + * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03) + * bank=1, bits 0xff: Audio addressing ASIC id + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc46 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc46)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc46)); +#endif + + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x11), 0xc46); /* ASIC ID = 0x11 */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc46 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc46)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc46)); +#endif + + /* + * I/O port 0xc47 FM Address Decode Register. + * + * bank=0, bits 0xff: Decode enable selection for various FM address bits + * bank=1, bits 0xff: Reserved + */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc47 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc47)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc47)); +#endif + + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */ + +#ifdef DEBUGXL + /* Debug printing */ + printk("Port 0xc47 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc47)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc47)); +#endif + + /* + * I/O port 0xc6f = Audio Disable Function Register + */ + +#ifdef DEBUGXL + printk("Port 0xc6f (before) = %02x\n", inb(0xc6f)); +#endif + + outb((0x80), 0xc6f); + +#ifdef DEBUGXL + printk("Port 0xc6f (after) = %02x\n", inb(0xc6f)); +#endif + + return 1; +} + +int probe_ms_sound(struct address_info *hw_config) +{ + unsigned char tmp; + + DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ + { + /* check_opl3(0x388, hw_config); */ + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + } + + if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */ + { + if (!init_deskpro(hw_config)) + return 0; + } + + if (deskpro_m) /* Compaq Deskpro M */ + { + if (!init_deskpro_m(hw_config)) + return 0; + } + + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00 or 0x0f. + */ + + if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */ + { + int ret; + + DDB(printk("I/O address is inactive (%x)\n", tmp)); + if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + return 0; + return 1; + } + DDB(printk("MSS signature = %x\n", tmp & 0x3f)); + if ((tmp & 0x3f) != 0x04 && + (tmp & 0x3f) != 0x0f && + (tmp & 0x3f) != 0x00) + { + int ret; + + MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3))); + DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n")); + if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + return 0; + + hw_config->card_subtype = 1; + return 1; + } + if ((hw_config->irq != 5) && + (hw_config->irq != 7) && + (hw_config->irq != 9) && + (hw_config->irq != 10) && + (hw_config->irq != 11) && + (hw_config->irq != 12)) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 0; + } + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); +} + +void attach_ms_sound(struct address_info *hw_config, struct module *owner) +{ + static signed char interrupt_bits[12] = + { + -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 + }; + signed char bits; + char dma2_bit = 0; + + static char dma_bits[4] = + { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0; + int version_port = hw_config->io_base + 3; + int dma = hw_config->dma; + int dma2 = hw_config->dma2; + + if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ + { + hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0, + hw_config->osp, + owner); + request_region(hw_config->io_base, 4, "WSS config"); + return; + } + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return; + } + outb((bits | 0x40), config_port); + if ((inb(version_port) & 0x40) == 0) + printk(KERN_ERR "[MSS: IRQ Conflict?]\n"); + +/* + * Handle the capture DMA channel + */ + + if (dma2 != -1 && dma2 != dma) + { + if (!((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0))) + { /* Unsupported combination. Try to swap channels */ + int tmp = dma; + + dma = dma2; + dma2 = tmp; + } + if ((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) + { + dma2_bit = 0x04; /* Enable capture DMA */ + } + else + { + printk(KERN_WARNING "MSS: Invalid capture DMA\n"); + dma2 = dma; + } + } + else + { + dma2 = dma; + } + + hw_config->dma = dma; + hw_config->dma2 = dma2; + + outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ + + hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, + hw_config->irq, + dma, dma2, 0, + hw_config->osp, + THIS_MODULE); + request_region(hw_config->io_base, 4, "WSS config"); +} + +void unload_ms_sound(struct address_info *hw_config) +{ + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0); + sound_unload_audiodev(hw_config->slots[0]); + release_region(hw_config->io_base, 4); +} + +#ifndef EXCLUDE_TIMERS + +/* + * Timer stuff (for /dev/music). + */ + +static unsigned int current_interval = 0; + +static unsigned int ad1848_tmr_start(int dev, unsigned int usecs) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */ + unsigned long divider; + + save_flags(flags); + cli(); + + /* + * Length of the timer interval (in nanoseconds) depends on the + * selected crystal oscillator. Check this from bit 0x01 of I8. + * + * AD1845 has just one oscillator which has cycle time of 10.050 us + * (when a 24.576 MHz xtal oscillator is used). + * + * Convert requested interval to nanoseconds before computing + * the timer divider. + */ + + if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) + xtal_nsecs = 10050; + else if (ad_read(devc, 8) & 0x01) + xtal_nsecs = 9920; + else + xtal_nsecs = 9969; + + divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs; + + if (divider < 100) /* Don't allow shorter intervals than about 1ms */ + divider = 100; + + if (divider > 65535) /* Overflow check */ + divider = 65535; + + ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */ + ad_write(devc, 20, divider & 0xff); /* Set lower bits */ + ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */ + devc->timer_running = 1; + restore_flags(flags); + + return current_interval = (divider * xtal_nsecs + 500) / 1000; +} + +static void ad1848_tmr_reprogram(int dev) +{ + /* + * Audio driver has changed sampling rate so that a different xtal + * oscillator was selected. We have to reprogram the timer rate. + */ + + ad1848_tmr_start(dev, current_interval); + sound_timer_syncinterval(current_interval); +} + +static void ad1848_tmr_disable(int dev) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + save_flags(flags); + cli(); + ad_write(devc, 16, ad_read(devc, 16) & ~0x40); + devc->timer_running = 0; + restore_flags(flags); +} + +static void ad1848_tmr_restart(int dev) +{ + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + if (current_interval == 0) + return; + + save_flags(flags); + cli(); + ad_write(devc, 16, ad_read(devc, 16) | 0x40); + devc->timer_running = 1; + restore_flags(flags); +} + +static struct sound_lowlev_timer ad1848_tmr = +{ + 0, + 2, + ad1848_tmr_start, + ad1848_tmr_disable, + ad1848_tmr_restart +}; + +static int ad1848_tmr_install(int dev) +{ + if (timer_installed != -1) + return 0; /* Don't install another timer */ + + timer_installed = ad1848_tmr.dev = dev; + sound_timer_init(&ad1848_tmr, audio_devs[dev]->name); + + return 1; +} +#endif /* EXCLUDE_TIMERS */ + +static int ad1848_suspend(ad1848_info *devc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + ad_mute(devc); + + restore_flags(flags); + return 0; +} + +static int ad1848_resume(ad1848_info *devc) +{ + unsigned long flags; + int mixer_levels[32], i; + + save_flags(flags); + cli(); + + /* Thinkpad is a bit more of PITA than normal. The BIOS tends to + restore it in a different config to the one we use. Need to + fix this somehow */ + + /* store old mixer levels */ + memcpy(mixer_levels, devc->levels, sizeof (mixer_levels)); + ad1848_init_hw(devc); + + /* restore mixer levels */ + for (i = 0; i < 32; i++) + ad1848_mixer_set(devc, devc->dev_no, mixer_levels[i]); + + if (!devc->subtype) { + static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 }; + static char dma_bits[4] = { 1, 2, 0, 3 }; + + signed char bits; + char dma2_bit = 0; + + int config_port = devc->base + 0; + + bits = interrupt_bits[devc->irq]; + if (bits == -1) { + printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq); + restore_flags(flags); + return -1; + } + + outb((bits | 0x40), config_port); + + if (devc->dma2 != -1 && devc->dma2 != devc->dma1) + if ( (devc->dma1 == 0 && devc->dma2 == 1) || + (devc->dma1 == 1 && devc->dma2 == 0) || + (devc->dma1 == 3 && devc->dma2 == 0)) + dma2_bit = 0x04; + + outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port); + } + + restore_flags(flags); + return 0; +} + +static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + ad1848_info *devc = dev->data; + if (devc) { + DEB(printk("ad1848: pm event received: 0x%x\n", rqst)); + + switch (rqst) { + case PM_SUSPEND: + ad1848_suspend(devc); + break; + case PM_RESUME: + ad1848_resume(devc); + break; + } + } + return 0; +} + + +EXPORT_SYMBOL(ad1848_detect); +EXPORT_SYMBOL(ad1848_init); +EXPORT_SYMBOL(ad1848_unload); +EXPORT_SYMBOL(ad1848_control); +EXPORT_SYMBOL(adintr); +EXPORT_SYMBOL(probe_ms_sound); +EXPORT_SYMBOL(attach_ms_sound); +EXPORT_SYMBOL(unload_ms_sound); + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata type = 0; + +MODULE_PARM(io, "i"); /* I/O for a raw AD1848 card */ +MODULE_PARM(irq, "i"); /* IRQ to use */ +MODULE_PARM(dma, "i"); /* First DMA channel */ +MODULE_PARM(dma2, "i"); /* Second DMA channel */ +MODULE_PARM(type, "i"); /* Card type */ +MODULE_PARM(deskpro_xl, "i"); /* Special magic for Deskpro XL boxen */ +MODULE_PARM(deskpro_m, "i"); /* Special magic for Deskpro M box */ +MODULE_PARM(soundpro, "i"); /* More special magic for SoundPro chips */ + +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "i"); +MODULE_PARM(isapnpjump, "i"); +MODULE_PARM(reverse, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); +MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); +MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); + +struct pci_dev *ad1848_dev = NULL; + +/* Please add new entries at the end of the table */ +static struct { + char *name; + unsigned short card_vendor, card_device, + vendor, function; + short mss_io, irq, dma, dma2; /* index into isapnp table */ + int type; +} ad1848_isapnp_list[] __initdata = { + {"CMI 8330 SoundPRO", + ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + 0, 0, 0,-1, 0}, + {"CS4232 based card", + ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), + 0, 0, 0, 1, 0}, + {"CS4232 based card", + ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), + 0, 0, 0, 1, 0}, + {"OPL3-SA2 WSS mode", + ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), + 1, 0, 0, 1, 1}, + {"Advanced Gravis InterWave Audio", + ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), + 0, 0, 0, 1, 0}, + {0} +}; + +static struct isapnp_device_id id_table[] __devinitdata = { + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 }, + { ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, id_table); + +static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +{ + int err; + + /* Device already active? Let's use it */ + if(dev->active) + return(dev); + + if((err = dev->activate(dev)) < 0) { + printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); + + dev->deactivate(dev); + + return(NULL); + } + return(dev); +} + +static struct pci_dev *ad1848_init_generic(struct pci_bus *bus, struct address_info *hw_config, int slot) +{ + + /* Configure Audio device */ + if((ad1848_dev = isapnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL))) + { + int ret; + ret = ad1848_dev->prepare(ad1848_dev); + /* If device is active, assume configured with /proc/isapnp + * and use anyway. Some other way to check this? */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "ad1848: ISAPnP found device that could not be autoconfigured.\n"); + return(NULL); + } + if(ret == -EBUSY) + audio_activated = 1; + + if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev))) + { + hw_config->io_base = ad1848_dev->resource[ad1848_isapnp_list[slot].mss_io].start; + hw_config->irq = ad1848_dev->irq_resource[ad1848_isapnp_list[slot].irq].start; + hw_config->dma = ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma].start; + if(ad1848_isapnp_list[slot].dma2 != -1) + hw_config->dma2 = ad1848_dev->dma_resource[ad1848_isapnp_list[slot].dma2].start; + else + hw_config->dma2 = -1; + hw_config->card_subtype = ad1848_isapnp_list[slot].type; + } else + return(NULL); + } else + return(NULL); + + return(ad1848_dev); +} + +static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pci_bus *bus, int slot) +{ + char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name; + + printk(KERN_INFO "ad1848: %s detected\n", busname); + + /* Initialize this baby. */ + + if(ad1848_init_generic(bus, hw_config, slot)) { + /* We got it. */ + + printk(KERN_NOTICE "ad1848: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", + busname, + hw_config->io_base, hw_config->irq, hw_config->dma, + hw_config->dma2); + return 1; + } + else + printk(KERN_INFO "ad1848: Failed to initialize %s\n", busname); + + return 0; +} + +static int __init ad1848_isapnp_probe(struct address_info *hw_config) +{ + static int first = 1; + int i; + + /* Count entries in sb_isapnp_list */ + for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++); + i--; + + /* Check and adjust isapnpjump */ + if( isapnpjump < 0 || isapnpjump > i) { + isapnpjump = reverse ? i : 0; + printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); + } + + if(!first || !reverse) + i = isapnpjump; + first = 0; + while(ad1848_isapnp_list[i].card_vendor != 0) { + static struct pci_bus *bus = NULL; + + while ((bus = isapnp_find_card( + ad1848_isapnp_list[i].card_vendor, + ad1848_isapnp_list[i].card_device, + bus))) { + + if(ad1848_isapnp_init(hw_config, bus, i)) { + isapnpjump = i; /* start next search from here */ + return 0; + } + } + i += reverse ? -1 : 1; + } + + return -ENODEV; +} +#endif + + +static int __init init_ad1848(void) +{ + printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + +#ifdef __ISAPNP__ + if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) { + printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + } +#endif + + if(io != -1) { + if( isapnp == 0 ) + { + if(irq == -1 || dma == -1) { + printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n"); + return -EINVAL; + } + + cfg.irq = irq; + cfg.io_base = io; + cfg.dma = dma; + cfg.dma2 = dma2; + cfg.card_subtype = type; + } + + if(!probe_ms_sound(&cfg)) + return -ENODEV; + attach_ms_sound(&cfg, THIS_MODULE); + loaded = 1; + } + return 0; +} + +static void __exit cleanup_ad1848(void) +{ + if(loaded) + unload_ms_sound(&cfg); + +#ifdef __ISAPNP__ + if(audio_activated) + if(ad1848_dev) + ad1848_dev->deactivate(ad1848_dev); +#endif +} + +module_init(init_ad1848); +module_exit(cleanup_ad1848); + +#ifndef MODULE +static int __init setup_ad1848(char *str) +{ + /* io, irq, dma, dma2, type */ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + type = ints[5]; + + return 1; +} + +__setup("ad1848=", setup_ad1848); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/ad1848.h linux-2.4.19-pre5-mjc/sound/oss/ad1848.h --- linux/sound/oss/ad1848.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ad1848.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,28 @@ +/* + * ad1848.c + * + * Copyright: Christoph Hellwig + */ + +#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */ +#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */ + +#define AD1848_SET_XTAL 1 +#define AD1848_MIXER_REROUTE 2 + +#define AD1848_REROUTE(oldctl, newctl) \ + ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl)) + + +int ad1848_init(char *name, int io_base, int irq, int dma_playback, + int dma_capture, int share_dma, int *osp, struct module *owner); +void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma); + +int ad1848_detect (int io_base, int *flags, int *osp); +int ad1848_control(int cmd, int arg); + +void adintr(int irq, void *dev_id, struct pt_regs * dummy); +void attach_ms_sound(struct address_info * hw_config, struct module * owner); + +int probe_ms_sound(struct address_info *hw_config); +void unload_ms_sound(struct address_info *hw_info); diff -Nru linux/sound/oss/ad1848_mixer.h linux-2.4.19-pre5-mjc/sound/oss/ad1848_mixer.h --- linux/sound/oss/ad1848_mixer.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ad1848_mixer.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,253 @@ +/* + * sound/ad1848_mixer.h + * + * Definitions for the mixer of AD1848 and compatible codecs. + */ + +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + + +/* + * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. + * Sound card manufacturers have connected actual inputs (CD, synth, line, + * etc) to these inputs in different order. Therefore it's difficult + * to assign mixer channels to these inputs correctly. The following + * contains two alternative mappings. The first one is for GUS MAX and + * the second is just a generic one (line1, line2 and line3). + * (Actually this is not a mapping but rather some kind of interleaving + * solution). + */ +#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \ + SOUND_MASK_LINE1 | SOUND_MASK_IMIX) + +#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_LINE1) + +#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \ + SOUND_MASK_LINE2 | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM | SOUND_MASK_IMIX) + +#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ + SOUND_MASK_MIC | \ + SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM | SOUND_MASK_IMIX) + +#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME) + +/* OPTi 82C930 has no IMIX level control, but it can still be selected as an + * input + */ +#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ + SOUND_MASK_MIC | SOUND_MASK_VOLUME | \ + SOUND_MASK_LINE3 | \ + SOUND_MASK_IGAIN | SOUND_MASK_PCM) + +#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \ + SOUND_MASK_LINE | SOUND_MASK_SYNTH | \ + SOUND_MASK_CD | SOUND_MASK_MIC | \ + SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \ + SOUND_MASK_OGAIN) + +struct mixer_def { + unsigned int regno:6; /* register number for volume */ + unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */ + unsigned int bitpos:3; /* position of bits in register for volume */ + unsigned int nbits:3; /* number of bits in register for volume */ + unsigned int mutereg:6; /* register number for mute bit */ + unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */ + unsigned int mutepos:4; /* position of mute bit in register */ + unsigned int recreg:6; /* register number for recording bit */ + unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */ + unsigned int recpos:4; /* position of recording bit in register */ +}; + +static char mix_cvt[101] = { + 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, + 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, + 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, + 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, + 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, + 100 +}; + +typedef struct mixer_def mixer_ent; +typedef mixer_ent mixer_ents[2]; + +/* + * Most of the mixer entries work in backwards. Setting the polarity field + * makes them to work correctly. + * + * The channel numbering used by individual sound cards is not fixed. Some + * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. + * The current version doesn't try to compensate this. + */ + +#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \ + [name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \ + {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}} + +#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ + rec_reg_l, rec_pola_l, rec_pos_l, \ + reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ + rec_reg_r, rec_pola_r, rec_pos_r) \ + [name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ + rec_reg_l, rec_pola_l, rec_pos_l}, \ + {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ + rec_reg_r, rec_pola_r, rec_pos_r}} + +static mixer_ents ad1848_mix_devices[32] = { + MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) +}; + +static mixer_ents iwave_mix_devices[32] = { + MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8), + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) +}; + +static mixer_ents cs42xb_mix_devices[32] = { + /* Digital master volume actually has seven bits, but we only use + six to avoid the discontinuity when the analog gain kicks in. */ + MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), + /* For the IMIX entry, it was not possible to use the MIX_ENT macro + because the mute bit is in different positions for the two + channels and requires reverse polarity. */ + [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8}, + {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}}, + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7) +}; + +/* OPTi 82C930 has somewhat different port addresses. + * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1 + * VOLUME, SYNTH, LINE, CD are not enabled above. + * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc. + */ +static mixer_ents c930_mix_devices[32] = { + MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7), + MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7), + MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7), + MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), + MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7), + MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7) +}; + +static mixer_ents spro_mix_devices[32] = { + MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8), + MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8, + 5, 1, 1, 4, 23, 0, 3, 0, 0, 8), + MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8), + MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8), + MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2, + 20, 0, 0, 4, 17, 1, 3, 16, 0, 1), + MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4, + 21, 0, 0, 4, 17, 1, 1, 16, 0, 3), + MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), + MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8), + /* This is external wavetable */ + MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4, + 22, 0, 0, 4, 23, 1, 0, 23, 0, 5), +}; + +static int default_mixer_levels[32] = +{ + 0x3232, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x4b4b, /* FM */ + 0x3232, /* PCM */ + 0x1515, /* PC Speaker */ + 0x2020, /* Ext Line */ + 0x1010, /* Mic */ + 0x4b4b, /* CD */ + 0x0000, /* Recording monitor */ + 0x4b4b, /* Second PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ + 0x4b4b, /* Output gain */ + 0x2020, /* Line1 */ + 0x2020, /* Line2 */ + 0x1515 /* Line3 (usually line in)*/ +}; + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +/* + * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1) + */ + +#ifndef AUDIO_SPEAKER +#define AUDIO_SPEAKER 0x01 /* Enable mono output */ +#define AUDIO_HEADPHONE 0x02 /* Sparc only */ +#define AUDIO_LINE_OUT 0x04 /* Sparc only */ +#endif diff -Nru linux/sound/oss/adlib_card.c linux-2.4.19-pre5-mjc/sound/oss/adlib_card.c --- linux/sound/oss/adlib_card.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/adlib_card.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,73 @@ +/* + * sound/adlib_card.c + * + * Detection routine for the AdLib card. + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include +#include + +#include "sound_config.h" + +#include "opl3.h" + +static void __init attach_adlib_card(struct address_info *hw_config) +{ + hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp, THIS_MODULE); +} + +static int __init probe_adlib(struct address_info *hw_config) +{ + return opl3_detect(hw_config->io_base, hw_config->osp); +} + +static struct address_info cfg; + +static int __initdata io = -1; + +MODULE_PARM(io, "i"); + +static int __init init_adlib(void) +{ + cfg.io_base = io; + + if (cfg.io_base == -1) { + printk(KERN_ERR "adlib: must specify I/O address.\n"); + return -EINVAL; + } + if (probe_adlib(&cfg) == 0) + return -ENODEV; + attach_adlib_card(&cfg); + + return 0; +} + +static void __exit cleanup_adlib(void) +{ + sound_unload_synthdev(cfg.slots[0]); + +} + +module_init(init_adlib); +module_exit(cleanup_adlib); + +#ifndef MODULE +static int __init setup_adlib(char *str) +{ + /* io */ + int ints[2]; + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + + return 1; +} +__setup("adlib=", setup_adlib); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/aedsp16.c linux-2.4.19-pre5-mjc/sound/oss/aedsp16.c --- linux/sound/oss/aedsp16.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/aedsp16.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1381 @@ +/* + drivers/sound/lowlevel/aedsp16.c + + Audio Excel DSP 16 software configuration routines + Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ +/* + * Include the main OSS Lite header file. It include all the os, OSS Lite, etc + * headers needed by this source. + */ +#include +#include +#include +#include +#include "sound_config.h" + +/* + * Sanity checks + */ + +#if defined(CONFIG_SOUND_ALSA_AEDSP16_SBPRO) && defined(CONFIG_SOUND_ALSA_AEDSP16_MSS) +#error You have to enable only one of the MSS and SBPRO emulations. +#endif + +/* + + READ THIS + + This module started to configure the Audio Excel DSP 16 Sound Card. + Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards. + + NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this + audio card and want to see the kernel support for it, please contact me. + + Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401 + compatible card. + It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq), + so before this module, the only way to configure the DSP under linux was + boot the MS-DOS loading the sound.sys device driver (this driver soft- + configure the sound board hardware by massaging someone of its registers), + and then ctrl-alt-del to boot linux with the DSP configured by the DOS + driver. + + This module works configuring your Audio Excel DSP 16's irq, dma and + mpu-401-irq. The OSS Lite routines rely on the fact that if the + hardware is there, they can detect it. The problem with AEDSP16 is + that no hardware can be found by the probe routines if the sound card + is not configured properly. Sometimes the kernel probe routines can find + an SBPRO even when the card is not configured (this is the standard setup + of the card), but the SBPRO emulation don't work well if the card is not + properly initialized. For this reason + + aedsp16_init_board() + + routine is called before the OSS Lite probe routines try to detect the + hardware. + + NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS) + + NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards + have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They + have to be configured by software. + + NOTE: The driver is merged with the new OSS Lite sound driver. It works + as a lowlevel driver. + + The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS; + the OSS Lite sound driver can be configured for SBPRO and MSS cards + at the same time, but the aedsp16 can't be two cards!! + When we configure it, we have to choose the SBPRO or the MSS emulation + for AEDSP16. We also can install a *REAL* card of the other type (see [1]). + + NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO + please let me know if it works. + + The MPU-401 support can be compiled in together with one of the other + two operating modes. + + NOTE: This is something like plug-and-play: we have only to plug + the AEDSP16 board in the socket, and then configure and compile + a kernel that uses the AEDSP16 software configuration capability. + No jumper setting is needed! + + For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3 + you have just to make config the OSS Lite package, configuring + the AEDSP16 sound card, then activating the SBPro emulation mode + and at last configuring IRQ and DMA. + Compile the kernel and run it. + + NOTE: This means for SC-6000 cards that you can choose irq and dma, + but not the I/O addresses. To change I/O addresses you have to set + them with jumpers. For SC-6600 cards you have no jumpers so you have + to set up your full card configuration in the make config. + + You can change the irq/dma/mirq settings WITHOUT THE NEED to open + your computer and massage the jumpers (there are no irq/dma/mirq + jumpers to be configured anyway, only I/O BASE values have to be + configured with jumpers) + + For some ununderstandable reason, the card default of irq 7, dma 1, + don't work for me. Seems to be an IRQ or DMA conflict. Under heavy + HDD work, the kernel start to erupt out a lot of messages like: + + 'Sound: DMA timed out - IRQ/DRQ config error?' + + For what I can say, I have NOT any conflict at irq 7 (under linux I'm + using the lp polling driver), and dma line 1 is unused as stated by + /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so + I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows! + Anyway a setting of irq 10, dma 3 works really fine. + + NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know + the emulation mode, all the installed hardware and the hardware + configuration (irq and dma settings of all the hardware). + + This init module should work with SBPRO+MSS, when one of the two is + the AEDSP16 emulation and the other the real card. (see [1]) + For example: + + AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other + AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other + + MPU401 should work. (see [2]) + + [1] + --- + Date: Mon, 29 Jul 1997 08:35:40 +0100 + From: Mr S J Greenaway + + [...] + Just to let you know got my Audio Excel (emulating a MSS) working + with my original SB16, thanks for the driver! + [...] + --- + + [2] Not tested by me for lack of hardware. + + TODO, WISHES AND TECH + + - About I/O ports allocation - + + Request the 2x0h region (port base) in any case if we are using this card. + + NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16 + port base region (see code) does not mean necessarily that we are emulating + sbpro. Even if this region is the sbpro I/O ports region, we use this + region to access the control registers of the card, and if emulating + sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro + registers are not used, in no way, to emulate an sbpro: they are + used only for configuration purposes. + + Started Fri Mar 17 16:13:18 MET 1995 + + v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c) + - Initial code. + v0.2 (ALPHA) + - Cleanups. + - Integrated with Linux voxware v 2.90-2 kernel sound driver. + - SoundBlaster Pro mode configuration. + - Microsoft Sound System mode configuration. + - MPU-401 mode configuration. + v0.3 (ALPHA) + - Cleanups. + - Rearranged the code to let aedsp16_init_board be more general. + - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h + inclusion too. We rely on os.h + - Used the to get a variable + len string (we are not sure about the len of Copyright string). + This works with any SB and compatible. + - Added the code to request_region at device init (should go in + the main body of voxware). + v0.4 (BETA) + - Better configure.c patch for aedsp16 configuration (better + logic of inclusion of AEDSP16 support) + - Modified the conditional compilation to better support more than + one sound card of the emulated type (read the NOTES above) + - Moved the sb init routine from the attach to the very first + probe in sb_card.c + - Rearrangements and cleanups + - Wiped out some unnecessary code and variables: this is kernel + code so it is better save some TEXT and DATA + - Fixed the request_region code. We must allocate the aedsp16 (sbpro) + I/O ports in any case because they are used to access the DSP + configuration registers and we can not allow anyone to get them. + v0.5 + - cleanups on comments + - prep for diffs against v3.0-proto-950402 + v0.6 + - removed the request_region()s when compiling the MODULE sound.o + because we are not allowed (by the actual voxware structure) to + release_region() + v0.7 (pre ALPHA, not distributed) + - started porting this module to kernel 1.3.84. Dummy probe/attach + routines. + v0.8 (ALPHA) + - attached all the init routines. + v0.9 (BETA) + - Integrated with linux-pre2.0.7 + - Integrated with configuration scripts. + - Cleaned up and beautyfied the code. + v0.9.9 (BETA) + - Thanks to Piercarlo Grandi: corrected the conditonal compilation code. + Now only the code configured is compiled in, with some memory saving. + v0.9.10 + - Integration into the sound/lowlevel/ section of the sound driver. + - Re-organized the code. + v0.9.11 (not distributed) + - Rewritten the init interface-routines to initialize the AEDSP16 in + one shot. + - More cosmetics. + - SC-6600 support. + - More soft/hard configuration. + v0.9.12 + - Refined the v0.9.11 code with conditional compilation to distinguish + between SC-6000 and SC-6600 code. + v1.0.0 + - Prep for merging with OSS Lite and Linux kernel 2.1.13 + - Corrected a bug in request/check/release region calls (thanks to the + new kernel exception handling). + v1.1 + - Revamped for integration with new modularized sound drivers: to enhance + the flexibility of modular version, I have removed all the conditional + compilation for SBPRO, MPU and MSS code. Now it is all managed with + the ae_config structure. + v1.2 + - Module informations added. + - Removed aedsp16_delay_10msec(), now using mdelay(10) + - All data and funcs moved to .*.init section. + v1.3 + Arnaldo Carvalho de Melo - 2000/09/27 + - got rid of check_region + + Known Problems: + - Audio Excel DSP 16 III don't work with this driver. + + Credits: + Many thanks to Gerald Britton . He helped me a + lot in testing the 0.9.11 and 0.9.12 versions of this driver. + + */ + + +#define VERSION "1.3" /* Version of Audio Excel DSP 16 driver */ + +#undef AEDSP16_DEBUG /* Define this to 1 to enable debug code */ +#undef AEDSP16_DEBUG_MORE /* Define this to 1 to enable more debug */ +#undef AEDSP16_INFO /* Define this to 1 to enable info code */ + +#if defined(AEDSP16_DEBUG) +# define DBG(x) printk x +# if defined(AEDSP16_DEBUG_MORE) +# define DBG1(x) printk x +# else +# define DBG1(x) +# endif +#else +# define DBG(x) +# define DBG1(x) +#endif + +/* + * Misc definitions + */ +#define TRUE 1 +#define FALSE 0 + +/* + * Region Size for request/check/release region. + */ +#define IOBASE_REGION_SIZE 0x10 + +/* + * Hardware related defaults + */ +#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */ +#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */ +#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */ +#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */ + +/* + * Commands of AEDSP16's DSP (SBPRO+special). + * Some of them are COMMAND_xx, in the future they may change. + */ +#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ +#define COMMAND_52 0x52 /* */ +#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ +#define COMMAND_5C 0x5c /* */ +#define COMMAND_60 0x60 /* */ +#define COMMAND_66 0x66 /* */ +#define COMMAND_6C 0x6c /* */ +#define COMMAND_6E 0x6e /* */ +#define COMMAND_88 0x88 /* */ +#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ +#define COMMAND_C5 0xc5 /* */ +#define GET_DSP_VERSION 0xe1 /* Get DSP Version */ +#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ + +/* + * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port + * to have the actual I/O port. + * Register permissions are: + * (wo) == Write Only + * (ro) == Read Only + * (w-) == Write + * (r-) == Read + */ +#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ +#define DSP_READ 0x0a /* offset of DSP READ (ro) */ +#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ +#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ +#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ +#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ + + +#define RETRY 10 /* Various retry values on I/O opera- */ +#define STATUSRETRY 1000 /* tions. Sometimes we have to */ +#define HARDRETRY 500000 /* wait for previous cmd to complete */ + +/* + * Size of character arrays that store name and version of sound card + */ +#define CARDNAMELEN 15 /* Size of the card's name in chars */ +#define CARDVERLEN 2 /* Size of the card's version in chars */ + +#if defined(CONFIG_SC6600) +/* + * Bitmapped flags of hard configuration + */ +/* + * Decode macros (xl == low byte, xh = high byte) + */ +#define IOBASE(xl) ((xl & 0x01)?0x240:0x220) +#define JOY(xl) (xl & 0x02) +#define MPUADDR(xl) ( \ + (xl & 0x0C)?0x330: \ + (xl & 0x08)?0x320: \ + (xl & 0x04)?0x310: \ + 0x300) +#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530) +#define CDROM(xh) (xh & 0x20) +#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200) +/* + * Encode macros + */ +#define BLDIOBASE(xl, val) { \ + xl &= ~0x01; \ + if (val == 0x240) \ + xl |= 0x01; \ + } +#define BLDJOY(xl, val) { \ + xl &= ~0x02; \ + if (val == 1) \ + xl |= 0x02; \ + } +#define BLDMPUADDR(xl, val) { \ + xl &= ~0x0C; \ + switch (val) { \ + case 0x330: \ + xl |= 0x0C; \ + break; \ + case 0x320: \ + xl |= 0x08; \ + break; \ + case 0x310: \ + xl |= 0x04; \ + break; \ + case 0x300: \ + xl |= 0x00; \ + break; \ + default: \ + xl |= 0x00; \ + break; \ + } \ + } +#define BLDWSSADDR(xl, val) { \ + xl &= ~0x10; \ + if (val == 0xE80) \ + xl |= 0x10; \ + } +#define BLDCDROM(xh, val) { \ + xh &= ~0x20; \ + if (val == 1) \ + xh |= 0x20; \ + } +#define BLDCDROMADDR(xh, val) { \ + int tmp = val; \ + tmp -= 0x200; \ + tmp >>= 4; \ + tmp &= 0x1F; \ + xh |= tmp; \ + xh &= 0x7F; \ + xh |= 0x40; \ + } +#endif /* CONFIG_SC6600 */ + +/* + * Bit mapped flags for calling aedsp16_init_board(), and saving the current + * emulation mode. + */ +#define INIT_NONE (0 ) +#define INIT_SBPRO (1<<0) +#define INIT_MSS (1<<1) +#define INIT_MPU401 (1<<2) + +static int soft_cfg __initdata = 0; /* bitmapped config */ +static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */ +static int ver[CARDVERLEN] __initdata = {0, 0}; /* DSP Ver: + hi->ver[0] lo->ver[1] */ + +#if defined(CONFIG_SC6600) +static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */ + __initdata = { 0, 0}; +#endif /* CONFIG_SC6600 */ + +#if defined(CONFIG_SC6600) +/* Decoded hard configuration */ +struct d_hcfg { + int iobase; + int joystick; + int mpubase; + int wssbase; + int cdrom; + int cdrombase; +}; + +struct d_hcfg decoded_hcfg __initdata = {0, }; + +#endif /* CONFIG_SC6600 */ + +/* orVals contain the values to be or'ed */ +struct orVals { + int val; /* irq|mirq|dma */ + int or; /* soft_cfg |= TheStruct.or */ +}; + +/* aedsp16_info contain the audio card configuration */ +struct aedsp16_info { + int base_io; /* base I/O address for accessing card */ + int irq; /* irq value for DSP I/O */ + int mpu_irq; /* irq for mpu401 interface I/O */ + int dma; /* dma value for DSP I/O */ + int mss_base; /* base I/O for Microsoft Sound System */ + int mpu_base; /* base I/O for MPU-401 emulation */ + int init; /* Initialization status of the card */ +}; + +/* + * Magic values that the DSP will eat when configuring irq/mirq/dma + */ +/* DSP IRQ conversion array */ +static struct orVals orIRQ[] __initdata = { + {0x05, 0x28}, + {0x07, 0x08}, + {0x09, 0x10}, + {0x0a, 0x18}, + {0x0b, 0x20}, + {0x00, 0x00} +}; + +/* MPU-401 IRQ conversion array */ +static struct orVals orMIRQ[] __initdata = { + {0x05, 0x04}, + {0x07, 0x44}, + {0x09, 0x84}, + {0x0a, 0xc4}, + {0x00, 0x00} +}; + +/* DMA Channels conversion array */ +static struct orVals orDMA[] __initdata = { + {0x00, 0x01}, + {0x01, 0x02}, + {0x03, 0x03}, + {0x00, 0x00} +}; + +static struct aedsp16_info ae_config __initdata = { + DEF_AEDSP16_IOB, + DEF_AEDSP16_IRQ, + DEF_AEDSP16_MRQ, + DEF_AEDSP16_DMA, + -1, + -1, + INIT_NONE +}; + +/* + * Buffers to store audio card informations + */ +static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, }; +static char DSPVersion[CARDVERLEN + 1] __initdata = {0, }; + +static int __init aedsp16_wait_data(int port) +{ + int loop = STATUSRETRY; + unsigned char ret = 0; + + DBG1(("aedsp16_wait_data (0x%x): ", port)); + + do { + ret = inb(port + DSP_DATAVAIL); + /* + * Wait for data available (bit 7 of ret == 1) + */ + } while (!(ret & 0x80) && loop--); + + if (ret & 0x80) { + DBG1(("success.\n")); + return TRUE; + } + + DBG1(("failure.\n")); + return FALSE; +} + +static int __init aedsp16_read(int port) +{ + int inbyte; + + DBG((" Read DSP Byte (0x%x): ", port)); + + if (aedsp16_wait_data(port) == FALSE) { + DBG(("failure.\n")); + return -1; + } + + inbyte = inb(port + DSP_READ); + + DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte)); + + return inbyte; +} + +static int __init aedsp16_test_dsp(int port) +{ + return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE); +} + +static int __init aedsp16_dsp_reset(int port) +{ + /* + * Reset DSP + */ + + DBG(("Reset DSP:\n")); + + outb(1, (port + DSP_RESET)); + udelay(10); + outb(0, (port + DSP_RESET)); + udelay(10); + udelay(10); + if (aedsp16_test_dsp(port) == TRUE) { + DBG(("success.\n")); + return TRUE; + } else + DBG(("failure.\n")); + return FALSE; +} + +static int __init aedsp16_write(int port, int cmd) +{ + unsigned char ret; + int loop = HARDRETRY; + + DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd)); + + do { + ret = inb(port + DSP_STATUS); + /* + * DSP ready to receive data if bit 7 of ret == 0 + */ + if (!(ret & 0x80)) { + outb(cmd, port + DSP_COMMAND); + DBG(("success.\n")); + return 0; + } + } while (loop--); + + DBG(("timeout.\n")); + printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd); + + return -1; +} + +#if defined(CONFIG_SC6600) + +#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) +void __init aedsp16_pinfo(void) { + DBG(("\n Base address: %x\n", decoded_hcfg.iobase)); + DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not")); + DBG((" WSS addr : %x\n", decoded_hcfg.wssbase)); + DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase)); + DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not")); + DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase)); +} +#endif + +void __init aedsp16_hard_decode(void) { + + DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + +/* + * Decode Cfg Bytes. + */ + decoded_hcfg.iobase = IOBASE(hard_cfg[0]); + decoded_hcfg.joystick = JOY(hard_cfg[0]); + decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]); + decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]); + decoded_hcfg.cdrom = CDROM(hard_cfg[1]); + decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]); + +#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) + printk(" Original sound card configuration:\n"); + aedsp16_pinfo(); +#endif + +/* + * Now set up the real kernel configuration. + */ + decoded_hcfg.iobase = ae_config.base_io; + decoded_hcfg.wssbase = ae_config.mss_base; + decoded_hcfg.mpubase = ae_config.mpu_base; + +#if defined(CONFIG_SC6600_JOY) + decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */ +#endif +#if defined(CONFIG_SC6600_CDROM) + decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */ +#endif +#if defined(CONFIG_SC6600_CDROMBASE) + decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */ +#endif + +#if defined(AEDSP16_DEBUG) + DBG((" New Values:\n")); + aedsp16_pinfo(); +#endif + + DBG(("success.\n")); +} + +void __init aedsp16_hard_encode(void) { + + DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + + hard_cfg[0] = 0; + hard_cfg[1] = 0; + + hard_cfg[0] |= 0x20; + + BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase); + BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase); + BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase); + BLDJOY(hard_cfg[0], decoded_hcfg.joystick); + BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom); + BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase); + +#if defined(AEDSP16_DEBUG) + aedsp16_pinfo(); +#endif + + DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + DBG(("success.\n")); + +} + +static int __init aedsp16_hard_write(int port) { + + DBG(("aedsp16_hard_write:\n")); + + if (aedsp16_write(port, COMMAND_6C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, hard_cfg[0])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, hard_cfg[1])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, COMMAND_C5)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5); + DBG(("failure.\n")); + return FALSE; + } + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_hard_read(int port) { + + DBG(("aedsp16_hard_read:\n")); + + if (aedsp16_write(port, READ_HARD_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + + if ((hard_cfg[0] = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + if ((hard_cfg[1] = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_read(port) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_ext_cfg_write(int port) { + + int extcfg, val; + + if (aedsp16_write(port, COMMAND_66)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66); + return FALSE; + } + + extcfg = 7; + if (decoded_hcfg.cdrom != 2) + extcfg = 0x0F; + if ((decoded_hcfg.cdrom == 4) || + (decoded_hcfg.cdrom == 3)) + extcfg &= ~2; + if (decoded_hcfg.cdrombase == 0) + extcfg &= ~2; + if (decoded_hcfg.mpubase == 0) + extcfg &= ~1; + + if (aedsp16_write(port, extcfg)) { + printk("[AEDSP16] Write extcfg: failed!\n"); + return FALSE; + } + if (aedsp16_write(port, 0)) { + printk("[AEDSP16] Write extcfg: failed!\n"); + return FALSE; + } + if (decoded_hcfg.cdrom == 3) { + if (aedsp16_write(port, COMMAND_52)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); + return FALSE; + } + if ((val = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n" + , COMMAND_52); + return FALSE; + } + val &= 0x7F; + if (aedsp16_write(port, COMMAND_60)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); + return FALSE; + } + if (aedsp16_write(port, val)) { + printk("[AEDSP16] Write val: failed!\n"); + return FALSE; + } + } + + return TRUE; +} + +#endif /* CONFIG_SC6600 */ + +static int __init aedsp16_cfg_write(int port) { + if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); + return FALSE; + } + if (aedsp16_write(port, soft_cfg)) { + printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n"); + return FALSE; + } + return TRUE; +} + +static int __init aedsp16_init_mss(int port) +{ + DBG(("aedsp16_init_mss:\n")); + + mdelay(10); + + if (aedsp16_write(port, DSP_INIT_MSS)) { + printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n", + DSP_INIT_MSS); + DBG(("failure.\n")); + return FALSE; + } + + mdelay(10); + + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; + + outb(soft_cfg_mss, ae_config.mss_base); + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_setup_board(int port) { + int loop = RETRY; + +#if defined(CONFIG_SC6600) + int val = 0; + + if (aedsp16_hard_read(port) == FALSE) { + printk("[AEDSP16] aedsp16_hard_read: failed!\n"); + return FALSE; + } + + if (aedsp16_write(port, COMMAND_52)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); + return FALSE; + } + + if ((val = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + COMMAND_52); + return FALSE; + } +#endif + + do { + if (aedsp16_write(port, COMMAND_88)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88); + return FALSE; + } + mdelay(10); + } while ((aedsp16_wait_data(port) == FALSE) && loop--); + + if (aedsp16_read(port) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + COMMAND_88); + return FALSE; + } + +#if !defined(CONFIG_SC6600) + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + return FALSE; + } +#endif + + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; + +#if defined(CONFIG_SC6600) + if (aedsp16_write(port, COMMAND_60)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); + return FALSE; + } + if (aedsp16_write(port, val)) { + printk("[AEDSP16] DATA 0x%x: failed!\n", val); + return FALSE; + } + if (aedsp16_write(port, COMMAND_6E)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E); + return FALSE; + } + if (aedsp16_write(port, ver[0])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]); + return FALSE; + } + if (aedsp16_write(port, ver[1])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]); + return FALSE; + } + + if (aedsp16_hard_write(port) == FALSE) { + printk("[AEDSP16] aedsp16_hard_write: failed!\n"); + return FALSE; + } + + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + return FALSE; + } + +#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET) + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; +#endif + +#endif + + return TRUE; +} + +static int __init aedsp16_stdcfg(int port) { + if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); + return FALSE; + } + /* + * 0x0A == (IRQ 7, DMA 1, MIRQ 0) + */ + if (aedsp16_write(port, 0x0A)) { + printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); + return FALSE; + } + return TRUE; +} + +static int __init aedsp16_dsp_version(int port) +{ + int len = 0; + int ret; + + DBG(("Get DSP Version:\n")); + + if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION); + DBG(("failed.\n")); + return FALSE; + } + + do { + if ((ret = aedsp16_read(port)) == -1) { + DBG(("failed.\n")); + return FALSE; + } + /* + * We already know how many int are stored (2), so we know when the + * string is finished. + */ + ver[len++] = ret; + } while (len < CARDVERLEN); + sprintf(DSPVersion, "%d.%d", ver[0], ver[1]); + + DBG(("success.\n")); + + return TRUE; +} + +static int __init aedsp16_dsp_copyright(int port) +{ + int len = 0; + int ret; + + DBG(("Get DSP Copyright:\n")); + + if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT); + DBG(("failed.\n")); + return FALSE; + } + + do { + if ((ret = aedsp16_read(port)) == -1) { + /* + * If no more data available, return to the caller, no error if len>0. + * We have no other way to know when the string is finished. + */ + if (len) + break; + else { + DBG(("failed.\n")); + return FALSE; + } + } + + DSPCopyright[len++] = ret; + + } while (len < CARDNAMELEN); + + DBG(("success.\n")); + + return TRUE; +} + +static void __init aedsp16_init_tables(void) +{ + int i = 0; + + memset(DSPCopyright, 0, CARDNAMELEN + 1); + memset(DSPVersion, 0, CARDVERLEN + 1); + + for (i = 0; orIRQ[i].or; i++) + if (orIRQ[i].val == ae_config.irq) { + soft_cfg |= orIRQ[i].or; + soft_cfg_mss |= orIRQ[i].or; + } + + for (i = 0; orMIRQ[i].or; i++) + if (orMIRQ[i].or == ae_config.mpu_irq) + soft_cfg |= orMIRQ[i].or; + + for (i = 0; orDMA[i].or; i++) + if (orDMA[i].val == ae_config.dma) { + soft_cfg |= orDMA[i].or; + soft_cfg_mss |= orDMA[i].or; + } +} + +static int __init aedsp16_init_board(void) +{ + aedsp16_init_tables(); + + if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_reset: failed!\n"); + return FALSE; + } + if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n"); + return FALSE; + } + + /* + * My AEDSP16 card return SC-6000 in DSPCopyright, so + * if we have something different, we have to be warned. + */ + if (strcmp("SC-6000", DSPCopyright)) + printk("[AEDSP16] Warning: non SC-6000 audio card!\n"); + + if (aedsp16_dsp_version(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_version: failed!\n"); + return FALSE; + } + + if (aedsp16_stdcfg(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); + return FALSE; + } + +#if defined(CONFIG_SC6600) + if (aedsp16_hard_read(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_hard_read: failed!\n"); + return FALSE; + } + + aedsp16_hard_decode(); + + aedsp16_hard_encode(); + + if (aedsp16_hard_write(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_hard_write: failed!\n"); + return FALSE; + } + + if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n"); + return FALSE; + } +#endif /* CONFIG_SC6600 */ + + if (aedsp16_setup_board(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_setup_board: failed!\n"); + return FALSE; + } + + if (ae_config.mss_base != -1) { + if (ae_config.init & INIT_MSS) { + if (aedsp16_init_mss(ae_config.base_io) == FALSE) { + printk("[AEDSP16] Can not initialize" + "Microsoft Sound System mode.\n"); + return FALSE; + } + } + } + +#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) + + printk("Audio Excel DSP 16 init v%s (%s %s) [", + VERSION, DSPCopyright, + DSPVersion); + + if (ae_config.mpu_base != -1) { + if (ae_config.init & INIT_MPU401) { + printk("MPU401"); + if ((ae_config.init & INIT_MSS) || + (ae_config.init & INIT_SBPRO)) + printk(" "); + } + } + + if (ae_config.mss_base == -1) { + if (ae_config.init & INIT_SBPRO) { + printk("SBPro"); + if (ae_config.init & INIT_MSS) + printk(" "); + } + } + + if (ae_config.mss_base != -1) + if (ae_config.init & INIT_MSS) + printk("MSS"); + + printk("]\n"); +#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */ + + mdelay(10); + + return TRUE; +} + +static int __init init_aedsp16_sb(void) +{ + DBG(("init_aedsp16_sb: ")); + +/* + * If the card is already init'ed MSS, we can not init it to SBPRO too + * because the board can not emulate simultaneously MSS and SBPRO. + */ + if (ae_config.init & INIT_MSS) + return FALSE; + if (ae_config.init & INIT_SBPRO) + return FALSE; + + ae_config.init |= INIT_SBPRO; + + DBG(("done.\n")); + + return TRUE; +} + +static void __init uninit_aedsp16_sb(void) +{ + DBG(("uninit_aedsp16_sb: ")); + + ae_config.init &= ~INIT_SBPRO; + + DBG(("done.\n")); +} + +static int __init init_aedsp16_mss(void) +{ + DBG(("init_aedsp16_mss: ")); + +/* + * If the card is already init'ed SBPRO, we can not init it to MSS too + * because the board can not emulate simultaneously MSS and SBPRO. + */ + if (ae_config.init & INIT_SBPRO) + return FALSE; + if (ae_config.init & INIT_MSS) + return FALSE; +/* + * We must allocate the CONFIG_AEDSP16_BASE region too because these are the + * I/O ports to access card's control registers. + */ + if (!(ae_config.init & INIT_MPU401)) { + if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, + "aedsp16 (base)")) { + printk( + "AEDSP16 BASE I/O port region is already in use.\n"); + return FALSE; + } + } + + ae_config.init |= INIT_MSS; + + DBG(("done.\n")); + + return TRUE; +} + +static void __init uninit_aedsp16_mss(void) +{ + DBG(("uninit_aedsp16_mss: ")); + + if ((!(ae_config.init & INIT_MPU401)) && + (ae_config.init & INIT_MSS)) { + release_region(ae_config.base_io, IOBASE_REGION_SIZE); + DBG(("AEDSP16 base region released.\n")); + } + + ae_config.init &= ~INIT_MSS; + DBG(("done.\n")); +} + +static int __init init_aedsp16_mpu(void) +{ + DBG(("init_aedsp16_mpu: ")); + + if (ae_config.init & INIT_MPU401) + return FALSE; + +/* + * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O + * ports to access card's control registers. + */ + if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) { + if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, + "aedsp16 (base)")) { + printk( + "AEDSP16 BASE I/O port region is already in use.\n"); + return FALSE; + } + } + + ae_config.init |= INIT_MPU401; + + DBG(("done.\n")); + + return TRUE; +} + +static void __init uninit_aedsp16_mpu(void) +{ + DBG(("uninit_aedsp16_mpu: ")); + + if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) && + (ae_config.init & INIT_MPU401)) { + release_region(ae_config.base_io, IOBASE_REGION_SIZE); + DBG(("AEDSP16 base region released.\n")); + } + + ae_config.init &= ~INIT_MPU401; + + DBG(("done.\n")); +} + +int __init init_aedsp16(void) +{ + int initialized = FALSE; + + DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n", + ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq)); + + if (ae_config.mss_base == -1) { + if (init_aedsp16_sb() == FALSE) { + uninit_aedsp16_sb(); + } else { + initialized = TRUE; + } + } + + if (ae_config.mpu_base != -1) { + if (init_aedsp16_mpu() == FALSE) { + uninit_aedsp16_mpu(); + } else { + initialized = TRUE; + } + } + +/* + * In the sequence of init routines, the MSS init MUST be the last! + * This because of the special register programming the MSS mode needs. + * A board reset would disable the MSS mode restoring the default SBPRO + * mode. + */ + if (ae_config.mss_base != -1) { + if (init_aedsp16_mss() == FALSE) { + uninit_aedsp16_mss(); + } else { + initialized = TRUE; + } + } + + if (initialized) + initialized = aedsp16_init_board(); + return initialized; +} + +void __init uninit_aedsp16(void) +{ + if (ae_config.mss_base != -1) + uninit_aedsp16_mss(); + else + uninit_aedsp16_sb(); + if (ae_config.mpu_base != -1) + uninit_aedsp16_mpu(); +} + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata mpu_irq = -1; +static int __initdata mss_base = -1; +static int __initdata mpu_base = -1; + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)"); +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)"); +MODULE_PARM(dma, "i"); +MODULE_PARM_DESC(dma, "dma line (0 1 3)"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)"); +MODULE_PARM(mss_base, "i"); +MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)"); +MODULE_PARM(mpu_base, "i"); +MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)"); +MODULE_AUTHOR("Riccardo Facchetti "); +MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION); +MODULE_LICENSE("GPL"); + +static int __init do_init_aedsp16(void) { + printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n"); + if (io == -1 || dma == -1 || irq == -1) { + printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n"); + return -EINVAL; + } + + ae_config.base_io = io; + ae_config.irq = irq; + ae_config.dma = dma; + + ae_config.mss_base = mss_base; + ae_config.mpu_base = mpu_base; + ae_config.mpu_irq = mpu_irq; + + if (init_aedsp16() == FALSE) { + printk(KERN_ERR "aedsp16: initialization failed\n"); + /* + * XXX + * What error should we return here ? + */ + return -EINVAL; + } + return 0; +} + +static void __exit cleanup_aedsp16(void) { + uninit_aedsp16(); +} + +module_init(do_init_aedsp16); +module_exit(cleanup_aedsp16); + +#ifndef MODULE +static int __init setup_aedsp16(char *str) +{ + /* io, irq, dma, mss_io, mpu_io, mpu_irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + mss_base = ints[4]; + mpu_base = ints[5]; + mpu_irq = ints[6]; + return 1; +} + +__setup("aedsp16=", setup_aedsp16); +#endif diff -Nru linux/sound/oss/audio.c linux-2.4.19-pre5-mjc/sound/oss/audio.c --- linux/sound/oss/audio.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/audio.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,987 @@ +/* + * sound/audio.c + * + * Device file manager for /dev/audio + */ + +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Thomas Sailer : moved several static variables into struct audio_operations + * (which is grossly misnamed btw.) because they have the same + * lifetime as the rest in there and dynamic allocation saves + * 12k or so + * Thomas Sailer : use more logical O_NONBLOCK semantics + * Daniel Rodriksson: reworked the use of the device specific copy_user + * still generic + * Horst von Brand: Add missing #include + * Chris Rankin : Update the module-usage counter for the coprocessor, + * and decrement the counters again if we cannot open + * the audio device. + */ + +#include +#include +#include + +#include "sound_config.h" +#include "ulaw.h" +#include "coproc.h" + +#define NEUTRAL8 0x80 +#define NEUTRAL16 0x00 + + +int dma_ioctl(int dev, unsigned int cmd, caddr_t arg); + +static int set_format(int dev, int fmt) +{ + if (fmt != AFMT_QUERY) + { + audio_devs[dev]->local_conversion = 0; + + if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ + { + if (fmt == AFMT_MU_LAW) + { + fmt = AFMT_U8; + audio_devs[dev]->local_conversion = CNV_MU_LAW; + } + else + fmt = AFMT_U8; /* This is always supported */ + } + audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt); + audio_devs[dev]->local_format = fmt; + } + else + return audio_devs[dev]->local_format; + + if (audio_devs[dev]->local_conversion) + return audio_devs[dev]->local_conversion; + else + return audio_devs[dev]->local_format; +} + +int audio_open(int dev, struct file *file) +{ + int ret; + int bits; + int dev_type = dev & 0x0f; + int mode = translate_mode(file); + const struct audio_driver *driver; + const struct coproc_operations *coprocessor; + + dev = dev >> 4; + + if (dev_type == SND_DEV_DSP16) + bits = 16; + else + bits = 8; + + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; + + driver = audio_devs[dev]->d; + if (driver->owner) + __MOD_INC_USE_COUNT(driver->owner); + + if ((ret = DMAbuf_open(dev, mode)) < 0) + goto error_1; + + if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { + if (coprocessor->owner) + __MOD_INC_USE_COUNT(coprocessor->owner); + + if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) { + printk(KERN_WARNING "Sound: Can't access coprocessor device\n"); + goto error_2; + } + } + + audio_devs[dev]->local_conversion = 0; + + if (dev_type == SND_DEV_AUDIO) + set_format(dev, AFMT_MU_LAW); + else + set_format(dev, bits); + + audio_devs[dev]->audio_mode = AM_NONE; + + return 0; + + /* + * Clean-up stack: this is what needs (un)doing if + * we can't open the audio device ... + */ + error_2: + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + DMAbuf_release(dev, mode); + + error_1: + if (driver->owner) + __MOD_DEC_USE_COUNT(driver->owner); + + return ret; +} + +static void sync_output(int dev) +{ + int p, i; + int l; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + if (dmap->fragment_size <= 0) + return; + dmap->flags |= DMA_POST; + + /* Align the write pointer with fragment boundaries */ + + if ((l = dmap->user_counter % dmap->fragment_size) > 0) + { + int len; + unsigned long offs = dmap->user_counter % dmap->bytes_in_use; + + len = dmap->fragment_size - l; + memset(dmap->raw_buf + offs, dmap->neutral_byte, len); + DMAbuf_move_wrpointer(dev, len); + } + + /* + * Clean all unused buffer fragments. + */ + + p = dmap->qtail; + dmap->flags |= DMA_POST; + + for (i = dmap->qlen + 1; i < dmap->nbufs; i++) + { + p = (p + 1) % dmap->nbufs; + if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) > + (dmap->raw_buf + dmap->buffsize)) + printk(KERN_ERR "audio: Buffer error 2\n"); + + memset(dmap->raw_buf + p * dmap->fragment_size, + dmap->neutral_byte, + dmap->fragment_size); + } + + dmap->flags |= DMA_DIRTY; +} + +void audio_release(int dev, struct file *file) +{ + const struct coproc_operations *coprocessor; + int mode = translate_mode(file); + + dev = dev >> 4; + + /* + * We do this in DMAbuf_release(). Why are we doing it + * here? Why don't we test the file mode before setting + * both flags? DMAbuf_release() does. + * ...pester...pester...pester... + */ + audio_devs[dev]->dmap_out->closing = 1; + audio_devs[dev]->dmap_in->closing = 1; + + /* + * We need to make sure we allocated the dmap_out buffer + * before we go mucking around with it in sync_output(). + */ + if (mode & OPEN_WRITE) + sync_output(dev); + + if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { + coprocessor->close(coprocessor->devc, COPR_PCM); + + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + } + DMAbuf_release(dev, mode); + + if (audio_devs[dev]->d->owner) + __MOD_DEC_USE_COUNT (audio_devs[dev]->d->owner); +} + +static void translate_bytes(const unsigned char *table, unsigned char *buff, int n) +{ + unsigned long i; + + if (n <= 0) + return; + + for (i = 0; i < n; ++i) + buff[i] = table[buff[i]]; +} + +int audio_write(int dev, struct file *file, const char *buf, int count) +{ + int c, p, l, buf_size, used, returned; + int err; + char *dma_buf; + + dev = dev >> 4; + + p = 0; + c = count; + + if(count < 0) + return -EINVAL; + + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EPERM; + + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_devs[dev]->audio_mode |= AM_WRITE; + else + audio_devs[dev]->audio_mode = AM_WRITE; + + if (!count) /* Flush output */ + { + sync_output(dev); + return 0; + } + + while (c) + { + if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0) + { + /* Handle nonblocking mode */ + if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN) + return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */ + return err; + } + l = c; + + if (l > buf_size) + l = buf_size; + + returned = l; + used = l; + if (!audio_devs[dev]->d->copy_user) + { + if ((dma_buf + l) > + (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize)) + { + printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); + return -EDOM; + } + if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) + { + printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); + return -EDOM; + } + if(copy_from_user(dma_buf, &(buf)[p], l)) + return -EFAULT; + } + else audio_devs[dev]->d->copy_user (dev, + dma_buf, 0, + buf, p, + c, buf_size, + &used, &returned, + l); + l = returned; + + if (audio_devs[dev]->local_conversion & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); + } + c -= used; + p += used; + DMAbuf_move_wrpointer(dev, l); + + } + + return count; +} + +int audio_read(int dev, struct file *file, char *buf, int count) +{ + int c, p, l; + char *dmabuf; + int buf_no; + + dev = dev >> 4; + p = 0; + c = count; + + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EPERM; + + if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + sync_output(dev); + + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_devs[dev]->audio_mode |= AM_READ; + else + audio_devs[dev]->audio_mode = AM_READ; + + while(c) + { + if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0) + { + /* + * Nonblocking mode handling. Return current # of bytes + */ + + if (p > 0) /* Avoid throwing away data */ + return p; /* Return it instead */ + + if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN) + return -EAGAIN; + + return buf_no; + } + if (l > c) + l = c; + + /* + * Insert any local processing here. + */ + + if (audio_devs[dev]->local_conversion & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + + translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l); + } + + { + char *fixit = dmabuf; + + if(copy_to_user(&(buf)[p], fixit, l)) + return -EFAULT; + }; + + DMAbuf_rmchars(dev, buf_no, l); + + p += l; + c -= l; + } + + return count - c; +} + +int audio_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) +{ + int val, count; + unsigned long flags; + struct dma_buffparms *dmap; + + dev = dev >> 4; + + if (_IOC_TYPE(cmd) == 'C') { + if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ + return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); + /* else + printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */ + return -ENXIO; + } + else switch (cmd) + { + case SNDCTL_DSP_SYNC: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return 0; + if (audio_devs[dev]->dmap_out->fragment_size == 0) + return 0; + sync_output(dev); + DMAbuf_sync(dev); + DMAbuf_reset(dev); + return 0; + + case SNDCTL_DSP_POST: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return 0; + if (audio_devs[dev]->dmap_out->fragment_size == 0) + return 0; + audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; + sync_output(dev); + dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0); + return 0; + + case SNDCTL_DSP_RESET: + audio_devs[dev]->audio_mode = AM_NONE; + DMAbuf_reset(dev); + return 0; + + case SNDCTL_DSP_GETFMTS: + val = audio_devs[dev]->format_mask | AFMT_MU_LAW; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = set_format(dev, val); + break; + + case SNDCTL_DSP_GETISPACE: + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return 0; + if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + return -EBUSY; + return dma_ioctl(dev, cmd, arg); + + case SNDCTL_DSP_GETOSPACE: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EPERM; + if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + return -EBUSY; + return dma_ioctl(dev, cmd, arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */ + if (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode == OPEN_READWRITE) + val |= DSP_CAP_DUPLEX; + if (audio_devs[dev]->coproc) + val |= DSP_CAP_COPROC; + if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ + val |= DSP_CAP_BATCH; + if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ + val |= DSP_CAP_TRIGGER; + break; + + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = audio_devs[dev]->d->set_speed(dev, val); + break; + + case SOUND_PCM_READ_RATE: + val = audio_devs[dev]->d->set_speed(dev, 0); + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val > 1 || val < 0) + return -EINVAL; + val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = audio_devs[dev]->d->set_channels(dev, val); + break; + + case SOUND_PCM_READ_CHANNELS: + val = audio_devs[dev]->d->set_channels(dev, 0); + break; + + case SOUND_PCM_READ_BITS: + val = audio_devs[dev]->d->set_bits(dev, 0); + break; + + case SNDCTL_DSP_SETDUPLEX: + if (audio_devs[dev]->open_mode != OPEN_READWRITE) + return -EPERM; + return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO; + + case SNDCTL_DSP_PROFILE: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + audio_devs[dev]->dmap_out->applic_profile = val; + if (audio_devs[dev]->open_mode & OPEN_READ) + audio_devs[dev]->dmap_in->applic_profile = val; + return 0; + + case SNDCTL_DSP_GETODELAY: + dmap = audio_devs[dev]->dmap_out; + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (!(dmap->flags & DMA_ALLOC_DONE)) + { + val=0; + break; + } + + save_flags (flags); + cli(); + /* Compute number of bytes that have been played */ + count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); + if (count < dmap->fragment_size && dmap->qhead != 0) + count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ + count += dmap->byte_counter; + + /* Substract current count from the number of bytes written by app */ + count = dmap->user_counter - count; + if (count < 0) + count = 0; + restore_flags (flags); + val = count; + break; + + default: + return dma_ioctl(dev, cmd, arg); + } + return put_user(val, (int *)arg); +} + +void audio_init_devices(void) +{ + /* + * NOTE! This routine could be called several times during boot. + */ +} + +void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) +{ + /* + * This routine breaks the physical device buffers to logical ones. + */ + + struct audio_operations *dsp_dev = audio_devs[dev]; + + unsigned i, n; + unsigned sr, nc, sz, bsz; + + sr = dsp_dev->d->set_speed(dev, 0); + nc = dsp_dev->d->set_channels(dev, 0); + sz = dsp_dev->d->set_bits(dev, 0); + + if (sz == 8) + dmap->neutral_byte = NEUTRAL8; + else + dmap->neutral_byte = NEUTRAL16; + + if (sr < 1 || nc < 1 || sz < 1) + { +/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/ + sr = DSP_DEFAULT_SPEED; + nc = 1; + sz = 8; + } + + sz = sr * nc * sz; + + sz /= 8; /* #bits -> #bytes */ + dmap->data_rate = sz; + + if (!dmap->needs_reorg) + return; + dmap->needs_reorg = 0; + + if (dmap->fragment_size == 0) + { + /* Compute the fragment size using the default algorithm */ + + /* + * Compute a buffer size for time not exceeding 1 second. + * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds + * of sound (using the current speed, sample size and #channels). + */ + + bsz = dmap->buffsize; + while (bsz > sz) + bsz /= 2; + + if (bsz == dmap->buffsize) + bsz /= 2; /* Needs at least 2 buffers */ + + /* + * Split the computed fragment to smaller parts. After 3.5a9 + * the default subdivision is 4 which should give better + * results when recording. + */ + + if (dmap->subdivision == 0) /* Not already set */ + { + dmap->subdivision = 4; /* Init to the default value */ + + if ((bsz / dmap->subdivision) > 4096) + dmap->subdivision *= 2; + if ((bsz / dmap->subdivision) < 4096) + dmap->subdivision = 1; + } + bsz /= dmap->subdivision; + + if (bsz < 16) + bsz = 16; /* Just a sanity check */ + + dmap->fragment_size = bsz; + } + else + { + /* + * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or + * the buffer size computation has already been done. + */ + if (dmap->fragment_size > (dmap->buffsize / 2)) + dmap->fragment_size = (dmap->buffsize / 2); + bsz = dmap->fragment_size; + } + + if (audio_devs[dev]->min_fragment) + if (bsz < (1 << audio_devs[dev]->min_fragment)) + bsz = 1 << audio_devs[dev]->min_fragment; + if (audio_devs[dev]->max_fragment) + if (bsz > (1 << audio_devs[dev]->max_fragment)) + bsz = 1 << audio_devs[dev]->max_fragment; + bsz &= ~0x07; /* Force size which is multiple of 8 bytes */ +#ifdef OS_DMA_ALIGN_CHECK + OS_DMA_ALIGN_CHECK(bsz); +#endif + + n = dmap->buffsize / bsz; + if (n > MAX_SUB_BUFFERS) + n = MAX_SUB_BUFFERS; + if (n > dmap->max_fragments) + n = dmap->max_fragments; + + if (n < 2) + { + n = 2; + bsz /= 2; + } + dmap->nbufs = n; + dmap->bytes_in_use = n * bsz; + dmap->fragment_size = bsz; + dmap->max_byte_counter = (dmap->data_rate * 60 * 60) + + dmap->bytes_in_use; /* Approximately one hour */ + + if (dmap->raw_buf) + { + memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); + } + + for (i = 0; i < dmap->nbufs; i++) + { + dmap->counts[i] = 0; + } + + dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; +} + +static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact) +{ + if (fact == 0) + { + fact = dmap->subdivision; + if (fact == 0) + fact = 1; + return fact; + } + if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */ + return -EINVAL; + + if (fact > MAX_REALTIME_FACTOR) + return -EINVAL; + + if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) + return -EINVAL; + + dmap->subdivision = fact; + return fact; +} + +static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact) +{ + int bytes, count; + + if (fact == 0) + return -EIO; + + if (dmap->subdivision != 0 || + dmap->fragment_size) /* Too late to change */ + return -EINVAL; + + bytes = fact & 0xffff; + count = (fact >> 16) & 0x7fff; + + if (count == 0) + count = MAX_SUB_BUFFERS; + else if (count < MAX_SUB_BUFFERS) + count++; + + if (bytes < 4 || bytes > 17) /* <16 || > 512k */ + return -EINVAL; + + if (count < 2) + return -EINVAL; + + if (audio_devs[dev]->min_fragment > 0) + if (bytes < audio_devs[dev]->min_fragment) + bytes = audio_devs[dev]->min_fragment; + + if (audio_devs[dev]->max_fragment > 0) + if (bytes > audio_devs[dev]->max_fragment) + bytes = audio_devs[dev]->max_fragment; + +#ifdef OS_DMA_MINBITS + if (bytes < OS_DMA_MINBITS) + bytes = OS_DMA_MINBITS; +#endif + + dmap->fragment_size = (1 << bytes); + dmap->max_fragments = count; + + if (dmap->fragment_size > dmap->buffsize) + dmap->fragment_size = dmap->buffsize; + + if (dmap->fragment_size == dmap->buffsize && + audio_devs[dev]->flags & DMA_AUTOMODE) + dmap->fragment_size /= 2; /* Needs at least 2 buffers */ + + dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ + return bytes | ((count - 1) << 16); +} + +int dma_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; + struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; + struct dma_buffparms *dmap; + audio_buf_info info; + count_info cinfo; + int fact, ret, changed, bits, count, err; + unsigned long flags; + + switch (cmd) + { + case SNDCTL_DSP_SUBDIVIDE: + ret = 0; + if (get_user(fact, (int *)arg)) + return -EFAULT; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + ret = dma_subdivide(dev, dmap_out, fact); + if (ret < 0) + return ret; + if (audio_devs[dev]->open_mode != OPEN_WRITE || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + ret = dma_subdivide(dev, dmap_in, fact); + if (ret < 0) + return ret; + break; + + case SNDCTL_DSP_GETISPACE: + case SNDCTL_DSP_GETOSPACE: + dmap = dmap_out; + if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ)) + return -EINVAL; + if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) + dmap = dmap_in; + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return -EINVAL; + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); + info.fragstotal = dmap->nbufs; + if (cmd == SNDCTL_DSP_GETISPACE) + info.fragments = dmap->qlen; + else + { + if (!DMAbuf_space_in_queue(dev)) + info.fragments = 0; + else + { + info.fragments = DMAbuf_space_in_queue(dev); + if (audio_devs[dev]->d->local_qlen) + { + int tmp = audio_devs[dev]->d->local_qlen(dev); + if (tmp && info.fragments) + tmp--; /* + * This buffer has been counted twice + */ + info.fragments -= tmp; + } + } + } + if (info.fragments < 0) + info.fragments = 0; + else if (info.fragments > dmap->nbufs) + info.fragments = dmap->nbufs; + + info.fragsize = dmap->fragment_size; + info.bytes = info.fragments * dmap->fragment_size; + + if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) + info.bytes -= dmap->counts[dmap->qhead]; + else + { + info.fragments = info.bytes / dmap->fragment_size; + info.bytes -= dmap->user_counter % dmap->fragment_size; + } + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(bits, (int *)arg)) + return -EFAULT; + bits &= audio_devs[dev]->open_mode; + if (audio_devs[dev]->d->trigger == NULL) + return -EINVAL; + if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) && + (bits & PCM_ENABLE_OUTPUT)) + return -EINVAL; + save_flags(flags); + cli(); + changed = audio_devs[dev]->enable_bits ^ bits; + if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) + { + reorganize_buffers(dev, dmap_in, 1); + if ((err = audio_devs[dev]->d->prepare_for_input(dev, + dmap_in->fragment_size, dmap_in->nbufs)) < 0) { + restore_flags(flags); + return -err; + } + dmap_in->dma_mode = DMODE_INPUT; + audio_devs[dev]->enable_bits = bits; + DMAbuf_activate_recording(dev, dmap_in); + } + if ((changed & bits) & PCM_ENABLE_OUTPUT && + (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) && + audio_devs[dev]->go) + { + if (!(dmap_out->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap_out, 0); + dmap_out->dma_mode = DMODE_OUTPUT; + audio_devs[dev]->enable_bits = bits; + dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; + DMAbuf_launch_output(dev, dmap_out); + } + audio_devs[dev]->enable_bits = bits; +#if 0 + if (changed && audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); +#endif + restore_flags(flags); + /* Falls through... */ + + case SNDCTL_DSP_GETTRIGGER: + ret = audio_devs[dev]->enable_bits; + break; + + case SNDCTL_DSP_SETSYNCRO: + if (!audio_devs[dev]->d->trigger) + return -EINVAL; + audio_devs[dev]->d->trigger(dev, 0); + audio_devs[dev]->go = 0; + return 0; + + case SNDCTL_DSP_GETIPTR: + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EINVAL; + save_flags(flags); + cli(); + cinfo.bytes = dmap_in->byte_counter; + cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3; + if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0) + cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */ + cinfo.blocks = dmap_in->qlen; + cinfo.bytes += cinfo.ptr; + if (dmap_in->mapping_flags & DMA_MAP_MAPPED) + dmap_in->qlen = 0; /* Reset interrupt counter */ + restore_flags(flags); + if (copy_to_user(arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + + save_flags(flags); + cli(); + cinfo.bytes = dmap_out->byte_counter; + cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3; + if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0) + cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ + cinfo.blocks = dmap_out->qlen; + cinfo.bytes += cinfo.ptr; + if (dmap_out->mapping_flags & DMA_MAP_MAPPED) + dmap_out->qlen = 0; /* Reset interrupt counter */ + restore_flags(flags); + if (copy_to_user(arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (!(dmap_out->flags & DMA_ALLOC_DONE)) + { + ret=0; + break; + } + save_flags(flags); + cli(); + /* Compute number of bytes that have been played */ + count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT); + if (count < dmap_out->fragment_size && dmap_out->qhead != 0) + count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ + count += dmap_out->byte_counter; + /* Substract current count from the number of bytes written by app */ + count = dmap_out->user_counter - count; + if (count < 0) + count = 0; + restore_flags (flags); + ret = count; + break; + + case SNDCTL_DSP_POST: + if (audio_devs[dev]->dmap_out->qlen > 0) + if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) + DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + dmap = dmap_out; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ)); + if (audio_devs[dev]->open_mode == OPEN_READ || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ)); + if (audio_devs[dev]->open_mode == OPEN_READ) + dmap = dmap_in; + ret = dmap->fragment_size; + break; + + case SNDCTL_DSP_SETFRAGMENT: + ret = 0; + if (get_user(fact, (int *)arg)) + return -EFAULT; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + ret = dma_set_fragment(dev, dmap_out, fact); + if (ret < 0) + return ret; + if (audio_devs[dev]->open_mode == OPEN_READ || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + ret = dma_set_fragment(dev, dmap_in, fact); + if (ret < 0) + return ret; + if (!arg) /* don't know what this is good for, but preserve old semantics */ + return 0; + break; + + default: + if (!audio_devs[dev]->d->ioctl) + return -EINVAL; + return audio_devs[dev]->d->ioctl(dev, cmd, arg); + } + return put_user(ret, (int *)arg); +} diff -Nru linux/sound/oss/audio_syms.c linux-2.4.19-pre5-mjc/sound/oss/audio_syms.c --- linux/sound/oss/audio_syms.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/audio_syms.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,21 @@ +/* + * Exported symbols for audio driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char audio_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +EXPORT_SYMBOL(DMAbuf_start_dma); +EXPORT_SYMBOL(DMAbuf_open_dma); +EXPORT_SYMBOL(DMAbuf_close_dma); +EXPORT_SYMBOL(DMAbuf_inputintr); +EXPORT_SYMBOL(DMAbuf_outputintr); +EXPORT_SYMBOL(dma_ioctl); +EXPORT_SYMBOL(audio_open); +EXPORT_SYMBOL(audio_release); diff -Nru linux/sound/oss/awe_hw.h linux-2.4.19-pre5-mjc/sound/oss/awe_hw.h --- linux/sound/oss/awe_hw.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/awe_hw.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,99 @@ +/* + * sound/awe_hw.h + * + * Access routines and definitions for the low level driver for the + * Creative AWE32/SB32/AWE64 wave table synth. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AWE_HW_H_DEF +#define AWE_HW_H_DEF + +/* + * Emu-8000 control registers + * name(channel) reg, port + */ + +#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch)) + +#define Data0 0 /* 0x620: doubleword r/w */ +#define Data1 1 /* 0xA20: doubleword r/w */ +#define Data2 2 /* 0xA22: word r/w */ +#define Data3 3 /* 0xE20: word r/w */ +#define Pointer 4 /* 0xE22 register pointer r/w */ + +#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */ +#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */ +#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */ +#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */ +#define AWE_0080(ch) awe_cmd_idx(4,ch), Data0 /* DW: ?? */ +#define AWE_00A0(ch) awe_cmd_idx(5,ch), Data0 /* DW: ?? */ +#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */ +#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */ +#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */ +#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */ +#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */ +#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */ +#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */ +#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */ +#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */ +#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */ +#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */ +#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */ +#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */ +#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */ +#define AWE_WC_Cmd awe_cmd_idx(1,27) +#define AWE_WC_Port Data2 +#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */ +#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */ +#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */ +#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */ +#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */ +#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */ +#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */ +#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */ +#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */ +#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */ +#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */ +#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */ +#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */ +#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */ +#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */ +#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */ +#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */ +#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */ +#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */ +#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */ +#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */ + +/* used during detection (returns ROM version?; not documented in ADIP) */ +#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */ +#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */ + + +#define AWE_MAX_VOICES 32 +#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/ + +#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */ +#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */ + +#define AWE_DRAM_OFFSET 0x200000 +#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */ + +#endif diff -Nru linux/sound/oss/awe_wave.c linux-2.4.19-pre5-mjc/sound/oss/awe_wave.c --- linux/sound/oss/awe_wave.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/awe_wave.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,6159 @@ +/* + * sound/awe_wave.c + * + * The low level driver for the AWE32/SB32/AWE64 wave table synth. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#include "awe_wave.h" +#include "awe_hw.h" + +#ifdef AWE_HAS_GUS_COMPATIBILITY +#include "tuning.h" +#include +#endif + +/* + * debug message + */ + +#ifdef AWE_DEBUG_ON +#define DEBUG(LVL,XXX) {if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }} +#define ERRMSG(XXX) {if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }} +#define FATALERR(XXX) XXX +#else +#define DEBUG(LVL,XXX) /**/ +#define ERRMSG(XXX) XXX +#define FATALERR(XXX) XXX +#endif + +/* + * bank and voice record + */ + +typedef struct _sf_list sf_list; +typedef struct _awe_voice_list awe_voice_list; +typedef struct _awe_sample_list awe_sample_list; + +/* soundfont record */ +struct _sf_list { + unsigned short sf_id; /* id number */ + unsigned short type; /* lock & shared flags */ + int num_info; /* current info table index */ + int num_sample; /* current sample table index */ + int mem_ptr; /* current word byte pointer */ + awe_voice_list *infos, *last_infos; /* instruments */ + awe_sample_list *samples, *last_samples; /* samples */ +#ifdef AWE_ALLOW_SAMPLE_SHARING + sf_list *shared; /* shared list */ + unsigned char name[AWE_PATCH_NAME_LEN]; /* sharing id */ +#endif + sf_list *next, *prev; +}; + +/* instrument list */ +struct _awe_voice_list { + awe_voice_info v; /* instrument information */ + sf_list *holder; /* parent sf_list of this record */ + unsigned char bank, instr; /* preset number information */ + char type, disabled; /* type=normal/mapped, disabled=boolean */ + awe_voice_list *next; /* linked list with same sf_id */ + awe_voice_list *next_instr; /* instrument list */ + awe_voice_list *next_bank; /* hash table list */ +}; + +/* voice list type */ +#define V_ST_NORMAL 0 +#define V_ST_MAPPED 1 + +/* sample list */ +struct _awe_sample_list { + awe_sample_info v; /* sample information */ + sf_list *holder; /* parent sf_list of this record */ + awe_sample_list *next; /* linked list with same sf_id */ +}; + +/* sample and information table */ +static int current_sf_id = 0; /* current number of fonts */ +static int locked_sf_id = 0; /* locked position */ +static sf_list *sfhead = NULL, *sftail = NULL; /* linked-lists */ + +#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0) +#define awe_free_info() (sftail ? sftail->num_info : 0) +#define awe_free_sample() (sftail ? sftail->num_sample : 0) + +#define AWE_MAX_PRESETS 256 +#define AWE_DEFAULT_PRESET 0 +#define AWE_DEFAULT_BANK 0 +#define AWE_DEFAULT_DRUM 0 +#define AWE_DRUM_BANK 128 + +#define MAX_LAYERS AWE_MAX_VOICES + +/* preset table index */ +static awe_voice_list *preset_table[AWE_MAX_PRESETS]; + +/* + * voice table + */ + +/* effects table */ +typedef struct FX_Rec { /* channel effects */ + unsigned char flags[AWE_FX_END]; + short val[AWE_FX_END]; +} FX_Rec; + + +/* channel parameters */ +typedef struct _awe_chan_info { + int channel; /* channel number */ + int bank; /* current tone bank */ + int instr; /* current program */ + int bender; /* midi pitchbend (-8192 - 8192) */ + int bender_range; /* midi bender range (x100) */ + int panning; /* panning (0-127) */ + int main_vol; /* channel volume (0-127) */ + int expression_vol; /* midi expression (0-127) */ + int chan_press; /* channel pressure */ + int sustained; /* sustain status in MIDI */ + FX_Rec fx; /* effects */ + FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */ +} awe_chan_info; + +/* voice parameters */ +typedef struct _voice_info { + int state; +#define AWE_ST_OFF (1<<0) /* no sound */ +#define AWE_ST_ON (1<<1) /* playing */ +#define AWE_ST_STANDBY (1<<2) /* stand by for playing */ +#define AWE_ST_SUSTAINED (1<<3) /* sustained */ +#define AWE_ST_MARK (1<<4) /* marked for allocation */ +#define AWE_ST_DRAM (1<<5) /* DRAM read/write */ +#define AWE_ST_FM (1<<6) /* reserved for FM */ +#define AWE_ST_RELEASED (1<<7) /* released */ + + int ch; /* midi channel */ + int key; /* internal key for search */ + int layer; /* layer number (for channel mode only) */ + int time; /* allocated time */ + awe_chan_info *cinfo; /* channel info */ + + int note; /* midi key (0-127) */ + int velocity; /* midi velocity (0-127) */ + int sostenuto; /* sostenuto on/off */ + awe_voice_info *sample; /* assigned voice */ + + /* EMU8000 parameters */ + int apitch; /* pitch parameter */ + int avol; /* volume parameter */ + int apan; /* panning parameter */ + int acutoff; /* cutoff parameter */ + short aaux; /* aux word */ +} voice_info; + +/* voice information */ +static voice_info voices[AWE_MAX_VOICES]; + +#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED)) +#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON) +#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED)) +#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM)) + + +/* MIDI channel effects information (for hw control) */ +static awe_chan_info channels[AWE_MAX_CHANNELS]; + + +/* + * global variables + */ + +#ifndef AWE_DEFAULT_BASE_ADDR +#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */ +#endif + +#ifndef AWE_DEFAULT_MEM_SIZE +#define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */ +#endif + +int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */ +int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */ +#ifdef __ISAPNP__ +static int isapnp = -1; +#else +static int isapnp = 0; +#endif + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "base i/o port of Emu8000"); +MODULE_PARM(memsize, "i"); +MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes"); +MODULE_PARM(isapnp, "i"); +MODULE_PARM_DESC(isapnp, "use ISAPnP detection"); +EXPORT_NO_SYMBOLS; + +/* DRAM start offset */ +static int awe_mem_start = AWE_DRAM_OFFSET; + +/* maximum channels for playing */ +static int awe_max_voices = AWE_MAX_VOICES; + +static int patch_opened = 0; /* sample already loaded? */ + +static char atten_relative = FALSE; +static short atten_offset = 0; + +static int awe_present = FALSE; /* awe device present? */ +static int awe_busy = FALSE; /* awe device opened? */ + +static int my_dev = -1; + +#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25)) +#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c))) +#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c))) +#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c))) +static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */ + +static int playing_mode = AWE_PLAY_INDIRECT; +#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT) +#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2) + +static int current_alloc_time = 0; /* voice allocation index for channel mode */ + +static struct synth_info awe_info = { + "AWE32 Synth", /* name */ + 0, /* device */ + SYNTH_TYPE_SAMPLE, /* synth_type */ + SAMPLE_TYPE_AWE32, /* synth_subtype */ + 0, /* perc_mode (obsolete) */ + AWE_MAX_VOICES, /* nr_voices */ + 0, /* nr_drums (obsolete) */ + 400 /* instr_bank_size */ +}; + + +static struct voice_alloc_info *voice_alloc; /* set at initialization */ + + +/* + * function prototypes + */ + +static int awe_check_port(void); +static void awe_request_region(void); +static void awe_release_region(void); + +static void awe_reset_samples(void); +/* emu8000 chip i/o access */ +static void setup_ports(int p1, int p2, int p3); +static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data); +static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data); +static unsigned short awe_peek(unsigned short cmd, unsigned short port); +static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port); +static void awe_wait(unsigned short delay); + +/* initialize emu8000 chip */ +static int _attach_awe(void); +static void _unload_awe(void); +static void awe_initialize(void); + +/* set voice parameters */ +static void awe_init_ctrl_parms(int init_all); +static void awe_init_voice_info(awe_voice_info *vp); +static void awe_init_voice_parm(awe_voice_parm *pp); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int freq_to_note(int freq); +static int calc_rate_offset(int Hz); +/*static int calc_parm_delay(int msec);*/ +static int calc_parm_hold(int msec); +static int calc_parm_attack(int msec); +static int calc_parm_decay(int msec); +static int calc_parm_search(int msec, short *table); +#endif /* gus compat */ + +/* turn on/off note */ +static void awe_note_on(int voice); +static void awe_note_off(int voice); +static void awe_terminate(int voice); +static void awe_exclusive_off(int voice); +static void awe_note_off_all(int do_sustain); + +/* calculate voice parameters */ +typedef void (*fx_affect_func)(int voice, int forced); +static void awe_set_pitch(int voice, int forced); +static void awe_set_voice_pitch(int voice, int forced); +static void awe_set_volume(int voice, int forced); +static void awe_set_voice_vol(int voice, int forced); +static void awe_set_pan(int voice, int forced); +static void awe_fx_fmmod(int voice, int forced); +static void awe_fx_tremfrq(int voice, int forced); +static void awe_fx_fm2frq2(int voice, int forced); +static void awe_fx_filterQ(int voice, int forced); +static void awe_calc_pitch(int voice); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_calc_pitch_from_freq(int voice, int freq); +#endif +static void awe_calc_volume(int voice); +static void awe_update_volume(void); +static void awe_change_master_volume(short val); +static void awe_voice_init(int voice, int init_all); +static void awe_channel_init(int ch, int init_all); +static void awe_fx_init(int ch); +static void awe_send_effect(int voice, int layer, int type, int val); +static void awe_modwheel_change(int voice, int value); + +/* sequencer interface */ +static int awe_open(int dev, int mode); +static void awe_close(int dev); +static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg); +static int awe_kill_note(int dev, int voice, int note, int velocity); +static int awe_start_note(int dev, int v, int note_num, int volume); +static int awe_set_instr(int dev, int voice, int instr_no); +static int awe_set_instr_2(int dev, int voice, int instr_no); +static void awe_reset(int dev); +static void awe_hw_control(int dev, unsigned char *event); +static int awe_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag); +static void awe_aftertouch(int dev, int voice, int pressure); +static void awe_controller(int dev, int voice, int ctrl_num, int value); +static void awe_panning(int dev, int voice, int value); +static void awe_volume_method(int dev, int mode); +static void awe_bender(int dev, int voice, int value); +static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc); +static void awe_setup_voice(int dev, int voice, int chn); + +#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press) + +/* hardware controls */ +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_hw_gus_control(int dev, int cmd, unsigned char *event); +#endif +static void awe_hw_awe_control(int dev, int cmd, unsigned char *event); +static void awe_voice_change(int voice, fx_affect_func func); +static void awe_sostenuto_on(int voice, int forced); +static void awe_sustain_off(int voice, int forced); +static void awe_terminate_and_init(int voice, int forced); + +/* voice search */ +static int awe_search_key(int bank, int preset, int note); +static awe_voice_list *awe_search_instr(int bank, int preset, int note); +static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist); +static void awe_alloc_multi_voices(int ch, int note, int velocity, int key); +static void awe_alloc_one_voice(int voice, int note, int velocity); +static int awe_clear_voice(void); + +/* load / remove patches */ +static int awe_open_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_close_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_load_info(awe_patch_info *patch, const char *addr, int count); +static int awe_remove_info(awe_patch_info *patch, const char *addr, int count); +static int awe_load_data(awe_patch_info *patch, const char *addr, int count); +static int awe_replace_data(awe_patch_info *patch, const char *addr, int count); +static int awe_load_map(awe_patch_info *patch, const char *addr, int count); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag); +#endif +/*static int awe_probe_info(awe_patch_info *patch, const char *addr, int count);*/ +static int awe_probe_data(awe_patch_info *patch, const char *addr, int count); +static sf_list *check_patch_opened(int type, char *name); +static int awe_write_wave_data(const char *addr, int offset, awe_sample_list *sp, int channels); +static int awe_create_sf(int type, char *name); +static void awe_free_sf(sf_list *sf); +static void add_sf_info(sf_list *sf, awe_voice_list *rec); +static void add_sf_sample(sf_list *sf, awe_sample_list *smp); +static void purge_old_list(awe_voice_list *rec, awe_voice_list *next); +static void add_info_list(awe_voice_list *rec); +static void awe_remove_samples(int sf_id); +static void rebuild_preset_list(void); +static short awe_set_sample(awe_voice_list *rec); +static awe_sample_list *search_sample_index(sf_list *sf, int sample); + +static int is_identical_holder(sf_list *sf1, sf_list *sf2); +#ifdef AWE_ALLOW_SAMPLE_SHARING +static int is_identical_name(unsigned char *name, sf_list *p); +static int is_shared_sf(unsigned char *name); +static int info_duplicated(sf_list *sf, awe_voice_list *rec); +#endif /* allow sharing */ + +/* lowlevel functions */ +static void awe_init_audio(void); +static void awe_init_dma(void); +static void awe_init_array(void); +static void awe_send_array(unsigned short *data); +static void awe_tweak_voice(int voice); +static void awe_tweak(void); +static void awe_init_fm(void); +static int awe_open_dram_for_write(int offset, int channels); +static void awe_open_dram_for_check(void); +static void awe_close_dram(void); +/*static void awe_write_dram(unsigned short c);*/ +static int awe_detect_base(int addr); +static int awe_detect(void); +static void awe_check_dram(void); +static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count); +static void awe_set_chorus_mode(int mode); +static void awe_update_chorus_mode(void); +static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count); +static void awe_set_reverb_mode(int mode); +static void awe_update_reverb_mode(void); +static void awe_equalizer(int bass, int treble); +static void awe_update_equalizer(void); + +#ifdef CONFIG_AWE32_MIXER +static void attach_mixer(void); +static void unload_mixer(void); +#endif + +#ifdef CONFIG_AWE32_MIDIEMU +static void attach_midiemu(void); +static void unload_midiemu(void); +#endif + +#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b) + +/* + * control parameters + */ + + +#ifdef AWE_USE_NEW_VOLUME_CALC +#define DEF_VOLUME_CALC TRUE +#else +#define DEF_VOLUME_CALC FALSE +#endif /* new volume */ + +#define DEF_ZERO_ATTEN 32 /* 12dB below */ +#define DEF_MOD_SENSE 18 +#define DEF_CHORUS_MODE 2 +#define DEF_REVERB_MODE 4 +#define DEF_BASS_LEVEL 5 +#define DEF_TREBLE_LEVEL 9 + +static struct CtrlParmsDef { + int value; + int init_each_time; + void (*update)(void); +} ctrl_parms[AWE_MD_END] = { + {0,0, NULL}, {0,0, NULL}, /* <-- not used */ + {AWE_VERSION_NUMBER, FALSE, NULL}, + {TRUE, FALSE, NULL}, /* exclusive */ + {TRUE, FALSE, NULL}, /* realpan */ + {AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */ + {FALSE, TRUE, NULL}, /* keep effect */ + {DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */ + {FALSE, FALSE, NULL}, /* chn_prior */ + {DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */ + {AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */ + {AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */ + {AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */ + {FALSE, FALSE, NULL}, /* toggle_drum_bank */ + {DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */ + {DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */ + {DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */ + {DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */ + {DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */ + {0, FALSE, NULL}, /* debug mode */ + {FALSE, FALSE, NULL}, /* pan exchange */ +}; + +static int ctrls[AWE_MD_END]; + + +/* + * synth operation table + */ + +static struct synth_operations awe_operations = +{ + owner: THIS_MODULE, + id: "EMU8K", + info: &awe_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_SAMPLE, + synth_subtype: SAMPLE_TYPE_AWE32, + open: awe_open, + close: awe_close, + ioctl: awe_ioctl, + kill_note: awe_kill_note, + start_note: awe_start_note, + set_instr: awe_set_instr_2, + reset: awe_reset, + hw_control: awe_hw_control, + load_patch: awe_load_patch, + aftertouch: awe_aftertouch, + controller: awe_controller, + panning: awe_panning, + volume_method: awe_volume_method, + bender: awe_bender, + alloc_voice: awe_alloc, + setup_voice: awe_setup_voice +}; + + +/* + * General attach / unload interface + */ + +static int __init _attach_awe(void) +{ + if (awe_present) return 0; /* for OSS38.. called twice? */ + + /* check presence of AWE32 card */ + if (! awe_detect()) { + printk(KERN_ERR "AWE32: not detected\n"); + return 0; + } + + /* check AWE32 ports are available */ + if (awe_check_port()) { + printk(KERN_ERR "AWE32: I/O area already used.\n"); + return 0; + } + + /* set buffers to NULL */ + sfhead = sftail = NULL; + + my_dev = sound_alloc_synthdev(); + if (my_dev == -1) { + printk(KERN_ERR "AWE32 Error: too many synthesizers\n"); + return 0; + } + + voice_alloc = &awe_operations.alloc; + voice_alloc->max_voice = awe_max_voices; + synth_devs[my_dev] = &awe_operations; + +#ifdef CONFIG_AWE32_MIXER + attach_mixer(); +#endif +#ifdef CONFIG_AWE32_MIDIEMU + attach_midiemu(); +#endif + + /* reserve I/O ports for awedrv */ + awe_request_region(); + + /* clear all samples */ + awe_reset_samples(); + + /* intialize AWE32 hardware */ + awe_initialize(); + + sprintf(awe_info.name, "AWE32-%s (RAM%dk)", + AWEDRV_VERSION, memsize/1024); + printk(KERN_INFO "\n", memsize/1024); + + awe_present = TRUE; + + return 1; +} + + +static void free_tables(void) +{ + if (sftail) { + sf_list *p, *prev; + for (p = sftail; p; p = prev) { + prev = p->prev; + awe_free_sf(p); + } + } + sfhead = sftail = NULL; +} + + +static void __exit _unload_awe(void) +{ + if (awe_present) { + awe_reset_samples(); + awe_release_region(); + free_tables(); +#ifdef CONFIG_AWE32_MIXER + unload_mixer(); +#endif +#ifdef CONFIG_AWE32_MIDIEMU + unload_midiemu(); +#endif + sound_unload_synthdev(my_dev); + awe_present = FALSE; + } +} + + +/* + * clear sample tables + */ + +static void +awe_reset_samples(void) +{ + /* free all bank tables */ + memset(preset_table, 0, sizeof(preset_table)); + free_tables(); + + current_sf_id = 0; + locked_sf_id = 0; + patch_opened = 0; +} + + +/* + * EMU register access + */ + +/* select a given AWE32 pointer */ +static int awe_ports[5]; +static int port_setuped = FALSE; +static int awe_cur_cmd = -1; +#define awe_set_cmd(cmd) \ +if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; } + +/* store values to i/o port array */ +static void setup_ports(int port1, int port2, int port3) +{ + awe_ports[0] = port1; + if (port2 == 0) + port2 = port1 + 0x400; + awe_ports[1] = port2; + awe_ports[2] = port2 + 2; + if (port3 == 0) + port3 = port1 + 0x800; + awe_ports[3] = port3; + awe_ports[4] = port3 + 2; + + port_setuped = TRUE; +} + +/* write 16bit data */ +static void +awe_poke(unsigned short cmd, unsigned short port, unsigned short data) +{ + awe_set_cmd(cmd); + outw(data, awe_ports[port]); +} + +/* write 32bit data */ +static void +awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data) +{ + unsigned short addr = awe_ports[port]; + awe_set_cmd(cmd); + outw(data, addr); /* write lower 16 bits */ + outw(data >> 16, addr + 2); /* write higher 16 bits */ +} + +/* read 16bit data */ +static unsigned short +awe_peek(unsigned short cmd, unsigned short port) +{ + unsigned short k; + awe_set_cmd(cmd); + k = inw(awe_ports[port]); + return k; +} + +/* read 32bit data */ +static unsigned int +awe_peek_dw(unsigned short cmd, unsigned short port) +{ + unsigned int k1, k2; + unsigned short addr = awe_ports[port]; + awe_set_cmd(cmd); + k1 = inw(addr); + k2 = inw(addr + 2); + k1 |= k2 << 16; + return k1; +} + +/* wait delay number of AWE32 44100Hz clocks */ +#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */ +static void +awe_wait(unsigned short delay) +{ + unsigned short clock, target; + unsigned short port = awe_ports[AWE_WC_Port]; + int counter; + + /* sample counter */ + awe_set_cmd(AWE_WC_Cmd); + clock = (unsigned short)inw(port); + target = clock + delay; + counter = 0; + if (target < clock) { + for (; (unsigned short)inw(port) > target; counter++) + if (counter > 65536) + break; + } + for (; (unsigned short)inw(port) < target; counter++) + if (counter > 65536) + break; +} +#else + +static void awe_wait(unsigned short delay) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((HZ*(unsigned long)delay + 44099)/44100); +} +/* +static void awe_wait(unsigned short delay) +{ + udelay(((unsigned long)delay * 1000000L + 44099) / 44100); +} +*/ +#endif /* wait by loop */ + +/* write a word data */ +#define awe_write_dram(c) awe_poke(AWE_SMLD, c) + + +/* + * port check / request + * 0x620-623, 0xA20-A23, 0xE20-E23 + */ + +static int __init +awe_check_port(void) +{ + if (! port_setuped) return 0; + return (check_region(awe_ports[0], 4) || + check_region(awe_ports[1], 4) || + check_region(awe_ports[3], 4)); +} + +static void __init +awe_request_region(void) +{ + if (! port_setuped) return; + request_region(awe_ports[0], 4, "sound driver (AWE32)"); + request_region(awe_ports[1], 4, "sound driver (AWE32)"); + request_region(awe_ports[3], 4, "sound driver (AWE32)"); +} + +static void __exit +awe_release_region(void) +{ + if (! port_setuped) return; + release_region(awe_ports[0], 4); + release_region(awe_ports[1], 4); + release_region(awe_ports[3], 4); +} + + +/* + * initialization of AWE driver + */ + +static void +awe_initialize(void) +{ + DEBUG(0,printk("AWE32: initializing..\n")); + + /* initialize hardware configuration */ + awe_poke(AWE_HWCF1, 0x0059); + awe_poke(AWE_HWCF2, 0x0020); + + /* disable audio; this seems to reduce a clicking noise a bit.. */ + awe_poke(AWE_HWCF3, 0); + + /* initialize audio channels */ + awe_init_audio(); + + /* initialize DMA */ + awe_init_dma(); + + /* initialize init array */ + awe_init_array(); + + /* check DRAM memory size */ + awe_check_dram(); + + /* initialize the FM section of the AWE32 */ + awe_init_fm(); + + /* set up voice envelopes */ + awe_tweak(); + + /* enable audio */ + awe_poke(AWE_HWCF3, 0x0004); + + /* set default values */ + awe_init_ctrl_parms(TRUE); + + /* set equalizer */ + awe_update_equalizer(); + + /* set reverb & chorus modes */ + awe_update_reverb_mode(); + awe_update_chorus_mode(); +} + + +/* + * AWE32 voice parameters + */ + +/* initialize voice_info record */ +static void +awe_init_voice_info(awe_voice_info *vp) +{ + vp->sample = 0; + vp->rate_offset = 0; + + vp->start = 0; + vp->end = 0; + vp->loopstart = 0; + vp->loopend = 0; + vp->mode = 0; + vp->root = 60; + vp->tune = 0; + vp->low = 0; + vp->high = 127; + vp->vellow = 0; + vp->velhigh = 127; + + vp->fixkey = -1; + vp->fixvel = -1; + vp->fixpan = -1; + vp->pan = -1; + + vp->exclusiveClass = 0; + vp->amplitude = 127; + vp->attenuation = 0; + vp->scaleTuning = 100; + + awe_init_voice_parm(&vp->parm); +} + +/* initialize voice_parm record: + * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. + * Vibrato and Tremolo effects are zero. + * Cutoff is maximum. + * Chorus and Reverb effects are zero. + */ +static void +awe_init_voice_parm(awe_voice_parm *pp) +{ + pp->moddelay = 0x8000; + pp->modatkhld = 0x7f7f; + pp->moddcysus = 0x7f7f; + pp->modrelease = 0x807f; + pp->modkeyhold = 0; + pp->modkeydecay = 0; + + pp->voldelay = 0x8000; + pp->volatkhld = 0x7f7f; + pp->voldcysus = 0x7f7f; + pp->volrelease = 0x807f; + pp->volkeyhold = 0; + pp->volkeydecay = 0; + + pp->lfo1delay = 0x8000; + pp->lfo2delay = 0x8000; + pp->pefe = 0; + + pp->fmmod = 0; + pp->tremfrq = 0; + pp->fm2frq2 = 0; + + pp->cutoff = 0xff; + pp->filterQ = 0; + + pp->chorus = 0; + pp->reverb = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* convert frequency mHz to abstract cents (= midi key * 100) */ +static int +freq_to_note(int mHz) +{ + /* abscents = log(mHz/8176) / log(2) * 1200 */ + unsigned int max_val = (unsigned int)0xffffffff / 10000; + int i, times; + unsigned int base; + unsigned int freq; + int note, tune; + + if (mHz == 0) + return 0; + if (mHz < 0) + return 12799; /* maximum */ + + freq = mHz; + note = 0; + for (base = 8176 * 2; freq >= base; base *= 2) { + note += 12; + if (note >= 128) /* over maximum */ + return 12799; + } + base /= 2; + + /* to avoid overflow... */ + times = 10000; + while (freq > max_val) { + max_val *= 10; + times /= 10; + base /= 10; + } + + freq = freq * times / base; + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + note++; + } + + tune = 0; + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + tune++; + } + + return note * 100 + tune; +} + + +/* convert Hz to AWE32 rate offset: + * sample pitch offset for the specified sample rate + * rate=44100 is no offset, each 4096 is 1 octave (twice). + * eg, when rate is 22050, this offset becomes -4096. + */ +static int +calc_rate_offset(int Hz) +{ + /* offset = log(Hz / 44100) / log(2) * 4096 */ + int freq, base, i; + + /* maybe smaller than max (44100Hz) */ + if (Hz <= 0 || Hz >= 44100) return 0; + + base = 0; + for (freq = Hz * 2; freq < 44100; freq *= 2) + base++; + base *= 1200; + + freq = 44100 * 10000 / (freq/2); + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + base += 100; + } + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + base++; + } + return -base * 4096 / 1200; +} + + +/* + * convert envelope time parameter to AWE32 raw parameter + */ + +/* attack & decay/release time table (msec) */ +static short attack_time_tbl[128] = { +32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, +707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, +361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, +180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, +90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, +45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, +22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, +11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, +}; + +static short decay_time_tbl[128] = { +32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, +2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, +1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, +691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, +345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, +172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, +86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, +43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, +}; + +#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725); + +/* delay time = 0x8000 - msec/92 */ +static int +calc_parm_hold(int msec) +{ + int val = (0x7f * 92 - msec) / 92; + if (val < 1) val = 1; + if (val > 127) val = 127; + return val; +} + +/* attack time: search from time table */ +static int +calc_parm_attack(int msec) +{ + return calc_parm_search(msec, attack_time_tbl); +} + +/* decay/release time: search from time table */ +static int +calc_parm_decay(int msec) +{ + return calc_parm_search(msec, decay_time_tbl); +} + +/* search an index for specified time from given time table */ +static int +calc_parm_search(int msec, short *table) +{ + int left = 1, right = 127, mid; + while (left < right) { + mid = (left + right) / 2; + if (msec < (int)table[mid]) + left = mid + 1; + else + right = mid; + } + return left; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/* + * effects table + */ + +/* set an effect value */ +#define FX_FLAG_OFF 0 +#define FX_FLAG_SET 1 +#define FX_FLAG_ADD 2 + +#define FX_SET(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value)) +#define FX_ADD(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value)) +#define FX_UNSET(rec,type) \ + ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0) + +/* check the effect value is set */ +#define FX_ON(rec,type) ((rec)->flags[type]) + +#define PARM_BYTE 0 +#define PARM_WORD 1 +#define PARM_SIGN 2 + +static struct PARM_DEFS { + int type; /* byte or word */ + int low, high; /* value range */ + fx_affect_func realtime; /* realtime paramater change */ +} parm_defs[] = { + {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */ + + {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */ + {PARM_SIGN, -128, 127, awe_fx_tremfrq}, /* lfo1 volume */ + {PARM_SIGN, -128, 127, awe_fx_fmmod}, /* lfo1 pitch */ + {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff */ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */ + {PARM_SIGN, -128, 127, awe_fx_fm2frq2}, /* lfo2 pitch */ + + {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* chorus */ + {PARM_BYTE, 0, 0xff, NULL}, /* reverb */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */ + {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */ + + {PARM_WORD, 0, 0xffff, NULL}, /* sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop end */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */ +}; + + +static unsigned char +FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) { + if (parm_defs[type].type == PARM_SIGN) { + if (value > 0x7f) + effect += (int)value - 0x100; + else + effect += (int)value; + } else { + effect += (int)value; + } + } + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned char)effect; + } + return value; +} + +/* get word effect value */ +static unsigned short +FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) + effect += (int)value; + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned short)effect; + } + return value; +} + +/* get word (upper=type1/lower=type2) effect value */ +static unsigned short +FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value) +{ + unsigned short tmp; + tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8)); + tmp <<= 8; + tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff)); + return tmp; +} + +/* address offset */ +static int +FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode) +{ + int addr = 0; + if (lay && FX_ON(lay, hi)) + addr = (short)lay->val[hi]; + else if (FX_ON(rec, hi)) + addr = (short)rec->val[hi]; + addr = addr << 15; + if (lay && FX_ON(lay, lo)) + addr += (short)lay->val[lo]; + else if (FX_ON(rec, lo)) + addr += (short)rec->val[lo]; + if (!(mode & AWE_SAMPLE_8BITS)) + addr /= 2; + return addr; +} + + +/* + * turn on/off sample + */ + +/* table for volume target calculation */ +static unsigned short voltarget[16] = { + 0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58, + 0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90 +}; + +static void +awe_note_on(int voice) +{ + unsigned int temp; + int addr; + int vtarget, ftarget, ptarget, pitch; + awe_voice_info *vp; + awe_voice_parm_block *parm; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* A voice sample must assigned before calling */ + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + parm = (awe_voice_parm_block*)&vp->parm; + + /* channel to be silent and idle */ + awe_poke(AWE_DCYSUSV(voice), 0x0080); + awe_poke(AWE_VTFT(voice), 0x0000FFFF); + awe_poke(AWE_CVCF(voice), 0x0000FFFF); + awe_poke(AWE_PTRX(voice), 0); + awe_poke(AWE_CPF(voice), 0); + + /* set pitch offset */ + awe_set_pitch(voice, TRUE); + + /* modulation & volume envelope */ + if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) { + awe_poke(AWE_ENVVAL(voice), 0xBFFF); + pitch = (parm->env1pit<<4) + voices[voice].apitch; + if (pitch > 0xffff) pitch = 0xffff; + /* calculate filter target */ + ftarget = parm->cutoff + parm->env1fc; + limitvalue(ftarget, 0, 255); + ftarget <<= 8; + } else { + awe_poke(AWE_ENVVAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay)); + ftarget = parm->cutoff; + ftarget <<= 8; + pitch = voices[voice].apitch; + } + + /* calcualte pitch target */ + if (pitch != 0xffff) { + ptarget = 1 << (pitch >> 12); + if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710; + if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710; + if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710; + ptarget += (ptarget>>1); + if (ptarget > 0xffff) ptarget = 0xffff; + + } else ptarget = 0xffff; + if (parm->modatk >= 0x80) + awe_poke(AWE_ATKHLD(voice), + FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f); + else + awe_poke(AWE_ATKHLD(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK, + vp->parm.modatkhld)); + awe_poke(AWE_DCYSUS(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY, + vp->parm.moddcysus)); + + if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) { + awe_poke(AWE_ENVVOL(voice), 0xBFFF); + vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4); + } else { + awe_poke(AWE_ENVVOL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay)); + vtarget = 0; + } + if (parm->volatk >= 0x80) + awe_poke(AWE_ATKHLDV(voice), + FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f); + else + awe_poke(AWE_ATKHLDV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK, + vp->parm.volatkhld)); + /* decay/sustain parameter for volume envelope must be set at last */ + + /* cutoff and volume */ + awe_set_volume(voice, TRUE); + + /* modulation envelope heights */ + awe_poke(AWE_PEFE(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF, + vp->parm.pefe)); + + /* lfo1/2 delay */ + awe_poke(AWE_LFO1VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay)); + awe_poke(AWE_LFO2VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay)); + + /* lfo1 pitch & cutoff shift */ + awe_fx_fmmod(voice, TRUE); + /* lfo1 volume & freq */ + awe_fx_tremfrq(voice, TRUE); + /* lfo2 pitch & freq */ + awe_fx_fm2frq2(voice, TRUE); + /* pan & loop start */ + awe_set_pan(voice, TRUE); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->loopend - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END, + AWE_FX_COARSE_LOOP_END, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus); + temp = (temp <<24) | (unsigned int)addr; + awe_poke_dw(AWE_CSL(voice), temp); + DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr)); + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->start - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START, + AWE_FX_COARSE_SAMPLE_START, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ); + temp = (temp<<28) | (unsigned int)addr; + awe_poke_dw(AWE_CCCA(voice), temp); + DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr)); + + /* clear unknown registers */ + awe_poke_dw(AWE_00A0(voice), 0); + awe_poke_dw(AWE_0080(voice), 0); + + /* reset volume */ + awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget); + awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget); + + /* set reverb */ + temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb); + temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux; + awe_poke_dw(AWE_PTRX(voice), temp); + awe_poke_dw(AWE_CPF(voice), ptarget << 16); + /* turn on envelope */ + awe_poke(AWE_DCYSUSV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, + vp->parm.voldcysus)); + + voices[voice].state = AWE_ST_ON; + + /* clear voice position for the next note on this channel */ + if (SINGLE_LAYER_MODE()) { + FX_UNSET(fx, AWE_FX_SAMPLE_START); + FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START); + } +} + + +/* turn off the voice */ +static void +awe_note_off(int voice) +{ + awe_voice_info *vp; + unsigned short tmp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if ((vp = voices[voice].sample) == NULL) { + voices[voice].state = AWE_ST_OFF; + return; + } + + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE, + (unsigned char)vp->parm.modrelease); + awe_poke(AWE_DCYSUS(voice), tmp); + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE, + (unsigned char)vp->parm.volrelease); + awe_poke(AWE_DCYSUSV(voice), tmp); + voices[voice].state = AWE_ST_RELEASED; +} + +/* force to terminate the voice (no releasing echo) */ +static void +awe_terminate(int voice) +{ + awe_poke(AWE_DCYSUSV(voice), 0x807F); + awe_tweak_voice(voice); + voices[voice].state = AWE_ST_OFF; +} + +/* turn off other voices with the same exclusive class (for drums) */ +static void +awe_exclusive_off(int voice) +{ + int i, exclass; + + if (voices[voice].sample == NULL) + return; + if ((exclass = voices[voice].sample->exclusiveClass) == 0) + return; /* not exclusive */ + + /* turn off voices with the same class */ + for (i = 0; i < awe_max_voices; i++) { + if (i != voice && IS_PLAYING(i) && + voices[i].sample && voices[i].ch == voices[voice].ch && + voices[i].sample->exclusiveClass == exclass) { + DEBUG(4,printk("AWE32: [exoff(%d)]\n", i)); + awe_terminate(i); + awe_voice_init(i, TRUE); + } + } +} + + +/* + * change the parameters of an audible voice + */ + +/* change pitch */ +static void +awe_set_pitch(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + awe_poke(AWE_IP(voice), voices[voice].apitch); + DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch)); +} + +/* calculate & change pitch */ +static void +awe_set_voice_pitch(int voice, int forced) +{ + awe_calc_pitch(voice); + awe_set_pitch(voice, forced); +} + +/* change volume & cutoff */ +static void +awe_set_volume(int voice, int forced) +{ + awe_voice_info *vp; + unsigned short tmp2; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (!IS_PLAYING(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, + (unsigned char)voices[voice].acutoff); + tmp2 = (tmp2 << 8); + tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN, + (unsigned char)voices[voice].avol); + awe_poke(AWE_IFATN(voice), tmp2); +} + +/* calculate & change volume */ +static void +awe_set_voice_vol(int voice, int forced) +{ + if (IS_EMPTY(voice)) + return; + awe_calc_volume(voice); + awe_set_volume(voice, forced); +} + + +/* change pan; this could make a click noise.. */ +static void +awe_set_pan(int voice, int forced) +{ + unsigned int temp; + int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ + if (vp->fixpan > 0) /* 0-127 */ + temp = 255 - (int)vp->fixpan * 2; + else { + int pos = 0; + if (vp->pan >= 0) /* 0-127 */ + pos = (int)vp->pan * 2 - 128; + pos += voices[voice].cinfo->panning; /* -128 - 127 */ + temp = 127 - pos; + } + limitvalue(temp, 0, 255); + if (ctrls[AWE_MD_PAN_EXCHANGE]) { + temp = 255 - temp; + } + if (forced || temp != voices[voice].apan) { + voices[voice].apan = temp; + if (temp == 0) + voices[voice].aaux = 0xff; + else + voices[voice].aaux = (-temp) & 0xff; + addr = vp->loopstart - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START, + AWE_FX_COARSE_LOOP_START, vp->mode); + temp = (temp<<24) | (unsigned int)addr; + awe_poke_dw(AWE_PSST(voice), temp); + DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr)); + } +} + +/* effects change during playing */ +static void +awe_fx_fmmod(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_FMMOD(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF, + vp->parm.fmmod)); +} + +/* set tremolo (lfo1) volume & frequency */ +static void +awe_fx_tremfrq(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_TREMFRQ(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ, + vp->parm.tremfrq)); +} + +/* set lfo2 pitch & frequency */ +static void +awe_fx_fm2frq2(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_FM2FRQ2(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ, + vp->parm.fm2frq2)); +} + + +/* Q & current address (Q 4bit value, MSB) */ +static void +awe_fx_filterQ(int voice, int forced) +{ + unsigned int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff; + addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28); + awe_poke_dw(AWE_CCCA(voice), addr); +} + +/* + * calculate pitch offset + * + * 0xE000 is no pitch offset at 44100Hz sample. + * Every 4096 is one octave. + */ + +static void +awe_calc_pitch(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int offset; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + + /* calculate offset */ + if (ap->fixkey >= 0) { + DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune)); + offset = (ap->fixkey - ap->root) * 4096 / 12; + } else { + DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune)); + offset = (vp->note - ap->root) * 4096 / 12; + DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset)); + } + offset = (offset * ap->scaleTuning) / 100; + DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset)); + offset += ap->tune * 4096 / 1200; + DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset)); + if (cp->bender != 0) { + DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender)); + /* (819200: 1 semitone) ==> (4096: 12 semitones) */ + offset += cp->bender * cp->bender_range / 2400; + } + + /* add initial pitch correction */ + if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH)) + offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH]; + else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) + offset += cp->fx.val[AWE_FX_INIT_PITCH]; + + /* 0xe000: root pitch */ + vp->apitch = 0xe000 + ap->rate_offset + offset; + DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset)); + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY +/* calculate MIDI key and semitone from the specified frequency */ +static void +awe_calc_pitch_from_freq(int voice, int freq) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + int offset; + int note; + + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + note = freq_to_note(freq); + offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200; + offset = (offset * ap->scaleTuning) / 100; + if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH)) + offset += fx_lay->val[AWE_FX_INIT_PITCH]; + else if (FX_ON(fx, AWE_FX_INIT_PITCH)) + offset += fx->val[AWE_FX_INIT_PITCH]; + vp->apitch = 0xe000 + ap->rate_offset + offset; + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/* + * calculate volume attenuation + * + * Voice volume is controlled by volume attenuation parameter. + * So volume becomes maximum when avol is 0 (no attenuation), and + * minimum when 255 (-96dB or silence). + */ + +static int vol_table[128] = { + 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, + 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, + 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, + 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, + 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, + 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, + 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, + 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, +}; + +/* tables for volume->attenuation calculation */ +static unsigned char voltab1[128] = { + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, + 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, + 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char voltab2[128] = { + 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, + 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, + 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char expressiontab[128] = { + 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, + 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, + 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, + 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, + 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, + 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static void +awe_calc_volume(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int vol; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + + ap = vp->sample; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + + if (ctrls[AWE_MD_NEW_VOLUME_CALC]) { + int main_vol = cp->main_vol * ap->amplitude / 127; + limitvalue(vp->velocity, 0, 127); + limitvalue(main_vol, 0, 127); + limitvalue(cp->expression_vol, 0, 127); + + vol = voltab1[main_vol] + voltab2[vp->velocity]; + vol = (vol * 8) / 3; + vol += ap->attenuation; + if (cp->expression_vol < 127) + vol += ((0x100 - vol) * expressiontab[cp->expression_vol])/128; + vol += atten_offset; + if (atten_relative) + vol += ctrls[AWE_MD_ZERO_ATTEN]; + limitvalue(vol, 0, 255); + vp->avol = vol; + + } else { + /* 0 - 127 */ + vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127); + vol = vol * ap->amplitude / 127; + + if (vol < 0) vol = 0; + if (vol > 127) vol = 127; + + /* calc to attenuation */ + vol = vol_table[vol]; + vol += (int)ap->attenuation; + vol += atten_offset; + if (atten_relative) + vol += ctrls[AWE_MD_ZERO_ATTEN]; + if (vol > 255) vol = 255; + + vp->avol = vol; + } + if (cp->bank != AWE_DRUM_BANK && ((awe_voice_parm_block*)(&ap->parm))->volatk < 0x7d) { + int atten; + if (vp->velocity < 70) atten = 70; + else atten = vp->velocity; + vp->acutoff = (atten * ap->parm.cutoff + 0xa0) >> 7; + } else { + vp->acutoff = ap->parm.cutoff; + } + DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol)); +} + +/* change master volume */ +static void +awe_change_master_volume(short val) +{ + limitvalue(val, 0, 127); + atten_offset = vol_table[val]; + atten_relative = TRUE; + awe_update_volume(); +} + +/* update volumes of all available channels */ +static void awe_update_volume(void) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + awe_set_voice_vol(i, TRUE); +} + +/* set sostenuto on */ +static void awe_sostenuto_on(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + voices[voice].sostenuto = 127; +} + + +/* drop sustain */ +static void awe_sustain_off(int voice, int forced) +{ + if (voices[voice].state == AWE_ST_SUSTAINED) { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + + +/* terminate and initialize voice */ +static void awe_terminate_and_init(int voice, int forced) +{ + awe_terminate(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, TRUE); +} + + +/* + * synth operation routines + */ + +#define AWE_VOICE_KEY(v) (0x8000 | (v)) +#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1)) +#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c)) + +/* initialize the voice */ +static void +awe_voice_init(int voice, int init_all) +{ + voice_info *vp = &voices[voice]; + + /* reset voice search key */ + if (playing_mode == AWE_PLAY_DIRECT) + vp->key = AWE_VOICE_KEY(voice); + else + vp->key = 0; + + /* clear voice mapping */ + voice_alloc->map[voice] = 0; + + /* touch the timing flag */ + vp->time = current_alloc_time; + + /* initialize other parameters if necessary */ + if (init_all) { + vp->note = -1; + vp->velocity = 0; + vp->sostenuto = 0; + + vp->sample = NULL; + vp->cinfo = &channels[voice]; + vp->ch = voice; + vp->state = AWE_ST_OFF; + + /* emu8000 parameters */ + vp->apitch = 0; + vp->avol = 255; + vp->apan = -1; + } +} + +/* clear effects */ +static void awe_fx_init(int ch) +{ + if (SINGLE_LAYER_MODE() && !ctrls[AWE_MD_KEEP_EFFECT]) { + memset(&channels[ch].fx, 0, sizeof(channels[ch].fx)); + memset(&channels[ch].fx_layer, 0, sizeof(&channels[ch].fx_layer)); + } +} + +/* initialize channel info */ +static void awe_channel_init(int ch, int init_all) +{ + awe_chan_info *cp = &channels[ch]; + cp->channel = ch; + if (init_all) { + cp->panning = 0; /* zero center */ + cp->bender_range = 200; /* sense * 100 */ + cp->main_vol = 127; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) { + cp->instr = ctrls[AWE_MD_DEF_DRUM]; + cp->bank = AWE_DRUM_BANK; + } else { + cp->instr = ctrls[AWE_MD_DEF_PRESET]; + cp->bank = ctrls[AWE_MD_DEF_BANK]; + } + } + + cp->bender = 0; /* zero tune skew */ + cp->expression_vol = 127; + cp->chan_press = 0; + cp->sustained = 0; + + if (! ctrls[AWE_MD_KEEP_EFFECT]) { + memset(&cp->fx, 0, sizeof(cp->fx)); + memset(&cp->fx_layer, 0, sizeof(cp->fx_layer)); + } +} + + +/* change the voice parameters; voice = channel */ +static void awe_voice_change(int voice, fx_affect_func func) +{ + int i; + switch (playing_mode) { + case AWE_PLAY_DIRECT: + func(voice, FALSE); + break; + case AWE_PLAY_INDIRECT: + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == AWE_VOICE_KEY(voice)) + func(i, FALSE); + break; + default: + for (i = 0; i < awe_max_voices; i++) + if (KEY_CHAN_MATCH(voices[i].key, voice)) + func(i, FALSE); + break; + } +} + + +/* + * device open / close + */ + +/* open device: + * reset status of all voices, and clear sample position flag + */ +static int +awe_open(int dev, int mode) +{ + if (awe_busy) + return -EBUSY; + + awe_busy = TRUE; + + /* set default mode */ + awe_init_ctrl_parms(FALSE); + atten_relative = TRUE; + atten_offset = 0; + drum_flags = DEFAULT_DRUM_FLAGS; + playing_mode = AWE_PLAY_INDIRECT; + + /* reset voices & channels */ + awe_reset(dev); + + patch_opened = 0; + + return 0; +} + + +/* close device: + * reset all voices again (terminate sounds) + */ +static void +awe_close(int dev) +{ + awe_reset(dev); + awe_busy = FALSE; +} + + +/* set miscellaneous mode parameters + */ +static void +awe_init_ctrl_parms(int init_all) +{ + int i; + for (i = 0; i < AWE_MD_END; i++) { + if (init_all || ctrl_parms[i].init_each_time) + ctrls[i] = ctrl_parms[i].value; + } +} + + +/* sequencer I/O control: + */ +static int +awe_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + switch (cmd) { + case SNDCTL_SYNTH_INFO: + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + memcpy((char*)arg, &awe_info, sizeof(awe_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + awe_reset(dev); + awe_reset_samples(); + return 0; + break; + + case SNDCTL_SEQ_PERCMODE: + /* what's this? */ + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return memsize - awe_free_mem_ptr() * 2; + + default: + printk(KERN_WARNING "AWE32: unsupported ioctl %d\n", cmd); + return -EINVAL; + } +} + + +static int voice_in_range(int voice) +{ + if (playing_mode == AWE_PLAY_DIRECT) { + if (voice < 0 || voice >= awe_max_voices) + return FALSE; + } else { + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return FALSE; + } + return TRUE; +} + +static void release_voice(int voice, int do_sustain) +{ + if (IS_NO_SOUND(voice)) + return; + if (do_sustain && (voices[voice].cinfo->sustained == 127 || + voices[voice].sostenuto == 127)) + voices[voice].state = AWE_ST_SUSTAINED; + else { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + +/* release all notes */ +static void awe_note_off_all(int do_sustain) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + release_voice(i, do_sustain); +} + +/* kill a voice: + * not terminate, just release the voice. + */ +static int +awe_kill_note(int dev, int voice, int note, int velocity) +{ + int i, v2, key; + + DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return -EINVAL; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + break; + + case AWE_PLAY_MULTI2: + v2 = voice_alloc->map[voice] >> 8; + voice_alloc->map[voice] = 0; + voice = v2; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + /* continue to below */ + default: + key = AWE_CHAN_KEY(voice, note); + break; + } + + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + release_voice(i, TRUE); + } + return 0; +} + + +static void start_or_volume_change(int voice, int velocity) +{ + voices[voice].velocity = velocity; + awe_calc_volume(voice); + if (voices[voice].state == AWE_ST_STANDBY) + awe_note_on(voice); + else if (voices[voice].state == AWE_ST_ON) + awe_set_volume(voice, FALSE); +} + +static void set_and_start_voice(int voice, int state) +{ + /* calculate pitch & volume parameters */ + voices[voice].state = state; + awe_calc_pitch(voice); + awe_calc_volume(voice); + if (state == AWE_ST_ON) + awe_note_on(voice); +} + +/* start a voice: + * if note is 255, identical with aftertouch function. + * Otherwise, start a voice with specified not and volume. + */ +static int +awe_start_note(int dev, int voice, int note, int velocity) +{ + int i, key, state, volonly; + + DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return -EINVAL; + + if (velocity == 0) + state = AWE_ST_STANDBY; /* stand by for playing */ + else + state = AWE_ST_ON; /* really play */ + volonly = FALSE; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + if (note == 255) + volonly = TRUE; + break; + + case AWE_PLAY_MULTI2: + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + /* continue to below */ + default: + if (note >= 128) { /* key volume mode */ + note -= 128; + volonly = TRUE; + } + key = AWE_CHAN_KEY(voice, note); + break; + } + + /* dynamic volume change */ + if (volonly) { + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + start_or_volume_change(i, velocity); + } + return 0; + } + + /* if the same note still playing, stop it */ + if (playing_mode != AWE_PLAY_DIRECT || ctrls[AWE_MD_EXCLUSIVE_SOUND]) { + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) { + if (voices[i].state == AWE_ST_ON) { + awe_note_off(i); + awe_voice_init(i, FALSE); + } else if (voices[i].state == AWE_ST_STANDBY) + awe_voice_init(i, TRUE); + } + } + + /* allocate voices */ + if (playing_mode == AWE_PLAY_DIRECT) + awe_alloc_one_voice(voice, note, velocity); + else + awe_alloc_multi_voices(voice, note, velocity, key); + + /* turn off other voices exlusively (for drums) */ + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) + awe_exclusive_off(i); + + /* set up pitch and volume parameters */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key && voices[i].state == AWE_ST_OFF) + set_and_start_voice(i, state); + } + + return 0; +} + + +/* calculate hash key */ +static int +awe_search_key(int bank, int preset, int note) +{ + unsigned int key; + +#if 1 /* new hash table */ + if (bank == AWE_DRUM_BANK) + key = preset + note + 128; + else + key = bank + preset; +#else + key = preset; +#endif + key %= AWE_MAX_PRESETS; + + return (int)key; +} + + +/* search instrument from hash table */ +static awe_voice_list * +awe_search_instr(int bank, int preset, int note) +{ + awe_voice_list *p; + int key, key2; + + key = awe_search_key(bank, preset, note); + for (p = preset_table[key]; p; p = p->next_bank) { + if (p->instr == preset && p->bank == bank) + return p; + } + key2 = awe_search_key(bank, preset, 0); /* search default */ + if (key == key2) + return NULL; + for (p = preset_table[key2]; p; p = p->next_bank) { + if (p->instr == preset && p->bank == bank) + return p; + } + return NULL; +} + + +/* assign the instrument to a voice */ +static int +awe_set_instr_2(int dev, int voice, int instr_no) +{ + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + } + return awe_set_instr(dev, voice, instr_no); +} + +/* assign the instrument to a channel; voice is the channel number */ +static int +awe_set_instr(int dev, int voice, int instr_no) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return -EINVAL; + + if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS) + return -EINVAL; + + cinfo = &channels[voice]; + cinfo->instr = instr_no; + DEBUG(2,printk("AWE32: [program(%d) %d]\n", voice, instr_no)); + + return 0; +} + + +/* reset all voices; terminate sounds and initialize parameters */ +static void +awe_reset(int dev) +{ + int i; + current_alloc_time = 0; + /* don't turn off voice 31 and 32. they are used also for FM voices */ + for (i = 0; i < awe_max_voices; i++) { + awe_terminate(i); + awe_voice_init(i, TRUE); + } + for (i = 0; i < AWE_MAX_CHANNELS; i++) + awe_channel_init(i, TRUE); + for (i = 0; i < 16; i++) { + awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127; + awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127; + } + awe_init_fm(); + awe_tweak(); +} + + +/* hardware specific control: + * GUS specific and AWE32 specific controls are available. + */ +static void +awe_hw_control(int dev, unsigned char *event) +{ + int cmd = event[2]; + if (cmd & _AWE_MODE_FLAG) + awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#ifdef AWE_HAS_GUS_COMPATIBILITY + else + awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#endif +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* GUS compatible controls */ +static void +awe_hw_gus_control(int dev, int cmd, unsigned char *event) +{ + int voice, i, key; + unsigned short p1; + short p2; + int plong; + + if (MULTI_LAYER_MODE()) + return; + if (cmd == _GUS_NUMVOICES) + return; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + plong = *(int*) &event[4]; + + switch (cmd) { + case _GUS_VOICESAMPLE: + awe_set_instr(dev, voice, p1); + return; + + case _GUS_VOICEBALA: + /* 0 to 15 --> -128 to 127 */ + awe_panning(dev, voice, ((int)p1 << 4) - 128); + return; + + case _GUS_VOICEVOL: + case _GUS_VOICEVOL2: + /* not supported yet */ + return; + + case _GUS_RAMPRANGE: + case _GUS_RAMPRATE: + case _GUS_RAMPMODE: + case _GUS_RAMPON: + case _GUS_RAMPOFF: + /* volume ramping not supported */ + return; + + case _GUS_VOLUME_SCALE: + return; + + case _GUS_VOICE_POS: + FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START, + (short)(plong & 0x7fff)); + FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff); + return; + } + + key = AWE_VOICE_KEY(voice); + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) { + switch (cmd) { + case _GUS_VOICEON: + awe_note_on(i); + break; + + case _GUS_VOICEOFF: + awe_terminate(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, TRUE); + break; + + case _GUS_VOICEFADE: + awe_note_off(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, FALSE); + break; + + case _GUS_VOICEFREQ: + awe_calc_pitch_from_freq(i, plong); + break; + } + } + } +} + +#endif /* gus_compat */ + + +/* AWE32 specific controls */ +static void +awe_hw_awe_control(int dev, int cmd, unsigned char *event) +{ + int voice; + unsigned short p1; + short p2; + int i; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + + switch (cmd) { + case _AWE_DEBUG_MODE: + ctrls[AWE_MD_DEBUG_MODE] = p1; + printk(KERN_DEBUG "AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]); + break; + case _AWE_REVERB_MODE: + ctrls[AWE_MD_REVERB_MODE] = p1; + awe_update_reverb_mode(); + break; + + case _AWE_CHORUS_MODE: + ctrls[AWE_MD_CHORUS_MODE] = p1; + awe_update_chorus_mode(); + break; + + case _AWE_REMOVE_LAST_SAMPLES: + DEBUG(0,printk("AWE32: remove last samples\n")); + awe_reset(0); + if (locked_sf_id > 0) + awe_remove_samples(locked_sf_id); + break; + + case _AWE_INITIALIZE_CHIP: + awe_initialize(); + break; + + case _AWE_SEND_EFFECT: + i = -1; + if (p1 >= 0x100) { + i = (p1 >> 8); + if (i < 0 || i >= MAX_LAYERS) + break; + } + awe_send_effect(voice, i, p1, p2); + break; + + case _AWE_RESET_CHANNEL: + awe_channel_init(voice, !p1); + break; + + case _AWE_TERMINATE_ALL: + awe_reset(0); + break; + + case _AWE_TERMINATE_CHANNEL: + awe_voice_change(voice, awe_terminate_and_init); + break; + + case _AWE_RELEASE_ALL: + awe_note_off_all(FALSE); + break; + case _AWE_NOTEOFF_ALL: + awe_note_off_all(TRUE); + break; + + case _AWE_INITIAL_VOLUME: + DEBUG(0,printk("AWE32: init attenuation %d\n", p1)); + atten_relative = (char)p2; + atten_offset = (short)p1; + awe_update_volume(); + break; + + case _AWE_CHN_PRESSURE: + channels[voice].chan_press = p1; + awe_modwheel_change(voice, p1); + break; + + case _AWE_CHANNEL_MODE: + DEBUG(0,printk("AWE32: channel mode = %d\n", p1)); + playing_mode = p1; + awe_reset(0); + break; + + case _AWE_DRUM_CHANNELS: + DEBUG(0,printk("AWE32: drum flags = %x\n", p1)); + drum_flags = *(unsigned int*)&event[4]; + break; + + case _AWE_MISC_MODE: + DEBUG(0,printk("AWE32: ctrl parms = %d %d\n", p1, p2)); + if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) { + ctrls[p1] = p2; + if (ctrl_parms[p1].update) + ctrl_parms[p1].update(); + } + break; + + case _AWE_EQUALIZER: + ctrls[AWE_MD_BASS_LEVEL] = p1; + ctrls[AWE_MD_TREBLE_LEVEL] = p2; + awe_update_equalizer(); + break; + + default: + DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice)); + break; + } +} + + +/* change effects */ +static void +awe_send_effect(int voice, int layer, int type, int val) +{ + awe_chan_info *cinfo; + FX_Rec *fx; + int mode; + + cinfo = &channels[voice]; + if (layer >= 0 && layer < MAX_LAYERS) + fx = &cinfo->fx_layer[layer]; + else + fx = &cinfo->fx; + + if (type & 0x40) + mode = FX_FLAG_OFF; + else if (type & 0x80) + mode = FX_FLAG_ADD; + else + mode = FX_FLAG_SET; + type &= 0x3f; + + if (type >= 0 && type < AWE_FX_END) { + DEBUG(2,printk("AWE32: effects (%d) %d %d\n", voice, type, val)); + if (mode == FX_FLAG_SET) + FX_SET(fx, type, val); + else if (mode == FX_FLAG_ADD) + FX_ADD(fx, type, val); + else + FX_UNSET(fx, type); + if (mode != FX_FLAG_OFF && parm_defs[type].realtime) { + DEBUG(2,printk("AWE32: fx_realtime (%d)\n", voice)); + awe_voice_change(voice, parm_defs[type].realtime); + } + } +} + + +/* change modulation wheel; voice is already mapped on multi2 mode */ +static void +awe_modwheel_change(int voice, int value) +{ + int i; + awe_chan_info *cinfo; + + cinfo = &channels[voice]; + i = value * ctrls[AWE_MD_MOD_SENSE] / 1200; + FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i); + awe_voice_change(voice, awe_fx_fmmod); + FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i); + awe_voice_change(voice, awe_fx_fm2frq2); +} + + +/* voice pressure change */ +static void +awe_aftertouch(int dev, int voice, int pressure) +{ + int note; + + DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure)); + if (! voice_in_range(voice)) + return; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + awe_start_note(dev, voice, 255, pressure); + break; + case AWE_PLAY_MULTI2: + note = (voice_alloc->map[voice] & 0xff) - 1; + awe_key_pressure(dev, voice, note + 0x80, pressure); + break; + } +} + + +/* voice control change */ +static void +awe_controller(int dev, int voice, int ctrl_num, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + + switch (ctrl_num) { + case CTL_BANK_SELECT: /* MIDI control #0 */ + DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value)); + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) && + !ctrls[AWE_MD_TOGGLE_DRUM_BANK]) + break; + if (value < 0 || value > 255) + break; + cinfo->bank = value; + if (cinfo->bank == AWE_DRUM_BANK) + DRUM_CHANNEL_ON(cinfo->channel); + else + DRUM_CHANNEL_OFF(cinfo->channel); + awe_set_instr(dev, voice, cinfo->instr); + break; + + case CTL_MODWHEEL: /* MIDI control #1 */ + DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value)); + awe_modwheel_change(voice, value); + break; + + case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */ + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value)); + /* zero centered */ + cinfo->bender = value; + awe_voice_change(voice, awe_set_voice_pitch); + break; + + case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value)); + /* value = sense x 100 */ + cinfo->bender_range = value; + /* no audible pitch change yet.. */ + break; + + case CTL_EXPRESSION: /* MIDI control #11 */ + if (SINGLE_LAYER_MODE()) + value /= 128; + case CTRL_EXPRESSION: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->expression_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_PAN: /* MIDI control #10 */ + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value)); + /* (0-127) -> signed 8bit */ + cinfo->panning = value * 2 - 128; + if (ctrls[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); + break; + + case CTL_MAIN_VOLUME: /* MIDI control #7 */ + if (SINGLE_LAYER_MODE()) + value = (value * 100) / 16383; + case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->main_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */ + DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2); + break; + + case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */ + DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2); + break; + + case 120: /* all sounds off */ + awe_note_off_all(FALSE); + break; + case 123: /* all notes off */ + awe_note_off_all(TRUE); + break; + + case CTL_SUSTAIN: /* MIDI control #64 */ + cinfo->sustained = value; + if (value != 127) + awe_voice_change(voice, awe_sustain_off); + break; + + case CTL_SOSTENUTO: /* MIDI control #66 */ + if (value == 127) + awe_voice_change(voice, awe_sostenuto_on); + else + awe_voice_change(voice, awe_sustain_off); + break; + + default: + DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n", + voice, ctrl_num, value)); + break; + } +} + + +/* voice pan change (value = -128 - 127) */ +static void +awe_panning(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + cinfo->panning = value; + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning)); + if (ctrls[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); +} + + +/* volume mode change */ +static void +awe_volume_method(int dev, int mode) +{ + /* not impremented */ + DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode)); +} + + +/* pitch wheel change: 0-16384 */ +static void +awe_bender(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + /* convert to zero centered value */ + cinfo = &channels[voice]; + cinfo->bender = value - 8192; + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender)); + awe_voice_change(voice, awe_set_voice_pitch); +} + + +/* + * load a sound patch: + * three types of patches are accepted: AWE, GUS, and SYSEX. + */ + +static int +awe_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + awe_patch_info patch; + int rc = 0; + +#ifdef AWE_HAS_GUS_COMPATIBILITY + if (format == GUS_PATCH) { + return awe_load_guspatch(addr, offs, count, pmgr_flag); + } else +#endif + if (format == SYSEX_PATCH) { + /* no system exclusive message supported yet */ + return 0; + } else if (format != AWE_PATCH) { + printk(KERN_WARNING "AWE32 Error: Invalid patch format (key) 0x%x\n", format); + return -EINVAL; + } + + if (count < AWE_PATCH_INFO_SIZE) { + printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); + return -EINVAL; + } + if (copy_from_user(((char*)&patch) + offs, addr + offs, + AWE_PATCH_INFO_SIZE - offs)) + return -EFAULT; + + count -= AWE_PATCH_INFO_SIZE; + if (count < patch.len) { + printk(KERN_WARNING "AWE32: sample: Patch record too short (%d<%d)\n", + count, patch.len); + return -EINVAL; + } + + switch (patch.type) { + case AWE_LOAD_INFO: + rc = awe_load_info(&patch, addr, count); + break; + case AWE_LOAD_DATA: + rc = awe_load_data(&patch, addr, count); + break; + case AWE_OPEN_PATCH: + rc = awe_open_patch(&patch, addr, count); + break; + case AWE_CLOSE_PATCH: + rc = awe_close_patch(&patch, addr, count); + break; + case AWE_UNLOAD_PATCH: + rc = awe_unload_patch(&patch, addr, count); + break; + case AWE_REPLACE_DATA: + rc = awe_replace_data(&patch, addr, count); + break; + case AWE_MAP_PRESET: + rc = awe_load_map(&patch, addr, count); + break; + /* case AWE_PROBE_INFO: + rc = awe_probe_info(&patch, addr, count); + break;*/ + case AWE_PROBE_DATA: + rc = awe_probe_data(&patch, addr, count); + break; + case AWE_REMOVE_INFO: + rc = awe_remove_info(&patch, addr, count); + break; + case AWE_LOAD_CHORUS_FX: + rc = awe_load_chorus_fx(&patch, addr, count); + break; + case AWE_LOAD_REVERB_FX: + rc = awe_load_reverb_fx(&patch, addr, count); + break; + + default: + printk(KERN_WARNING "AWE32 Error: unknown patch format type %d\n", + patch.type); + rc = -EINVAL; + } + + return rc; +} + + +/* create an sf list record */ +static int +awe_create_sf(int type, char *name) +{ + sf_list *rec; + + /* terminate sounds */ + awe_reset(0); + rec = (sf_list *)kmalloc(sizeof(*rec), GFP_KERNEL); + if (rec == NULL) + return 1; /* no memory */ + rec->sf_id = current_sf_id + 1; + rec->type = type; + if (/*current_sf_id == 0 ||*/ (type & AWE_PAT_LOCKED) != 0) + locked_sf_id = current_sf_id + 1; + rec->num_info = awe_free_info(); + rec->num_sample = awe_free_sample(); + rec->mem_ptr = awe_free_mem_ptr(); + rec->infos = rec->last_infos = NULL; + rec->samples = rec->last_samples = NULL; + + /* add to linked-list */ + rec->next = NULL; + rec->prev = sftail; + if (sftail) + sftail->next = rec; + else + sfhead = rec; + sftail = rec; + current_sf_id++; + +#ifdef AWE_ALLOW_SAMPLE_SHARING + rec->shared = NULL; + if (name) + memcpy(rec->name, name, AWE_PATCH_NAME_LEN); + else + strcpy(rec->name, "*TEMPORARY*"); + if (current_sf_id > 1 && name && (type & AWE_PAT_SHARED) != 0) { + /* is the current font really a shared font? */ + if (is_shared_sf(rec->name)) { + /* check if the shared font is already installed */ + sf_list *p; + for (p = rec->prev; p; p = p->prev) { + if (is_identical_name(rec->name, p)) { + rec->shared = p; + break; + } + } + } + } +#endif /* allow sharing */ + + return 0; +} + + +#ifdef AWE_ALLOW_SAMPLE_SHARING + +/* check if the given name is a valid shared name */ +#define ASC_TO_KEY(c) ((c) - 'A' + 1) +static int is_shared_sf(unsigned char *name) +{ + static unsigned char id_head[4] = { + ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'), + AWE_MAJOR_VERSION, + }; + if (memcmp(name, id_head, 4) == 0) + return TRUE; + return FALSE; +} + +/* check if the given name matches to the existing list */ +static int is_identical_name(unsigned char *name, sf_list *p) +{ + char *id = p->name; + if (is_shared_sf(id) && memcmp(id, name, AWE_PATCH_NAME_LEN) == 0) + return TRUE; + return FALSE; +} + +/* check if the given voice info exists */ +static int info_duplicated(sf_list *sf, awe_voice_list *rec) +{ + /* search for all sharing lists */ + for (; sf; sf = sf->shared) { + awe_voice_list *p; + for (p = sf->infos; p; p = p->next) { + if (p->type == V_ST_NORMAL && + p->bank == rec->bank && + p->instr == rec->instr && + p->v.low == rec->v.low && + p->v.high == rec->v.high && + p->v.sample == rec->v.sample) + return TRUE; + } + } + return FALSE; +} + +#endif /* AWE_ALLOW_SAMPLE_SHARING */ + + +/* free sf_list record */ +/* linked-list in this function is not cared */ +static void +awe_free_sf(sf_list *sf) +{ + if (sf->infos) { + awe_voice_list *p, *next; + for (p = sf->infos; p; p = next) { + next = p->next; + kfree(p); + } + } + if (sf->samples) { + awe_sample_list *p, *next; + for (p = sf->samples; p; p = next) { + next = p->next; + kfree(p); + } + } + kfree(sf); +} + + +/* open patch; create sf list and set opened flag */ +static int +awe_open_patch(awe_patch_info *patch, const char *addr, int count) +{ + awe_open_parm parm; + int shared; + + if (copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm))) + return -EFAULT; + shared = FALSE; + +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (sftail && (parm.type & AWE_PAT_SHARED) != 0) { + /* is the previous font the same font? */ + if (is_identical_name(parm.name, sftail)) { + /* then append to the previous */ + shared = TRUE; + awe_reset(0); + if (parm.type & AWE_PAT_LOCKED) + locked_sf_id = current_sf_id; + } + } +#endif /* allow sharing */ + if (! shared) { + if (awe_create_sf(parm.type, parm.name)) { + printk(KERN_ERR "AWE32: can't open: failed to alloc new list\n"); + return -ENOMEM; + } + } + patch_opened = TRUE; + return current_sf_id; +} + +/* check if the patch is already opened */ +static sf_list * +check_patch_opened(int type, char *name) +{ + if (! patch_opened) { + if (awe_create_sf(type, name)) { + printk(KERN_ERR "AWE32: failed to alloc new list\n"); + return NULL; + } + patch_opened = TRUE; + return sftail; + } + return sftail; +} + +/* close the patch; if no voice is loaded, remove the patch */ +static int +awe_close_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (patch_opened && sftail) { + /* if no voice is loaded, release the current patch */ + if (sftail->infos == NULL) { + awe_reset(0); + awe_remove_samples(current_sf_id - 1); + } + } + patch_opened = 0; + return 0; +} + + +/* remove the latest patch */ +static int +awe_unload_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (current_sf_id > 0 && current_sf_id > locked_sf_id) { + awe_reset(0); + awe_remove_samples(current_sf_id - 1); + } + return 0; +} + +/* allocate voice info list records */ +static awe_voice_list * +alloc_new_info(void) +{ + awe_voice_list *newlist; + + newlist = (awe_voice_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); + if (newlist == NULL) { + printk(KERN_ERR "AWE32: can't alloc info table\n"); + return NULL; + } + return newlist; +} + +/* allocate sample info list records */ +static awe_sample_list * +alloc_new_sample(void) +{ + awe_sample_list *newlist; + + newlist = (awe_sample_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); + if (newlist == NULL) { + printk(KERN_ERR "AWE32: can't alloc sample table\n"); + return NULL; + } + return newlist; +} + +/* load voice map */ +static int +awe_load_map(awe_patch_info *patch, const char *addr, int count) +{ + awe_voice_map map; + awe_voice_list *rec, *p; + sf_list *sf; + + /* get the link info */ + if (count < sizeof(map)) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) + return -EFAULT; + + /* check if the identical mapping already exists */ + p = awe_search_instr(map.map_bank, map.map_instr, map.map_key); + for (; p; p = p->next_instr) { + if (p->type == V_ST_MAPPED && + p->v.start == map.src_instr && + p->v.end == map.src_bank && + p->v.fixkey == map.src_key) + return 0; /* already present! */ + } + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MAP, NULL)) == NULL) + return -ENOMEM; + + if ((rec = alloc_new_info()) == NULL) + return -ENOMEM; + + rec->bank = map.map_bank; + rec->instr = map.map_instr; + rec->type = V_ST_MAPPED; + rec->disabled = FALSE; + awe_init_voice_info(&rec->v); + if (map.map_key >= 0) { + rec->v.low = map.map_key; + rec->v.high = map.map_key; + } + rec->v.start = map.src_instr; + rec->v.end = map.src_bank; + rec->v.fixkey = map.src_key; + add_sf_info(sf, rec); + add_info_list(rec); + + return 0; +} + +#if 0 +/* probe preset in the current list -- nothing to be loaded */ +static int +awe_probe_info(awe_patch_info *patch, const char *addr, int count) +{ +#ifdef AWE_ALLOW_SAMPLE_SHARING + awe_voice_map map; + awe_voice_list *p; + + if (! patch_opened) + return -EINVAL; + + /* get the link info */ + if (count < sizeof(map)) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) + return -EFAULT; + + /* check if the identical mapping already exists */ + if (sftail == NULL) + return -EINVAL; + p = awe_search_instr(map.src_bank, map.src_instr, map.src_key); + for (; p; p = p->next_instr) { + if (p->type == V_ST_NORMAL && + is_identical_holder(p->holder, sftail) && + p->v.low <= map.src_key && + p->v.high >= map.src_key) + return 0; /* already present! */ + } +#endif /* allow sharing */ + return -EINVAL; +} +#endif + +/* probe sample in the current list -- nothing to be loaded */ +static int +awe_probe_data(awe_patch_info *patch, const char *addr, int count) +{ +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (! patch_opened) + return -EINVAL; + + /* search the specified sample by optarg */ + if (search_sample_index(sftail, patch->optarg) != NULL) + return 0; +#endif /* allow sharing */ + return -EINVAL; +} + + +/* remove the present instrument layers */ +static int +remove_info(sf_list *sf, int bank, int instr) +{ + awe_voice_list *prev, *next, *p; + int removed = 0; + + prev = NULL; + for (p = sf->infos; p; p = next) { + next = p->next; + if (p->type == V_ST_NORMAL && + p->bank == bank && p->instr == instr) { + /* remove this layer */ + if (prev) + prev->next = next; + else + sf->infos = next; + if (p == sf->last_infos) + sf->last_infos = prev; + sf->num_info--; + removed++; + kfree(p); + } else + prev = p; + } + if (removed) + rebuild_preset_list(); + return removed; +} + +/* load voice information data */ +static int +awe_load_info(awe_patch_info *patch, const char *addr, int count) +{ + int offset; + awe_voice_rec_hdr hdr; + int i; + int total_size; + sf_list *sf; + awe_voice_list *rec; + + if (count < AWE_VOICE_REC_SIZE) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE)) + return -EFAULT; + offset += AWE_VOICE_REC_SIZE; + + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk(KERN_WARNING "AWE32 Error: Invalid voice number %d\n", hdr.nvoices); + return -EINVAL; + } + total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices; + if (count < total_size) { + printk(KERN_WARNING "AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return -EINVAL; + } + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) + return -ENOMEM; + + switch (hdr.write_mode) { + case AWE_WR_EXCLUSIVE: + /* exclusive mode - if the instrument already exists, + return error */ + for (rec = sf->infos; rec; rec = rec->next) { + if (rec->type == V_ST_NORMAL && + rec->bank == hdr.bank && + rec->instr == hdr.instr) + return -EINVAL; + } + break; + case AWE_WR_REPLACE: + /* replace mode - remove the instrument if it already exists */ + remove_info(sf, hdr.bank, hdr.instr); + break; + } + + /* append new layers */ + for (i = 0; i < hdr.nvoices; i++) { + rec = alloc_new_info(); + if (rec == NULL) + return -ENOMEM; + + rec->bank = hdr.bank; + rec->instr = hdr.instr; + rec->type = V_ST_NORMAL; + rec->disabled = FALSE; + + /* copy awe_voice_info parameters */ + if (copy_from_user(&rec->v, addr + offset, AWE_VOICE_INFO_SIZE)) { + kfree(rec); + return -EFAULT; + } + offset += AWE_VOICE_INFO_SIZE; +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (sf && sf->shared) { + if (info_duplicated(sf, rec)) { + kfree(rec); + continue; + } + } +#endif /* allow sharing */ + if (rec->v.mode & AWE_MODE_INIT_PARM) + awe_init_voice_parm(&rec->v.parm); + add_sf_info(sf, rec); + awe_set_sample(rec); + add_info_list(rec); + } + + return 0; +} + + +/* remove instrument layers */ +static int +awe_remove_info(awe_patch_info *patch, const char *addr, int count) +{ + unsigned char bank, instr; + sf_list *sf; + + if (! patch_opened || (sf = sftail) == NULL) { + printk(KERN_WARNING "AWE32: remove_info: patch not opened\n"); + return -EINVAL; + } + + bank = ((unsigned short)patch->optarg >> 8) & 0xff; + instr = (unsigned short)patch->optarg & 0xff; + if (! remove_info(sf, bank, instr)) + return -EINVAL; + return 0; +} + + +/* load wave sample data */ +static int +awe_load_data(awe_patch_info *patch, const char *addr, int count) +{ + int offset, size; + int rc; + awe_sample_info tmprec; + awe_sample_list *rec; + sf_list *sf; + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) + return -ENOMEM; + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE)) + return -EFAULT; + offset += AWE_SAMPLE_INFO_SIZE; + if (size != tmprec.size) { + printk(KERN_WARNING "AWE32: load: sample size differed (%d != %d)\n", + tmprec.size, size); + return -EINVAL; + } + + if (search_sample_index(sf, tmprec.sample) != NULL) { +#ifdef AWE_ALLOW_SAMPLE_SHARING + /* if shared sample, skip this data */ + if (sf->type & AWE_PAT_SHARED) + return 0; +#endif /* allow sharing */ + DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample)); + return -EINVAL; + } + + if ((rec = alloc_new_sample()) == NULL) + return -ENOMEM; + + memcpy(&rec->v, &tmprec, sizeof(tmprec)); + + if (rec->v.size > 0) { + if ((rc = awe_write_wave_data(addr, offset, rec, -1)) < 0) { + kfree(rec); + return rc; + } + sf->mem_ptr += rc; + } + + add_sf_sample(sf, rec); + return 0; +} + + +/* replace wave sample data */ +static int +awe_replace_data(awe_patch_info *patch, const char *addr, int count) +{ + int offset; + int size; + int rc; + int channels; + awe_sample_info cursmp; + int save_mem_ptr; + sf_list *sf; + awe_sample_list *rec; + + if (! patch_opened || (sf = sftail) == NULL) { + printk(KERN_WARNING "AWE32: replace: patch not opened\n"); + return -EINVAL; + } + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE)) + return -EFAULT; + offset += AWE_SAMPLE_INFO_SIZE; + if (cursmp.size == 0 || size != cursmp.size) { + printk(KERN_WARNING "AWE32: replace: invalid sample size (%d!=%d)\n", + cursmp.size, size); + return -EINVAL; + } + channels = patch->optarg; + if (channels <= 0 || channels > AWE_NORMAL_VOICES) { + printk(KERN_WARNING "AWE32: replace: invalid channels %d\n", channels); + return -EINVAL; + } + + for (rec = sf->samples; rec; rec = rec->next) { + if (rec->v.sample == cursmp.sample) + break; + } + if (rec == NULL) { + printk(KERN_WARNING "AWE32: replace: cannot find existing sample data %d\n", + cursmp.sample); + return -EINVAL; + } + + if (rec->v.size != cursmp.size) { + printk(KERN_WARNING "AWE32: replace: exiting size differed (%d!=%d)\n", + rec->v.size, cursmp.size); + return -EINVAL; + } + + save_mem_ptr = awe_free_mem_ptr(); + sftail->mem_ptr = rec->v.start - awe_mem_start; + memcpy(&rec->v, &cursmp, sizeof(cursmp)); + rec->v.sf_id = current_sf_id; + if ((rc = awe_write_wave_data(addr, offset, rec, channels)) < 0) + return rc; + sftail->mem_ptr = save_mem_ptr; + + return 0; +} + + +/*----------------------------------------------------------------*/ + +static const char *readbuf_addr; +static int readbuf_offs; +static int readbuf_flags; + +/* initialize read buffer */ +static int +readbuf_init(const char *addr, int offset, awe_sample_info *sp) +{ + readbuf_addr = addr; + readbuf_offs = offset; + readbuf_flags = sp->mode_flags; + return 0; +} + +/* read directly from user buffer */ +static unsigned short +readbuf_word(int pos) +{ + unsigned short c; + /* read from user buffer */ + if (readbuf_flags & AWE_SAMPLE_8BITS) { + unsigned char cc; + get_user(cc, (unsigned char*)(readbuf_addr + readbuf_offs + pos)); + c = (unsigned short)cc << 8; /* convert 8bit -> 16bit */ + } else { + get_user(c, (unsigned short*)(readbuf_addr + readbuf_offs + pos * 2)); + } + if (readbuf_flags & AWE_SAMPLE_UNSIGNED) + c ^= 0x8000; /* unsigned -> signed */ + return c; +} + +#define readbuf_word_cache readbuf_word +#define readbuf_end() /**/ + +/*----------------------------------------------------------------*/ + +#define BLANK_LOOP_START 8 +#define BLANK_LOOP_END 40 +#define BLANK_LOOP_SIZE 48 + +/* loading onto memory - return the actual written size */ +static int +awe_write_wave_data(const char *addr, int offset, awe_sample_list *list, int channels) +{ + int i, truesize, dram_offset; + awe_sample_info *sp = &list->v; + int rc; + + /* be sure loop points start < end */ + if (sp->loopstart > sp->loopend) { + int tmp = sp->loopstart; + sp->loopstart = sp->loopend; + sp->loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->size; + if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) + truesize += sp->loopend - sp->loopstart; + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + if (awe_free_mem_ptr() + truesize >= memsize/2) { + DEBUG(-1,printk("AWE32 Error: Sample memory full\n")); + return -ENOSPC; + } + + /* recalculate address offset */ + sp->end -= sp->start; + sp->loopstart -= sp->start; + sp->loopend -= sp->start; + + dram_offset = awe_free_mem_ptr() + awe_mem_start; + sp->start = dram_offset; + sp->end += dram_offset; + sp->loopstart += dram_offset; + sp->loopend += dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + if (sp->size == 0) + sp->checksum = 0; + else + sp->checksum = truesize; + + if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0) + return rc; + + if (readbuf_init(addr, offset, sp) < 0) + return -ENOSPC; + + for (i = 0; i < sp->size; i++) { + unsigned short c; + c = readbuf_word(i); + awe_write_dram(c); + if (i == sp->loopend && + (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) { + int looplen = sp->loopend - sp->loopstart; + /* copy reverse loop */ + int k; + for (k = 1; k <= looplen; k++) { + c = readbuf_word_cache(i - k); + awe_write_dram(c); + } + if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) { + sp->end += looplen; + } else { + sp->start += looplen; + sp->end += looplen; + } + } + } + readbuf_end(); + + /* if no blank loop is attached in the sample, add it */ + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) { + for (i = 0; i < BLANK_LOOP_SIZE; i++) + awe_write_dram(0); + if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) { + sp->loopstart = sp->end + BLANK_LOOP_START; + sp->loopend = sp->end + BLANK_LOOP_END; + } + } + + awe_close_dram(); + + /* initialize FM */ + awe_init_fm(); + + return truesize; +} + + +/*----------------------------------------------------------------*/ + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* calculate GUS envelope time: + * is this correct? i have no idea.. + */ +static int +calc_gus_envelope_time(int rate, int start, int end) +{ + int r, p, t; + r = (3 - ((rate >> 6) & 3)) * 3; + p = rate & 0x3f; + t = end - start; + if (t < 0) t = -t; + if (13 > r) + t = t << (13 - r); + else + t = t >> (r - 13); + return (t * 10) / (p * 441); +} + +#define calc_gus_sustain(val) (0x7f - vol_table[(val)/2]) +#define calc_gus_attenuation(val) vol_table[(val)/2] + +/* load GUS patch */ +static int +awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag) +{ + struct patch_info patch; + awe_voice_info *rec; + awe_sample_info *smp; + awe_voice_list *vrec; + awe_sample_list *smprec; + int sizeof_patch; + int note, rc; + sf_list *sf; + + sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */ + if (size < sizeof_patch) { + printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); + return -EINVAL; + } + if (copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs)) + return -EFAULT; + size -= sizeof_patch; + if (size < patch.len) { + printk(KERN_WARNING "AWE32 Error: Patch record too short (%d<%d)\n", + size, patch.len); + return -EINVAL; + } + if ((sf = check_patch_opened(AWE_PAT_TYPE_GUS, NULL)) == NULL) + return -ENOMEM; + if ((smprec = alloc_new_sample()) == NULL) + return -ENOMEM; + if ((vrec = alloc_new_info()) == NULL) { + kfree(smprec); + return -ENOMEM; + } + + smp = &smprec->v; + smp->sample = sf->num_sample; + smp->start = 0; + smp->end = patch.len; + smp->loopstart = patch.loop_start; + smp->loopend = patch.loop_end; + smp->size = patch.len; + + /* set up mode flags */ + smp->mode_flags = 0; + if (!(patch.mode & WAVE_16_BITS)) + smp->mode_flags |= AWE_SAMPLE_8BITS; + if (patch.mode & WAVE_UNSIGNED) + smp->mode_flags |= AWE_SAMPLE_UNSIGNED; + smp->mode_flags |= AWE_SAMPLE_NO_BLANK; + if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) + smp->mode_flags |= AWE_SAMPLE_SINGLESHOT; + if (patch.mode & WAVE_BIDIR_LOOP) + smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP; + if (patch.mode & WAVE_LOOP_BACK) + smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP; + + DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags)); + if (patch.mode & WAVE_16_BITS) { + /* convert to word offsets */ + smp->size /= 2; + smp->end /= 2; + smp->loopstart /= 2; + smp->loopend /= 2; + } + smp->checksum_flag = 0; + smp->checksum = 0; + + if ((rc = awe_write_wave_data(addr, sizeof_patch, smprec, -1)) < 0) + return rc; + sf->mem_ptr += rc; + add_sf_sample(sf, smprec); + + /* set up voice info */ + rec = &vrec->v; + awe_init_voice_info(rec); + rec->sample = sf->num_info; /* the last sample */ + rec->rate_offset = calc_rate_offset(patch.base_freq); + note = freq_to_note(patch.base_note); + rec->root = note / 100; + rec->tune = -(note % 100); + rec->low = freq_to_note(patch.low_note) / 100; + rec->high = freq_to_note(patch.high_note) / 100; + DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n", + rec->rate_offset, note, + rec->low, rec->high, + patch.low_note, patch.high_note)); + /* panning position; -128 - 127 => 0-127 */ + rec->pan = (patch.panning + 128) / 2; + + /* detuning is ignored */ + /* 6points volume envelope */ + if (patch.mode & WAVE_ENVELOPES) { + int attack, hold, decay, release; + attack = calc_gus_envelope_time + (patch.env_rate[0], 0, patch.env_offset[0]); + hold = calc_gus_envelope_time + (patch.env_rate[1], patch.env_offset[0], + patch.env_offset[1]); + decay = calc_gus_envelope_time + (patch.env_rate[2], patch.env_offset[1], + patch.env_offset[2]); + release = calc_gus_envelope_time + (patch.env_rate[3], patch.env_offset[1], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[4], patch.env_offset[3], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[5], patch.env_offset[4], + patch.env_offset[5]); + rec->parm.volatkhld = (calc_parm_hold(hold) << 8) | + calc_parm_attack(attack); + rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + calc_parm_decay(decay); + rec->parm.volrelease = 0x8000 | calc_parm_decay(release); + DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release)); + rec->attenuation = calc_gus_attenuation(patch.env_offset[0]); + } + + /* tremolo effect */ + if (patch.mode & WAVE_TREMOLO) { + int rate = (patch.tremolo_rate * 1000 / 38) / 42; + rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + /* vibrato effect */ + if (patch.mode & WAVE_VIBRATO) { + int rate = (patch.vibrato_rate * 1000 / 38) / 42; + rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + + /* scale_freq, scale_factor, volume, and fractions not implemented */ + + /* append to the tail of the list */ + vrec->bank = ctrls[AWE_MD_GUS_BANK]; + vrec->instr = patch.instr_no; + vrec->disabled = FALSE; + vrec->type = V_ST_NORMAL; + + add_sf_info(sf, vrec); + add_info_list(vrec); + + /* set the voice index */ + awe_set_sample(vrec); + + return 0; +} + +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + +/* + * sample and voice list handlers + */ + +/* append this to the current sf list */ +static void add_sf_info(sf_list *sf, awe_voice_list *rec) +{ + if (sf == NULL) + return; + rec->holder = sf; + rec->v.sf_id = sf->sf_id; + if (sf->last_infos) + sf->last_infos->next = rec; + else + sf->infos = rec; + sf->last_infos = rec; + rec->next = NULL; + sf->num_info++; +} + +/* prepend this sample to sf list */ +static void add_sf_sample(sf_list *sf, awe_sample_list *rec) +{ + if (sf == NULL) + return; + rec->holder = sf; + rec->v.sf_id = sf->sf_id; + if (sf->last_samples) + sf->last_samples->next = rec; + else + sf->samples = rec; + sf->last_samples = rec; + rec->next = NULL; + sf->num_sample++; +} + +/* purge the old records which don't belong with the same file id */ +static void purge_old_list(awe_voice_list *rec, awe_voice_list *next) +{ + rec->next_instr = next; + if (rec->bank == AWE_DRUM_BANK) { + /* remove samples with the same note range */ + awe_voice_list *cur, *prev = rec; + int low = rec->v.low; + int high = rec->v.high; + for (cur = next; cur; cur = cur->next_instr) { + if (cur->v.low == low && + cur->v.high == high && + ! is_identical_holder(cur->holder, rec->holder)) + prev->next_instr = cur->next_instr; + else + prev = cur; + } + } else { + if (! is_identical_holder(next->holder, rec->holder)) + /* remove all samples */ + rec->next_instr = NULL; + } +} + +/* prepend to top of the preset table */ +static void add_info_list(awe_voice_list *rec) +{ + awe_voice_list *prev, *cur; + int key; + + if (rec->disabled) + return; + + key = awe_search_key(rec->bank, rec->instr, rec->v.low); + prev = NULL; + for (cur = preset_table[key]; cur; cur = cur->next_bank) { + /* search the first record with the same bank number */ + if (cur->instr == rec->instr && cur->bank == rec->bank) { + /* replace the list with the new record */ + rec->next_bank = cur->next_bank; + if (prev) + prev->next_bank = rec; + else + preset_table[key] = rec; + purge_old_list(rec, cur); + return; + } + prev = cur; + } + + /* this is the first bank record.. just add this */ + rec->next_instr = NULL; + rec->next_bank = preset_table[key]; + preset_table[key] = rec; +} + +/* remove samples later than the specified sf_id */ +static void +awe_remove_samples(int sf_id) +{ + sf_list *p, *prev; + + if (sf_id <= 0) { + awe_reset_samples(); + return; + } + /* already removed? */ + if (current_sf_id <= sf_id) + return; + + for (p = sftail; p; p = prev) { + if (p->sf_id <= sf_id) + break; + prev = p->prev; + awe_free_sf(p); + } + sftail = p; + if (sftail) { + sf_id = sftail->sf_id; + sftail->next = NULL; + } else { + sf_id = 0; + sfhead = NULL; + } + current_sf_id = sf_id; + if (locked_sf_id > sf_id) + locked_sf_id = sf_id; + + rebuild_preset_list(); +} + +/* rebuild preset search list */ +static void rebuild_preset_list(void) +{ + sf_list *p; + awe_voice_list *rec; + + memset(preset_table, 0, sizeof(preset_table)); + + for (p = sfhead; p; p = p->next) { + for (rec = p->infos; rec; rec = rec->next) + add_info_list(rec); + } +} + +/* compare the given sf_id pair */ +static int is_identical_holder(sf_list *sf1, sf_list *sf2) +{ + if (sf1 == NULL || sf2 == NULL) + return FALSE; + if (sf1 == sf2) + return TRUE; +#ifdef AWE_ALLOW_SAMPLE_SHARING + { + /* compare with the sharing id */ + sf_list *p; + int counter = 0; + if (sf1->sf_id < sf2->sf_id) { /* make sure id1 > id2 */ + sf_list *tmp; tmp = sf1; sf1 = sf2; sf2 = tmp; + } + for (p = sf1->shared; p; p = p->shared) { + if (counter++ > current_sf_id) + break; /* strange sharing loop.. quit */ + if (p == sf2) + return TRUE; + } + } +#endif /* allow sharing */ + return FALSE; +} + +/* search the sample index matching with the given sample id */ +static awe_sample_list * +search_sample_index(sf_list *sf, int sample) +{ + awe_sample_list *p; +#ifdef AWE_ALLOW_SAMPLE_SHARING + int counter = 0; + while (sf) { + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample) + return p; + } + sf = sf->shared; + if (counter++ > current_sf_id) + break; /* strange sharing loop.. quit */ + } +#else + if (sf) { + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample) + return p; + } + } +#endif + return NULL; +} + +/* search the specified sample */ +/* non-zero = found */ +static short +awe_set_sample(awe_voice_list *rec) +{ + awe_sample_list *smp; + awe_voice_info *vp = &rec->v; + + vp->index = 0; + if ((smp = search_sample_index(rec->holder, vp->sample)) == NULL) + return 0; + + /* set the actual sample offsets */ + vp->start += smp->v.start; + vp->end += smp->v.end; + vp->loopstart += smp->v.loopstart; + vp->loopend += smp->v.loopend; + /* copy mode flags */ + vp->mode = smp->v.mode_flags; + /* set flag */ + vp->index = 1; + + return 1; +} + + +/* + * voice allocation + */ + +/* look for all voices associated with the specified note & velocity */ +static int +awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, + awe_voice_info **vlist) +{ + int nvoices; + + nvoices = 0; + for (; rec; rec = rec->next_instr) { + if (note >= rec->v.low && + note <= rec->v.high && + velocity >= rec->v.vellow && + velocity <= rec->v.velhigh) { + if (rec->type == V_ST_MAPPED) { + /* mapper */ + vlist[0] = &rec->v; + return -1; + } + vlist[nvoices++] = &rec->v; + if (nvoices >= AWE_MAX_VOICES) + break; + } + } + return nvoices; +} + +/* store the voice list from the specified note and velocity. + if the preset is mapped, seek for the destination preset, and rewrite + the note number if necessary. + */ +static int +really_alloc_voices(int bank, int instr, int *note, int velocity, awe_voice_info **vlist) +{ + int nvoices; + awe_voice_list *vrec; + int level = 0; + + for (;;) { + vrec = awe_search_instr(bank, instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + if (nvoices == 0) { + if (bank == AWE_DRUM_BANK) + /* search default drumset */ + vrec = awe_search_instr(bank, ctrls[AWE_MD_DEF_DRUM], *note); + else + /* search default preset */ + vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + } + if (nvoices == 0) { + if (bank == AWE_DRUM_BANK && ctrls[AWE_MD_DEF_DRUM] != 0) + /* search default drumset */ + vrec = awe_search_instr(bank, 0, *note); + else if (bank != AWE_DRUM_BANK && ctrls[AWE_MD_DEF_BANK] != 0) + /* search default preset */ + vrec = awe_search_instr(0, instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + } + if (nvoices < 0) { /* mapping */ + int key = vlist[0]->fixkey; + instr = vlist[0]->start; + bank = vlist[0]->end; + if (level++ > 5) { + printk(KERN_ERR "AWE32: too deep mapping level\n"); + return 0; + } + if (key >= 0) + *note = key; + } else + break; + } + + return nvoices; +} + +/* allocate voices corresponding note and velocity; supports multiple insts. */ +static void +awe_alloc_multi_voices(int ch, int note, int velocity, int key) +{ + int i, v, nvoices, bank; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) + bank = AWE_DRUM_BANK; /* always search drumset */ + else + bank = channels[ch].bank; + + /* check the possible voices; note may be changeable if mapped */ + nvoices = really_alloc_voices(bank, channels[ch].instr, + ¬e, velocity, vlist); + + /* set the voices */ + current_alloc_time++; + for (i = 0; i < nvoices; i++) { + v = awe_clear_voice(); + voices[v].key = key; + voices[v].ch = ch; + voices[v].note = note; + voices[v].velocity = velocity; + voices[v].time = current_alloc_time; + voices[v].cinfo = &channels[ch]; + voices[v].sample = vlist[i]; + voices[v].state = AWE_ST_MARK; + voices[v].layer = nvoices - i - 1; /* in reverse order */ + } + + /* clear the mark in allocated voices */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].state == AWE_ST_MARK) + voices[i].state = AWE_ST_OFF; + + } +} + + +/* search an empty voice. + if no empty voice is found, at least terminate a voice + */ +static int +awe_clear_voice(void) +{ + enum { + OFF=0, RELEASED, SUSTAINED, PLAYING, END + }; + struct voice_candidate_t { + int best; + int time; + int vtarget; + } candidate[END]; + int i, type, vtarget; + + vtarget = 0xffff; + for (type = OFF; type < END; type++) { + candidate[type].best = -1; + candidate[type].time = current_alloc_time + 1; + candidate[type].vtarget = vtarget; + } + + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].state & AWE_ST_OFF) + type = OFF; + else if (voices[i].state & AWE_ST_RELEASED) + type = RELEASED; + else if (voices[i].state & AWE_ST_SUSTAINED) + type = SUSTAINED; + else if (voices[i].state & ~AWE_ST_MARK) + type = PLAYING; + else + continue; +#ifdef AWE_CHECK_VTARGET + /* get current volume */ + vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff; +#endif + if (candidate[type].best < 0 || + vtarget < candidate[type].vtarget || + (vtarget == candidate[type].vtarget && + voices[i].time < candidate[type].time)) { + candidate[type].best = i; + candidate[type].time = voices[i].time; + candidate[type].vtarget = vtarget; + } + } + + for (type = OFF; type < END; type++) { + if ((i = candidate[type].best) >= 0) { + if (voices[i].state != AWE_ST_OFF) + awe_terminate(i); + awe_voice_init(i, TRUE); + return i; + } + } + return 0; +} + + +/* search sample for the specified note & velocity and set it on the voice; + * note that voice is the voice index (not channel index) + */ +static void +awe_alloc_one_voice(int voice, int note, int velocity) +{ + int ch, nvoices, bank; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + ch = voices[voice].ch; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice)) + bank = AWE_DRUM_BANK; /* always search drumset */ + else + bank = voices[voice].cinfo->bank; + + nvoices = really_alloc_voices(bank, voices[voice].cinfo->instr, + ¬e, velocity, vlist); + if (nvoices > 0) { + voices[voice].time = ++current_alloc_time; + voices[voice].sample = vlist[0]; /* use the first one */ + voices[voice].layer = 0; + voices[voice].note = note; + voices[voice].velocity = velocity; + } +} + + +/* + * sequencer2 functions + */ + +/* search an empty voice; used by sequencer2 */ +static int +awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + playing_mode = AWE_PLAY_MULTI2; + awe_info.nr_voices = AWE_MAX_CHANNELS; + return awe_clear_voice(); +} + + +/* set up voice; used by sequencer2 */ +static void +awe_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info; + if (synth_devs[dev] == NULL || + (info = &synth_devs[dev]->chn_info[chn]) == NULL) + return; + + if (voice < 0 || voice >= awe_max_voices) + return; + + DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn)); + channels[chn].expression_vol = info->controllers[CTL_EXPRESSION]; + channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME]; + channels[chn].panning = + info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */ + channels[chn].bender = info->bender_value; /* zero center */ + channels[chn].bank = info->controllers[CTL_BANK_SELECT]; + channels[chn].sustained = info->controllers[CTL_SUSTAIN]; + if (info->controllers[CTL_EXT_EFF_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_REVERB, + info->controllers[CTL_EXT_EFF_DEPTH] * 2); + } + if (info->controllers[CTL_CHORUS_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_CHORUS, + info->controllers[CTL_CHORUS_DEPTH] * 2); + } + awe_set_instr(dev, chn, info->pgm_num); +} + + +#ifdef CONFIG_AWE32_MIXER +/* + * AWE32 mixer device control + */ + +static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg); + +static int my_mixerdev = -1; + +static struct mixer_operations awe_mixer_operations = { + owner: THIS_MODULE, + id: "AWE", + name: "AWE32 Equalizer", + ioctl: awe_mixer_ioctl, +}; + +static void __init attach_mixer(void) +{ + if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) { + mixer_devs[my_mixerdev] = &awe_mixer_operations; + } +} + +static void __exit unload_mixer(void) +{ + if (my_mixerdev >= 0) + sound_unload_mixerdev(my_mixerdev); +} + +static int +awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int i, level, value; + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + level = *(int*)arg; + level = ((level & 0xff) + (level >> 8)) / 2; + DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level)); + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + value = level * 12 / 100; + if (value >= 12) + value = 11; + ctrls[AWE_MD_BASS_LEVEL] = value; + awe_update_equalizer(); + break; + case SOUND_MIXER_TREBLE: + value = level * 12 / 100; + if (value >= 12) + value = 11; + ctrls[AWE_MD_TREBLE_LEVEL] = value; + awe_update_equalizer(); + break; + case SOUND_MIXER_VOLUME: + level = level * 127 / 100; + if (level >= 128) level = 127; + atten_relative = FALSE; + atten_offset = vol_table[level]; + awe_update_volume(); + break; + } + } + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + level = ctrls[AWE_MD_BASS_LEVEL] * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_TREBLE: + level = ctrls[AWE_MD_TREBLE_LEVEL] * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_VOLUME: + value = atten_offset; + if (atten_relative) + value += ctrls[AWE_MD_ZERO_ATTEN]; + for (i = 127; i > 0; i--) { + if (value <= vol_table[i]) + break; + } + level = i * 100 / 127; + level = (level << 8) | level; + break; + case SOUND_MIXER_DEVMASK: + level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME; + break; + default: + level = 0; + break; + } + return *(int*)arg = level; +} +#endif /* CONFIG_AWE32_MIXER */ + + +/* + * initialization of Emu8000 + */ + +/* intiailize audio channels */ +static void +awe_init_audio(void) +{ + int ch; + + /* turn off envelope engines */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_DCYSUSV(ch), 0x80); + } + + /* reset all other parameters to zero */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_ENVVOL(ch), 0); + awe_poke(AWE_ENVVAL(ch), 0); + awe_poke(AWE_DCYSUS(ch), 0); + awe_poke(AWE_ATKHLDV(ch), 0); + awe_poke(AWE_LFO1VAL(ch), 0); + awe_poke(AWE_ATKHLD(ch), 0); + awe_poke(AWE_LFO2VAL(ch), 0); + awe_poke(AWE_IP(ch), 0); + awe_poke(AWE_IFATN(ch), 0); + awe_poke(AWE_PEFE(ch), 0); + awe_poke(AWE_FMMOD(ch), 0); + awe_poke(AWE_TREMFRQ(ch), 0); + awe_poke(AWE_FM2FRQ2(ch), 0); + awe_poke_dw(AWE_PTRX(ch), 0); + awe_poke_dw(AWE_VTFT(ch), 0); + awe_poke_dw(AWE_PSST(ch), 0); + awe_poke_dw(AWE_CSL(ch), 0); + awe_poke_dw(AWE_CCCA(ch), 0); + } + + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke_dw(AWE_CPF(ch), 0); + awe_poke_dw(AWE_CVCF(ch), 0); + } +} + + +/* initialize DMA address */ +static void +awe_init_dma(void) +{ + awe_poke_dw(AWE_SMALR, 0); + awe_poke_dw(AWE_SMARR, 0); + awe_poke_dw(AWE_SMALW, 0); + awe_poke_dw(AWE_SMARW, 0); +} + + +/* initialization arrays; from ADIP */ + +static unsigned short init1[128] = { + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, + + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, + + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, + + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +static unsigned short init2[128] = { + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, + + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, + + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, + + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +static unsigned short init3[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +static unsigned short init4[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + + +/* send initialization arrays to start up */ +static void +awe_init_array(void) +{ + awe_send_array(init1); + awe_wait(1024); + awe_send_array(init2); + awe_send_array(init3); + awe_poke_dw(AWE_HWCF4, 0); + awe_poke_dw(AWE_HWCF5, 0x83); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_send_array(init4); +} + +/* send an initialization array */ +static void +awe_send_array(unsigned short *data) +{ + int i; + unsigned short *p; + + p = data; + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT1(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT2(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT3(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT4(i), *p); +} + + +/* + * set up awe32 channels to some known state. + */ + +/* set the envelope & LFO parameters to the default values; see ADIP */ +static void +awe_tweak_voice(int i) +{ + /* set all mod/vol envelope shape to minimum */ + awe_poke(AWE_ENVVOL(i), 0x8000); + awe_poke(AWE_ENVVAL(i), 0x8000); + awe_poke(AWE_DCYSUS(i), 0x7F7F); + awe_poke(AWE_ATKHLDV(i), 0x7F7F); + awe_poke(AWE_ATKHLD(i), 0x7F7F); + awe_poke(AWE_PEFE(i), 0); /* mod envelope height to zero */ + awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */ + awe_poke(AWE_LFO2VAL(i), 0x8000); + awe_poke(AWE_IP(i), 0xE000); /* no pitch shift */ + awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */ + awe_poke(AWE_FMMOD(i), 0); + awe_poke(AWE_TREMFRQ(i), 0); + awe_poke(AWE_FM2FRQ2(i), 0); +} + +static void +awe_tweak(void) +{ + int i; + /* reset all channels */ + for (i = 0; i < awe_max_voices; i++) + awe_tweak_voice(i); +} + + +/* + * initializes the FM section of AWE32; + * see Vince Vu's unofficial AWE32 programming guide + */ + +static void +awe_init_fm(void) +{ +#ifndef AWE_ALWAYS_INIT_FM + /* if no extended memory is on board.. */ + if (memsize <= 0) + return; +#endif + DEBUG(3,printk("AWE32: initializing FM\n")); + + /* Initialize the last two channels for DRAM refresh and producing + the reverb and chorus effects for Yamaha OPL-3 synthesizer */ + + /* 31: FM left channel, 0xffffe0-0xffffe8 */ + awe_poke(AWE_DCYSUSV(30), 0x80); + awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */ + awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(30), 0); + awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3); + + /* 32: FM right channel, 0xfffff0-0xfffff8 */ + awe_poke(AWE_DCYSUSV(31), 0x80); + awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */ + awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(31), 0x8000); + awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3); + + /* skew volume & cutoff */ + awe_poke_dw(AWE_VTFT(30), 0x8000FFFF); + awe_poke_dw(AWE_VTFT(31), 0x8000FFFF); + + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* change maximum channels to 30 */ + awe_max_voices = AWE_NORMAL_VOICES; + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + voice_alloc->max_voice = awe_max_voices; +} + +/* + * AWE32 DRAM access routines + */ + +/* open DRAM write accessing mode */ +static int +awe_open_dram_for_write(int offset, int channels) +{ + int vidx[AWE_NORMAL_VOICES]; + int i; + + if (channels < 0 || channels >= AWE_NORMAL_VOICES) { + channels = AWE_NORMAL_VOICES; + for (i = 0; i < AWE_NORMAL_VOICES; i++) + vidx[i] = i; + } else { + for (i = 0; i < channels; i++) { + vidx[i] = awe_clear_voice(); + voices[vidx[i]].state = AWE_ST_MARK; + } + } + + /* use all channels for DMA transfer */ + for (i = 0; i < channels; i++) { + if (vidx[i] < 0) continue; + awe_poke(AWE_DCYSUSV(vidx[i]), 0x80); + awe_poke_dw(AWE_VTFT(vidx[i]), 0); + awe_poke_dw(AWE_CVCF(vidx[i]), 0); + awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000); + awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000); + awe_poke_dw(AWE_PSST(vidx[i]), 0); + awe_poke_dw(AWE_CSL(vidx[i]), 0); + awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000); + voices[vidx[i]].state = AWE_ST_DRAM; + } + /* point channels 31 & 32 to ROM samples for DRAM refresh */ + awe_poke_dw(AWE_VTFT(30), 0); + awe_poke_dw(AWE_PSST(30), 0x1d8); + awe_poke_dw(AWE_CSL(30), 0x1e0); + awe_poke_dw(AWE_CCCA(30), 0x1d8); + awe_poke_dw(AWE_VTFT(31), 0); + awe_poke_dw(AWE_PSST(31), 0x1d8); + awe_poke_dw(AWE_CSL(31), 0x1e0); + awe_poke_dw(AWE_CCCA(31), 0x1d8); + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* if full bit is on, not ready to write on */ + if (awe_peek_dw(AWE_SMALW) & 0x80000000) { + for (i = 0; i < channels; i++) { + awe_poke_dw(AWE_CCCA(vidx[i]), 0); + voices[vidx[i]].state = AWE_ST_OFF; + } + printk("awe: not ready to write..\n"); + return -EPERM; + } + + /* set address to write */ + awe_poke_dw(AWE_SMALW, offset); + + return 0; +} + +/* open DRAM for RAM size detection */ +static void +awe_open_dram_for_check(void) +{ + int i; + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + awe_poke(AWE_DCYSUSV(i), 0x80); + awe_poke_dw(AWE_VTFT(i), 0); + awe_poke_dw(AWE_CVCF(i), 0); + awe_poke_dw(AWE_PTRX(i), 0x40000000); + awe_poke_dw(AWE_CPF(i), 0x40000000); + awe_poke_dw(AWE_PSST(i), 0); + awe_poke_dw(AWE_CSL(i), 0); + if (i & 1) /* DMA write */ + awe_poke_dw(AWE_CCCA(i), 0x06000000); + else /* DMA read */ + awe_poke_dw(AWE_CCCA(i), 0x04000000); + voices[i].state = AWE_ST_DRAM; + } +} + + +/* close dram access */ +static void +awe_close_dram(void) +{ + int i; + /* wait until FULL bit in SMAxW register be false */ + for (i = 0; i < 10000; i++) { + if (!(awe_peek_dw(AWE_SMALW) & 0x80000000)) + break; + awe_wait(10); + } + + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + if (voices[i].state == AWE_ST_DRAM) { + awe_poke_dw(AWE_CCCA(i), 0); + awe_poke(AWE_DCYSUSV(i), 0x807F); + voices[i].state = AWE_ST_OFF; + } + } +} + + +/* + * detect presence of AWE32 and check memory size + */ + +/* detect emu8000 chip on the specified address; from VV's guide */ + +static int __init +awe_detect_base(int addr) +{ + setup_ports(addr, 0, 0); + if ((awe_peek(AWE_U1) & 0x000F) != 0x000C) + return 0; + if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058) + return 0; + if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003) + return 0; + DEBUG(0,printk("AWE32 found at %x\n", addr)); + return 1; +} + +#ifdef __ISAPNP__ +static struct { + unsigned short card_vendor, card_device; + unsigned short vendor; + unsigned short function; + char *name; +} isapnp_awe_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0021), + "AWE32 WaveTable" }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0022), + "AWE64 WaveTable" }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0023), + "AWE64 Gold WaveTable" }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_awe_list); + +static struct pci_dev *idev = NULL; + +static int __init awe_probe_isapnp(int *port) +{ + int i; + + for (i = 0; isapnp_awe_list[i].vendor != 0; i++) { + while ((idev = isapnp_find_dev(NULL, + isapnp_awe_list[i].vendor, + isapnp_awe_list[i].function, + idev))) { + if (idev->prepare(idev) < 0) + continue; + if (idev->activate(idev) < 0 || + !idev->resource[0].start) { + idev->deactivate(idev); + idev->deactivate(idev); + continue; + } + *port = idev->resource[0].start; + break; + } + if (!idev) + continue; + printk(KERN_INFO "ISAPnP reports %s at i/o %#x\n", + isapnp_awe_list[i].name, *port); + return 0; + } + return -ENODEV; +} + +static void __exit awe_deactivate_isapnp(void) +{ +#if 1 + if (idev) { + idev->deactivate(idev); + idev = NULL; + } +#endif +} + +#endif + +static int __init +awe_detect(void) +{ + int base; + +#ifdef __ISAPNP__ + if (isapnp) { + if (awe_probe_isapnp(&io) < 0) { + printk(KERN_ERR "AWE32: No ISAPnP cards found\n"); + if (isapnp != -1) + return 0; + } else { + setup_ports(io, 0, 0); + return 1; + } + } +#endif /* isapnp */ + + if (io) /* use default i/o port value */ + setup_ports(io, 0, 0); + else { /* probe it */ + for (base = 0x620; base <= 0x680; base += 0x20) + if (awe_detect_base(base)) + return 1; + DEBUG(0,printk("AWE32 not found\n")); + return 0; + } + + return 1; +} + + +/* + * check dram size on AWE board + */ + +/* any three numbers you like */ +#define UNIQUE_ID1 0x1234 +#define UNIQUE_ID2 0x4321 +#define UNIQUE_ID3 0xABCD + +static void __init +awe_check_dram(void) +{ + if (awe_present) /* already initialized */ + return; + + if (memsize >= 0) { /* given by config file or module option */ + memsize *= 1024; /* convert to Kbytes */ + return; + } + + awe_open_dram_for_check(); + + memsize = 0; + + /* set up unique two id numbers */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET); + awe_poke(AWE_SMLD, UNIQUE_ID1); + awe_poke(AWE_SMLD, UNIQUE_ID2); + + while (memsize < AWE_MAX_DRAM_SIZE) { + awe_wait(5); + /* read a data on the DRAM start address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID1) + break; + if (awe_peek(AWE_SMLD) != UNIQUE_ID2) + break; + memsize += 512; /* increment 512kbytes */ + /* Write a unique data on the test address; + * if the address is out of range, the data is written on + * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are + * broken by this data. + */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + memsize*512L); + awe_poke(AWE_SMLD, UNIQUE_ID3); + awe_wait(5); + /* read a data on the just written DRAM address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + memsize*512L); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID3) + break; + } + awe_close_dram(); + + DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", memsize)); + + /* convert to Kbytes */ + memsize *= 1024; +} + + +/*----------------------------------------------------------------*/ + +/* + * chorus and reverb controls; from VV's guide + */ + +/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ +static char chorus_defined[AWE_CHORUS_NUMBERS]; +static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */ +}; + +static int +awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) { + printk(KERN_WARNING "AWE32 Error: invalid chorus mode %d for uploading\n", patch->optarg); + return -EINVAL; + } + if (count < sizeof(awe_chorus_fx_rec)) { + printk(KERN_WARNING "AWE32 Error: too short chorus fx parameters\n"); + return -EINVAL; + } + if (copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, + sizeof(awe_chorus_fx_rec))) + return -EFAULT; + chorus_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_chorus_mode(int effect) +{ + if (effect < 0 || effect >= AWE_CHORUS_NUMBERS || + (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback); + awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset); + awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth); + awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay); + awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_poke_dw(AWE_HWCF7, 0x0000); +} + +static void +awe_update_chorus_mode(void) +{ + awe_set_chorus_mode(ctrls[AWE_MD_CHORUS_MODE]); +} + +/*----------------------------------------------------------------*/ + +/* reverb mode settings; write the following 28 data of 16 bit length + * on the corresponding ports in the reverb_cmds array + */ +static char reverb_defined[AWE_CHORUS_NUMBERS]; +static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = { +{{ /* room 1 */ + 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, + 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 2 */ + 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 3 */ + 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, + 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, +}}, +{{ /* hall 1 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, + 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, +}}, +{{ /* hall 2 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, + 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, + 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* plate */ + 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, + 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* delay */ + 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +{{ /* panning delay */ + 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +}; + +static struct ReverbCmdPair { + unsigned short cmd, port; +} reverb_cmds[28] = { + {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, + {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, + {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, + {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, + {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, + {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, + {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, +}; + +static int +awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) { + printk(KERN_WARNING "AWE32 Error: invalid reverb mode %d for uploading\n", patch->optarg); + return -EINVAL; + } + if (count < sizeof(awe_reverb_fx_rec)) { + printk(KERN_WARNING "AWE32 Error: too short reverb fx parameters\n"); + return -EINVAL; + } + if (copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, + sizeof(awe_reverb_fx_rec))) + return -EFAULT; + reverb_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_reverb_mode(int effect) +{ + int i; + if (effect < 0 || effect >= AWE_REVERB_NUMBERS || + (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect])) + return; + for (i = 0; i < 28; i++) + awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port, + reverb_parm[effect].parms[i]); +} + +static void +awe_update_reverb_mode(void) +{ + awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]); +} + +/* + * treble/bass equalizer control + */ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC348, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +static void +awe_equalizer(int bass, int treble) +{ + unsigned short w; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]); + awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]); + awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]); + awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]); + awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]); + awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]); + awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]); + awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]); + awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]); + awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262)); + awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362)); +} + +static void awe_update_equalizer(void) +{ + awe_equalizer(ctrls[AWE_MD_BASS_LEVEL], ctrls[AWE_MD_TREBLE_LEVEL]); +} + + +/*----------------------------------------------------------------*/ + +#ifdef CONFIG_AWE32_MIDIEMU + +/* + * Emu8000 MIDI Emulation + */ + +/* + * midi queue record + */ + +/* queue type */ +enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, }; + +#define MAX_MIDIBUF 64 + +/* midi status */ +typedef struct MidiStatus { + int queue; /* queue type */ + int qlen; /* queue length */ + int read; /* chars read */ + int status; /* current status */ + int chan; /* current channel */ + unsigned char buf[MAX_MIDIBUF]; +} MidiStatus; + +/* MIDI mode type */ +enum { MODE_GM, MODE_GS, MODE_XG, }; + +/* NRPN / CC -> Emu8000 parameter converter */ +typedef struct { + int control; + int awe_effect; + unsigned short (*convert)(int val); +} ConvTable; + + +/* + * prototypes + */ + +static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int)); +static void awe_midi_close(int dev); +static int awe_midi_ioctl(int dev, unsigned cmd, caddr_t arg); +static int awe_midi_outputc(int dev, unsigned char midi_byte); + +static void init_midi_status(MidiStatus *st); +static void clear_rpn(void); +static void get_midi_char(MidiStatus *st, int c); +/*static void queue_varlen(MidiStatus *st, int c);*/ +static void special_event(MidiStatus *st, int c); +static void queue_read(MidiStatus *st, int c); +static void midi_note_on(MidiStatus *st); +static void midi_note_off(MidiStatus *st); +static void midi_key_pressure(MidiStatus *st); +static void midi_channel_pressure(MidiStatus *st); +static void midi_pitch_wheel(MidiStatus *st); +static void midi_program_change(MidiStatus *st); +static void midi_control_change(MidiStatus *st); +static void midi_select_bank(MidiStatus *st, int val); +static void midi_nrpn_event(MidiStatus *st); +static void midi_rpn_event(MidiStatus *st); +static void midi_detune(int chan, int coarse, int fine); +static void midi_system_exclusive(MidiStatus *st); +static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); +static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); +static int xg_control_change(MidiStatus *st, int cmd, int val); + +#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) + + +/* + * OSS Midi device record + */ + +static struct midi_operations awe_midi_operations = +{ + owner: THIS_MODULE, + info: {"AWE Midi Emu", 0, 0, SNDCARD_SB}, + in_info: {0}, + open: awe_midi_open, /*open*/ + close: awe_midi_close, /*close*/ + ioctl: awe_midi_ioctl, /*ioctl*/ + outputc: awe_midi_outputc, /*outputc*/ +}; + +static int my_mididev = -1; + +static void __init attach_midiemu(void) +{ + if ((my_mididev = sound_alloc_mididev()) < 0) + printk ("Sound: Too many midi devices detected\n"); + else + midi_devs[my_mididev] = &awe_midi_operations; +} + +static void __exit unload_midiemu(void) +{ + if (my_mididev >= 0) + sound_unload_mididev(my_mididev); +} + + +/* + * open/close midi device + */ + +static int midi_opened = FALSE; + +static int midi_mode; +static int coarsetune = 0, finetune = 0; + +static int xg_mapping = TRUE; +static int xg_bankmode = 0; + +/* effect sensitivity */ + +#define FX_CUTOFF 0 +#define FX_RESONANCE 1 +#define FX_ATTACK 2 +#define FX_RELEASE 3 +#define FX_VIBRATE 4 +#define FX_VIBDEPTH 5 +#define FX_VIBDELAY 6 +#define FX_NUMS 7 + +#define DEF_FX_CUTOFF 170 +#define DEF_FX_RESONANCE 6 +#define DEF_FX_ATTACK 50 +#define DEF_FX_RELEASE 50 +#define DEF_FX_VIBRATE 30 +#define DEF_FX_VIBDEPTH 4 +#define DEF_FX_VIBDELAY 1500 + +/* effect sense: */ +static int gs_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; +static int xg_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + + +/* current status */ +static MidiStatus curst; + + +static int +awe_midi_open (int dev, int mode, + void (*input)(int,unsigned char), + void (*output)(int)) +{ + if (midi_opened) + return -EBUSY; + + midi_opened = TRUE; + + midi_mode = MODE_GM; + + curst.queue = Q_NONE; + curst.qlen = 0; + curst.read = 0; + curst.status = 0; + curst.chan = 0; + memset(curst.buf, 0, sizeof(curst.buf)); + + init_midi_status(&curst); + + return 0; +} + +static void +awe_midi_close (int dev) +{ + midi_opened = FALSE; +} + + +static int +awe_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + return -EPERM; +} + +static int +awe_midi_outputc (int dev, unsigned char midi_byte) +{ + if (! midi_opened) + return 1; + + /* force to change playing mode */ + playing_mode = AWE_PLAY_MULTI; + + get_midi_char(&curst, midi_byte); + return 1; +} + + +/* + * initialize + */ + +static void init_midi_status(MidiStatus *st) +{ + clear_rpn(); + coarsetune = 0; + finetune = 0; +} + + +/* + * RPN & NRPN + */ + +#define MAX_MIDI_CHANNELS 16 + +/* RPN & NRPN */ +static unsigned char nrpn[MAX_MIDI_CHANNELS]; /* current event is NRPN? */ +static int msb_bit; /* current event is msb for RPN/NRPN */ +/* RPN & NRPN indeces */ +static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS]; +/* RPN & NRPN values */ +static int rpn_val[MAX_MIDI_CHANNELS]; + +static void clear_rpn(void) +{ + int i; + for (i = 0; i < MAX_MIDI_CHANNELS; i++) { + nrpn[i] = 0; + rpn_msb[i] = 127; + rpn_lsb[i] = 127; + rpn_val[i] = 0; + } + msb_bit = 0; +} + + +/* + * process midi queue + */ + +/* status event types */ +typedef void (*StatusEvent)(MidiStatus *st); +static struct StatusEventList { + StatusEvent process; + int qlen; +} status_event[8] = { + {midi_note_off, 2}, + {midi_note_on, 2}, + {midi_key_pressure, 2}, + {midi_control_change, 2}, + {midi_program_change, 1}, + {midi_channel_pressure, 1}, + {midi_pitch_wheel, 2}, + {NULL, 0}, +}; + + +/* read a char from fifo and process it */ +static void get_midi_char(MidiStatus *st, int c) +{ + if (c == 0xfe) { + /* ignore active sense */ + st->queue = Q_NONE; + return; + } + + switch (st->queue) { + /* case Q_VARLEN: queue_varlen(st, c); break;*/ + case Q_READ: + case Q_SYSEX: + queue_read(st, c); + break; + case Q_NONE: + st->read = 0; + if ((c & 0xf0) == 0xf0) { + special_event(st, c); + } else if (c & 0x80) { /* status change */ + st->status = (c >> 4) & 0x07; + st->chan = c & 0x0f; + st->queue = Q_READ; + st->qlen = status_event[st->status].qlen; + if (st->qlen == 0) + st->queue = Q_NONE; + } + break; + } +} + +/* 0xfx events */ +static void special_event(MidiStatus *st, int c) +{ + switch (c) { + case 0xf0: /* system exclusive */ + st->queue = Q_SYSEX; + st->qlen = 0; + break; + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* song select */ + st->queue = Q_READ; + st->qlen = 1; + break; + case 0xf2: /* song position */ + st->queue = Q_READ; + st->qlen = 2; + break; + } +} + +#if 0 +/* read variable length value */ +static void queue_varlen(MidiStatus *st, int c) +{ + st->qlen += (c & 0x7f); + if (c & 0x80) { + st->qlen <<= 7; + return; + } + if (st->qlen <= 0) { + st->qlen = 0; + st->queue = Q_NONE; + } + st->queue = Q_READ; + st->read = 0; +} +#endif + + +/* read a char */ +static void queue_read(MidiStatus *st, int c) +{ + if (st->read < MAX_MIDIBUF) { + if (st->queue != Q_SYSEX) + c &= 0x7f; + st->buf[st->read] = (unsigned char)c; + } + st->read++; + if (st->queue == Q_SYSEX && c == 0xf7) { + midi_system_exclusive(st); + st->queue = Q_NONE; + } else if (st->queue == Q_READ && st->read >= st->qlen) { + if (status_event[st->status].process) + status_event[st->status].process(st); + st->queue = Q_NONE; + } +} + + +/* + * status events + */ + +/* note on */ +static void midi_note_on(MidiStatus *st) +{ + DEBUG(2,printk("midi: note_on (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); + if (st->buf[1] == 0) + midi_note_off(st); + else + awe_start_note(0, st->chan, st->buf[0], st->buf[1]); +} + +/* note off */ +static void midi_note_off(MidiStatus *st) +{ + DEBUG(2,printk("midi: note_off (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); + awe_kill_note(0, st->chan, st->buf[0], st->buf[1]); +} + +/* key pressure change */ +static void midi_key_pressure(MidiStatus *st) +{ + awe_key_pressure(0, st->chan, st->buf[0], st->buf[1]); +} + +/* channel pressure change */ +static void midi_channel_pressure(MidiStatus *st) +{ + channels[st->chan].chan_press = st->buf[0]; + awe_modwheel_change(st->chan, st->buf[0]); +} + +/* pitch wheel change */ +static void midi_pitch_wheel(MidiStatus *st) +{ + int val = (int)st->buf[1] * 128 + st->buf[0]; + awe_bender(0, st->chan, val); +} + +/* program change */ +static void midi_program_change(MidiStatus *st) +{ + int preset; + preset = st->buf[0]; + if (midi_mode == MODE_GS && IS_DRUM_CHANNEL(st->chan) && preset == 127) + preset = 0; + else if (midi_mode == MODE_XG && xg_mapping && IS_DRUM_CHANNEL(st->chan)) + preset += 64; + + awe_set_instr(0, st->chan, preset); +} + +#define send_effect(chan,type,val) awe_send_effect(chan,-1,type,val) +#define add_effect(chan,type,val) awe_send_effect(chan,-1,(type)|0x80,val) +#define unset_effect(chan,type) awe_send_effect(chan,-1,(type)|0x40,0) + +/* midi control change */ +static void midi_control_change(MidiStatus *st) +{ + int cmd = st->buf[0]; + int val = st->buf[1]; + + DEBUG(2,printk("midi: control (%d) %d %d\n", st->chan, cmd, val)); + if (midi_mode == MODE_XG) { + if (xg_control_change(st, cmd, val)) + return; + } + + /* controls #31 - #64 are LSB of #0 - #31 */ + msb_bit = 1; + if (cmd >= 0x20 && cmd < 0x40) { + msb_bit = 0; + cmd -= 0x20; + } + + switch (cmd) { + case CTL_SOFT_PEDAL: + if (val == 127) + add_effect(st->chan, AWE_FX_CUTOFF, -160); + else + unset_effect(st->chan, AWE_FX_CUTOFF); + break; + + case CTL_BANK_SELECT: + midi_select_bank(st, val); + break; + + /* set RPN/NRPN parameter */ + case CTL_REGIST_PARM_NUM_MSB: + nrpn[st->chan]=0; rpn_msb[st->chan]=val; + break; + case CTL_REGIST_PARM_NUM_LSB: + nrpn[st->chan]=0; rpn_lsb[st->chan]=val; + break; + case CTL_NONREG_PARM_NUM_MSB: + nrpn[st->chan]=1; rpn_msb[st->chan]=val; + break; + case CTL_NONREG_PARM_NUM_LSB: + nrpn[st->chan]=1; rpn_lsb[st->chan]=val; + break; + + /* send RPN/NRPN entry */ + case CTL_DATA_ENTRY: + if (msb_bit) + rpn_val[st->chan] = val * 128; + else + rpn_val[st->chan] |= val; + if (nrpn[st->chan]) + midi_nrpn_event(st); + else + midi_rpn_event(st); + break; + + /* increase/decrease data entry */ + case CTL_DATA_INCREMENT: + rpn_val[st->chan]++; + midi_rpn_event(st); + break; + case CTL_DATA_DECREMENT: + rpn_val[st->chan]--; + midi_rpn_event(st); + break; + + /* default */ + default: + awe_controller(0, st->chan, cmd, val); + break; + } +} + +/* tone bank change */ +static void midi_select_bank(MidiStatus *st, int val) +{ + if (midi_mode == MODE_XG && msb_bit) { + xg_bankmode = val; + /* XG MSB value; not normal bank selection */ + switch (val) { + case 127: /* remap to drum channel */ + awe_controller(0, st->chan, CTL_BANK_SELECT, 128); + break; + default: /* remap to normal channel */ + awe_controller(0, st->chan, CTL_BANK_SELECT, val); + break; + } + return; + } else if (midi_mode == MODE_GS && !msb_bit) + /* ignore LSB bank in GS mode (used for mapping) */ + return; + + /* normal bank controls; accept both MSB and LSB */ + if (! IS_DRUM_CHANNEL(st->chan)) { + if (midi_mode == MODE_XG) { + if (xg_bankmode) return; + if (val == 64 || val == 126) + val = 0; + } else if (midi_mode == MODE_GS && val == 127) + val = 0; + awe_controller(0, st->chan, CTL_BANK_SELECT, val); + } +} + + +/* + * RPN events + */ + +static void midi_rpn_event(MidiStatus *st) +{ + int type; + type = (rpn_msb[st->chan]<<8) | rpn_lsb[st->chan]; + switch (type) { + case 0x0000: /* Pitch bend sensitivity */ + /* MSB only / 1 semitone per 128 */ + if (msb_bit) { + channels[st->chan].bender_range = + rpn_val[st->chan] * 100 / 128; + } + break; + + case 0x0001: /* fine tuning: */ + /* MSB/LSB, 8192=center, 100/8192 cent step */ + finetune = rpn_val[st->chan] - 8192; + midi_detune(st->chan, coarsetune, finetune); + break; + + case 0x0002: /* coarse tuning */ + /* MSB only / 8192=center, 1 semitone per 128 */ + if (msb_bit) { + coarsetune = rpn_val[st->chan] - 8192; + midi_detune(st->chan, coarsetune, finetune); + } + break; + + case 0x7F7F: /* "lock-in" RPN */ + break; + } +} + + +/* tuning: + * coarse = -8192 to 8192 (100 cent per 128) + * fine = -8192 to 8192 (max=100cent) + */ +static void midi_detune(int chan, int coarse, int fine) +{ + /* 4096 = 1200 cents in AWE parameter */ + int val; + val = coarse * 4096 / (12 * 128); + val += fine / 24; + if (val) + send_effect(chan, AWE_FX_INIT_PITCH, val); + else + unset_effect(chan, AWE_FX_INIT_PITCH); +} + + +/* + * system exclusive message + * GM/GS/XG macros are accepted + */ + +static void midi_system_exclusive(MidiStatus *st) +{ + /* GM on */ + static unsigned char gm_on_macro[] = { + 0x7e,0x7f,0x09,0x01, + }; + /* XG on */ + static unsigned char xg_on_macro[] = { + 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00, + }; + /* GS prefix + * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off + * reverb mode: XX=0x01, YY=0x30, ZZ=0-7 + * chorus mode: XX=0x01, YY=0x38, ZZ=0-7 + */ + static unsigned char gs_pfx_macro[] = { + 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/ + }; + +#if 0 + /* SC88 system mode set + * single module mode: XX=1 + * double module mode: XX=0 + */ + static unsigned char gs_mode_macro[] = { + 0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/ + }; + /* SC88 display macro: XX=01:bitmap, 00:text + */ + static unsigned char gs_disp_macro[] = { + 0x41,0x10,0x45,0x12,0x10,/*XX,00*/ + }; +#endif + + /* GM on */ + if (memcmp(st->buf, gm_on_macro, sizeof(gm_on_macro)) == 0) { + if (midi_mode != MODE_GS && midi_mode != MODE_XG) + midi_mode = MODE_GM; + init_midi_status(st); + } + + /* GS macros */ + else if (memcmp(st->buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) { + if (midi_mode != MODE_GS && midi_mode != MODE_XG) + midi_mode = MODE_GS; + + if (st->buf[5] == 0x00 && st->buf[6] == 0x7f && st->buf[7] == 0x00) { + /* GS reset */ + init_midi_status(st); + } + + else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x15) { + /* drum pattern */ + int p = st->buf[5] & 0x0f; + if (p == 0) p = 9; + else if (p < 10) p--; + if (st->buf[7] == 0) + DRUM_CHANNEL_OFF(p); + else + DRUM_CHANNEL_ON(p); + + } else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x21) { + /* program */ + int p = st->buf[5] & 0x0f; + if (p == 0) p = 9; + else if (p < 10) p--; + if (! IS_DRUM_CHANNEL(p)) + awe_set_instr(0, p, st->buf[7]); + + } else if (st->buf[5] == 0x01 && st->buf[6] == 0x30) { + /* reverb mode */ + awe_set_reverb_mode(st->buf[7]); + + } else if (st->buf[5] == 0x01 && st->buf[6] == 0x38) { + /* chorus mode */ + awe_set_chorus_mode(st->buf[7]); + + } else if (st->buf[5] == 0x00 && st->buf[6] == 0x04) { + /* master volume */ + awe_change_master_volume(st->buf[7]); + + } + } + + /* XG on */ + else if (memcmp(st->buf, xg_on_macro, sizeof(xg_on_macro)) == 0) { + midi_mode = MODE_XG; + xg_mapping = TRUE; + xg_bankmode = 0; + } +} + + +/*----------------------------------------------------------------*/ + +/* + * convert NRPN/control values + */ + +static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + send_effect(st->chan, table[i].awe_effect, cval); + return TRUE; + } + } + return FALSE; +} + +static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + add_effect(st->chan, table[i].awe_effect|0x80, cval); + return TRUE; + } + } + return FALSE; +} + + +/* + * AWE32 NRPN effects + */ + +static unsigned short fx_delay(int val); +static unsigned short fx_attack(int val); +static unsigned short fx_hold(int val); +static unsigned short fx_decay(int val); +static unsigned short fx_the_value(int val); +static unsigned short fx_twice_value(int val); +static unsigned short fx_conv_pitch(int val); +static unsigned short fx_conv_Q(int val); + +/* function for each NRPN */ /* [range] units */ +#define fx_env1_delay fx_delay /* [0,5900] 4msec */ +#define fx_env1_attack fx_attack /* [0,5940] 1msec */ +#define fx_env1_hold fx_hold /* [0,8191] 1msec */ +#define fx_env1_decay fx_decay /* [0,5940] 4msec */ +#define fx_env1_release fx_decay /* [0,5940] 4msec */ +#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ +#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ + +#define fx_env2_delay fx_delay /* [0,5900] 4msec */ +#define fx_env2_attack fx_attack /* [0,5940] 1msec */ +#define fx_env2_hold fx_hold /* [0,8191] 1msec */ +#define fx_env2_decay fx_decay /* [0,5940] 4msec */ +#define fx_env2_release fx_decay /* [0,5940] 4msec */ +#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ + +#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ +#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ + +#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ + +#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ +#define fx_chorus fx_the_value /* [0,255] -- */ +#define fx_reverb fx_the_value /* [0,255] -- */ +#define fx_cutoff fx_twice_value /* [0,127] 62Hz */ +#define fx_filterQ fx_conv_Q /* [0,127] -- */ + +static unsigned short fx_delay(int val) +{ + return (unsigned short)calc_parm_delay(val); +} + +static unsigned short fx_attack(int val) +{ + return (unsigned short)calc_parm_attack(val); +} + +static unsigned short fx_hold(int val) +{ + return (unsigned short)calc_parm_hold(val); +} + +static unsigned short fx_decay(int val) +{ + return (unsigned short)calc_parm_decay(val); +} + +static unsigned short fx_the_value(int val) +{ + return (unsigned short)(val & 0xff); +} + +static unsigned short fx_twice_value(int val) +{ + return (unsigned short)((val * 2) & 0xff); +} + +static unsigned short fx_conv_pitch(int val) +{ + return (short)(val * 4096 / 1200); +} + +static unsigned short fx_conv_Q(int val) +{ + return (unsigned short)((val / 8) & 0xff); +} + + +static ConvTable awe_effects[] = +{ + { 0, AWE_FX_LFO1_DELAY, fx_lfo1_delay}, + { 1, AWE_FX_LFO1_FREQ, fx_lfo1_freq}, + { 2, AWE_FX_LFO2_DELAY, fx_lfo2_delay}, + { 3, AWE_FX_LFO2_FREQ, fx_lfo2_freq}, + + { 4, AWE_FX_ENV1_DELAY, fx_env1_delay}, + { 5, AWE_FX_ENV1_ATTACK,fx_env1_attack}, + { 6, AWE_FX_ENV1_HOLD, fx_env1_hold}, + { 7, AWE_FX_ENV1_DECAY, fx_env1_decay}, + { 8, AWE_FX_ENV1_SUSTAIN, fx_env1_sustain}, + { 9, AWE_FX_ENV1_RELEASE, fx_env1_release}, + + {10, AWE_FX_ENV2_DELAY, fx_env2_delay}, + {11, AWE_FX_ENV2_ATTACK, fx_env2_attack}, + {12, AWE_FX_ENV2_HOLD, fx_env2_hold}, + {13, AWE_FX_ENV2_DECAY, fx_env2_decay}, + {14, AWE_FX_ENV2_SUSTAIN, fx_env2_sustain}, + {15, AWE_FX_ENV2_RELEASE, fx_env2_release}, + + {16, AWE_FX_INIT_PITCH, fx_init_pitch}, + {17, AWE_FX_LFO1_PITCH, fx_lfo1_pitch}, + {18, AWE_FX_LFO2_PITCH, fx_lfo2_pitch}, + {19, AWE_FX_ENV1_PITCH, fx_env1_pitch}, + {20, AWE_FX_LFO1_VOLUME, fx_lfo1_volume}, + {21, AWE_FX_CUTOFF, fx_cutoff}, + {22, AWE_FX_FILTERQ, fx_filterQ}, + {23, AWE_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, + {24, AWE_FX_ENV1_CUTOFF, fx_env1_cutoff}, + {25, AWE_FX_CHORUS, fx_chorus}, + {26, AWE_FX_REVERB, fx_reverb}, +}; + +static int num_awe_effects = numberof(awe_effects); + + +/* + * GS(SC88) NRPN effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static unsigned short gs_cutoff(int val) +{ + return (val - 64) * gs_sense[FX_CUTOFF] / 50; +} + +/* resonance: 0 to 15(max) */ +static unsigned short gs_filterQ(int val) +{ + return (val - 64) * gs_sense[FX_RESONANCE] / 50; +} + +/* attack: */ +static unsigned short gs_attack(int val) +{ + return -(val - 64) * gs_sense[FX_ATTACK] / 50; +} + +/* decay: */ +static unsigned short gs_decay(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* release: */ +static unsigned short gs_release(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* vibrato freq: 0.042Hz step, max=255 */ +static unsigned short gs_vib_rate(int val) +{ + return (val - 64) * gs_sense[FX_VIBRATE] / 50; +} + +/* vibrato depth: max=127, 1 octave */ +static unsigned short gs_vib_depth(int val) +{ + return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; +} + +/* vibrato delay: -0.725msec step */ +static unsigned short gs_vib_delay(int val) +{ + return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; +} + +static ConvTable gs_effects[] = +{ + {32, AWE_FX_CUTOFF, gs_cutoff}, + {33, AWE_FX_FILTERQ, gs_filterQ}, + {99, AWE_FX_ENV2_ATTACK, gs_attack}, + {100, AWE_FX_ENV2_DECAY, gs_decay}, + {102, AWE_FX_ENV2_RELEASE, gs_release}, + {8, AWE_FX_LFO1_FREQ, gs_vib_rate}, + {9, AWE_FX_LFO1_VOLUME, gs_vib_depth}, + {10, AWE_FX_LFO1_DELAY, gs_vib_delay}, +}; + +static int num_gs_effects = numberof(gs_effects); + + +/* + * NRPN events: accept as AWE32/SC88 specific controls + */ + +static void midi_nrpn_event(MidiStatus *st) +{ + if (rpn_msb[st->chan] == 127 && rpn_lsb[st->chan] <= 26) { + if (! msb_bit) /* both MSB/LSB necessary */ + send_converted_effect(awe_effects, num_awe_effects, + st, rpn_lsb[st->chan], + rpn_val[st->chan] - 8192); + } else if (rpn_msb[st->chan] == 1) { + if (msb_bit) /* only MSB is valid */ + add_converted_effect(gs_effects, num_gs_effects, + st, rpn_lsb[st->chan], + rpn_val[st->chan] / 128); + } +} + + +/* + * XG control effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static unsigned short xg_cutoff(int val) +{ + return (val - 64) * xg_sense[FX_CUTOFF] / 64; +} + +/* resonance: 0(open) to 15(most nasal) */ +static unsigned short xg_filterQ(int val) +{ + return (val - 64) * xg_sense[FX_RESONANCE] / 64; +} + +/* attack: */ +static unsigned short xg_attack(int val) +{ + return -(val - 64) * xg_sense[FX_ATTACK] / 64; +} + +/* release: */ +static unsigned short xg_release(int val) +{ + return -(val - 64) * xg_sense[FX_RELEASE] / 64; +} + +static ConvTable xg_effects[] = +{ + {71, AWE_FX_CUTOFF, xg_cutoff}, + {74, AWE_FX_FILTERQ, xg_filterQ}, + {72, AWE_FX_ENV2_RELEASE, xg_release}, + {73, AWE_FX_ENV2_ATTACK, xg_attack}, +}; + +static int num_xg_effects = numberof(xg_effects); + +static int xg_control_change(MidiStatus *st, int cmd, int val) +{ + return add_converted_effect(xg_effects, num_xg_effects, st, cmd, val); +} + +#endif /* CONFIG_AWE32_MIDIEMU */ + + +/*----------------------------------------------------------------*/ + +/* + * device / lowlevel (module) interface + */ + +int __init attach_awe(void) +{ + return _attach_awe() ? 0 : -ENODEV; +} + +void __exit unload_awe(void) +{ + _unload_awe(); +#ifdef __ISAPNP__ + if (isapnp) + awe_deactivate_isapnp(); +#endif /* isapnp */ +} + + +module_init(attach_awe); +module_exit(unload_awe); + +#ifndef MODULE +static int __init setup_awe(char *str) +{ + /* io, memsize, isapnp */ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + memsize = ints[2]; + isapnp = ints[3]; + + return 1; +} + +__setup("awe=", setup_awe); +#endif diff -Nru linux/sound/oss/awe_wave.h linux-2.4.19-pre5-mjc/sound/oss/awe_wave.h --- linux/sound/oss/awe_wave.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/awe_wave.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,77 @@ +/* + * sound/awe_config.h + * + * Configuration of AWE32/SB32/AWE64 wave table synth driver. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-1998 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * chorus & reverb effects send for FM chip: from 0 to 0xff + * larger numbers often cause weird sounds. + */ + +#define DEF_FM_CHORUS_DEPTH 0x10 +#define DEF_FM_REVERB_DEPTH 0x10 + + +/* + * other compile conditions + */ + +/* initialize FM passthrough even without extended RAM */ +#undef AWE_ALWAYS_INIT_FM + +/* debug on */ +#define AWE_DEBUG_ON + +/* GUS compatible mode */ +#define AWE_HAS_GUS_COMPATIBILITY + +/* add MIDI emulation by wavetable */ +#define CONFIG_AWE32_MIDIEMU + +/* add mixer control of emu8000 equalizer */ +#undef CONFIG_AWE32_MIXER + +/* use new volume calculation method as default */ +#define AWE_USE_NEW_VOLUME_CALC + +/* check current volume target for searching empty voices */ +#define AWE_CHECK_VTARGET + +/* allow sample sharing */ +#define AWE_ALLOW_SAMPLE_SHARING + +/* + * AWE32 card configuration: + * uncomment the following lines *ONLY* when auto detection doesn't + * work properly on your machine. + */ + +/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */ +/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */ + +/* + * AWE driver version number + */ +#define AWE_MAJOR_VERSION 0 +#define AWE_MINOR_VERSION 4 +#define AWE_TINY_VERSION 4 +#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION) +#define AWEDRV_VERSION "0.4.4" diff -Nru linux/sound/oss/bin2hex.c linux-2.4.19-pre5-mjc/sound/oss/bin2hex.c --- linux/sound/oss/bin2hex.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/bin2hex.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,38 @@ +#include +#include + +int main( int argc, const char * argv [] ) +{ + const char * varname; + int i = 0; + int c; + int id = 0; + + if(argv[1] && strcmp(argv[1],"-i")==0) + { + argv++; + argc--; + id=1; + } + + if(argc==1) + { + fprintf(stderr, "bin2hex: [-i] firmware\n"); + exit(1); + } + + varname = argv[1]; + printf( "/* automatically generated by bin2hex */\n" ); + printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":""); + + while ( ( c = getchar( ) ) != EOF ) + { + if ( i != 0 && i % 10 == 0 ) + printf( "\n" ); + printf( "0x%02lx,", c & 0xFFl ); + i++; + } + + printf( "};\nstatic int %sLen = %d;\n", varname, i ); + return 0; +} diff -Nru linux/sound/oss/btaudio.c linux-2.4.19-pre5-mjc/sound/oss/btaudio.c --- linux/sound/oss/btaudio.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/btaudio.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1072 @@ +/* + btaudio - bt878 audio dma driver for linux 2.4.x + + (c) 2000 Gerd Knorr + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mmio access */ +#define btwrite(dat,adr) writel((dat), (bta->mmio+(adr))) +#define btread(adr) readl(bta->mmio+(adr)) + +#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) + +/* registers (shifted because bta->mmio is long) */ +#define REG_INT_STAT (0x100 >> 2) +#define REG_INT_MASK (0x104 >> 2) +#define REG_GPIO_DMA_CTL (0x10c >> 2) +#define REG_PACKET_LEN (0x110 >> 2) +#define REG_RISC_STRT_ADD (0x114 >> 2) +#define REG_RISC_COUNT (0x120 >> 2) + +/* IRQ bits - REG_INT_(STAT|MASK) */ +#define IRQ_SCERR (1 << 19) +#define IRQ_OCERR (1 << 18) +#define IRQ_PABORT (1 << 17) +#define IRQ_RIPERR (1 << 16) +#define IRQ_PPERR (1 << 15) +#define IRQ_FDSR (1 << 14) +#define IRQ_FTRGT (1 << 13) +#define IRQ_FBUS (1 << 12) +#define IRQ_RISCI (1 << 11) +#define IRQ_OFLOW (1 << 3) + +#define IRQ_BTAUDIO (IRQ_SCERR | IRQ_OCERR | IRQ_PABORT | IRQ_RIPERR |\ + IRQ_PPERR | IRQ_FDSR | IRQ_FTRGT | IRQ_FBUS |\ + IRQ_RISCI) + +/* REG_GPIO_DMA_CTL bits */ +#define DMA_CTL_A_PWRDN (1 << 26) +#define DMA_CTL_DA_SBR (1 << 14) +#define DMA_CTL_DA_ES2 (1 << 13) +#define DMA_CTL_ACAP_EN (1 << 4) +#define DMA_CTL_RISC_EN (1 << 1) +#define DMA_CTL_FIFO_EN (1 << 0) + +/* RISC instructions */ +#define RISC_WRITE (0x01 << 28) +#define RISC_JUMP (0x07 << 28) +#define RISC_SYNC (0x08 << 28) + +/* RISC bits */ +#define RISC_WR_SOL (1 << 27) +#define RISC_WR_EOL (1 << 26) +#define RISC_IRQ (1 << 24) +#define RISC_SYNC_RESYNC (1 << 15) +#define RISC_SYNC_FM1 0x06 +#define RISC_SYNC_VRO 0x0c + +#define HWBASE_AD (448000) + +/* -------------------------------------------------------------- */ + +struct btaudio { + /* linked list */ + struct btaudio *next; + + /* device info */ + int dsp_digital; + int dsp_analog; + int mixer_dev; + struct pci_dev *pci; + unsigned int irq; + unsigned long mem; + unsigned long *mmio; + + /* locking */ + int users; + struct semaphore lock; + + /* risc instructions */ + unsigned int risc_size; + unsigned long *risc_cpu; + dma_addr_t risc_dma; + + /* audio data */ + unsigned int buf_size; + unsigned char *buf_cpu; + dma_addr_t buf_dma; + + /* buffer setup */ + int line_bytes; + int line_count; + int block_bytes; + int block_count; + + /* read fifo management */ + int recording; + int dma_block; + int read_offset; + int read_count; + wait_queue_head_t readq; + + /* settings */ + int gain[3]; + int source; + int bits; + int decimation; + int mixcount; + int sampleshift; + int channels; + int analog; +}; + +static struct btaudio *btaudios = NULL; +static unsigned int dsp1 = -1; +static unsigned int dsp2 = -1; +static unsigned int mixer = -1; +static unsigned int debug = 0; +static unsigned int irq_debug = 0; +static int digital = 1; +static int analog = 1; +static int rate = 32000; + +/* -------------------------------------------------------------- */ + +#define BUF_DEFAULT 128*1024 +#define BUF_MIN 8192 + +static int alloc_buffer(struct btaudio *bta) +{ + if (NULL == bta->buf_cpu) { + for (bta->buf_size = BUF_DEFAULT; bta->buf_size >= BUF_MIN; + bta->buf_size = bta->buf_size >> 1) { + bta->buf_cpu = pci_alloc_consistent + (bta->pci, bta->buf_size, &bta->buf_dma); + if (NULL != bta->buf_cpu) + break; + } + if (NULL == bta->buf_cpu) + return -ENOMEM; + memset(bta->buf_cpu,0,bta->buf_size); + } + if (NULL == bta->risc_cpu) { + bta->risc_size = PAGE_SIZE; + bta->risc_cpu = pci_alloc_consistent + (bta->pci, bta->risc_size, &bta->risc_dma); + if (NULL == bta->risc_cpu) + return -ENOMEM; + } + return 0; +} + +static void free_buffer(struct btaudio *bta) +{ + if (NULL != bta->buf_cpu) { + pci_free_consistent(bta->pci, bta->buf_size, + bta->buf_cpu, bta->buf_dma); + bta->buf_cpu = NULL; + } + if (NULL != bta->risc_cpu) { + pci_free_consistent(bta->pci, bta->risc_size, + bta->risc_cpu, bta->risc_dma); + bta->risc_cpu = NULL; + } +} + +static int make_risc(struct btaudio *bta) +{ + int rp, bp, line, block; + unsigned long risc; + + bta->block_bytes = bta->buf_size >> 4; + bta->block_count = 1 << 4; + bta->line_bytes = bta->block_bytes; + bta->line_count = bta->block_count; + while (bta->line_bytes > 4095) { + bta->line_bytes >>= 1; + bta->line_count <<= 1; + } + if (bta->line_count > 255) + return -EINVAL; + if (debug) + printk(KERN_DEBUG + "btaudio: bufsize=%d - bs=%d bc=%d - ls=%d, lc=%d\n", + bta->buf_size,bta->block_bytes,bta->block_count, + bta->line_bytes,bta->line_count); + rp = 0; bp = 0; + block = 0; + bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_FM1); + bta->risc_cpu[rp++] = cpu_to_le32(0); + for (line = 0; line < bta->line_count; line++) { + risc = RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL; + risc |= bta->line_bytes; + if (0 == (bp & (bta->block_bytes-1))) { + risc |= RISC_IRQ; + risc |= (block & 0x0f) << 16; + risc |= (~block & 0x0f) << 20; + block++; + } + bta->risc_cpu[rp++] = cpu_to_le32(risc); + bta->risc_cpu[rp++] = cpu_to_le32(bta->buf_dma + bp); + bp += bta->line_bytes; + } + bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_VRO); + bta->risc_cpu[rp++] = cpu_to_le32(0); + bta->risc_cpu[rp++] = cpu_to_le32(RISC_JUMP); + bta->risc_cpu[rp++] = cpu_to_le32(bta->risc_dma); + return 0; +} + +static int start_recording(struct btaudio *bta) +{ + int ret; + + if (0 != (ret = alloc_buffer(bta))) + return ret; + if (0 != (ret = make_risc(bta))) + return ret; + + btwrite(bta->risc_dma, REG_RISC_STRT_ADD); + btwrite((bta->line_count << 16) | bta->line_bytes, + REG_PACKET_LEN); + btwrite(IRQ_BTAUDIO, REG_INT_MASK); + if (bta->analog) { + btwrite(DMA_CTL_ACAP_EN | + DMA_CTL_RISC_EN | + DMA_CTL_FIFO_EN | + DMA_CTL_DA_ES2 | + ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | + (bta->gain[bta->source] << 28) | + (bta->source << 24) | + (bta->decimation << 8), + REG_GPIO_DMA_CTL); + } else { + btwrite(DMA_CTL_ACAP_EN | + DMA_CTL_RISC_EN | + DMA_CTL_FIFO_EN | + DMA_CTL_DA_ES2 | + DMA_CTL_A_PWRDN | + (1 << 6) | + ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | + (bta->gain[bta->source] << 28) | + (bta->source << 24) | + (bta->decimation << 8), + REG_GPIO_DMA_CTL); + } + bta->dma_block = 0; + bta->read_offset = 0; + bta->read_count = 0; + bta->recording = 1; + if (debug) + printk(KERN_DEBUG "btaudio: recording started\n"); + return 0; +} + +static void stop_recording(struct btaudio *bta) +{ + btand(~15, REG_GPIO_DMA_CTL); + bta->recording = 0; + if (debug) + printk(KERN_DEBUG "btaudio: recording stopped\n"); +} + + +/* -------------------------------------------------------------- */ + +static int btaudio_mixer_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct btaudio *bta; + + for (bta = btaudios; bta != NULL; bta = bta->next) + if (bta->mixer_dev == minor) + break; + if (NULL == bta) + return -ENODEV; + + if (debug) + printk("btaudio: open mixer [%d]\n",minor); + file->private_data = bta; + return 0; +} + +static int btaudio_mixer_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int btaudio_mixer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct btaudio *bta = file->private_data; + int ret,val=0,i=0; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id,"bt878",sizeof(info.id)-1); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1); + info.modify_counter = bta->mixcount; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + memset(&info,0,sizeof(info)); + strncpy(info.id,"bt878",sizeof(info.id)-1); + strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)-1); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + /* read */ + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (cmd) { + case MIXER_READ(SOUND_MIXER_CAPS): + ret = SOUND_CAP_EXCL_INPUT; + break; + case MIXER_READ(SOUND_MIXER_STEREODEVS): + ret = 0; + break; + case MIXER_READ(SOUND_MIXER_RECMASK): + case MIXER_READ(SOUND_MIXER_DEVMASK): + ret = SOUND_MASK_LINE1|SOUND_MASK_LINE2|SOUND_MASK_LINE3; + break; + + case MIXER_WRITE(SOUND_MIXER_RECSRC): + if (val & SOUND_MASK_LINE1 && bta->source != 0) + bta->source = 0; + else if (val & SOUND_MASK_LINE2 && bta->source != 1) + bta->source = 1; + else if (val & SOUND_MASK_LINE3 && bta->source != 2) + bta->source = 2; + btaor((bta->gain[bta->source] << 28) | + (bta->source << 24), + 0x0cffffff, REG_GPIO_DMA_CTL); + case MIXER_READ(SOUND_MIXER_RECSRC): + switch (bta->source) { + case 0: ret = SOUND_MASK_LINE1; break; + case 1: ret = SOUND_MASK_LINE2; break; + case 2: ret = SOUND_MASK_LINE3; break; + default: ret = 0; + } + break; + + case MIXER_WRITE(SOUND_MIXER_LINE1): + case MIXER_WRITE(SOUND_MIXER_LINE2): + case MIXER_WRITE(SOUND_MIXER_LINE3): + if (MIXER_WRITE(SOUND_MIXER_LINE1) == cmd) + i = 0; + if (MIXER_WRITE(SOUND_MIXER_LINE2) == cmd) + i = 1; + if (MIXER_WRITE(SOUND_MIXER_LINE3) == cmd) + i = 2; + bta->gain[i] = (val & 0xff) * 15 / 100; + if (bta->gain[i] > 15) bta->gain[i] = 15; + if (bta->gain[i] < 0) bta->gain[i] = 0; + if (i == bta->source) + btaor((bta->gain[bta->source]<<28), + 0x0fffffff, REG_GPIO_DMA_CTL); + ret = bta->gain[i] * 100 / 15; + ret |= ret << 8; + break; + + case MIXER_READ(SOUND_MIXER_LINE1): + case MIXER_READ(SOUND_MIXER_LINE2): + case MIXER_READ(SOUND_MIXER_LINE3): + if (MIXER_READ(SOUND_MIXER_LINE1) == cmd) + i = 0; + if (MIXER_READ(SOUND_MIXER_LINE2) == cmd) + i = 1; + if (MIXER_READ(SOUND_MIXER_LINE3) == cmd) + i = 2; + ret = bta->gain[i] * 100 / 15; + ret |= ret << 8; + break; + + default: + return -EINVAL; + } + if (put_user(ret, (int *)arg)) + return -EFAULT; + return 0; +} + +static struct file_operations btaudio_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + open: btaudio_mixer_open, + release: btaudio_mixer_release, + ioctl: btaudio_mixer_ioctl, +}; + +/* -------------------------------------------------------------- */ + +static int btaudio_dsp_open(struct inode *inode, struct file *file, + struct btaudio *bta, int analog) +{ + down(&bta->lock); + if (bta->users) + goto busy; + bta->users++; + file->private_data = bta; + + bta->analog = analog; + bta->dma_block = 0; + bta->read_offset = 0; + bta->read_count = 0; + bta->sampleshift = 0; + + up(&bta->lock); + return 0; + + busy: + up(&bta->lock); + return -EBUSY; +} + +static int btaudio_dsp_open_digital(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct btaudio *bta; + + for (bta = btaudios; bta != NULL; bta = bta->next) + if (bta->dsp_digital == minor) + break; + if (NULL == bta) + return -ENODEV; + + if (debug) + printk("btaudio: open digital dsp [%d]\n",minor); + return btaudio_dsp_open(inode,file,bta,0); +} + +static int btaudio_dsp_open_analog(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct btaudio *bta; + + for (bta = btaudios; bta != NULL; bta = bta->next) + if (bta->dsp_analog == minor) + break; + if (NULL == bta) + return -ENODEV; + + if (debug) + printk("btaudio: open analog dsp [%d]\n",minor); + return btaudio_dsp_open(inode,file,bta,1); +} + +static int btaudio_dsp_release(struct inode *inode, struct file *file) +{ + struct btaudio *bta = file->private_data; + + down(&bta->lock); + if (bta->recording) + stop_recording(bta); + bta->users--; + up(&bta->lock); + return 0; +} + +static ssize_t btaudio_dsp_read(struct file *file, char *buffer, + size_t swcount, loff_t *ppos) +{ + struct btaudio *bta = file->private_data; + int hwcount = swcount << bta->sampleshift; + int nsrc, ndst, err, ret = 0; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&bta->readq, &wait); + down(&bta->lock); + while (swcount > 0) { + if (0 == bta->read_count) { + if (!bta->recording) { + if (0 != (err = start_recording(bta))) { + if (0 == ret) + ret = err; + break; + } + } + if (file->f_flags & O_NONBLOCK) { + if (0 == ret) + ret = -EAGAIN; + break; + } + up(&bta->lock); + current->state = TASK_INTERRUPTIBLE; + schedule(); + down(&bta->lock); + if(signal_pending(current)) { + if (0 == ret) + ret = -EINTR; + break; + } + } + nsrc = (bta->read_count < hwcount) ? bta->read_count : hwcount; + if (nsrc > bta->buf_size - bta->read_offset) + nsrc = bta->buf_size - bta->read_offset; + ndst = nsrc >> bta->sampleshift; + + if ((bta->analog && 0 == bta->sampleshift) || + (!bta->analog && 2 == bta->channels)) { + /* just copy */ + if (copy_to_user(buffer + ret, bta->buf_cpu + bta->read_offset, nsrc)) { + if (0 == ret) + ret = -EFAULT; + break; + } + + } else if (!bta->analog) { + /* stereo => mono (digital audio) */ + __s16 *src = (__s16*)(bta->buf_cpu + bta->read_offset); + __s16 *dst = (__s16*)(buffer + ret); + __s16 avg; + int n = ndst>>1; + if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { + if (0 == ret) + ret = -EFAULT; + break; + } + for (; n; n--, dst++) { + avg = (__s16)le16_to_cpu(*src) / 2; src++; + avg += (__s16)le16_to_cpu(*src) / 2; src++; + __put_user(cpu_to_le16(avg),(__u16*)(dst)); + } + + } else if (8 == bta->bits) { + /* copy + byte downsampling (audio A/D) */ + __u8 *src = bta->buf_cpu + bta->read_offset; + __u8 *dst = buffer + ret; + int n = ndst; + if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { + if (0 == ret) + ret = -EFAULT; + break; + } + for (; n; n--, src += (1 << bta->sampleshift), dst++) + __put_user(*src,(__u8*)(dst)); + + } else { + /* copy + word downsampling (audio A/D) */ + __u16 *src = (__u16*)(bta->buf_cpu + bta->read_offset); + __u16 *dst = (__u16*)(buffer + ret); + int n = ndst>>1; + if (0 != verify_area(VERIFY_WRITE,dst,ndst)) { + if (0 == ret) + ret = -EFAULT; + break; + } + for (; n; n--, src += (1 << bta->sampleshift), dst++) + __put_user(*src,(__u16*)(dst)); + } + + ret += ndst; + swcount -= ndst; + hwcount -= nsrc; + bta->read_count -= nsrc; + bta->read_offset += nsrc; + if (bta->read_offset == bta->buf_size) + bta->read_offset = 0; + } + up(&bta->lock); + remove_wait_queue(&bta->readq, &wait); + current->state = TASK_RUNNING; + return ret; +} + +static ssize_t btaudio_dsp_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static int btaudio_dsp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct btaudio *bta = file->private_data; + int s, i, ret, val = 0; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_GETCAPS: + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int*)arg)) + return -EFAULT; + if (bta->analog) { + for (s = 0; s < 16; s++) + if (val << s >= HWBASE_AD*4/15) + break; + for (i = 15; i >= 5; i--) + if (val << s <= HWBASE_AD*4/i) + break; + bta->sampleshift = s; + bta->decimation = i; + if (debug) + printk(KERN_DEBUG "btaudio: rate: req=%d " + "dec=%d shift=%d hwrate=%d swrate=%d\n", + val,i,s,(HWBASE_AD*4/i),(HWBASE_AD*4/i)>>s); + } else { + bta->sampleshift = (bta->channels == 2) ? 0 : 1; + bta->decimation = 0; + } + if (bta->recording) { + down(&bta->lock); + stop_recording(bta); + start_recording(bta); + up(&bta->lock); + } + /* fall through */ + case SOUND_PCM_READ_RATE: + if (bta->analog) { + return put_user(HWBASE_AD*4/bta->decimation>>bta->sampleshift, (int*)arg); + } else { + return put_user(rate, (int*)arg); + } + + case SNDCTL_DSP_STEREO: + if (!bta->analog) { + if (get_user(val, (int*)arg)) + return -EFAULT; + bta->channels = (val > 0) ? 2 : 1; + bta->sampleshift = (bta->channels == 2) ? 0 : 1; + if (debug) + printk(KERN_INFO + "btaudio: stereo=%d channels=%d\n", + val,bta->channels); + } else { + if (val == 1) + return -EFAULT; + else { + bta->channels = 1; + if (debug) + printk(KERN_INFO + "btaudio: stereo=0 channels=1\n"); + } + } + return put_user((bta->channels)-1, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (!analog) { + if (get_user(val, (int*)arg)) + return -EFAULT; + bta->channels = (val > 1) ? 2 : 1; + bta->sampleshift = (bta->channels == 2) ? 0 : 1; + if (debug) + printk(KERN_DEBUG + "btaudio: val=%d channels=%d\n", + val,bta->channels); + } + /* fall through */ + case SOUND_PCM_READ_CHANNELS: + return put_user(bta->channels, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + if (analog) + return put_user(AFMT_S16_LE|AFMT_S8, (int*)arg); + else + return put_user(AFMT_S16_LE, (int*)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int*)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (analog) + bta->bits = (val == AFMT_S8) ? 8 : 16; + else + bta->bits = 16; + if (bta->recording) { + down(&bta->lock); + stop_recording(bta); + start_recording(bta); + up(&bta->lock); + } + } + if (debug) + printk(KERN_DEBUG "btaudio: fmt: bits=%d\n",bta->bits); + return put_user((bta->bits==16) ? AFMT_S16_LE : AFMT_S8, + (int*)arg); + break; + case SOUND_PCM_READ_BITS: + return put_user(bta->bits, (int*)arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (bta->recording) { + down(&bta->lock); + stop_recording(bta); + up(&bta->lock); + } + return 0; + case SNDCTL_DSP_GETBLKSIZE: + if (!bta->recording) { + if (0 != (ret = alloc_buffer(bta))) + return ret; + if (0 != (ret = make_risc(bta))) + return ret; + } + return put_user(bta->block_bytes>>bta->sampleshift,(int*)arg); + + case SNDCTL_DSP_SYNC: + /* NOP */ + return 0; + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info info; + if (!bta->recording) + return -EINVAL; + info.fragsize = bta->block_bytes>>bta->sampleshift; + info.fragstotal = bta->block_count; + info.bytes = bta->read_count; + info.fragments = info.bytes / info.fragsize; + if (debug) + printk(KERN_DEBUG "btaudio: SNDCTL_DSP_GETISPACE " + "returns %d/%d/%d/%d\n", + info.fragsize, info.fragstotal, + info.bytes, info.fragments); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#if 0 /* TODO */ + case SNDCTL_DSP_GETTRIGGER: + case SNDCTL_DSP_SETTRIGGER: + case SNDCTL_DSP_SETFRAGMENT: +#endif + default: + return -EINVAL; + } +} + +static unsigned int btaudio_dsp_poll(struct file *file, struct poll_table_struct *wait) +{ + struct btaudio *bta = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &bta->readq, wait); + + if (0 == bta->read_count) + mask |= (POLLIN | POLLRDNORM); + + return mask; +} + +static struct file_operations btaudio_digital_dsp_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + open: btaudio_dsp_open_digital, + release: btaudio_dsp_release, + read: btaudio_dsp_read, + write: btaudio_dsp_write, + ioctl: btaudio_dsp_ioctl, + poll: btaudio_dsp_poll, +}; + +static struct file_operations btaudio_analog_dsp_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + open: btaudio_dsp_open_analog, + release: btaudio_dsp_release, + read: btaudio_dsp_read, + write: btaudio_dsp_write, + ioctl: btaudio_dsp_ioctl, + poll: btaudio_dsp_poll, +}; + +/* -------------------------------------------------------------- */ + +static char *irq_name[] = { "", "", "", "OFLOW", "", "", "", "", "", "", "", + "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR", + "RIPERR", "PABORT", "OCERR", "SCERR" }; + +static void btaudio_irq(int irq, void *dev_id, struct pt_regs * regs) +{ + int count = 0; + u32 stat,astat; + struct btaudio *bta = dev_id; + + for (;;) { + count++; + stat = btread(REG_INT_STAT); + astat = stat & btread(REG_INT_MASK); + if (!astat) + return; + btwrite(astat,REG_INT_STAT); + + if (irq_debug) { + int i; + printk(KERN_DEBUG "btaudio: irq loop=%d risc=%x, bits:", + count, stat>>28); + for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) { + if (stat & (1 << i)) + printk(" %s",irq_name[i]); + if (astat & (1 << i)) + printk("*"); + } + printk("\n"); + } + if (stat & IRQ_RISCI) { + int blocks; + blocks = (stat >> 28) - bta->dma_block; + if (blocks < 0) + blocks += bta->block_count; + bta->dma_block = stat >> 28; + if (bta->read_count + 2*bta->block_bytes > bta->buf_size) { + stop_recording(bta); + printk(KERN_INFO "btaudio: buffer overrun\n"); + } + if (blocks > 0) { + bta->read_count += blocks * bta->block_bytes; + wake_up_interruptible(&bta->readq); + } + } + if (count > 10) { + printk(KERN_WARNING + "btaudio: Oops - irq mask cleared\n"); + btwrite(0, REG_INT_MASK); + } + } + return; +} + +/* -------------------------------------------------------------- */ + +static int __devinit btaudio_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct btaudio *bta; + unsigned char revision,latency; + int rc = -EBUSY; + + if (pci_enable_device(pci_dev)) + return -EIO; + if (!request_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0), + "btaudio")) { + return -EBUSY; + } + + bta = kmalloc(sizeof(*bta),GFP_ATOMIC); + memset(bta,0,sizeof(*bta)); + + bta->pci = pci_dev; + bta->irq = pci_dev->irq; + bta->mem = pci_resource_start(pci_dev,0); + bta->mmio = ioremap(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + + bta->source = 1; + bta->bits = 8; + bta->channels = 1; + if (bta->analog) { + bta->decimation = 15; + } else { + bta->decimation = 0; + bta->sampleshift = 1; + } + + init_MUTEX(&bta->lock); + init_waitqueue_head(&bta->readq); + + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &latency); + printk(KERN_INFO "btaudio: Bt%x (rev %d) at %02x:%02x.%x, ", + pci_dev->device,revision,pci_dev->bus->number, + PCI_SLOT(pci_dev->devfn),PCI_FUNC(pci_dev->devfn)); + printk("irq: %d, latency: %d, memory: 0x%lx\n", + bta->irq, latency, bta->mem); + + /* init hw */ + btwrite(0, REG_GPIO_DMA_CTL); + btwrite(0, REG_INT_MASK); + btwrite(~0x0UL, REG_INT_STAT); + pci_set_master(pci_dev); + + if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT, + "btaudio",(void *)bta)) < 0) { + printk(KERN_WARNING + "btaudio: can't request irq (rc=%d)\n",rc); + goto fail1; + } + + /* register devices */ + if (digital) { + rc = bta->dsp_digital = + register_sound_dsp(&btaudio_digital_dsp_fops,dsp1); + if (rc < 0) { + printk(KERN_WARNING + "btaudio: can't register digital dsp (rc=%d)\n",rc); + goto fail2; + } + } + if (analog) { + rc = bta->dsp_analog = + register_sound_dsp(&btaudio_analog_dsp_fops,dsp2); + if (rc < 0) { + printk(KERN_WARNING + "btaudio: can't register analog dsp (rc=%d)\n",rc); + goto fail3; + } + rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer); + if (rc < 0) { + printk(KERN_WARNING + "btaudio: can't register mixer (rc=%d)\n",rc); + goto fail4; + } + } + if (debug) + printk(KERN_DEBUG "btaudio: minors: digital=%d, analog=%d, mixer=%d\n", + bta->dsp_digital, bta->dsp_analog, bta->mixer_dev); + + /* hook into linked list */ + bta->next = btaudios; + btaudios = bta; + + pci_set_drvdata(pci_dev,bta); + return 0; + + fail4: + unregister_sound_dsp(bta->dsp_analog); + fail3: + if (digital) + unregister_sound_dsp(bta->dsp_digital); + fail2: + free_irq(bta->irq,bta); + fail1: + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + kfree(bta); + return rc; +} + +static void __devexit btaudio_remove(struct pci_dev *pci_dev) +{ + struct btaudio *bta = pci_get_drvdata(pci_dev); + struct btaudio *walk; + + /* turn off all DMA / IRQs */ + btand(~15, REG_GPIO_DMA_CTL); + btwrite(0, REG_INT_MASK); + btwrite(~0x0UL, REG_INT_STAT); + + /* unregister devices */ + if (digital) { + unregister_sound_dsp(bta->dsp_digital); + } + if (analog) { + unregister_sound_dsp(bta->dsp_analog); + unregister_sound_mixer(bta->mixer_dev); + } + + /* free resources */ + free_buffer(bta); + free_irq(bta->irq,bta); + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + + /* remove from linked list */ + if (bta == btaudios) { + btaudios = NULL; + } else { + for (walk = btaudios; walk->next != bta; walk = walk->next) + ; /* if (NULL == walk->next) BUG(); */ + walk->next = bta->next; + } + + pci_set_drvdata(pci_dev, NULL); + kfree(bta); + return; +} + +/* -------------------------------------------------------------- */ + +static struct pci_device_id btaudio_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_BROOKTREE, 0x0878, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_BROOKTREE, 0x0879, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +static struct pci_driver btaudio_pci_driver = { + name: "btaudio", + id_table: btaudio_pci_tbl, + probe: btaudio_probe, + remove: __devexit_p(btaudio_remove), +}; + +int btaudio_init_module(void) +{ + printk(KERN_INFO "btaudio: driver version 0.6 loaded [%s%s%s]\n", + analog ? "analog" : "", + analog && digital ? "+" : "", + digital ? "digital" : ""); + return pci_module_init(&btaudio_pci_driver); +} + +void btaudio_cleanup_module(void) +{ + pci_unregister_driver(&btaudio_pci_driver); + return; +} + +module_init(btaudio_init_module); +module_exit(btaudio_cleanup_module); + +MODULE_PARM(dsp1,"i"); +MODULE_PARM(dsp2,"i"); +MODULE_PARM(mixer,"i"); +MODULE_PARM(debug,"i"); +MODULE_PARM(irq_debug,"i"); +MODULE_PARM(digital,"i"); +MODULE_PARM(analog,"i"); +MODULE_PARM(rate,"i"); + +MODULE_DEVICE_TABLE(pci, btaudio_pci_tbl); +MODULE_DESCRIPTION("bt878 audio dma driver"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -Nru linux/sound/oss/cmpci.c linux-2.4.19-pre5-mjc/sound/oss/cmpci.c --- linux/sound/oss/cmpci.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cmpci.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,3195 @@ +/*****************************************************************************/ +/* + * cmpci.c -- C-Media PCI audio driver. + * + * Copyright (C) 1999 ChenLi Tien (cltien@cmedia.com.tw) + * C-media support (support@cmedia.com.tw) + * + * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * For update, visit: + * http://members.home.net/puresoft/cmedia.html + * http://www.cmedia.com.tw + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi, Jan Pfeifer + * + * + * Module command line parameters: + * none so far + * + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.98 0.1 Initial release + * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in cm_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.98 0.4 Don't allow excessive interrupt rates + * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.98 0.6 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.98 0.7 Fix realplayer problems - dac.count issues + * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs + * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 18.08.99 1.5 Only deallocate DMA buffer when unloading. + * 02.09.99 1.6 Enable SPDIF LOOP + * Change the mixer read back + * 21.09.99 2.33 Use RCS version as driver version. + * Add support for modem, S/PDIF loop and 4 channels. + * (8738 only) + * Fix bug cause x11amp cannot play. + * + * Fixes: + * Arnaldo Carvalho de Melo + * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it + * was calling prog_dmabuf with s->lock held, call missing + * unlock_kernel in cm_midi_release + * 08/10/2001 - use set_current_state in some more places + * + * Carlos Eduardo Gorges + * Fri May 25 2001 + * - SMP support ( spin[un]lock* revision ) + * - speaker mixer support + * Mon Aug 13 2001 + * - optimizations and cleanups + * + */ +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" + +/* --------------------------------------------------------------------- */ +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#undef DMABYTEIO +/* --------------------------------------------------------------------- */ + +#define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A) + +/* CM8338 registers definition ****************/ + +#define CODEC_CMI_FUNCTRL0 (0x00) +#define CODEC_CMI_FUNCTRL1 (0x04) +#define CODEC_CMI_CHFORMAT (0x08) +#define CODEC_CMI_INT_HLDCLR (0x0C) +#define CODEC_CMI_INT_STATUS (0x10) +#define CODEC_CMI_LEGACY_CTRL (0x14) +#define CODEC_CMI_MISC_CTRL (0x18) +#define CODEC_CMI_TDMA_POS (0x1C) +#define CODEC_CMI_MIXER (0x20) +#define CODEC_SB16_DATA (0x22) +#define CODEC_SB16_ADDR (0x23) +#define CODEC_CMI_MIXER1 (0x24) +#define CODEC_CMI_MIXER2 (0x25) +#define CODEC_CMI_AUX_VOL (0x26) +#define CODEC_CMI_MISC (0x27) +#define CODEC_CMI_AC97 (0x28) + +#define CODEC_CMI_CH0_FRAME1 (0x80) +#define CODEC_CMI_CH0_FRAME2 (0x84) +#define CODEC_CMI_CH1_FRAME1 (0x88) +#define CODEC_CMI_CH1_FRAME2 (0x8C) + +#define CODEC_CMI_EXT_REG (0xF0) + +/* Mixer registers for SB16 ******************/ + +#define DSP_MIX_DATARESETIDX ((unsigned char)(0x00)) + +#define DSP_MIX_MASTERVOLIDX_L ((unsigned char)(0x30)) +#define DSP_MIX_MASTERVOLIDX_R ((unsigned char)(0x31)) +#define DSP_MIX_VOICEVOLIDX_L ((unsigned char)(0x32)) +#define DSP_MIX_VOICEVOLIDX_R ((unsigned char)(0x33)) +#define DSP_MIX_FMVOLIDX_L ((unsigned char)(0x34)) +#define DSP_MIX_FMVOLIDX_R ((unsigned char)(0x35)) +#define DSP_MIX_CDVOLIDX_L ((unsigned char)(0x36)) +#define DSP_MIX_CDVOLIDX_R ((unsigned char)(0x37)) +#define DSP_MIX_LINEVOLIDX_L ((unsigned char)(0x38)) +#define DSP_MIX_LINEVOLIDX_R ((unsigned char)(0x39)) + +#define DSP_MIX_MICVOLIDX ((unsigned char)(0x3A)) +#define DSP_MIX_SPKRVOLIDX ((unsigned char)(0x3B)) + +#define DSP_MIX_OUTMIXIDX ((unsigned char)(0x3C)) + +#define DSP_MIX_ADCMIXIDX_L ((unsigned char)(0x3D)) +#define DSP_MIX_ADCMIXIDX_R ((unsigned char)(0x3E)) + +#define DSP_MIX_INGAINIDX_L ((unsigned char)(0x3F)) +#define DSP_MIX_INGAINIDX_R ((unsigned char)(0x40)) +#define DSP_MIX_OUTGAINIDX_L ((unsigned char)(0x41)) +#define DSP_MIX_OUTGAINIDX_R ((unsigned char)(0x42)) + +#define DSP_MIX_AGCIDX ((unsigned char)(0x43)) + +#define DSP_MIX_TREBLEIDX_L ((unsigned char)(0x44)) +#define DSP_MIX_TREBLEIDX_R ((unsigned char)(0x45)) +#define DSP_MIX_BASSIDX_L ((unsigned char)(0x46)) +#define DSP_MIX_BASSIDX_R ((unsigned char)(0x47)) + +#define CM_CH0_RESET 0x04 +#define CM_CH1_RESET 0x08 +#define CM_EXTENT_CODEC 0x100 +#define CM_EXTENT_MIDI 0x2 +#define CM_EXTENT_SYNTH 0x4 +#define CM_INT_CH0 1 +#define CM_INT_CH1 2 + +#define CM_CFMT_STEREO 0x01 +#define CM_CFMT_16BIT 0x02 +#define CM_CFMT_MASK 0x03 +#define CM_CFMT_DACSHIFT 2 +#define CM_CFMT_ADCSHIFT 0 + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define CM_ENABLE_CH1 0x2 +#define CM_ENABLE_CH0 0x1 + +/* MIDI buffer sizes **************************/ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 2 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +#define SND_DEV_DSP16 5 + +#define NR_DEVICE 3 /* maximum number of devices */ + +/*********************************************/ + +struct cm_state { + unsigned int magic; /* magic */ + struct cm_state *next; /* we keep cm cards in a linked list */ + + int dev_audio; /* soundcore stuff */ + int dev_mixer; + int dev_midi; + int dev_dmfm; + + unsigned int iosb, iobase, iosynth, + iomidi, iogame, irq; /* hardware resources */ + unsigned short deviceid; /* pci_id */ + + struct { /* mixer stuff */ + unsigned int modcnt; + unsigned short vol[13]; + } mix; + + unsigned int rateadc, ratedac; /* wave stuff */ + unsigned char fmt, enable; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + unsigned rawphys; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + + unsigned fragsize; /* redundant, but makes calculations easier */ + unsigned dmasize; + unsigned fragsamples; + unsigned dmasamples; + + unsigned mapped:1; /* OSS stuff */ + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + struct { /* midi stuff */ + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + int chip_version; + int max_channels; + int curr_channels; + int speakers; /* number of speakers */ + int capability; /* HW capability, various for chip versions */ + + int status; /* HW or SW state */ + + int spdif_counter; /* spdif frame counter */ +}; + +/* flags used for capability */ +#define CAN_AC3_HW 0x00000001 /* 037 or later */ +#define CAN_AC3_SW 0x00000002 /* 033 or later */ +#define CAN_AC3 (CAN_AC3_HW | CAN_AC3_SW) +#define CAN_DUAL_DAC 0x00000004 /* 033 or later */ +#define CAN_MULTI_CH_HW 0x00000008 /* 039 or later */ +#define CAN_MULTI_CH (CAN_MULTI_CH_HW | CAN_DUAL_DAC) +#define CAN_LINE_AS_REAR 0x00000010 /* 033 or later */ +#define CAN_LINE_AS_BASS 0x00000020 /* 039 or later */ +#define CAN_MIC_AS_BASS 0x00000040 /* 039 or later */ + +/* flags used for status */ +#define DO_AC3_HW 0x00000001 +#define DO_AC3_SW 0x00000002 +#define DO_AC3 (DO_AC3_HW | DO_AC3_SW) +#define DO_DUAL_DAC 0x00000004 +#define DO_MULTI_CH_HW 0x00000008 +#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC) +#define DO_LINE_AS_REAR 0x00000010 /* 033 or later */ +#define DO_LINE_AS_BASS 0x00000020 /* 039 or later */ +#define DO_MIC_AS_BASS 0x00000040 /* 039 or later */ +#define DO_SPDIF_OUT 0x00000100 +#define DO_SPDIF_IN 0x00000200 +#define DO_SPDIF_LOOP 0x00000400 + +static struct cm_state *devs; +static unsigned long wavetable_mem; + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned exp=16,l=5,r=0; + static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000}; + + /* num: 2, 4, 16, 256, 65536 */ + /* exp: 1, 2, 4, 8, 16 */ + + while(l--) { + if( x >= num[l] ) { + if(num[l]>2) x >>= exp; + r+=exp; + } + exp>>=1; + } + + return r; +} + +/* --------------------------------------------------------------------- */ + +static void maskb(unsigned int addr, unsigned int mask, unsigned int value) +{ + outb((inb(addr) & mask) | value, addr); +} + +static void maskw(unsigned int addr, unsigned int mask, unsigned int value) +{ + outw((inw(addr) & mask) | value, addr); +} + +static void maskl(unsigned int addr, unsigned int mask, unsigned int value) +{ + outl((inl(addr) & mask) | value, addr); +} + +static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count) +{ + if (addr) + outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~1, 0); +} + +static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count) +{ + outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 1); +} + +static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count) +{ + outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 0); + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, 0, count); +} + +static void set_countadc(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); +} + +static void set_countdac(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); + if (s->status & DO_DUAL_DAC) + set_countadc(s, count); +} + +static inline unsigned get_dmadac(struct cm_state *s) +{ + unsigned int curr_addr; + + curr_addr = inw(s->iobase + CODEC_CMI_CH1_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; + curr_addr = s->dma_dac.dmasize - curr_addr; + + return curr_addr; +} + +static inline unsigned get_dmaadc(struct cm_state *s) +{ + unsigned int curr_addr; + + curr_addr = inw(s->iobase + CODEC_CMI_CH0_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; + curr_addr = s->dma_adc.dmasize - curr_addr; + + return curr_addr; +} + +static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data) +{ + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + outb(data, s->iobase + CODEC_SB16_DATA); + udelay(10); +} + +static unsigned char rdmixer(struct cm_state *s, unsigned char idx) +{ + unsigned char v; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + v = inb(s->iobase + CODEC_SB16_DATA); + udelay(10); + spin_unlock_irqrestore(&s->lock, flags); + return v; +} + +static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data) +{ + if (mask) + { + s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); + udelay(10); +} + +static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_fmt_unlocked(s,mask,data); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); + udelay(10); +} + +static struct { + unsigned rate; + unsigned lower; + unsigned upper; + unsigned char freq; +} rate_lookup[] = +{ + { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0 }, + { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4 }, + { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1 }, + { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5 }, + { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, + { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, + { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, + { 48000, (44100 + 48000) / 2, 48000, 7 } +}; + +static void set_spdifout_unlocked(struct cm_state *s, unsigned rate) +{ + if (rate == 48000 || rate == 44100) { + // SPDIFI48K SPDF_ACc97 + maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~0x01008000, rate == 48000 ? 0x01008000 : 0); + // ENSPDOUT + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, 0x80); + // SPDF_1 SPD2DAC + maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x240); + // CDPLAY + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); + s->status |= DO_SPDIF_OUT; + } else { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0x80, 0); + maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0x240, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); + s->status &= ~DO_SPDIF_OUT; + } +} + +static void set_spdifout(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_spdifout_unlocked(s,rate); + spin_unlock_irqrestore(&s->lock, flags); +} + +/* find parity for bit 4~30 */ +static unsigned parity(unsigned data) +{ + unsigned parity = 0; + int counter = 4; + + data >>= 4; // start from bit 4 + while (counter <= 30) { + if (data & 1) + parity++; + data >>= 1; + counter++; + } + return parity & 1; +} + +static void set_ac3_unlocked(struct cm_state *s, unsigned rate) +{ + /* enable AC3 */ + if (rate == 48000 || rate == 44100) { + // mute DAC + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x40); + // AC3EN for 037, 0x10 + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); + // AC3EN for 039, 0x04 + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x04); + if (s->capability & CAN_AC3_HW) { + // SPD24SEL for 037, 0x02 + // SPD24SEL for 039, 0x20, but cannot be set + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); + s->status |= DO_AC3_HW; + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); + } else { + // SPD32SEL for 037 & 039, 0x20 + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x20); + // set 176K sample rate to fix 033 HW bug + if (s->chip_version == 33) { + if (rate == 48000) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08); + else + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + } + s->status |= DO_AC3_SW; + } + } else { + maskb(s->iobase + CODEC_CMI_MIXER1, ~0x40, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0x32, 0); + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x24, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + if (s->chip_version == 33) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); + s->status &= ~DO_AC3; + } + s->spdif_counter = 0; + +} + +static void set_ac3(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_spdifout_unlocked(s, rate); + set_ac3_unlocked(s,rate); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void trans_ac3(struct cm_state *s, void *dest, const char *source, int size) +{ + int i = size / 2; + unsigned long data; + unsigned long *dst = (unsigned long *) dest; + unsigned short *src = (unsigned short *)source; + + do { + data = (unsigned long) *src++; + data <<= 12; // ok for 16-bit data + if (s->spdif_counter == 2 || s->spdif_counter == 3) + data |= 0x40000000; // indicate AC-3 raw data + if (parity(data)) + data |= 0x80000000; // parity + if (s->spdif_counter == 0) + data |= 3; // preamble 'M' + else if (s->spdif_counter & 1) + data |= 5; // odd, 'W' + else + data |= 9; // even, 'M' + *dst++ = data; + s->spdif_counter++; + if (s->spdif_counter == 384) + s->spdif_counter = 0; + } while (--i); +} + +static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate) +{ + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->rateadc = rate; + freq <<= 2; + + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); +} + +static void set_adc_rate(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->rateadc = rate; + freq <<= 2; + + spin_lock_irqsave(&s->lock, flags); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac_rate(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->ratedac = rate; + freq <<= 5; + + spin_lock_irqsave(&s->lock, flags); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0xe0, freq); + + + if (s->curr_channels <= 2) + set_spdifout_unlocked(s, rate); + if (s->status & DO_DUAL_DAC) + set_adc_rate_unlocked(s, rate); + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ +static inline void reset_adc(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + udelay(10); + outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); +} + +static inline void reset_dac(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if (s->status & DO_DUAL_DAC) + reset_adc(s); +} + +static inline void pause_adc(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 4); +} + +static inline void pause_dac(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 8); + if (s->status & DO_DUAL_DAC) + pause_adc(s); +} + +static inline void disable_adc(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~CM_ENABLE_CH0; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_adc(s); +} + +static inline void disable_dac(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~CM_ENABLE_CH1; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_dac(s); + if (s->status & DO_DUAL_DAC) + disable_adc(s); +} + +static inline void enable_adc(struct cm_state *s) +{ + if (!(s->enable & CM_ENABLE_CH0)) { + /* enable channel */ + s->enable |= CM_ENABLE_CH0; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~4, 0); +} + +static inline void enable_dac_unlocked(struct cm_state *s) +{ + if (!(s->enable & CM_ENABLE_CH1)) { + /* enable channel */ + s->enable |= CM_ENABLE_CH1; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~8, 0); + + if (s->status & DO_DUAL_DAC) + enable_adc(s); +} + +static inline void enable_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + enable_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_adc_unlocked(struct cm_state *s) +{ + if (s->enable & CM_ENABLE_CH0) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~1, 0); + disable_adc(s); + } +} + +static inline void stop_adc(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); + +} + +static inline void stop_dac_unlocked(struct cm_state *s) +{ + if (s->enable & CM_ENABLE_CH1) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~2, 0); + disable_dac(s); + } + if (s->status & DO_DUAL_DAC) + stop_adc_unlocked(s); +} + +static inline void stop_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + stop_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc_unlocked(struct cm_state *s) +{ + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); + enable_adc(s); + } +} + +static void start_adc(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + start_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1_unlocked(struct cm_state *s) +{ + if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) { + /* enable interrupt */ +// maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); + enable_dac_unlocked(s); + } +} + +static void start_dac_unlocked(struct cm_state *s) +{ + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2); + enable_dac_unlocked(s); + } + if (s->status & DO_DUAL_DAC) + start_dac1_unlocked(s); +} + +static void start_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + start_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static int prog_dmabuf(struct cm_state *s, unsigned rec); + +static int set_dac_channels(struct cm_state *s, int channels) +{ + unsigned long flags; + spin_lock_irqsave(&s->lock, flags); + + if ((channels > 2) && (channels <= s->max_channels) + && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) { + set_spdifout_unlocked(s, 0); + if (s->capability & CAN_MULTI_CH_HW) { + // NXCHG + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, 0x80); + // CHB3D or CHB3D5C + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, channels > 4 ? 0x80 : 0x20); + // CHB3D6C + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, channels == 6 ? 0x80 : 0); + // ENCENTER + maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0x80, channels == 6 ? 0x80 : 0); + s->status |= DO_MULTI_CH_HW; + } else if (s->capability & CAN_DUAL_DAC) { + unsigned char fmtm = ~0, fmts = 0; + ssize_t ret; + + // ENDBDAC, turn on double DAC mode + // XCHGDAC, CH0 -> back, CH1->front + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0xC0); + s->status |= DO_DUAL_DAC; + // prepare secondary buffer + + spin_unlock_irqrestore(&s->lock, flags); + ret = prog_dmabuf(s, 1); + if (ret) return ret; + spin_lock_irqsave(&s->lock, flags); + + // copy the hw state + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); + // the HW only support 16-bit stereo + fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + + set_fmt_unlocked(s, fmtm, fmts); + set_adc_rate_unlocked(s, s->ratedac); + + } + + if (s->speakers > 2) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0x04, 0); + s->curr_channels = channels; + } else { + if (s->status & DO_MULTI_CH_HW) { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x80, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, 0); + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, 0); + } else if (s->status & DO_DUAL_DAC) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x80, 0); + } + // N4SPK3D, enable 4 speaker mode (analog duplicate) + if (s->speakers > 2) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, 0x04); + s->status &= ~DO_MULTI_CH; + s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; + } + + spin_unlock_irqrestore(&s->lock, flags); + return s->curr_channels; +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static void dealloc_dmabuf(struct dmabuf *db) +{ + struct page *pstart, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) + mem_map_unreserve(pstart); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +/* Ch1 is used for playback, Ch0 is used for recording */ + +static int prog_dmabuf(struct cm_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *pstart, *pend; + unsigned char fmt; + unsigned long flags; + + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= CM_CFMT_ADCSHIFT; + } else { + stop_dac(s); + fmt >>= CM_CFMT_DACSHIFT; + } + + fmt &= CM_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + db->rawphys = virt_to_bus(db->rawbuf); + if ((db->rawphys ^ (db->rawphys + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + (long) db->rawphys, PAGE_SIZE << db->buforder); + if ((db->rawphys + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + (long) db->rawphys, PAGE_SIZE << db->buforder); + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) + mem_map_reserve(pstart); + } + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + /* to make fragsize >= 4096 */ + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + db->dmasamples = db->dmasize >> sample_shift[fmt]; + memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, db->rawphys, db->dmasize >> sample_shift[fmt]); + else + set_dmaadc(s, db->rawphys, db->dmasize >> sample_shift[fmt]); + /* program sample counts */ + set_countdac(s, db->fragsamples); + } else { + set_dmadac(s, db->rawphys, db->dmasize >> sample_shift[fmt]); + /* program sample counts */ + set_countdac(s, db->fragsamples); + } + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + return 0; +} + +static inline void clear_advance(struct cm_state *s) +{ + unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned char *buf1 = s->dma_adc.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, len); +} + +/* call with spinlock held! */ +static void cm_update_ptr(struct cm_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + if (s->status & DO_DUAL_DAC) { + hwptr = get_dmaadc(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + if (s->dma_adc.mapped) { + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + s->dma_adc.count -= diff; + if (s->dma_adc.count <= 0) { + pause_adc(s); + s->dma_adc.error++; + } else if (s->dma_adc.count <= (signed)s->dma_adc.fragsize && !s->dma_adc.endcleared) { + clear_advance(s); + s->dma_adc.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_adc.wait); + } + } else { + hwptr = get_dmaadc(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + pause_adc(s); + s->dma_adc.error++; + } + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmadac(s) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + pause_dac(s); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI +/* hold spinlock for the following! */ +static void cm_handle_midi(struct cm_state *s) +{ + unsigned char ch; + int wake; + + wake = 0; + while (!(inb(s->iomidi+1) & 0x80)) { + ch = inb(s->iomidi); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->iomidi); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} +#endif + +static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cm_state *s = (struct cm_state *)dev_id; + unsigned int intsrc, intstat; + unsigned char mask = 0; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); + /* acknowledge interrupt */ + if (intsrc & CM_INT_CH0) + mask |= 1; + if (intsrc & CM_INT_CH1) + mask |= 2; + outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + cm_update_ptr(s); +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI + cm_handle_midi(s); +#endif + spin_unlock(&s->lock); +} + +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI +static void cm_midi_timer(unsigned long data) +{ + struct cm_state *s = (struct cm_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + cm_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} +#endif + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n"; + +#ifdef CONFIG_SOUND_ALSA_CMPCI /* support multiple chips */ +#define VALIDATE_STATE(s) +#else +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CM_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) +#endif + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 +#define MT_5MUTEMONO 5 + +static const struct { + unsigned left; + unsigned right; + unsigned type; + unsigned rec; + unsigned play; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x02 }, + [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x08 }, + [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 }, + [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 }, + [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, + [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, + [SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX, DSP_MIX_SPKRVOLIDX, MT_5MUTEMONO, 0x01, 0x01 } +}; + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = +{ + [SOUND_MIXER_CD] = 1, + [SOUND_MIXER_LINE] = 2, + [SOUND_MIXER_MIC] = 3, + [SOUND_MIXER_SYNTH] = 4, + [SOUND_MIXER_VOLUME] = 5, + [SOUND_MIXER_PCM] = 6, + [SOUND_MIXER_SPEAKER]= 7 +}; + +static unsigned mixer_recmask(struct cm_state *s) +{ + int i, j, k; + + j = rdmixer(s, DSP_MIX_ADCMIXIDX_L); + j &= 0x7f; + for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (j & mixtable[i].rec) + k |= 1 << i; + return k; +} + +static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val, j; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "cmpci", sizeof(info.id)); + strncpy(info.name, "C-Media PCI", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "cmpci", sizeof(info.id)); + strncpy(info.name, "C-Media cmpci", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg); + + case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg);//need fix + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].play) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (!volidx[i]) + return -EINVAL; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, (int *)arg)) + return -EFAULT; + i = generic_hweight32(val); + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].rec) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].rec; + } + spin_lock_irqsave(&s->lock, flags); + wrmixer(s, DSP_MIX_ADCMIXIDX_L, j); + wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1)); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, (int *)arg)) + return -EFAULT; + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].play) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].play; + } + spin_lock_irqsave(&s->lock, flags); + frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, j); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + r = (val >> 8) & 0xff; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rl = (l < 4 ? 0 : (l - 5) / 3) & 31; + rr = (rl >> 2) & 7; + wrmixer(s, mixtable[i].left, rl<<3); + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); + break; + + case MT_5MUTEMONO: + r = l; + rl = l < 4 ? 0 : (l - 5) / 3; + rr = rl >> 2; + wrmixer(s, mixtable[i].left, rl<<3); + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); + break; + + case MT_5MUTE: + rl = l < 4 ? 0 : (l - 5) / 3; + rr = r < 4 ? 0 : (r - 5) / 3; + wrmixer(s, mixtable[i].left, rl<<3); + wrmixer(s, mixtable[i].right, rr<<3); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x00; + else + rl = l * 2 / 3; + if (r < 6) + rr = 0x00; + else + rr = r * 2 / 3; + wrmixer(s, mixtable[i].left, rl); + wrmixer(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); + + if (!volidx[i]) + return -EINVAL; + s->mix.vol[volidx[i]-1] = val; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static int cm_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int cm_release_mixdev(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations cm_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: cm_ioctl_mixdev, + open: cm_open_mixdev, + release: cm_release_mixdev, +}; + + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct cm_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; + tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cmpci: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t cm_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { + printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + set_dmaadc(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); + /* program sample counts */ + set_countadc(s, s->dma_adc.fragsamples); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +static ssize_t cm_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (s->status & DO_DUAL_DAC) { + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + } + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + if (s->status & DO_DUAL_DAC) { + s->dma_adc.swptr = s->dma_dac.swptr; + s->dma_adc.count = s->dma_dac.count; + s->dma_adc.endcleared = s->dma_dac.endcleared; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->status & DO_AC3_SW) { + if (s->dma_dac.count + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - s->dma_dac.count) / 2; + } else { + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if ((s->status & DO_DUAL_DAC) && (cnt > count / 2)) + cnt = count / 2; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { + printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + spin_lock_irqsave(&s->lock, flags); + stop_dac_unlocked(s); + set_dmadac(s, s->dma_dac.rawphys, s->dma_dac.dmasamples); + /* program sample counts */ + set_countdac(s, s->dma_dac.fragsamples); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + if (s->status & DO_DUAL_DAC) { + set_dmadac1(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (s->status & DO_AC3_SW) { + // clip exceeded data, caught by 033 and 037 + if (swptr + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - swptr) / 2; + trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt); + swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize; + } else if (s->status & DO_DUAL_DAC) { + int i; + unsigned long *src, *dst0, *dst1; + + src = (unsigned long *) buffer; + dst0 = (unsigned long *) (s->dma_dac.rawbuf + swptr); + dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr); + // copy left/right sample at one time + for (i = 0; i <= cnt / 4; i++) { + *dst0++ = *src++; + *dst1++ = *src++; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } else { + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + if (s->status & DO_AC3_SW) + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->status & DO_DUAL_DAC) { + count -= cnt; + buffer += cnt; + ret += cnt; + } + start_dac(s); + } + return ret; +} + +static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int cm_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_BIND, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + s->dma_adc.ready = 0; + set_adc_rate_unlocked(s, val); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + if ((s->capability & CAN_MULTI_CH) + && (file->f_mode & FMODE_WRITE)) { + val = set_dac_channels(s, val); + return put_user(val, (int *)arg); + } + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) + : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8|AFMT_AC3, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE || val == AFMT_AC3) + fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT); + if (val == AFMT_AC3) { + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + set_ac3(s, s->ratedac); + } else + set_ac3(s, 0); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + } + if (s->status & DO_AC3) return put_user(AFMT_AC3, (int *)arg); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) + : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (s->status & DO_DUAL_DAC) { + if (file->f_mode & FMODE_WRITE && + (s->enable & CM_ENABLE_CH1) && + (s->enable & CM_ENABLE_CH0)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + } + if (file->f_mode & FMODE_READ && s->enable & CM_ENABLE_CH0) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & CM_ENABLE_CH1) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (s->status & DO_DUAL_DAC) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + } + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->enable & CM_ENABLE_CH1) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->enable & CM_ENABLE_CH0) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + if (s->status & DO_DUAL_DAC) { + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + } + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + if (s->status & DO_DUAL_DAC) { + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(2 * s->dma_dac.fragsize, (int *)arg); + } + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ossfragshift = s->dma_dac.ossfragshift; + s->dma_adc.ossmaxfrags = s->dma_dac.ossmaxfrags; + } + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.subdivision = val; + if (s->status & DO_DUAL_DAC) + s->dma_adc.subdivision = val; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_READ_FILTER: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_GETCHANNELMASK: + return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE|DSP_BIND_SPDIF, (int *)arg); + + case SNDCTL_DSP_BIND_CHANNEL: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val == DSP_BIND_QUERY) { + val = DSP_BIND_FRONT; + if (s->status & DO_SPDIF_OUT) + val |= DSP_BIND_SPDIF; + else { + if (s->curr_channels == 4) + val |= DSP_BIND_SURR; + if (s->curr_channels > 4) + val |= DSP_BIND_CENTER_LFE; + } + } else { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val & DSP_BIND_SPDIF) { + set_spdifout(s, s->ratedac); + set_dac_channels(s, s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1); + if (!(s->status & DO_SPDIF_OUT)) + val &= ~DSP_BIND_SPDIF; + } else { + int channels; + int mask; + + mask = val & (DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; + break; + } + set_dac_channels(s, channels); + } + } + } + return put_user(val, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int cm_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + unsigned char fmtm = ~0, fmts = 0; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + // clear previous multichannel, spdif, ac3 state + set_spdifout(s, 0); + if (s->deviceid == PCI_DEVICE_ID_CMEDIA_CM8738) { + set_ac3(s, 0); + set_dac_channels(s, 1); + } + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + return 0; +} + +static int cm_release(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + + dealloc_dmabuf(&s->dma_dac); + if (s->status & DO_DUAL_DAC) + dealloc_dmabuf(&s->dma_adc); + + if (s->status & DO_MULTI_CH) + set_dac_channels(s, 0); + if (s->status & DO_AC3) + set_ac3(s, 0); + if (s->status & DO_SPDIF_OUT) + set_spdifout(s, 0); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations cm_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: cm_read, + write: cm_write, + poll: cm_poll, + ioctl: cm_ioctl, + mmap: cm_mmap, + open: cm_open, + release: cm_release, +}; + +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI +/* --------------------------------------------------------------------- */ + +static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) + { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + cm_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + cm_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +static unsigned int cm_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int cm_midi_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + /* enable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 4); + outb(0xff, s->iomidi+1); /* reset command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + outb(0x3f, s->iomidi+1); /* uart command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = cm_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int cm_midi_release(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + lock_kernel(); + + if (file->f_mode & FMODE_WRITE) { + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + unlock_kernel(); + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "cmpci: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + del_timer(&s->midi.timer); + outb(0xff, s->iomidi+1); /* reset command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + /* disable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~4, 0); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations cm_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: cm_midi_read, + write: cm_midi_write, + poll: cm_midi_poll, + open: cm_midi_open, + release: cm_midi_release, +}; +#endif + +/* --------------------------------------------------------------------- */ + +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM +static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct cm_state *s = (struct cm_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->iosynth + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->iosynth); + outb((p.kbd_split & 1) << 6, s->iosynth+1); + outb(0xbd, s->iosynth); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->iosynth+2); + outb(arg, s->iosynth+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->iosynth+2); + outb(arg & 1, s->iosynth+3); + return 0; + } + return -EINVAL; +} + +static int cm_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct cm_state *s = devs; + + while (s && s->dev_dmfm != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + /* init the stuff */ + outb(1, s->iosynth); + outb(0x20, s->iosynth+1); /* enable waveforms */ + outb(4, s->iosynth+2); + outb(0, s->iosynth+3); /* no 4op enabled */ + outb(5, s->iosynth+2); + outb(1, s->iosynth+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + return 0; +} + +static int cm_dmfm_release(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations cm_dmfm_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: cm_dmfm_ioctl, + open: cm_dmfm_open, + release: cm_dmfm_release, +}; +#endif /* CONFIG_SOUND_ALSA_CMPCI_FM */ + + + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_CD, 0x4f4f }, + { SOUND_MIXER_WRITE_LINE, 0x4f4f }, + { SOUND_MIXER_WRITE_MIC, 0x4f4f }, + { SOUND_MIXER_WRITE_SYNTH, 0x4f4f }, + { SOUND_MIXER_WRITE_VOLUME, 0x4f4f }, + { SOUND_MIXER_WRITE_PCM, 0x4f4f } +}; + +/* check chip version and capability */ +static int query_chip(struct cm_state *s) +{ + int ChipVersion = -1; + unsigned char RegValue; + + // check reg 0Ch, bit 24-31 + RegValue = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 3); + if (RegValue == 0) { + // check reg 08h, bit 24-28 + RegValue = inb(s->iobase + CODEC_CMI_CHFORMAT + 3); + RegValue &= 0x1f; + if (RegValue == 0) { + ChipVersion = 33; + s->max_channels = 4; + s->capability |= CAN_AC3_SW; + s->capability |= CAN_DUAL_DAC; + } else { + ChipVersion = 37; + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + } + } else { + // check reg 0Ch, bit 26 + if (RegValue & (1 << (26-24))) { + ChipVersion = 39; + if (RegValue & (1 << (24-24))) + s->max_channels = 6; + else + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + } else { + ChipVersion = 55; // 4 or 6 channels + s->max_channels = 6; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + } + } + // still limited to number of speakers + if (s->max_channels > s->speakers) + s->max_channels = s->speakers; + return ChipVersion; +} + +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI +static int mpuio = CONFIG_SOUND_ALSA_CMPCI_MPUIO; +#else +static int mpuio; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM +static int fmio = CONFIG_SOUND_ALSA_CMPCI_FMIO; +#else +static int fmio; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_SPDIFINVERSE +static int spdif_inverse = 1; +#else +static int spdif_inverse; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_SPDIFLOOP +static int spdif_loop = 1; +#else +static int spdif_loop; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_SPEAKERS +static int speakers = CONFIG_SOUND_ALSA_CMPCI_SPEAKERS; +#else +static int speakers = 2; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_LINE_REAR +static int use_line_as_rear = 1; +#else +static int use_line_as_rear; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_LINE_BASS +static int use_line_as_bass = 1; +#else +static int use_line_as_bass; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_JOYSTICK +static int joystick = 1; +#else +static int joystick; +#endif +MODULE_PARM(mpuio, "i"); +MODULE_PARM(fmio, "i"); +MODULE_PARM(spdif_inverse, "i"); +MODULE_PARM(spdif_loop, "i"); +MODULE_PARM(speakers, "i"); +MODULE_PARM(use_line_as_rear, "i"); +MODULE_PARM(use_line_as_bass, "i"); +MODULE_PARM(joystick, "i"); +MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable"); +MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable"); +MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal"); +MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly"); +MODULE_PARM_DESC(speakers, "(2-6) Number of speakers you connect"); +MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out"); +MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center"); +MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver"); + +static struct pci_device_id cmpci_pci_tbl[] = { + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, cmpci_pci_tbl); + +void initialize_chip(struct pci_dev *pcidev) +{ + struct cm_state *s; + mm_segment_t fs; + int i, val; +#if defined(CONFIG_SOUND_ALSA_CMPCI_MIDI) || defined(CONFIG_SOUND_ALSA_CMPCI_FM) + unsigned char reg_mask = 0; +#endif + struct { + unsigned short deviceid; + char *devicename; + } devicetable[] = + { + { PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" }, + { PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" }, + { PCI_DEVICE_ID_CMEDIA_CM8738, "CM8738" }, + { PCI_DEVICE_ID_CMEDIA_CM8738B, "CM8738B" }, + }; + char *devicename = "unknown"; + { + if (pci_enable_device(pcidev)) + return; + if (pcidev->irq == 0) + return; + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + printk(KERN_WARNING "cmpci: out of memory\n"); + return; + } + /* search device name */ + for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) { + if (devicetable[i].deviceid == pcidev->device) + { + devicename = devicetable[i].devicename; + break; + } + } + memset(s, 0, sizeof(struct cm_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = CM_MAGIC; + s->iobase = pci_resource_start(pcidev, 0); + s->iosynth = fmio; + s->iomidi = mpuio; + s->status = 0; + /* range check */ + if (speakers < 2) + speakers = 2; + else if (speakers > 6) + speakers = 6; + s->speakers = speakers; + if (s->iobase == 0) + return; + s->irq = pcidev->irq; + + if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) { + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); + goto err_region5; + } +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI + /* disable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); + if (s->iomidi) { + if (!request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi")) { + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1); + s->iomidi = 0; + } else { + /* set IO based at 0x330 */ + switch (s->iomidi) { + case 0x330: + reg_mask = 0; + break; + case 0x320: + reg_mask = 0x20; + break; + case 0x310: + reg_mask = 0x40; + break; + case 0x300: + reg_mask = 0x60; + break; + default: + s->iomidi = 0; + break; + } + outb((inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60) | reg_mask, s->iobase + CODEC_CMI_LEGACY_CTRL + 3); + /* enable MPU-401 */ + if (s->iomidi) { + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); + } + } + } +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM + /* disable FM */ + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); + if (s->iosynth) { + if (!request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM")) { + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1); + s->iosynth = 0; + } else { + /* set IO based at 0x388 */ + switch (s->iosynth) { + case 0x388: + reg_mask = 0; + break; + case 0x3C8: + reg_mask = 0x01; + break; + case 0x3E0: + reg_mask = 0x02; + break; + case 0x3E8: + reg_mask = 0x03; + break; + default: + s->iosynth = 0; + break; + } + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x03, reg_mask); + /* enable FM */ + if (s->iosynth) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8); + } + } + } +#endif + /* enable joystick */ + if (joystick) + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x02); + else + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); + /* initialize codec registers */ + outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ + /* reset mixer */ + wrmixer(s, DSP_MIX_DATARESETIDX, 0); + + /* request irq */ + if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) { + printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n", + devicename, s->iobase, s->irq); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) + goto err_dev2; +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI + if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0) + goto err_dev3; +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM + if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0) + goto err_dev4; +#endif + pci_set_master(pcidev); /* enable bus mastering */ + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + /* set mixer output */ + frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f); + /* set mixer input */ + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + /* use channel 0 for record, channel 1 for play */ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 1); + s->deviceid = pcidev->device; + + if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) { + + /* chip version and hw capability check */ + s->chip_version = query_chip(s); + printk(KERN_INFO "cmpci: chip version = 0%d\n", s->chip_version); + + /* seet SPDIF-in inverse before enable SPDIF loop */ + if (spdif_inverse) { + /* turn on spdif-in inverse */ + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1); + printk(KERN_INFO "cmpci: Inverse SPDIF-in\n"); + } else { + /* turn off spdif-ininverse */ + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0); + } + + /* enable SPDIF loop */ + if (spdif_loop) { + s->status |= DO_SPDIF_LOOP; + /* turn on spdif-in to spdif-out */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x80); + printk(KERN_INFO "cmpci: Enable SPDIF loop\n"); + } else { + s->status &= ~DO_SPDIF_LOOP; + /* turn off spdif-in to spdif-out */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x80, 0); + } + if (use_line_as_rear) { + s->capability |= CAN_LINE_AS_REAR; + s->status |= DO_LINE_AS_REAR; + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x20); + } else + maskb(s->iobase + CODEC_CMI_MIXER1, ~0x20, 0); + if (s->chip_version >= 39) { + if (use_line_as_bass) { + s->capability |= CAN_LINE_AS_BASS; + s->status |= DO_LINE_AS_BASS; + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, 0x60); + } else + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x60, 0); + } + } else { + /* 8338 will fall here */ + s->max_channels = 2; + } + /* queue it for later freeing */ + s->next = devs; + devs = s; + return; + +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM + unregister_sound_special(s->dev_dmfm); + err_dev4: +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI + unregister_sound_midi(s->dev_midi); + err_dev3: +#endif + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "cmpci: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM + if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI + if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); +#endif + release_region(s->iobase, CM_EXTENT_CODEC); + err_region5: + kfree(s); + } + if (!devs) { + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + return; + } + return; +} + +static int __init init_cmpci(void) +{ + struct pci_dev *pcidev = NULL; + int index = 0; + +#ifdef CONFIG_PCI + if (!pci_present()) /* No PCI bus in this machine! */ +#endif + return -ENODEV; + printk(KERN_INFO "cmpci: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n"); + + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { + initialize_chip(pcidev); + index++; + } + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)))) { + initialize_chip(pcidev); + index++; + } + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)))) { + initialize_chip(pcidev); + index++; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw"); +MODULE_DESCRIPTION("CM8x38 Audio Driver"); +MODULE_LICENSE("GPL"); + + +static void __exit cleanup_cmpci(void) +{ + struct cm_state *s; + + while ((s = devs)) { + devs = devs->next; + outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ + synchronize_irq(); + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ + free_irq(s->irq, s); + + /* reset mixer */ + wrmixer(s, DSP_MIX_DATARESETIDX, 0); + + release_region(s->iobase, CM_EXTENT_CODEC); +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI + if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM + if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); +#endif + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); +#ifdef CONFIG_SOUND_ALSA_CMPCI_MIDI + unregister_sound_midi(s->dev_midi); +#endif +#ifdef CONFIG_SOUND_ALSA_CMPCI_FM + unregister_sound_special(s->dev_dmfm); +#endif + kfree(s); + } + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + printk(KERN_INFO "cmpci: unloading\n"); +} + +module_init(init_cmpci); +module_exit(cleanup_cmpci); diff -Nru linux/sound/oss/coproc.h linux-2.4.19-pre5-mjc/sound/oss/coproc.h --- linux/sound/oss/coproc.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/coproc.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,12 @@ +/* + * Definitions for various on board processors on the sound cards. For + * example DSP processors. + */ + +/* + * Coprocessor access types + */ +#define COPR_CUSTOM 0x0001 /* Custom applications */ +#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */ +#define COPR_PCM 0x0004 /* Digitized voice applications */ +#define COPR_SYNTH 0x0008 /* Music synthesis */ diff -Nru linux/sound/oss/cs4232.c linux-2.4.19-pre5-mjc/sound/oss/cs4232.c --- linux/sound/oss/cs4232.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4232.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,507 @@ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * cs4232.c + * + * The low level driver for Crystal CS4232 based cards. The CS4232 is + * a PnP compatible chip which contains a CS4231A codec, SB emulation, + * a MPU401 compatible MIDI port, joystick and synthesizer and IDE CD-ROM + * interfaces. This is just a temporary driver until full PnP support + * gets implemented. Just the WSS codec, FM synth and the MIDI ports are + * supported. Other interfaces are left uninitialized. + * + * ifdef ...WAVEFRONT... + * + * Support is provided for initializing the WaveFront synth + * interface as well, which is logical device #4. Note that if + * you have a Tropez+ card, you probably don't need to setup + * the CS4232-supported MIDI interface, since it corresponds to + * the internal 26-pin header that's hard to access. Using this + * requires an additional IRQ, a resource none too plentiful in + * this environment. Just don't set module parameters mpuio and + * mpuirq, and the MIDI port will be left uninitialized. You can + * still use the ICS2115 hosted MIDI interface which corresponds + * to the 9-pin D connector on the back of the card. + * + * endif ...WAVEFRONT... + * + * Supported chips are: + * CS4232 + * CS4236 + * CS4236B + * + * Note: You will need a PnP config setup to initialise some CS4232 boards + * anyway. + * + * Changes + * Alan Cox Modularisation, Basic cleanups. + * Paul Barton-Davis Separated MPU configuration, added + * Tropez+ (WaveFront) support + * Christoph Hellwig Adapted to module_init/module_exit, + * simple cleanups + * Arnaldo C. de Melo got rid of attach_uart401 + * Bartlomiej Zolnierkiewicz + * Added some __init/__initdata/__exit + * Marcus Meissner Added ISA PnP support. + */ + +#include +#include +#include +#include + +#include "sound_config.h" + +#include "cs4232.h" +#include "ad1848.h" +#include "mpu401.h" + +#define KEY_PORT 0x279 /* Same as LPT1 status port */ +#define CSN_NUM 0x99 /* Just a random number */ + +static void CS_OUT(unsigned char a) +{ + outb(a, KEY_PORT); +} + +#define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);} +#define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);} + +static int mpu_base = 0, mpu_irq = 0; +static int synth_base = 0, synth_irq = 0; +static int mpu_detected = 0; + +int __init probe_cs4232_mpu(struct address_info *hw_config) +{ + /* + * Just write down the config values. + */ + + mpu_base = hw_config->io_base; + mpu_irq = hw_config->irq; + + return 1; +} + +static unsigned char crystal_key[] __initdata = /* A 32 byte magic key sequence */ +{ + 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc, + 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2, + 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13, + 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a +}; + +static void sleep(unsigned howlong) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured) +{ + int i, n; + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + /* + * Verify that the I/O port range is free. + */ + + if (check_region(base, 4)) + { + printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base); + return 0; + } + if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) { + return 1; /* The card is already active */ + } + if (isapnp_configured) { + printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n"); + return 0; + } + + /* + * This version of the driver doesn't use the PnP method when configuring + * the card but a simplified method defined by Crystal. This means that + * just one CS4232 compatible device can exist on the system. Also this + * method conflicts with possible PnP support in the OS. For this reason + * driver is just a temporary kludge. + * + * Also the Cirrus/Crystal method doesnt always work. Try ISA PnP first ;) + */ + + /* + * Repeat initialization few times since it doesn't always succeed in + * first time. + */ + + for (n = 0; n < 4; n++) + { + /* + * Wake up the card by sending a 32 byte Crystal key to the key port. + */ + + for (i = 0; i < 32; i++) + CS_OUT(crystal_key[i]); + + sleep(HZ / 10); + + /* + * Now set the CSN (Card Select Number). + */ + + CS_OUT2(0x06, CSN_NUM); + + /* + * Then set some config bytes. First logical device 0 + */ + + CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ + CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */ + + if (check_region(0x388, 4)) /* Not free */ + CS_OUT3(0x48, 0x00, 0x00) /* FM base off */ + else + CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */ + + CS_OUT3(0x42, 0x00, 0x00); /* SB base off */ + CS_OUT2(0x22, irq); /* SB+WSS IRQ */ + CS_OUT2(0x2a, dma1); /* SB+WSS DMA */ + + if (dma2 != -1) + CS_OUT2(0x25, dma2) /* WSS DMA2 */ + else + CS_OUT2(0x25, 4); /* No WSS DMA2 */ + + CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */ + + sleep(HZ / 10); + + /* + * Initialize logical device 3 (MPU) + */ + + if (mpu_base != 0 && mpu_irq != 0) + { + CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */ + CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */ + CS_OUT2(0x22, mpu_irq); /* MPU IRQ */ + CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */ + } + + if(synth_base != 0) + { + CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */ + CS_OUT3 (0x47, (synth_base >> 8) & 0xff, + synth_base & 0xff); /* base */ + CS_OUT2 (0x22, synth_irq); /* IRQ */ + CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */ + } + + /* + * Finally activate the chip + */ + + CS_OUT(0x79); + + sleep(HZ / 5); + + /* + * Then try to detect the codec part of the chip + */ + + if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) + return 1; + + sleep(HZ); + } + return 0; +} + +void __init attach_cs4232(struct address_info *hw_config) +{ + int base = hw_config->io_base, + irq = hw_config->irq, + dma1 = hw_config->dma, + dma2 = hw_config->dma2; + + if (base == -1 || irq == -1 || dma1 == -1) { + printk(KERN_ERR "cs4232: dma, irq and io must be set.\n"); + return; + } + + if (dma2 == -1) + dma2 = dma1; + + hw_config->slots[0] = ad1848_init("Crystal audio controller", base, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0, + hw_config->osp, + THIS_MODULE); + + if (hw_config->slots[0] != -1 && + audio_devs[hw_config->slots[0]]->mixer_dev!=-1) + { + /* Assume the mixer map is as suggested in the CS4232 databook */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */ + } + if (mpu_base != 0 && mpu_irq != 0) + { + static struct address_info hw_config2 = { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + + if (probe_uart401(&hw_config2, THIS_MODULE)) + { + mpu_detected = 1; + } + else + { + mpu_base = mpu_irq = 0; + } + hw_config->slots[1] = hw_config2.slots[1]; + } +} + +static void __exit unload_cs4232(struct address_info *hw_config) +{ + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = dma1; + + ad1848_unload(base, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0); + + sound_unload_audiodev(hw_config->slots[0]); + if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) + { + static struct address_info hw_config2 = + { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + hw_config2.slots[1] = hw_config->slots[1]; + + unload_uart401(&hw_config2); + } +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata mpuio = -1; +static int __initdata mpuirq = -1; +static int __initdata synthio = -1; +static int __initdata synthirq = -1; +static int __initdata isapnp = 1; + +MODULE_DESCRIPTION("CS4232 based soundcard driver"); +MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(io,"i"); +MODULE_PARM_DESC(io,"base I/O port for AD1848"); +MODULE_PARM(irq,"i"); +MODULE_PARM_DESC(irq,"IRQ for AD1848 chip"); +MODULE_PARM(dma,"i"); +MODULE_PARM_DESC(dma,"8 bit DMA for AD1848 chip"); +MODULE_PARM(dma2,"i"); +MODULE_PARM_DESC(dma2,"16 bit DMA for AD1848 chip"); +MODULE_PARM(mpuio,"i"); +MODULE_PARM_DESC(mpuio,"MPU 401 base address"); +MODULE_PARM(mpuirq,"i"); +MODULE_PARM_DESC(mpuirq,"MPU 401 IRQ"); +MODULE_PARM(synthio,"i"); +MODULE_PARM_DESC(synthio,"Maui WaveTable base I/O port"); +MODULE_PARM(synthirq,"i"); +MODULE_PARM_DESC(synthirq,"Maui WaveTable IRQ"); +MODULE_PARM(isapnp,"i"); +MODULE_PARM_DESC(isapnp,"Enable ISAPnP probing (default 1)"); + +/* + * Install a CS4232 based card. Need to have ad1848 and mpu401 + * loaded ready. + */ + +/* All cs4232 based cards have the main ad1848 card either as CSC0000 or + * CSC0100. */ +struct isapnp_device_id isapnp_cs4232_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), + 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), + 0 }, + /* Guillemot Turtlebeach something appears to be cs4232 compatible + * (untested) */ + { ISAPNP_VENDOR('C','S','C'), ISAPNP_ANY_ID, + ISAPNP_VENDOR('G','I','M'), ISAPNP_FUNCTION(0x0100), + 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_cs4232_list); + +int cs4232_isapnp_probe(struct pci_dev *dev, const struct isapnp_device_id *id) +{ + int ret; + struct address_info *isapnpcfg; + + isapnpcfg=(struct address_info*)kmalloc(sizeof(*isapnpcfg),GFP_KERNEL); + if (!isapnpcfg) + return -ENOMEM; + /* + * If device is active, assume configured with /proc/isapnp + * and use anyway. Any other way to check this? + */ + ret = dev->prepare(dev); + if(ret && ret != -EBUSY) { + printk(KERN_ERR "cs4232: ISA PnP found device that could not be autoconfigured.\n"); + kfree(isapnpcfg); + return -ENODEV; + } + if(ret != -EBUSY) { + if(dev->activate(dev) < 0) { + printk(KERN_WARNING "cs4232: ISA PnP activate failed\n"); + kfree(isapnpcfg); + return -ENODEV; + } + } /* else subfunction is already activated */ + + isapnpcfg->irq = dev->irq_resource[0].start; + isapnpcfg->dma = dev->dma_resource[0].start; + isapnpcfg->dma2 = dev->dma_resource[1].start; + isapnpcfg->io_base = dev->resource[0].start; + if (probe_cs4232(isapnpcfg,TRUE) == 0) { + printk(KERN_ERR "cs4232: ISA PnP card found, but not detected?\n"); + return -ENODEV; + } + attach_cs4232(isapnpcfg); + pci_set_drvdata(dev,isapnpcfg); + return 0; +} + +static int __init init_cs4232(void) +{ +#ifdef CONFIG_SOUND_ALSA_WAVEFRONT_MODULE + if(synthio == -1) + printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n"); + else { + synth_base = synthio; + synth_irq = synthirq; + } +#else + if(synthio != -1) + printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n"); +#endif + cfg.irq = -1; + + if (isapnp && + (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0) + ) + return 0; + + if(io==-1||irq==-1||dma==-1) + { + printk(KERN_ERR "cs4232: Must set io, irq and dma.\n"); + return -ENODEV; + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg_mpu.io_base = -1; + cfg_mpu.irq = -1; + + if (mpuio != -1 && mpuirq != -1) { + cfg_mpu.io_base = mpuio; + cfg_mpu.irq = mpuirq; + probe_cs4232_mpu(&cfg_mpu); /* Bug always returns 0 not OK -- AC */ + } + + if (probe_cs4232(&cfg,FALSE) == 0) + return -ENODEV; + attach_cs4232(&cfg); + + return 0; +} + +static int __exit cs4232_isapnp_remove(struct pci_dev *dev, + const struct isapnp_device_id *id) +{ + struct address_info *cfg = (struct address_info*)pci_get_drvdata(dev); + if (cfg) + unload_cs4232(cfg); + pci_set_drvdata(dev,NULL); + dev->deactivate(dev); + return 0; +} + +static void __exit cleanup_cs4232(void) +{ + isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_remove); + if (cfg.irq != -1) + unload_cs4232(&cfg); /* Unloads global MPU as well, if needed */ +} + +module_init(init_cs4232); +module_exit(cleanup_cs4232); + +#ifndef MODULE +static int __init setup_cs4232(char *str) +{ + /* io, irq, dma, dma2 mpuio, mpuirq*/ + int ints[7]; + + /* If we have isapnp cards, no need for options */ + if (isapnp_probe_devs(isapnp_cs4232_list, cs4232_isapnp_probe) > 0) + return 1; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mpuio = ints[5]; + mpuirq = ints[6]; + + return 1; +} + +__setup("cs4232=", setup_cs4232); +#endif diff -Nru linux/sound/oss/cs4232.h linux-2.4.19-pre5-mjc/sound/oss/cs4232.h --- linux/sound/oss/cs4232.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4232.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,11 @@ +/* + * cs4232.h + * + * Copyright: Christoph Hellwig + * + */ + +int probe_cs4232 (struct address_info *hw_config,int useisapnp); +void attach_cs4232 (struct address_info *hw_config); +int probe_cs4232_mpu (struct address_info *hw_config); +void attach_cs4232_mpu (struct address_info *hw_config); diff -Nru linux/sound/oss/cs4281/Makefile linux-2.4.19-pre5-mjc/sound/oss/cs4281/Makefile --- linux/sound/oss/cs4281/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,12 @@ +# Makefile for Cirrus Logic-Crystal CS4281 +# + +O_TARGET := cs4281.o + +obj-y := cs4281m.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s diff -Nru linux/sound/oss/cs4281/cs4281_hwdefs.h linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_hwdefs.h --- linux/sound/oss/cs4281/cs4281_hwdefs.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_hwdefs.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1234 @@ +//**************************************************************************** +// +// HWDEFS.H - Definitions of the registers and data structures used by the +// CS4281 +// +// Copyright (c) 1999,2000,2001 Crystal Semiconductor Corp. +// +//**************************************************************************** + +#ifndef _H_HWDEFS +#define _H_HWDEFS + +//**************************************************************************** +// +// The following define the offsets of the registers located in the PCI +// configuration space of the CS4281 part. +// +//**************************************************************************** +#define PCICONFIG_DEVID_VENID 0x00000000L +#define PCICONFIG_STATUS_COMMAND 0x00000004L +#define PCICONFIG_CLASS_REVISION 0x00000008L +#define PCICONFIG_LATENCY_TIMER 0x0000000CL +#define PCICONFIG_BA0 0x00000010L +#define PCICONFIG_BA1 0x00000014L +#define PCICONFIG_SUBSYSID_SUBSYSVENID 0x0000002CL +#define PCICONFIG_INTERRUPT 0x0000003CL + +//**************************************************************************** +// +// The following define the offsets of the registers accessed via base address +// register zero on the CS4281 part. +// +//**************************************************************************** +#define BA0_HISR 0x00000000L +#define BA0_HICR 0x00000008L +#define BA0_HIMR 0x0000000CL +#define BA0_IIER 0x00000010L +#define BA0_HDSR0 0x000000F0L +#define BA0_HDSR1 0x000000F4L +#define BA0_HDSR2 0x000000F8L +#define BA0_HDSR3 0x000000FCL +#define BA0_DCA0 0x00000110L +#define BA0_DCC0 0x00000114L +#define BA0_DBA0 0x00000118L +#define BA0_DBC0 0x0000011CL +#define BA0_DCA1 0x00000120L +#define BA0_DCC1 0x00000124L +#define BA0_DBA1 0x00000128L +#define BA0_DBC1 0x0000012CL +#define BA0_DCA2 0x00000130L +#define BA0_DCC2 0x00000134L +#define BA0_DBA2 0x00000138L +#define BA0_DBC2 0x0000013CL +#define BA0_DCA3 0x00000140L +#define BA0_DCC3 0x00000144L +#define BA0_DBA3 0x00000148L +#define BA0_DBC3 0x0000014CL +#define BA0_DMR0 0x00000150L +#define BA0_DCR0 0x00000154L +#define BA0_DMR1 0x00000158L +#define BA0_DCR1 0x0000015CL +#define BA0_DMR2 0x00000160L +#define BA0_DCR2 0x00000164L +#define BA0_DMR3 0x00000168L +#define BA0_DCR3 0x0000016CL +#define BA0_DLMR 0x00000170L +#define BA0_DLSR 0x00000174L +#define BA0_FCR0 0x00000180L +#define BA0_FCR1 0x00000184L +#define BA0_FCR2 0x00000188L +#define BA0_FCR3 0x0000018CL +#define BA0_FPDR0 0x00000190L +#define BA0_FPDR1 0x00000194L +#define BA0_FPDR2 0x00000198L +#define BA0_FPDR3 0x0000019CL +#define BA0_FCHS 0x0000020CL +#define BA0_FSIC0 0x00000210L +#define BA0_FSIC1 0x00000214L +#define BA0_FSIC2 0x00000218L +#define BA0_FSIC3 0x0000021CL +#define BA0_PCICFG00 0x00000300L +#define BA0_PCICFG04 0x00000304L +#define BA0_PCICFG08 0x00000308L +#define BA0_PCICFG0C 0x0000030CL +#define BA0_PCICFG10 0x00000310L +#define BA0_PCICFG14 0x00000314L +#define BA0_PCICFG18 0x00000318L +#define BA0_PCICFG1C 0x0000031CL +#define BA0_PCICFG20 0x00000320L +#define BA0_PCICFG24 0x00000324L +#define BA0_PCICFG28 0x00000328L +#define BA0_PCICFG2C 0x0000032CL +#define BA0_PCICFG30 0x00000330L +#define BA0_PCICFG34 0x00000334L +#define BA0_PCICFG38 0x00000338L +#define BA0_PCICFG3C 0x0000033CL +#define BA0_PCICFG40 0x00000340L +#define BA0_PMCS 0x00000344L +#define BA0_CWPR 0x000003E0L +#define BA0_EPPMC 0x000003E4L +#define BA0_GPIOR 0x000003E8L +#define BA0_SPMC 0x000003ECL +#define BA0_CFLR 0x000003F0L +#define BA0_IISR 0x000003F4L +#define BA0_TMS 0x000003F8L +#define BA0_SSVID 0x000003FCL +#define BA0_CLKCR1 0x00000400L +#define BA0_FRR 0x00000410L +#define BA0_SLT12O 0x0000041CL +#define BA0_SERMC 0x00000420L +#define BA0_SERC1 0x00000428L +#define BA0_SERC2 0x0000042CL +#define BA0_SLT12M 0x0000045CL +#define BA0_ACCTL 0x00000460L +#define BA0_ACSTS 0x00000464L +#define BA0_ACOSV 0x00000468L +#define BA0_ACCAD 0x0000046CL +#define BA0_ACCDA 0x00000470L +#define BA0_ACISV 0x00000474L +#define BA0_ACSAD 0x00000478L +#define BA0_ACSDA 0x0000047CL +#define BA0_JSPT 0x00000480L +#define BA0_JSCTL 0x00000484L +#define BA0_MIDCR 0x00000490L +#define BA0_MIDCMD 0x00000494L +#define BA0_MIDSR 0x00000494L +#define BA0_MIDWP 0x00000498L +#define BA0_MIDRP 0x0000049CL +#define BA0_AODSD1 0x000004A8L +#define BA0_AODSD2 0x000004ACL +#define BA0_CFGI 0x000004B0L +#define BA0_SLT12M2 0x000004DCL +#define BA0_ACSTS2 0x000004E4L +#define BA0_ACISV2 0x000004F4L +#define BA0_ACSAD2 0x000004F8L +#define BA0_ACSDA2 0x000004FCL +#define BA0_IOTGP 0x00000500L +#define BA0_IOTSB 0x00000504L +#define BA0_IOTFM 0x00000508L +#define BA0_IOTDMA 0x0000050CL +#define BA0_IOTAC0 0x00000500L +#define BA0_IOTAC1 0x00000504L +#define BA0_IOTAC2 0x00000508L +#define BA0_IOTAC3 0x0000050CL +#define BA0_IOTPCP 0x0000052CL +#define BA0_IOTCC 0x00000530L +#define BA0_IOTCR 0x0000058CL +#define BA0_PCPRR 0x00000600L +#define BA0_PCPGR 0x00000604L +#define BA0_PCPCR 0x00000608L +#define BA0_PCPCIEN 0x00000608L +#define BA0_SBMAR 0x00000700L +#define BA0_SBMDR 0x00000704L +#define BA0_SBRR 0x00000708L +#define BA0_SBRDP 0x0000070CL +#define BA0_SBWDP 0x00000710L +#define BA0_SBWBS 0x00000710L +#define BA0_SBRBS 0x00000714L +#define BA0_FMSR 0x00000730L +#define BA0_B0AP 0x00000730L +#define BA0_FMDP 0x00000734L +#define BA0_B1AP 0x00000738L +#define BA0_B1DP 0x0000073CL +#define BA0_SSPM 0x00000740L +#define BA0_DACSR 0x00000744L +#define BA0_ADCSR 0x00000748L +#define BA0_SSCR 0x0000074CL +#define BA0_FMLVC 0x00000754L +#define BA0_FMRVC 0x00000758L +#define BA0_SRCSA 0x0000075CL +#define BA0_PPLVC 0x00000760L +#define BA0_PPRVC 0x00000764L +#define BA0_PASR 0x00000768L +#define BA0_CASR 0x0000076CL + +//**************************************************************************** +// +// The following define the offsets of the AC97 shadow registers, which appear +// as a virtual extension to the base address register zero memory range. +// +//**************************************************************************** +#define AC97_REG_OFFSET_MASK 0x0000007EL +#define AC97_CODEC_NUMBER_MASK 0x00003000L + +#define BA0_AC97_RESET 0x00001000L +#define BA0_AC97_MASTER_VOLUME 0x00001002L +#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L +#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L +#define BA0_AC97_MASTER_TONE 0x00001008L +#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL +#define BA0_AC97_PHONE_VOLUME 0x0000100CL +#define BA0_AC97_MIC_VOLUME 0x0000100EL +#define BA0_AC97_LINE_IN_VOLUME 0x00001010L +#define BA0_AC97_CD_VOLUME 0x00001012L +#define BA0_AC97_VIDEO_VOLUME 0x00001014L +#define BA0_AC97_AUX_VOLUME 0x00001016L +#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L +#define BA0_AC97_RECORD_SELECT 0x0000101AL +#define BA0_AC97_RECORD_GAIN 0x0000101CL +#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL +#define BA0_AC97_GENERAL_PURPOSE 0x00001020L +#define BA0_AC97_3D_CONTROL 0x00001022L +#define BA0_AC97_MODEM_RATE 0x00001024L +#define BA0_AC97_POWERDOWN 0x00001026L +#define BA0_AC97_EXT_AUDIO_ID 0x00001028L +#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL +#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL +#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL +#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L +#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L +#define BA0_AC97_MIC_ADC_RATE 0x00001034L +#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L +#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L +#define BA0_AC97_RESERVED_3A 0x0000103AL +#define BA0_AC97_EXT_MODEM_ID 0x0000103CL +#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL +#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L +#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L +#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L +#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L +#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L +#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL +#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL +#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL +#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L +#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L +#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L +#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L +#define BA0_AC97_RESERVED_58 0x00001058L +#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL +#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL +#define BA0_AC97_AC_MODE 0x0000105EL +#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L +#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L +#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L +#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L +#define BA0_AC97_SPDIF_CONTROL 0x00001068L +#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL +#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL +#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL +#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L +#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L +#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L +#define BA0_AC97_CAL_ADDRESS 0x00001076L +#define BA0_AC97_CAL_DATA 0x00001078L +#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL +#define BA0_AC97_VENDOR_ID1 0x0000107CL +#define BA0_AC97_VENDOR_ID2 0x0000107EL + +//**************************************************************************** +// +// The following define the offsets of the registers and memories accessed via +// base address register one on the CS4281 part. +// +//**************************************************************************** + +//**************************************************************************** +// +// The following defines are for the flags in the PCI device ID/vendor ID +// register. +// +//**************************************************************************** +#define PDV_VENID_MASK 0x0000FFFFL +#define PDV_DEVID_MASK 0xFFFF0000L +#define PDV_VENID_SHIFT 0L +#define PDV_DEVID_SHIFT 16L +#define VENID_CIRRUS_LOGIC 0x1013L +#define DEVID_CS4281 0x6005L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI status and command +// register. +// +//**************************************************************************** +#define PSC_IO_SPACE_ENABLE 0x00000001L +#define PSC_MEMORY_SPACE_ENABLE 0x00000002L +#define PSC_BUS_MASTER_ENABLE 0x00000004L +#define PSC_SPECIAL_CYCLES 0x00000008L +#define PSC_MWI_ENABLE 0x00000010L +#define PSC_VGA_PALETTE_SNOOP 0x00000020L +#define PSC_PARITY_RESPONSE 0x00000040L +#define PSC_WAIT_CONTROL 0x00000080L +#define PSC_SERR_ENABLE 0x00000100L +#define PSC_FAST_B2B_ENABLE 0x00000200L +#define PSC_UDF_MASK 0x007F0000L +#define PSC_FAST_B2B_CAPABLE 0x00800000L +#define PSC_PARITY_ERROR_DETECTED 0x01000000L +#define PSC_DEVSEL_TIMING_MASK 0x06000000L +#define PSC_TARGET_ABORT_SIGNALLED 0x08000000L +#define PSC_RECEIVED_TARGET_ABORT 0x10000000L +#define PSC_RECEIVED_MASTER_ABORT 0x20000000L +#define PSC_SIGNALLED_SERR 0x40000000L +#define PSC_DETECTED_PARITY_ERROR 0x80000000L +#define PSC_UDF_SHIFT 16L +#define PSC_DEVSEL_TIMING_SHIFT 25L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI class/revision ID +// register. +// +//**************************************************************************** +#define PCR_REVID_MASK 0x000000FFL +#define PCR_INTERFACE_MASK 0x0000FF00L +#define PCR_SUBCLASS_MASK 0x00FF0000L +#define PCR_CLASS_MASK 0xFF000000L +#define PCR_REVID_SHIFT 0L +#define PCR_INTERFACE_SHIFT 8L +#define PCR_SUBCLASS_SHIFT 16L +#define PCR_CLASS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI latency timer register. +// +//**************************************************************************** +#define PLT_CACHE_LINE_SIZE_MASK 0x000000FFL +#define PLT_LATENCY_TIMER_MASK 0x0000FF00L +#define PLT_HEADER_TYPE_MASK 0x00FF0000L +#define PLT_BIST_MASK 0xFF000000L +#define PLT_CACHE_LINE_SIZE_SHIFT 0L +#define PLT_LATENCY_TIMER_SHIFT 8L +#define PLT_HEADER_TYPE_SHIFT 16L +#define PLT_BIST_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI base address registers. +// +//**************************************************************************** +#define PBAR_MEMORY_SPACE_INDICATOR 0x00000001L +#define PBAR_LOCATION_TYPE_MASK 0x00000006L +#define PBAR_NOT_PREFETCHABLE 0x00000008L +#define PBAR_ADDRESS_MASK 0xFFFFFFF0L +#define PBAR_LOCATION_TYPE_SHIFT 1L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI subsystem ID/subsystem +// vendor ID register. +// +//**************************************************************************** +#define PSS_SUBSYSTEM_VENDOR_ID_MASK 0x0000FFFFL +#define PSS_SUBSYSTEM_ID_MASK 0xFFFF0000L +#define PSS_SUBSYSTEM_VENDOR_ID_SHIFT 0L +#define PSS_SUBSYSTEM_ID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI interrupt register. +// +//**************************************************************************** +#define PI_LINE_MASK 0x000000FFL +#define PI_PIN_MASK 0x0000FF00L +#define PI_MIN_GRANT_MASK 0x00FF0000L +#define PI_MAX_LATENCY_MASK 0xFF000000L +#define PI_LINE_SHIFT 0L +#define PI_PIN_SHIFT 8L +#define PI_MIN_GRANT_SHIFT 16L +#define PI_MAX_LATENCY_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt status +// register. +// +//**************************************************************************** +#define HISR_HVOLMASK 0x00000003L +#define HISR_VDNI 0x00000001L +#define HISR_VUPI 0x00000002L +#define HISR_GP1I 0x00000004L +#define HISR_GP3I 0x00000008L +#define HISR_GPSI 0x00000010L +#define HISR_GPPI 0x00000020L +#define HISR_DMAI 0x00040000L +#define HISR_FIFOI 0x00100000L +#define HISR_HVOL 0x00200000L +#define HISR_MIDI 0x00400000L +#define HISR_SBINT 0x00800000L +#define HISR_INTENA 0x80000000L +#define HISR_DMA_MASK 0x00000F00L +#define HISR_FIFO_MASK 0x0000F000L +#define HISR_DMA_SHIFT 8L +#define HISR_FIFO_SHIFT 12L +#define HISR_FIFO0 0x00001000L +#define HISR_FIFO1 0x00002000L +#define HISR_FIFO2 0x00004000L +#define HISR_FIFO3 0x00008000L +#define HISR_DMA0 0x00000100L +#define HISR_DMA1 0x00000200L +#define HISR_DMA2 0x00000400L +#define HISR_DMA3 0x00000800L +#define HISR_RESERVED 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt control +// register. +// +//**************************************************************************** +#define HICR_IEV 0x00000001L +#define HICR_CHGM 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Mode Register n +// (DMRn) +// +//**************************************************************************** +#define DMRn_TR_MASK 0x0000000CL +#define DMRn_TR_SHIFT 2L +#define DMRn_AUTO 0x00000010L +#define DMRn_TR_READ 0x00000008L +#define DMRn_TR_WRITE 0x00000004L +#define DMRn_TYPE_MASK 0x000000C0L +#define DMRn_TYPE_SHIFT 6L +#define DMRn_SIZE8 0x00010000L +#define DMRn_MONO 0x00020000L +#define DMRn_BEND 0x00040000L +#define DMRn_USIGN 0x00080000L +#define DMRn_SIZE20 0x00100000L +#define DMRn_SWAPC 0x00400000L +#define DMRn_CBC 0x01000000L +#define DMRn_TBC 0x02000000L +#define DMRn_POLL 0x10000000L +#define DMRn_DMA 0x20000000L +#define DMRn_FSEL_MASK 0xC0000000L +#define DMRn_FSEL_SHIFT 30L +#define DMRn_FSEL0 0x00000000L +#define DMRn_FSEL1 0x40000000L +#define DMRn_FSEL2 0x80000000L +#define DMRn_FSEL3 0xC0000000L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Command Register n +// (DCRn) +// +//**************************************************************************** +#define DCRn_HTCIE 0x00020000L +#define DCRn_TCIE 0x00010000L +#define DCRn_MSK 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the FIFO Control +// register n.(FCRn) +// +//**************************************************************************** +#define FCRn_OF_MASK 0x0000007FL +#define FCRn_OF_SHIFT 0L +#define FCRn_SZ_MASK 0x00007F00L +#define FCRn_SZ_SHIFT 8L +#define FCRn_LS_MASK 0x001F0000L +#define FCRn_LS_SHIFT 16L +#define FCRn_RS_MASK 0x1F000000L +#define FCRn_RS_SHIFT 24L +#define FCRn_FEN 0x80000000L +#define FCRn_PSH 0x20000000L +#define FCRn_DACZ 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port Power Management +// control register.(SPMC) +// +//**************************************************************************** +#define SPMC_RSTN 0x00000001L +#define SPMC_ASYN 0x00000002L +#define SPMC_WUP1 0x00000004L +#define SPMC_WUP2 0x00000008L +#define SPMC_ASDI2E 0x00000100L +#define SPMC_ESSPD 0x00000200L +#define SPMC_GISPEN 0x00004000L +#define SPMC_GIPPEN 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the Configuration Load register. +// (CFLR) +// +//**************************************************************************** +#define CFLR_CLOCK_SOURCE_MASK 0x00000003L +#define CFLR_CLOCK_SOURCE_AC97 0x00000001L + +#define CFLR_CB0_MASK 0x000000FFL +#define CFLR_CB1_MASK 0x0000FF00L +#define CFLR_CB2_MASK 0x00FF0000L +#define CFLR_CB3_MASK 0xFF000000L +#define CFLR_CB0_SHIFT 0L +#define CFLR_CB1_SHIFT 8L +#define CFLR_CB2_SHIFT 16L +#define CFLR_CB3_SHIFT 24L + +#define IOTCR_DMA0 0x00000000L +#define IOTCR_DMA1 0x00000400L +#define IOTCR_DMA2 0x00000800L +#define IOTCR_DMA3 0x00000C00L +#define IOTCR_CCLS 0x00000100L +#define IOTCR_PCPCI 0x00000200L +#define IOTCR_DDMA 0x00000300L + +#define SBWBS_WBB 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the SRC Slot Assignment Register +// (SRCSA) +// +//**************************************************************************** +#define SRCSA_PLSS_MASK 0x0000001FL +#define SRCSA_PLSS_SHIFT 0L +#define SRCSA_PRSS_MASK 0x00001F00L +#define SRCSA_PRSS_SHIFT 8L +#define SRCSA_CLSS_MASK 0x001F0000L +#define SRCSA_CLSS_SHIFT 16L +#define SRCSA_CRSS_MASK 0x1F000000L +#define SRCSA_CRSS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Power Management +// register.(SSPM) +// +//**************************************************************************** +#define SSPM_FPDN 0x00000080L +#define SSPM_MIXEN 0x00000040L +#define SSPM_CSRCEN 0x00000020L +#define SSPM_PSRCEN 0x00000010L +#define SSPM_JSEN 0x00000008L +#define SSPM_ACLEN 0x00000004L +#define SSPM_FMEN 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Control +// Register. (SSCR) +// +//**************************************************************************** +#define SSCR_SB 0x00000004L +#define SSCR_HVC 0x00000008L +#define SSCR_LPFIFO 0x00000040L +#define SSCR_LPSRC 0x00000080L +#define SSCR_XLPSRC 0x00000100L +#define SSCR_MVMD 0x00010000L +#define SSCR_MVAD 0x00020000L +#define SSCR_MVLD 0x00040000L +#define SSCR_MVCS 0x00080000L + +//**************************************************************************** +// +// The following defines are for the flags in the Clock Control Register 1. +// (CLKCR1) +// +//**************************************************************************** +#define CLKCR1_DLLSS_MASK 0x0000000CL +#define CLKCR1_DLLSS_SHIFT 2L +#define CLKCR1_DLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_DLLOS 0x00000040L +#define CLKCR1_CKRA 0x00010000L +#define CLKCR1_CKRN 0x00020000L +#define CLKCR1_DLLRDY 0x01000000L +#define CLKCR1_CLKON 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound Blaster Read Buffer +// Status.(SBRBS) +// +//**************************************************************************** +#define SBRBS_RD_MASK 0x0000007FL +#define SBRBS_RD_SHIFT 0L +#define SBRBS_RBF 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port master control +// register.(SERMC) +// +//**************************************************************************** +#define SERMC_MSPE 0x00000001L +#define SERMC_PTC_MASK 0x0000000EL +#define SERMC_PTC_SHIFT 1L +#define SERMC_PTC_AC97 0x00000002L +#define SERMC_PLB 0x00000010L +#define SERMC_PXLB 0x00000020L +#define SERMC_LOFV 0x00080000L +#define SERMC_SLB 0x00100000L +#define SERMC_SXLB 0x00200000L +#define SERMC_ODSEN1 0x01000000L +#define SERMC_ODSEN2 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the General Purpose I/O Register. +// (GPIOR) +// +//**************************************************************************** +#define GPIOR_VDNS 0x00000001L +#define GPIOR_VUPS 0x00000002L +#define GPIOR_GP1S 0x00000004L +#define GPIOR_GP3S 0x00000008L +#define GPIOR_GPSS 0x00000010L +#define GPIOR_GPPS 0x00000020L +#define GPIOR_GP1D 0x00000400L +#define GPIOR_GP3D 0x00000800L +#define GPIOR_VDNLT 0x00010000L +#define GPIOR_VDNPO 0x00020000L +#define GPIOR_VDNST 0x00040000L +#define GPIOR_VDNW 0x00080000L +#define GPIOR_VUPLT 0x00100000L +#define GPIOR_VUPPO 0x00200000L +#define GPIOR_VUPST 0x00400000L +#define GPIOR_VUPW 0x00800000L +#define GPIOR_GP1OE 0x01000000L +#define GPIOR_GP1PT 0x02000000L +#define GPIOR_GP1ST 0x04000000L +#define GPIOR_GP1W 0x08000000L +#define GPIOR_GP3OE 0x10000000L +#define GPIOR_GP3PT 0x20000000L +#define GPIOR_GP3ST 0x40000000L +#define GPIOR_GP3W 0x80000000L + +//**************************************************************************** +// +// The following defines are for the flags in the clock control register 1. +// +//**************************************************************************** +#define CLKCR1_PLLSS_MASK 0x0000000CL +#define CLKCR1_PLLSS_SERIAL 0x00000000L +#define CLKCR1_PLLSS_CRYSTAL 0x00000004L +#define CLKCR1_PLLSS_PCI 0x00000008L +#define CLKCR1_PLLSS_RESERVED 0x0000000CL +#define CLKCR1_PLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_PLLOS 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the feature reporting register. +// +//**************************************************************************** +#define FRR_FAB_MASK 0x00000003L +#define FRR_MASK_MASK 0x0000001CL +#define FRR_ID_MASK 0x00003000L +#define FRR_FAB_SHIFT 0L +#define FRR_MASK_SHIFT 2L +#define FRR_ID_SHIFT 12L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 1 configuration +// register. +// +//**************************************************************************** +#define SERC1_VALUE 0x00000003L +#define SERC1_SO1EN 0x00000001L +#define SERC1_SO1F_MASK 0x0000000EL +#define SERC1_SO1F_CS423X 0x00000000L +#define SERC1_SO1F_AC97 0x00000002L +#define SERC1_SO1F_DAC 0x00000004L +#define SERC1_SO1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 2 configuration +// register. +// +//**************************************************************************** +#define SERC2_VALUE 0x00000003L +#define SERC2_SI1EN 0x00000001L +#define SERC2_SI1F_MASK 0x0000000EL +#define SERC2_SI1F_CS423X 0x00000000L +#define SERC2_SI1F_AC97 0x00000002L +#define SERC2_SI1F_ADC 0x00000004L +#define SERC2_SI1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 control register. +// +//**************************************************************************** +#define ACCTL_ESYN 0x00000002L +#define ACCTL_VFRM 0x00000004L +#define ACCTL_DCV 0x00000008L +#define ACCTL_CRW 0x00000010L +#define ACCTL_TC 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register. +// +//**************************************************************************** +#define ACSTS_CRDY 0x00000001L +#define ACSTS_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 output slot valid +// register. +// +//**************************************************************************** +#define ACOSV_SLV3 0x00000001L +#define ACOSV_SLV4 0x00000002L +#define ACOSV_SLV5 0x00000004L +#define ACOSV_SLV6 0x00000008L +#define ACOSV_SLV7 0x00000010L +#define ACOSV_SLV8 0x00000020L +#define ACOSV_SLV9 0x00000040L +#define ACOSV_SLV10 0x00000080L +#define ACOSV_SLV11 0x00000100L +#define ACOSV_SLV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command address +// register. +// +//**************************************************************************** +#define ACCAD_CI_MASK 0x0000007FL +#define ACCAD_CI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command data register. +// +//**************************************************************************** +#define ACCDA_CD_MASK 0x0000FFFFL +#define ACCDA_CD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register. +// +//**************************************************************************** +#define ACISV_ISV3 0x00000001L +#define ACISV_ISV4 0x00000002L +#define ACISV_ISV5 0x00000004L +#define ACISV_ISV6 0x00000008L +#define ACISV_ISV7 0x00000010L +#define ACISV_ISV8 0x00000020L +#define ACISV_ISV9 0x00000040L +#define ACISV_ISV10 0x00000080L +#define ACISV_ISV11 0x00000100L +#define ACISV_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register. +// +//**************************************************************************** +#define ACSAD_SI_MASK 0x0000007FL +#define ACSAD_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register. +// +//**************************************************************************** +#define ACSDA_SD_MASK 0x0000FFFFL +#define ACSDA_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers (all 12). +// +//**************************************************************************** +#define IOTAC_SA_MASK 0x0000FFFFL +#define IOTAC_MSK_MASK 0x000F0000L +#define IOTAC_IODC_MASK 0x06000000L +#define IOTAC_IODC_16_BIT 0x00000000L +#define IOTAC_IODC_10_BIT 0x02000000L +#define IOTAC_IODC_12_BIT 0x04000000L +#define IOTAC_WSPI 0x08000000L +#define IOTAC_RSPI 0x10000000L +#define IOTAC_WSE 0x20000000L +#define IOTAC_WE 0x40000000L +#define IOTAC_RE 0x80000000L +#define IOTAC_SA_SHIFT 0L +#define IOTAC_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI master enable +// register. +// +//**************************************************************************** +#define PCPCIEN_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick poll/trigger +// register. +// +//**************************************************************************** +#define JSPT_CAX 0x00000001L +#define JSPT_CAY 0x00000002L +#define JSPT_CBX 0x00000004L +#define JSPT_CBY 0x00000008L +#define JSPT_BA1 0x00000010L +#define JSPT_BA2 0x00000020L +#define JSPT_BB1 0x00000040L +#define JSPT_BB2 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick control register. +// The TBF bit has been moved from MIDSR register to JSCTL register bit 8. +// +//**************************************************************************** +#define JSCTL_SP_MASK 0x00000003L +#define JSCTL_SP_SLOW 0x00000000L +#define JSCTL_SP_MEDIUM_SLOW 0x00000001L +#define JSCTL_SP_MEDIUM_FAST 0x00000002L +#define JSCTL_SP_FAST 0x00000003L +#define JSCTL_ARE 0x00000004L +#define JSCTL_TBF 0x00000100L + + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI control register. +// +//**************************************************************************** +#define MIDCR_TXE 0x00000001L +#define MIDCR_RXE 0x00000002L +#define MIDCR_RIE 0x00000004L +#define MIDCR_TIE 0x00000008L +#define MIDCR_MLB 0x00000010L +#define MIDCR_MRST 0x00000020L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI status register. +// +//**************************************************************************** +#define MIDSR_RBE 0x00000080L +#define MIDSR_RDA 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI write port register. +// +//**************************************************************************** +#define MIDWP_MWD_MASK 0x000000FFL +#define MIDWP_MWD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI read port register. +// +//**************************************************************************** +#define MIDRP_MRD_MASK 0x000000FFL +#define MIDRP_MRD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the configuration interface +// register. +// +//**************************************************************************** +#define CFGI_CLK 0x00000001L +#define CFGI_DOUT 0x00000002L +#define CFGI_DIN_EEN 0x00000004L +#define CFGI_EELD 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the subsystem ID and vendor ID +// register. +// +//**************************************************************************** +#define SSVID_VID_MASK 0x0000FFFFL +#define SSVID_SID_MASK 0xFFFF0000L +#define SSVID_VID_SHIFT 0L +#define SSVID_SID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the GPIO pin interface register. +// +//**************************************************************************** +#define GPIOR_VOLDN 0x00000001L +#define GPIOR_VOLUP 0x00000002L +#define GPIOR_SI2D 0x00000004L +#define GPIOR_SI2OE 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register 2. +// +//**************************************************************************** +#define ACSTS2_CRDY 0x00000001L +#define ACSTS2_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register 2. +// +//**************************************************************************** +#define ACISV2_ISV3 0x00000001L +#define ACISV2_ISV4 0x00000002L +#define ACISV2_ISV5 0x00000004L +#define ACISV2_ISV6 0x00000008L +#define ACISV2_ISV7 0x00000010L +#define ACISV2_ISV8 0x00000020L +#define ACISV2_ISV9 0x00000040L +#define ACISV2_ISV10 0x00000080L +#define ACISV2_ISV11 0x00000100L +#define ACISV2_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register 2. +// +//**************************************************************************** +#define ACSAD2_SI_MASK 0x0000007FL +#define ACSAD2_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register 2. +// +//**************************************************************************** +#define ACSDA2_SD_MASK 0x0000FFFFL +#define ACSDA2_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap control register. +// +//**************************************************************************** +#define IOTCR_ITD 0x00000001L +#define IOTCR_HRV 0x00000002L +#define IOTCR_SRV 0x00000004L +#define IOTCR_DTI 0x00000008L +#define IOTCR_DFI 0x00000010L +#define IOTCR_DDP 0x00000020L +#define IOTCR_JTE 0x00000040L +#define IOTCR_PPE 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Hardware Master Volume. +// +//**************************************************************************** +#define IOTGP_SA_MASK 0x0000FFFFL +#define IOTGP_MSK_MASK 0x000F0000L +#define IOTGP_IODC_MASK 0x06000000L +#define IOTGP_IODC_16_BIT 0x00000000L +#define IOTGP_IODC_10_BIT 0x02000000L +#define IOTGP_IODC_12_BIT 0x04000000L +#define IOTGP_WSPI 0x08000000L +#define IOTGP_RSPI 0x10000000L +#define IOTGP_WSE 0x20000000L +#define IOTGP_WE 0x40000000L +#define IOTGP_RE 0x80000000L +#define IOTGP_SA_SHIFT 0L +#define IOTGP_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Sound Blaster +// +//**************************************************************************** +#define IOTSB_SA_MASK 0x0000FFFFL +#define IOTSB_MSK_MASK 0x000F0000L +#define IOTSB_IODC_MASK 0x06000000L +#define IOTSB_IODC_16_BIT 0x00000000L +#define IOTSB_IODC_10_BIT 0x02000000L +#define IOTSB_IODC_12_BIT 0x04000000L +#define IOTSB_WSPI 0x08000000L +#define IOTSB_RSPI 0x10000000L +#define IOTSB_WSE 0x20000000L +#define IOTSB_WE 0x40000000L +#define IOTSB_RE 0x80000000L +#define IOTSB_SA_SHIFT 0L +#define IOTSB_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for FM. +// +//**************************************************************************** +#define IOTFM_SA_MASK 0x0000FFFFL +#define IOTFM_MSK_MASK 0x000F0000L +#define IOTFM_IODC_MASK 0x06000000L +#define IOTFM_IODC_16_BIT 0x00000000L +#define IOTFM_IODC_10_BIT 0x02000000L +#define IOTFM_IODC_12_BIT 0x04000000L +#define IOTFM_WSPI 0x08000000L +#define IOTFM_RSPI 0x10000000L +#define IOTFM_WSE 0x20000000L +#define IOTFM_WE 0x40000000L +#define IOTFM_RE 0x80000000L +#define IOTFM_SA_SHIFT 0L +#define IOTFM_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI request register. +// +//**************************************************************************** +#define PCPRR_RDC_MASK 0x00000007L +#define PCPRR_REQ 0x00008000L +#define PCPRR_RDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI grant register. +// +//**************************************************************************** +#define PCPGR_GDC_MASK 0x00000007L +#define PCPGR_VL 0x00008000L +#define PCPGR_GDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI Control Register. +// +//**************************************************************************** +#define PCPCR_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the debug index register. +// +//**************************************************************************** +#define DREG_REGID_MASK 0x0000007FL +#define DREG_DEBUG 0x00000080L +#define DREG_RGBK_MASK 0x00000700L +#define DREG_TRAP 0x00000800L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000L +#endif +#endif +#define DREG_REGID_SHIFT 0L +#define DREG_RGBK_SHIFT 8L +#define DREG_RGBK_REGID_MASK 0x0000077FL +#define DREG_REGID_R0 0x00000010L +#define DREG_REGID_R1 0x00000011L +#define DREG_REGID_R2 0x00000012L +#define DREG_REGID_R3 0x00000013L +#define DREG_REGID_R4 0x00000014L +#define DREG_REGID_R5 0x00000015L +#define DREG_REGID_R6 0x00000016L +#define DREG_REGID_R7 0x00000017L +#define DREG_REGID_R8 0x00000018L +#define DREG_REGID_R9 0x00000019L +#define DREG_REGID_RA 0x0000001AL +#define DREG_REGID_RB 0x0000001BL +#define DREG_REGID_RC 0x0000001CL +#define DREG_REGID_RD 0x0000001DL +#define DREG_REGID_RE 0x0000001EL +#define DREG_REGID_RF 0x0000001FL +#define DREG_REGID_RA_BUS_LOW 0x00000020L +#define DREG_REGID_RA_BUS_HIGH 0x00000038L +#define DREG_REGID_YBUS_LOW 0x00000050L +#define DREG_REGID_YBUS_HIGH 0x00000058L +#define DREG_REGID_TRAP_0 0x00000100L +#define DREG_REGID_TRAP_1 0x00000101L +#define DREG_REGID_TRAP_2 0x00000102L +#define DREG_REGID_TRAP_3 0x00000103L +#define DREG_REGID_TRAP_4 0x00000104L +#define DREG_REGID_TRAP_5 0x00000105L +#define DREG_REGID_TRAP_6 0x00000106L +#define DREG_REGID_TRAP_7 0x00000107L +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010EL +#define DREG_REGID_TOP_OF_STACK 0x0000010FL +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110L +#define DREG_REGID_TRAP_9 0x00000111L +#define DREG_REGID_TRAP_10 0x00000112L +#define DREG_REGID_TRAP_11 0x00000113L +#define DREG_REGID_TRAP_12 0x00000114L +#define DREG_REGID_TRAP_13 0x00000115L +#define DREG_REGID_TRAP_14 0x00000116L +#define DREG_REGID_TRAP_15 0x00000117L +#define DREG_REGID_TRAP_16 0x00000118L +#define DREG_REGID_TRAP_17 0x00000119L +#define DREG_REGID_TRAP_18 0x0000011AL +#define DREG_REGID_TRAP_19 0x0000011BL +#define DREG_REGID_TRAP_20 0x0000011CL +#define DREG_REGID_TRAP_21 0x0000011DL +#define DREG_REGID_TRAP_22 0x0000011EL +#define DREG_REGID_TRAP_23 0x0000011FL +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200L +#define DREG_REGID_RSA0_HIGH 0x00000201L +#define DREG_REGID_RSA1_LOW 0x00000202L +#define DREG_REGID_RSA1_HIGH 0x00000203L +#define DREG_REGID_RSA2 0x00000204L +#define DREG_REGID_RSA3 0x00000205L +#define DREG_REGID_RSI0_LOW 0x00000206L +#define DREG_REGID_RSI0_HIGH 0x00000207L +#define DREG_REGID_RSI1 0x00000208L +#define DREG_REGID_RSI2 0x00000209L +#define DREG_REGID_SAGUSTATUS 0x0000020AL +#define DREG_REGID_RSCONFIG01_LOW 0x0000020BL +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020CL +#define DREG_REGID_RSCONFIG23_LOW 0x0000020DL +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020EL +#define DREG_REGID_RSDMA01E 0x0000020FL +#define DREG_REGID_RSDMA23E 0x00000210L +#define DREG_REGID_RSD0_LOW 0x00000211L +#define DREG_REGID_RSD0_HIGH 0x00000212L +#define DREG_REGID_RSD1_LOW 0x00000213L +#define DREG_REGID_RSD1_HIGH 0x00000214L +#define DREG_REGID_RSD2_LOW 0x00000215L +#define DREG_REGID_RSD2_HIGH 0x00000216L +#define DREG_REGID_RSD3_LOW 0x00000217L +#define DREG_REGID_RSD3_HIGH 0x00000218L +#define DREG_REGID_SRAR_HIGH 0x0000021AL +#define DREG_REGID_SRAR_LOW 0x0000021BL +#define DREG_REGID_DMA_STATE 0x0000021CL +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021DL +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021EL +#define DREG_REGID_CPU_STATUS 0x00000300L +#define DREG_REGID_MAC_MODE 0x00000301L +#define DREG_REGID_STACK_AND_REPEAT 0x00000302L +#define DREG_REGID_INDEX0 0x00000304L +#define DREG_REGID_INDEX1 0x00000305L +#define DREG_REGID_DMA_STATE_0_3 0x00000400L +#define DREG_REGID_DMA_STATE_4_7 0x00000404L +#define DREG_REGID_DMA_STATE_8_11 0x00000408L +#define DREG_REGID_DMA_STATE_12_15 0x0000040CL +#define DREG_REGID_DMA_STATE_16_19 0x00000410L +#define DREG_REGID_DMA_STATE_20_23 0x00000414L +#define DREG_REGID_DMA_STATE_24_27 0x00000418L +#define DREG_REGID_DMA_STATE_28_31 0x0000041CL +#define DREG_REGID_DMA_STATE_32_35 0x00000420L +#define DREG_REGID_DMA_STATE_36_39 0x00000424L +#define DREG_REGID_DMA_STATE_40_43 0x00000428L +#define DREG_REGID_DMA_STATE_44_47 0x0000042CL +#define DREG_REGID_DMA_STATE_48_51 0x00000430L +#define DREG_REGID_DMA_STATE_52_55 0x00000434L +#define DREG_REGID_DMA_STATE_56_59 0x00000438L +#define DREG_REGID_DMA_STATE_60_63 0x0000043CL +#define DREG_REGID_DMA_STATE_64_67 0x00000440L +#define DREG_REGID_DMA_STATE_68_71 0x00000444L +#define DREG_REGID_DMA_STATE_72_75 0x00000448L +#define DREG_REGID_DMA_STATE_76_79 0x0000044CL +#define DREG_REGID_DMA_STATE_80_83 0x00000450L +#define DREG_REGID_DMA_STATE_84_87 0x00000454L +#define DREG_REGID_DMA_STATE_88_91 0x00000458L +#define DREG_REGID_DMA_STATE_92_95 0x0000045CL +#define DREG_REGID_TRAP_SELECT 0x00000500L +#define DREG_REGID_TRAP_WRITE_0 0x00000500L +#define DREG_REGID_TRAP_WRITE_1 0x00000501L +#define DREG_REGID_TRAP_WRITE_2 0x00000502L +#define DREG_REGID_TRAP_WRITE_3 0x00000503L +#define DREG_REGID_TRAP_WRITE_4 0x00000504L +#define DREG_REGID_TRAP_WRITE_5 0x00000505L +#define DREG_REGID_TRAP_WRITE_6 0x00000506L +#define DREG_REGID_TRAP_WRITE_7 0x00000507L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510L +#define DREG_REGID_TRAP_WRITE_9 0x00000511L +#define DREG_REGID_TRAP_WRITE_10 0x00000512L +#define DREG_REGID_TRAP_WRITE_11 0x00000513L +#define DREG_REGID_TRAP_WRITE_12 0x00000514L +#define DREG_REGID_TRAP_WRITE_13 0x00000515L +#define DREG_REGID_TRAP_WRITE_14 0x00000516L +#define DREG_REGID_TRAP_WRITE_15 0x00000517L +#define DREG_REGID_TRAP_WRITE_16 0x00000518L +#define DREG_REGID_TRAP_WRITE_17 0x00000519L +#define DREG_REGID_TRAP_WRITE_18 0x0000051AL +#define DREG_REGID_TRAP_WRITE_19 0x0000051BL +#define DREG_REGID_TRAP_WRITE_20 0x0000051CL +#define DREG_REGID_TRAP_WRITE_21 0x0000051DL +#define DREG_REGID_TRAP_WRITE_22 0x0000051EL +#define DREG_REGID_TRAP_WRITE_23 0x0000051FL +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600L +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601L +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602L +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603L +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604L +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605L +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606L +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607L +#define DREG_REGID_MAC0_ACC0_MID 0x00000608L +#define DREG_REGID_MAC0_ACC1_MID 0x00000609L +#define DREG_REGID_MAC0_ACC2_MID 0x0000060AL +#define DREG_REGID_MAC0_ACC3_MID 0x0000060BL +#define DREG_REGID_MAC1_ACC0_MID 0x0000060CL +#define DREG_REGID_MAC1_ACC1_MID 0x0000060DL +#define DREG_REGID_MAC1_ACC2_MID 0x0000060EL +#define DREG_REGID_MAC1_ACC3_MID 0x0000060FL +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610L +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611L +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612L +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613L +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614L +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615L +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616L +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617L +#define DREG_REGID_RSHOUT_LOW 0x00000620L +#define DREG_REGID_RSHOUT_MID 0x00000628L +#define DREG_REGID_RSHOUT_HIGH 0x00000630L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 S/PDIF Control register. +// +//**************************************************************************** +#define SPDIF_CONTROL_SPDIF_EN 0x00008000L +#define SPDIF_CONTROL_VAL 0x00004000L +#define SPDIF_CONTROL_COPY 0x00000004L +#define SPDIF_CONTROL_CC0 0x00000010L +#define SPDIF_CONTROL_CC1 0x00000020L +#define SPDIF_CONTROL_CC2 0x00000040L +#define SPDIF_CONTROL_CC3 0x00000080L +#define SPDIF_CONTROL_CC4 0x00000100L +#define SPDIF_CONTROL_CC5 0x00000200L +#define SPDIF_CONTROL_CC6 0x00000400L +#define SPDIF_CONTROL_L 0x00000800L + +#endif // _H_HWDEFS diff -Nru linux/sound/oss/cs4281/cs4281_wrapper-24.c linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_wrapper-24.c --- linux/sound/oss/cs4281/cs4281_wrapper-24.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281_wrapper-24.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,42 @@ +/******************************************************************************* +* +* "cs4281_wrapper.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/20/00 trw - new file. +* +*******************************************************************************/ + +#include + +void cs4281_null(struct pci_dev *pcidev) { return; } +#define cs4x_mem_map_reserve(page) mem_map_reserve(page) +#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page) + +#define free_dmabuf(state, dmabuf) \ + pci_free_consistent(state->pcidev, \ + PAGE_SIZE << (dmabuf)->buforder, \ + (dmabuf)->rawbuf, (dmabuf)->dmaaddr); +#define free_dmabuf2(state, dmabuf) \ + pci_free_consistent((state)->pcidev, \ + PAGE_SIZE << (state)->buforder_tmpbuff, \ + (state)->tmpbuff, (state)->dmaaddr_tmpbuff); +#define cs4x_pgoff(vma) ((vma)->vm_pgoff) + diff -Nru linux/sound/oss/cs4281/cs4281m.c linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281m.c --- linux/sound/oss/cs4281/cs4281m.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281m.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,4528 @@ +/******************************************************************************* +* +* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- adapted from drivers by Thomas Sailer, +* -- but don't bug him; Problems should go to: +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Module command line parameters: +* none +* +* Supported devices: +* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible +* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible +* /dev/midi simple MIDI UART interface, no ioctl +* +* Modification History +* 08/20/00 trw - silence and no stopping DAC until release +* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop. +* 09/18/00 trw - added 16bit only record with conversion +* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous +* capture/playback rates) +* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin +* libOSSm.so) +* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal) +* 11/03/00 trw - fixed interrupt loss/stutter, added debug. +* 11/10/00 bkz - added __devinit to cs4281_hw_init() +* 11/10/00 trw - fixed SMP and capture spinlock hang. +* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm. +* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix. +* 12/08/00 trw - added PM support. +* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 +* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident. +* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup. +* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +* +*******************************************************************************/ + +/* uncomment the following line to disable building PM support into the driver */ +//#define NOT_CS4281_PM 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "cs_dm.h" +#include "cs4281_hwdefs.h" +#include "cs4281pm.h" + +struct cs4281_state; +EXPORT_NO_SYMBOLS; + +static void stop_dac(struct cs4281_state *s); +static void stop_adc(struct cs4281_state *s); +static void start_dac(struct cs4281_state *s); +static void start_adc(struct cs4281_state *s); +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +// --------------------------------------------------------------------- + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281 +#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005 +#endif + +#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS) +#define CS4281_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ + +// buffer order determines the size of the dma buffer for the driver. +// under Linux, a smaller buffer allows more responsiveness from many of the +// applications (e.g. games). A larger buffer allows some of the apps (esound) +// to not underrun the dma buffer as easily. As default, use 32k (order=3) +// rather than 64k as some of the games work more responsively. +// log base 2( buff sz = 32k). +static unsigned long defaultorder = 3; +MODULE_PARM(defaultorder, "i"); + +// +// Turn on/off debugging compilation by commenting out "#define CSDEBUG" +// +#define CSDEBUG 1 +#if CSDEBUG +#define CSDEBUG_INTERFACE 1 +#else +#undef CSDEBUG_INTERFACE +#endif +// +// cs_debugmask areas +// +#define CS_INIT 0x00000001 // initialization and probe functions +#define CS_ERROR 0x00000002 // tmp debugging bit placeholder +#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other) +#define CS_FUNCTION 0x00000008 // enter/leave functions +#define CS_WAVE_WRITE 0x00000010 // write information for wave +#define CS_WAVE_READ 0x00000020 // read information for wave +#define CS_MIDI_WRITE 0x00000040 // write information for midi +#define CS_MIDI_READ 0x00000080 // read information for midi +#define CS_MPU401_WRITE 0x00000100 // write information for mpu401 +#define CS_MPU401_READ 0x00000200 // read information for mpu401 +#define CS_OPEN 0x00000400 // all open functions in the driver +#define CS_RELEASE 0x00000800 // all release functions in the driver +#define CS_PARMS 0x00001000 // functional and operational parameters +#define CS_IOCTL 0x00002000 // ioctl (non-mixer) +#define CS_PM 0x00004000 // power management +#define CS_TMP 0x10000000 // tmp debug mask bit + +#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend +#define CS_IOCTL_CMD_RESUME 0x2 // resume +// +// CSDEBUG is usual mode is set to 1, then use the +// cs_debuglevel and cs_debugmask to turn on or off debugging. +// Debug level of 1 has been defined to be kernel errors and info +// that should be printed on any released driver. +// +#if CSDEBUG +#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;} +#else +#define CS_DBGOUT(mask,level,x) +#endif + +#if CSDEBUG +static unsigned long cs_debuglevel = 1; // levels range from 1-9 +static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask values +MODULE_PARM(cs_debuglevel, "i"); +MODULE_PARM(cs_debugmask, "i"); +#endif +#define CS_TRUE 1 +#define CS_FALSE 0 + +// MIDI buffer sizes +#define MIDIINBUF 500 +#define MIDIOUTBUF 500 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define CS4281_MAJOR_VERSION 1 +#define CS4281_MINOR_VERSION 13 +#ifdef __ia64__ +#define CS4281_ARCH 64 //architecture key +#else +#define CS4281_ARCH 32 //architecture key +#endif + +#define CS_TYPE_ADC 0 +#define CS_TYPE_DAC 1 + + +static const char invalid_magic[] = + KERN_CRIT "cs4281: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CS4281_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +//LIST_HEAD(cs4281_devs); +struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs }; + +struct cs4281_state; + +#include "cs4281_wrapper-24.c" + +struct cs4281_state { + // magic + unsigned int magic; + + // we keep the cards in a linked list + struct cs4281_state *next; + + // pcidev is needed to turn off the DDMA controller at driver shutdown + struct pci_dev *pcidev; + struct list_head list; + + // soundcore stuff + int dev_audio; + int dev_mixer; + int dev_midi; + + // hardware resources + unsigned int pBA0phys, pBA1phys; + char *pBA0, *pBA1; + unsigned int irq; + + // mixer registers + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + // wave stuff + struct properties { + unsigned fmt; + unsigned fmt_original; // original requested format + unsigned channels; + unsigned rate; + unsigned char clkdiv; + } prop_dac, prop_adc; + unsigned conversion:1; // conversion from 16 to 8 bit in progress + void *tmpbuff; // tmp buffer for sample conversions + unsigned ena; + spinlock_t lock; + struct semaphore open_sem; + struct semaphore open_sem_adc; + struct semaphore open_sem_dac; + mode_t open_mode; + wait_queue_head_t open_wait; + wait_queue_head_t open_wait_adc; + wait_queue_head_t open_wait_dac; + + dma_addr_t dmaaddr_tmpbuff; + unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. + struct dmabuf { + void *rawbuf; // Physical address of + dma_addr_t dmaaddr; + unsigned buforder; // Log base 2 of 'rawbuf' size in bytes.. + unsigned numfrag; // # of 'fragments' in the buffer. + unsigned fragshift; // Log base 2 of fragment size. + unsigned hwptr, swptr; + unsigned total_bytes; // # bytes process since open. + unsigned blocks; // last returned blocks value GETOPTR + unsigned wakeup; // interrupt occurred on block + int count; + unsigned underrun; // underrun flag + unsigned error; // over/underrun + wait_queue_head_t wait; + // redundant, but makes calculations easier + unsigned fragsize; // 2**fragshift.. + unsigned dmasize; // 2**buforder. + unsigned fragsamples; + // OSS stuff + unsigned mapped:1; // Buffer mapped in cs4281_mmap()? + unsigned ready:1; // prog_dmabuf_dac()/adc() successful? + unsigned endcleared:1; + unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + // midi stuff + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct cs4281_pm pm; + struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES]; +}; + +#include "cs4281pm-24.c" + +#if CSDEBUG + +// DEBUG ROUTINES + +#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) +#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) +#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) +#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) + +#define SOUND_MIXER_CS_APM _SIOWR('M',124, int) + + +static void cs_printioctl(unsigned int x) +{ + unsigned int i; + unsigned char vidx; + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + switch (x) { + case SOUND_MIXER_CS_GETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_GETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); + break; + case SOUND_MIXER_CS_SETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_SETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); + break; + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); + break; + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); + break; + case SNDCTL_DSP_SETDUPLEX: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); + break; + case SNDCTL_DSP_GETCAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); + break; + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); + break; + case SNDCTL_DSP_SPEED: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); + break; + case SNDCTL_DSP_STEREO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); + break; + case SNDCTL_DSP_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); + break; + case SNDCTL_DSP_GETFMTS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); + break; + case SNDCTL_DSP_SETFMT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); + break; + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); + break; + case SNDCTL_DSP_GETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); + break; + case SNDCTL_DSP_SETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); + break; + case SNDCTL_DSP_GETOSPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); + break; + case SNDCTL_DSP_GETISPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); + break; + case SNDCTL_DSP_NONBLOCK: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); + break; + case SNDCTL_DSP_GETODELAY: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); + break; + case SNDCTL_DSP_GETIPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); + break; + case SNDCTL_DSP_GETOPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); + break; + case SNDCTL_DSP_GETBLKSIZE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); + break; + case SNDCTL_DSP_SETFRAGMENT: + CS_DBGOUT(CS_IOCTL, 4, + printk("SNDCTL_DSP_SETFRAGMENT:\n")); + break; + case SNDCTL_DSP_SUBDIVIDE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); + break; + case SOUND_PCM_READ_RATE: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); + break; + case SOUND_PCM_READ_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_READ_CHANNELS:\n")); + break; + case SOUND_PCM_READ_BITS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); + break; + case SOUND_PCM_WRITE_FILTER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_WRITE_FILTER:\n")); + break; + case SNDCTL_DSP_SETSYNCRO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); + break; + case SOUND_PCM_READ_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); + break; + case SOUND_MIXER_PRIVATE1: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); + break; + case SOUND_MIXER_PRIVATE2: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); + break; + case SOUND_MIXER_PRIVATE3: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); + break; + case SOUND_MIXER_PRIVATE4: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); + break; + case SOUND_MIXER_PRIVATE5: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); + break; + case SOUND_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); + break; + case SOUND_OLD_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); + break; + + default: + switch (_IOC_NR(x)) { + case SOUND_MIXER_VOLUME: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_VOLUME:\n")); + break; + case SOUND_MIXER_SPEAKER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SPEAKER:\n")); + break; + case SOUND_MIXER_RECLEV: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECLEV:\n")); + break; + case SOUND_MIXER_MIC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_MIC:\n")); + break; + case SOUND_MIXER_SYNTH: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SYNTH:\n")); + break; + case SOUND_MIXER_RECSRC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECSRC:\n")); + break; + case SOUND_MIXER_DEVMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_DEVMASK:\n")); + break; + case SOUND_MIXER_RECMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECMASK:\n")); + break; + case SOUND_MIXER_STEREODEVS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_STEREODEVS:\n")); + break; + case SOUND_MIXER_CAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n")); + break; + default: + i = _IOC_NR(x); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) { + CS_DBGOUT(CS_IOCTL, 4, printk + ("UNKNOWN IOCTL: 0x%.8x NR=%d\n", + x, i)); + } else { + CS_DBGOUT(CS_IOCTL, 4, printk + ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n", + x, i)); + } + break; + } + } +} +#endif +static int prog_dmabuf_adc(struct cs4281_state *s); +static void prog_codec(struct cs4281_state *s, unsigned type); + +// --------------------------------------------------------------------- +// +// Hardware Interfaces For the CS4281 +// + + +//****************************************************************************** +// "delayus()-- Delay for the specified # of microseconds. +//****************************************************************************** +static void delayus(struct cs4281_state *s, u32 delay) +{ + u32 j; + if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) { + j = (delay * HZ) / 1000000; /* calculate delay in jiffies */ + if (j < 1) + j = 1; /* minimum one jiffy. */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(j); + } else + udelay(delay); + return; +} + + +//****************************************************************************** +// "cs4281_read_ac97" -- Reads a word from the specified location in the +// CS4281's address space(based on the BA0 register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 register, +// 0h for reads. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h +// 5. if DCV not cleared, break and return error +// 6. Read ACSTS = Status Register = 464h, check VSTS bit +//**************************************************************************** +static int cs4281_read_ac97(struct cs4281_state *card, u32 offset, + u32 * value) +{ + u32 count, status; + + // Make sure that there is not data sitting + // around from a previous uncompleted access. + // ACSDA = Status Data Register = 47Ch + status = readl(card->pBA0 + BA0_ACSDA); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // bit DCV - will clear when process completed + // bit CRW - Read command + // bit VFRM - valid frame enabled + // bit ESYN - ASYNC generation enabled + + // Get the actual AC97 register from the offset + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(0, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the read to occur. + for (count = 0; count < 10; count++) { + // First, we want to wait for a short time. + udelay(25); + + // Now, check to see if the read has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 17h + if (!(readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV)) + break; + } + + // Make sure the read completed. + if (readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV) + return 1; + + // Wait for the valid status bit to go active. + for (count = 0; count < 10; count++) { + // Read the AC97 status register. + // ACSTS = Status Register = 464h + status = readl(card->pBA0 + BA0_ACSTS); + + // See if we have valid status. + // VSTS - Valid Status + if (status & ACSTS_VSTS) + break; + // Wait for a short while. + udelay(25); + } + + // Make sure we got valid status. + if (!(status & ACSTS_VSTS)) + return 1; + + // Read the data returned from the AC97 register. + // ACSDA = Status Data Register = 474h + *value = readl(card->pBA0 + BA0_ACSDA); + + // Success. + return (0); +} + + +//**************************************************************************** +// +// "cs4281_write_ac97()"-- writes a word to the specified location in the +// CS461x's address space (based on the part's base address zero register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 reg. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h +// 5. if DCV not cleared, break and return error +// +//**************************************************************************** +static int cs4281_write_ac97(struct cs4281_state *card, u32 offset, + u32 value) +{ + u32 count, status=0; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()+ \n")); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // set DCV - will clear when process completed + // reset CRW - Write command + // set VFRM - valid frame enabled + // set ESYN - ASYNC generation enabled + // set RSTN - ARST# inactive, AC97 codec not reset + + // Get the actual AC97 register from the offset + + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(value, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the write to occur. + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25); + // Now, check to see if the write has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 07h + status = readl(card->pBA0 + BA0_ACCTL); + if (!(status & ACCTL_DCV)) + break; + } + + // Make sure the write completed. + if (status & ACCTL_DCV) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs_4281_write_ac97()- unable to write. ACCTL_DCV active\n")); + return 1; + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()- 0\n")); + // Success. + return 0; +} + + +//****************************************************************************** +// "Init4281()" -- Bring up the part. +//****************************************************************************** +static __devinit int cs4281_hw_init(struct cs4281_state *card) +{ + u32 ac97_slotid; + u32 temp1, temp2; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()+ \n")); +#ifndef NOT_CS4281_PM + if(!card) + return 1; +#endif + temp2 = readl(card->pBA0 + BA0_CFLR); + CS_DBGOUT(CS_INIT | CS_ERROR | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR 0x%x\n", temp2)); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n", + temp2,CS4281_CFLR_DEFAULT)); + writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR); + temp2 = readl(card->pBA0 + BA0_CFLR); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n")); + return 1; + } + } + + //***************************************7 + // Set up the Sound System Configuration + //*************************************** + + // Set the 'Configuration Write Protect' register + // to 4281h. Allows vendor-defined configuration + // space between 0e4h and 0ffh to be written. + + writel(0x4281, card->pBA0 + BA0_CWPR); // (3e0h) + + // (0), Blast the clock control register to zero so that the + // PLL starts out in a known state, and blast the master serial + // port control register to zero so that the serial ports also + // start out in a known state. + + writel(0, card->pBA0 + BA0_CLKCR1); // (400h) + writel(0, card->pBA0 + BA0_SERMC); // (420h) + + + // (1), Make ESYN go to zero to turn off + // the Sync pulse on the AC97 link. + + writel(0, card->pBA0 + BA0_ACCTL); + udelay(50); + + + // (2) Drive the ARST# pin low for a minimum of 1uS (as defined in + // the AC97 spec) and then drive it high. This is done for non + // AC97 modes since there might be logic external to the CS461x + // that uses the ARST# line for a reset. + + writel(0, card->pBA0 + BA0_SPMC); // (3ech) + udelay(100); + writel(SPMC_RSTN, card->pBA0 + BA0_SPMC); + delayus(card,50000); // Wait 50 ms for ABITCLK to become stable. + + // (3) Turn on the Sound System Clocks. + writel(CLKCR1_PLLP, card->pBA0 + BA0_CLKCR1); // (400h) + delayus(card,50000); // Wait for the PLL to stabilize. + // Turn on clocking of the core (CLKCR1(400h) = 0x00000030) + writel(CLKCR1_PLLP | CLKCR1_SWCE, card->pBA0 + BA0_CLKCR1); + + // (4) Power on everything for now.. + writel(0x7E, card->pBA0 + BA0_SSPM); // (740h) + + // (5) Wait for clock stabilization. + for (temp1 = 0; temp1 < 1000; temp1++) { + udelay(1000); + if (readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY) + break; + } + if (!(readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: DLLRDY failed!\n")); + return -EIO; + } + // (6) Enable ASYNC generation. + writel(ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // Now wait 'for a short while' to allow the AC97 + // part to start generating bit clock. (so we don't + // Try to start the PLL without an input clock.) + delayus(card,50000); + + // Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + // (7) Wait for the codec ready signal from the AC97 codec. + + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + udelay(1000); + if (readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY) // If ready, (464h) + break; // exit the 'for' loop. + } + if (!(readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY)) // If never came ready, + { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: ACSTS never came ready!\n")); + return -EIO; // exit initialization. + } + // (8) Assert the 'valid frame' signal so we can + // begin sending commands to the AC97 codec. + writel(ACCTL_VFRM | ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // (9), Wait until CODEC calibration is finished. + // Print an error message if it doesn't. + for (temp1 = 0; temp1 < 1000; temp1++) { + delayus(card,10000); + // Read the AC97 Powerdown Control/Status Register. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp2); + if ((temp2 & 0x0000000F) == 0x0000000F) + break; + } + if ((temp2 & 0x0000000F) != 0x0000000F) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: Codec failed to calibrate. Status = %.8x.\n", + temp2)); + return -EIO; + } + // (10), Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + + // (11) Wait until we've sampled input slots 3 & 4 as valid, meaning + // that the codec is pumping ADC data across the AC link. + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + delayus(card,1000); //(test) + + // Read the input slot valid register; See + // if input slots 3 and 4 are valid yet. + if ( + (readl(card->pBA0 + BA0_ACISV) & + (ACISV_ISV3 | ACISV_ISV4)) == + (ACISV_ISV3 | ACISV_ISV4)) break; // Exit the 'for' if slots are valid. + } + // If we never got valid data, exit initialization. + if ((readl(card->pBA0 + BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) + != (ACISV_ISV3 | ACISV_ISV4)) { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_ERR + "cs4281: Never got valid data!\n")); + return -EIO; // If no valid data, exit initialization. + } + // (12), Start digital data transfer of audio data to the codec. + writel(ACOSV_SLV3 | ACOSV_SLV4, card->pBA0 + BA0_ACOSV); // (468h) + + + //************************************** + // Unmute the Master and Alternate + // (headphone) volumes. Set to max. + //************************************** + cs4281_write_ac97(card, BA0_AC97_HEADPHONE_VOLUME, 0); + cs4281_write_ac97(card, BA0_AC97_MASTER_VOLUME, 0); + + //****************************************** + // Power on the DAC(AddDACUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfdff); + + // Wait until we sample a DAC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the DAC ready state bit is set, stop waiting. + if (temp1 & 0x2) + break; + } + + //****************************************** + // Power on the ADC(AddADCUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfeff); + + // Wait until we sample ADC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the ADC ready state bit is set, stop waiting. + if (temp1 & 0x1) + break; + } + // Set up 4281 Register contents that + // don't change for boot duration. + + // For playback, we map AC97 slot 3 and 4(Left + // & Right PCM playback) to DMA Channel 0. + // Set the fifo to be 15 bytes at offset zero. + + ac97_slotid = 0x01000f00; // FCR0.RS[4:0]=1(=>slot4, right PCM playback). + // FCR0.LS[4:0]=0(=>slot3, left PCM playback). + // FCR0.SZ[6-0]=15; FCR0.OF[6-0]=0. + writel(ac97_slotid, card->pBA0 + BA0_FCR0); // (180h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR0); // Turn on FIFO Enable. + + // For capture, we map AC97 slot 10 and 11(Left + // and Right PCM Record) to DMA Channel 1. + // Set the fifo to be 15 bytes at offset sixteen. + ac97_slotid = 0x0B0A0f10; // FCR1.RS[4:0]=11(=>slot11, right PCM record). + // FCR1.LS[4:0]=10(=>slot10, left PCM record). + // FCR1.SZ[6-0]=15; FCR1.OF[6-0]=16. + writel(ac97_slotid | FCRn_PSH, card->pBA0 + BA0_FCR1); // (184h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR1); // Turn on FIFO Enable. + + // Map the Playback SRC to the same AC97 slots(3 & 4-- + // --Playback left & right)as DMA channel 0. + // Map the record SRC to the same AC97 slots(10 & 11-- + // -- Record left & right) as DMA channel 1. + + ac97_slotid = 0x0b0a0100; // SCRSA.PRSS[4:0]=1(=>slot4, right PCM playback). + // SCRSA.PLSS[4:0]=0(=>slot3, left PCM playback). + // SCRSA.CRSS[4:0]=11(=>slot11, right PCM record) + // SCRSA.CLSS[4:0]=10(=>slot10, left PCM record). + writel(ac97_slotid, card->pBA0 + BA0_SRCSA); // (75ch) + + // Set 'Half Terminal Count Interrupt Enable' and 'Terminal + // Count Interrupt Enable' in DMA Control Registers 0 & 1. + // Set 'MSK' flag to 1 to keep the DMA engines paused. + temp1 = (DCRn_HTCIE | DCRn_TCIE | DCRn_MSK); // (00030001h) + writel(temp1, card->pBA0 + BA0_DCR0); // (154h + writel(temp1, card->pBA0 + BA0_DCR1); // (15ch) + + // Set 'Auto-Initialize Control' to 'enabled'; For playback, + // set 'Transfer Type Control'(TR[1:0]) to 'read transfer', + // for record, set Transfer Type Control to 'write transfer'. + // All other bits set to zero; Some will be changed @ transfer start. + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_READ); // (20000018h) + writel(temp1, card->pBA0 + BA0_DMR0); // (150h) + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE); // (20000014h) + writel(temp1, card->pBA0 + BA0_DMR1); // (158h) + + // Enable DMA interrupts generally, and + // DMA0 & DMA1 interrupts specifically. + temp1 = readl(card->pBA0 + BA0_HIMR) & 0xfffbfcff; + writel(temp1, card->pBA0 + BA0_HIMR); + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()- 0\n")); + return 0; +} + +#ifndef NOT_CS4281_PM +static void printpm(struct cs4281_state *s) +{ + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", + (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); + CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", + s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", + s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", + s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", + s->pm.u32SSCR,s->pm.u32SRCSA)); + CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", + s->pm.u32DacASR,s->pm.u32AdcASR)); + CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", + s->pm.u32DacSR,s->pm.u32AdcSR)); + CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", + s->pm.u32MIDCR_Save)); + +} +static void printpipe(struct cs4281_pipeline *pl) +{ + + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x number: 0%x\n", + (unsigned)pl->flags,pl->number)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnValue: 0%x u32DBCnValue: 0x%x\n", + pl->u32DBAnValue,pl->u32DBCnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnValue: 0x%x u32DCRnValue: 0x%x\n", + pl->u32DMRnValue,pl->u32DCRnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnAddress: 0x%x u32DBCnAddress: 0x%x\n", + pl->u32DBAnAddress,pl->u32DBCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAnAddress: 0x%x u32DCCnAddress: 0x%x\n", + pl->u32DCCnAddress,pl->u32DCCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnAddress: 0x%x u32DCRnAddress: 0x%x\n", + pl->u32DMRnAddress,pl->u32DCRnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32HDSRnAddress: 0x%x u32DBAn_Save: 0x%x\n", + pl->u32HDSRnAddress,pl->u32DBAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DBCn_Save: 0x%x u32DMRn_Save: 0x%x\n", + pl->u32DBCn_Save,pl->u32DMRn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCRn_Save: 0x%x u32DCCn_Save: 0x%x\n", + pl->u32DCRn_Save,pl->u32DCCn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAn_Save: 0x%x\n", + pl->u32DCAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRn_Save: 0x%x u32FSICn_Save: 0x%x\n", + pl->u32FCRn_Save,pl->u32FSICn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnValue: 0x%x u32FSICnValue: 0x%x\n", + pl->u32FCRnValue,pl->u32FSICnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnAddress: 0x%x u32FSICnAddress: 0x%x\n", + pl->u32FCRnAddress,pl->u32FSICnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32FPDRnValue: 0x%x u32FPDRnAddress: 0x%x\n", + pl->u32FPDRnValue,pl->u32FPDRnAddress)); +} +static void printpipelines(struct cs4281_state *s) +{ + int i; + for(i=0;ipl[i].flags & CS4281_PIPELINE_VALID) + { + printpipe(&s->pl[i]); + } + } +} +/**************************************************************************** +* +* Suspend - save the ac97 regs, mute the outputs and power down the part. +* +****************************************************************************/ +void cs4281_ac97_suspend(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n")); +/* +* change the state, save the current hwptr, then stop the dac/adc +*/ + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0); + s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1); + stop_dac(s); + stop_adc(s); + + for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_read_ac97(s, BA0_AC97_RESET + Count, &s->pm.ac97[i]); + } +/* +* Save the ac97 volume registers as well as the current powerdown state. +* Now, mute the all the outputs (master, headphone, and mono), as well +* as the PCM volume, in preparation for powering down the entire part. +*/ + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME, &s->pm.u32AC97_master_volume); + cs4281_read_ac97(s, BA0_AC97_HEADPHONE_VOLUME, &s->pm.u32AC97_headphone_volume); + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, &s->pm.u32AC97_master_volume_mono); + cs4281_read_ac97(s, BA0_AC97_PCM_OUT_VOLUME, &s->pm.u32AC97_pcm_out_volume); + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs4281_write_ac97(s, BA0_AC97_PCM_OUT_VOLUME, 0x8000); + + cs4281_read_ac97(s, BA0_AC97_POWERDOWN, &s->pm.u32AC97_powerdown); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &s->pm.u32AC97_general_purpose); + +/* +* And power down everything on the AC97 codec. +*/ + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, 0xff00); + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()-\n")); +} + +/**************************************************************************** +* +* Resume - power up the part and restore its registers.. +* +****************************************************************************/ +void cs4281_ac97_resume(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()+\n")); + +/* do not save the power state registers at this time + // + // If we saved away the power control registers, write them into the + // shadows so those saved values get restored instead of the current + // shadowed value. + // + if( bPowerStateSaved ) + { + PokeShadow( 0x26, ulSaveReg0x26 ); + bPowerStateSaved = FALSE; + } +*/ + +// +// First, we restore the state of the general purpose register. This +// contains the mic select (mic1 or mic2) and if we restore this after +// we restore the mic volume/boost state and mic2 was selected at +// suspend time, we will end up with a brief period of time where mic1 +// is selected with the volume/boost settings for mic2, causing +// acoustic feedback. So we restore the general purpose register +// first, thereby getting the correct mic selected before we restore +// the mic volume/boost. +// + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, s->pm.u32AC97_general_purpose); + +// +// Now, while the outputs are still muted, restore the state of power +// on the AC97 part. +// + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, s->pm.u32AC97_powerdown); + +/* +* Restore just the first set of registers, from register number +* 0x02 to the register number that ulHighestRegToRestore specifies. +*/ + for( Count = 0x2, i=0; + (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_write_ac97(s, BA0_AC97_RESET + Count, s->pm.ac97[i]); + } + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()-\n")); +} + +/* do not save the power state registers at this time +**************************************************************************** +* +* SavePowerState - Save the power registers away. +* +**************************************************************************** +void +HWAC97codec::SavePowerState(void) +{ + ENTRY(TM_OBJECTCALLS, "HWAC97codec::SavePowerState()\r\n"); + + ulSaveReg0x26 = PeekShadow(0x26); + + // + // Note that we have saved registers that need to be restored during a + // resume instead of ulAC97Regs[]. + // + bPowerStateSaved = TRUE; + +} // SavePowerState +*/ + +void cs4281_SuspendFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to save the contents of the BASIC FIFO Registers. + */ + pl->u32FCRn_Save = readl(s->pBA0 + pl->u32FCRnAddress); + pl->u32FSICn_Save = readl(s->pBA0 + pl->u32FSICnAddress); +} +void cs4281_ResumeFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to restore the contents of the BASIC FIFO Registers. + */ + writel(pl->u32FCRn_Save,s->pBA0 + pl->u32FCRnAddress); + writel(pl->u32FSICn_Save,s->pBA0 + pl->u32FSICnAddress); +} +void cs4281_SuspendDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + pl->u32DBAn_Save = readl(s->pBA0 + pl->u32DBAnAddress); + pl->u32DBCn_Save = readl(s->pBA0 + pl->u32DBCnAddress); + pl->u32DMRn_Save = readl(s->pBA0 + pl->u32DMRnAddress); + pl->u32DCRn_Save = readl(s->pBA0 + pl->u32DCRnAddress); + pl->u32DCCn_Save = readl(s->pBA0 + pl->u32DCCnAddress); + pl->u32DCAn_Save = readl(s->pBA0 + pl->u32DCAnAddress); +} +void cs4281_ResumeDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + writel( pl->u32DBAn_Save, s->pBA0 + pl->u32DBAnAddress); + writel( pl->u32DBCn_Save, s->pBA0 + pl->u32DBCnAddress); + writel( pl->u32DMRn_Save, s->pBA0 + pl->u32DMRnAddress); + writel( pl->u32DCRn_Save, s->pBA0 + pl->u32DCRnAddress); + writel( pl->u32DCCn_Save, s->pBA0 + pl->u32DCCnAddress); + writel( pl->u32DCAn_Save, s->pBA0 + pl->u32DCAnAddress); +} + +int cs4281_suspend(struct cs4281_state *s) +{ + int i; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()+ flags=%d\n", + (unsigned)s->pm.flags)); +/* +* check the current state, only suspend if IDLE +*/ + if(!(s->pm.flags & CS4281_PM_IDLE)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_suspend() unable to suspend, not IDLE\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + + pm->u32CLKCR1_SAVE = u32CLKCR1; + if(!(u32CLKCR1 & 0x00010000 ) ) + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + +// +// First, turn on the clocks (yikes) to the devices, so that they will +// respond when we try to save their state. +// + if(!(u32CLKCR1 & CLKCR1_SWCE)) + { + writel(u32CLKCR1 | CLKCR1_SWCE , s->pBA0 + BA0_CLKCR1); + } + + // + // Save the power state + // + pm->u32SSPMValue = readl(s->pBA0 + BA0_SSPM); + + // + // Disable interrupts. + // + writel(HICR_CHGM, s->pBA0 + BA0_HICR); + + // + // Save the PCM Playback Left and Right Volume Control. + // + pm->u32PPLVCvalue = readl(s->pBA0 + BA0_PPLVC); + pm->u32PPRVCvalue = readl(s->pBA0 + BA0_PPRVC); + + // + // Save the FM Synthesis Left and Right Volume Control. + // + pm->u32FMLVCvalue = readl(s->pBA0 + BA0_FMLVC); + pm->u32FMRVCvalue = readl(s->pBA0 + BA0_FMRVC); + + // + // Save the GPIOR value. + // + pm->u32GPIORvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save the JSCTL value. + // + pm->u32JSCTLvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save Sound System Control Register + // + pm->u32SSCR = readl(s->pBA0 + BA0_SSCR); + + // + // Save SRC Slot Assinment register + // + pm->u32SRCSA = readl(s->pBA0 + BA0_SRCSA); + + // + // Save sample rate + // + pm->u32DacASR = readl(s->pBA0 + BA0_PASR); + pm->u32AdcASR = readl(s->pBA0 + BA0_CASR); + pm->u32DacSR = readl(s->pBA0 + BA0_DACSR); + pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Suspend. + // + cs4281_SuspendDMAengine(s,&s->pl[i]); + cs4281_SuspendFIFO(s,&s->pl[i]); + } + } + // + // We need to save the contents of the Midi Control Register. + // + pm->u32MIDCR_Save = readl(s->pBA0 + BA0_MIDCR); +/* +* save off the AC97 part information +*/ + cs4281_ac97_suspend(s); + + // + // Turn off the serial ports. + // + writel(0, s->pBA0 + BA0_SERMC); + + // + // Power off FM, Joystick, AC link, + // + writel(0, s->pBA0 + BA0_SSPM); + + // + // DLL off. + // + writel(0, s->pBA0 + BA0_CLKCR1); + + // + // AC link off. + // + writel(0, s->pBA0 + BA0_SPMC); + + // + // Put the chip into D3(hot) state. + // + // PokeBA0(BA0_PMCS, 0x00000003); + + // + // Gershwin CLKRUN - Clear CKRA + // + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 & 0xFFFEFFFF, s->pBA0 + BA0_CLKCR1); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif + + s->pm.flags &= ~CS4281_PM_SUSPENDING; + s->pm.flags |= CS4281_PM_SUSPENDED; + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +int cs4281_resume(struct cs4281_state *s) +{ + int i; + unsigned temp1; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs4281: cs4281_resume()+ flags=%d\n", + (unsigned)s->pm.flags)); + if(!(s->pm.flags & CS4281_PM_SUSPENDED)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_resume() unable to resume, not SUSPENDED\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_SUSPENDED; + s->pm.flags |= CS4281_PM_RESUMING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + + // + // set the power state. + // + //old PokeBA0(BA0_PMCS, 0); + + // + // Program the clock circuit and serial ports. + // + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, + printk(KERN_ERR + "cs4281: resume cs4281_hw_init() error.\n")); + return -1; + } + + // + // restore the Power state + // + writel(pm->u32SSPMValue, s->pBA0 + BA0_SSPM); + + // + // Set post SRC mix setting (FM or ALT48K) + // + writel(pm->u32SSPM_BITS, s->pBA0 + BA0_SSPM); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Resume. + // + cs4281_ResumeDMAengine(s,&s->pl[i]); + cs4281_ResumeFIFO(s,&s->pl[i]); + } + } + // + // We need to restore the contents of the Midi Control Register. + // + writel(pm->u32MIDCR_Save, s->pBA0 + BA0_MIDCR); + + cs4281_ac97_resume(s); + // + // Restore the PCM Playback Left and Right Volume Control. + // + writel(pm->u32PPLVCvalue, s->pBA0 + BA0_PPLVC); + writel(pm->u32PPRVCvalue, s->pBA0 + BA0_PPRVC); + + // + // Restore the FM Synthesis Left and Right Volume Control. + // + writel(pm->u32FMLVCvalue, s->pBA0 + BA0_FMLVC); + writel(pm->u32FMRVCvalue, s->pBA0 + BA0_FMRVC); + + // + // Restore the JSCTL value. + // + writel(pm->u32JSCTLvalue, s->pBA0 + BA0_JSCTL); + + // + // Restore the GPIOR register value. + // + writel(pm->u32GPIORvalue, s->pBA0 + BA0_GPIOR); + + // + // Restore Sound System Control Register + // + writel(pm->u32SSCR, s->pBA0 + BA0_SSCR); + + // + // Restore SRC Slot Assignment register + // + writel(pm->u32SRCSA, s->pBA0 + BA0_SRCSA); + + // + // Restore sample rate + // + writel(pm->u32DacASR, s->pBA0 + BA0_PASR); + writel(pm->u32AdcASR, s->pBA0 + BA0_CASR); + writel(pm->u32DacSR, s->pBA0 + BA0_DACSR); + writel(pm->u32AdcSR, s->pBA0 + BA0_ADCSR); + + // + // Restore CFL1/2 registers we saved to compensate for OEM bugs. + // + // PokeBA0(BA0_CFLR, ulConfig); + + // + // Gershwin CLKRUN - Clear CKRA + // + writel(pm->u32CLKCR1_SAVE, s->pBA0 + BA0_CLKCR1); + + // + // Enable interrupts on the part. + // + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif +/* +* change the state, restore the current hwptrs, then stop the dac/adc +*/ + s->pm.flags |= CS4281_PM_IDLE; + s->pm.flags &= ~(CS4281_PM_SUSPENDING | CS4281_PM_SUSPENDED + | CS4281_PM_RESUMING | CS4281_PM_RESUMED); + + writel(s->pm.u32hwptr_playback, s->pBA0 + BA0_DCA0); + writel(s->pm.u32hwptr_capture, s->pBA0 + BA0_DCA1); + start_dac(s); + start_adc(s); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, printk("cs4281: cs4281_resume()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +#endif + +//****************************************************************************** +// "cs4281_play_rate()" -- +//****************************************************************************** +static void cs4281_play_rate(struct cs4281_state *card, u32 playrate) +{ + u32 DACSRvalue = 1; + + // Based on the sample rate, program the DACSR register. + if (playrate == 8000) + DACSRvalue = 5; + if (playrate == 11025) + DACSRvalue = 4; + else if (playrate == 22050) + DACSRvalue = 2; + else if (playrate == 44100) + DACSRvalue = 1; + else if ((playrate <= 48000) && (playrate >= 6023)) + DACSRvalue = 24576000 / (playrate * 16); + else if (playrate < 6023) + // Not allowed by open. + return; + else if (playrate > 48000) + // Not allowed by open. + return; + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_play_rate(): DACSRvalue=0x%.8x playrate=%d\n", + DACSRvalue, playrate)); + // Write the 'sample rate select code' + // to the 'DAC Sample Rate' register. + writel(DACSRvalue, card->pBA0 + BA0_DACSR); // (744h) +} + +//****************************************************************************** +// "cs4281_record_rate()" -- Initialize the record sample rate converter. +//****************************************************************************** +static void cs4281_record_rate(struct cs4281_state *card, u32 outrate) +{ + u32 ADCSRvalue = 1; + + // + // Based on the sample rate, program the ADCSR register + // + if (outrate == 8000) + ADCSRvalue = 5; + if (outrate == 11025) + ADCSRvalue = 4; + else if (outrate == 22050) + ADCSRvalue = 2; + else if (outrate == 44100) + ADCSRvalue = 1; + else if ((outrate <= 48000) && (outrate >= 6023)) + ADCSRvalue = 24576000 / (outrate * 16); + else if (outrate < 6023) { + // Not allowed by open. + return; + } else if (outrate > 48000) { + // Not allowed by open. + return; + } + CS_DBGOUT(CS_WAVE_READ | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_record_rate(): ADCSRvalue=0x%.8x outrate=%d\n", + ADCSRvalue, outrate)); + // Write the 'sample rate select code + // to the 'ADC Sample Rate' register. + writel(ADCSRvalue, card->pBA0 + BA0_ADCSR); // (748h) +} + + + +static void stop_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: stop_dac():\n")); + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR0); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void start_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4281: start_dac()+\n")); + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || + (s->dma_dac.count > 0 + && s->dma_dac.ready)) +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + s->ena |= FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK; // Clear DMA0 channel mask. + writel(temp1, s->pBA0 + BA0_DCR0); // Start DMA'ing. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + + writel(7, s->pBA0 + BA0_PPRVC); + writel(7, s->pBA0 + BA0_PPLVC); + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO + "cs4281: start_dac(): writel 0x%x start dma\n", temp1)); + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: start_dac()-\n")); +} + + +static void stop_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: stop_adc()+\n")); + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + + if (s->conversion == 1) { + s->conversion = 0; + s->prop_adc.fmt = s->prop_adc.fmt_original; + } + temp1 = readl(s->pBA0 + BA0_DCR1) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR1); + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: stop_adc()-\n")); +} + + +static void start_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()+\n")); + + if (!(s->ena & FMODE_READ) && + (s->dma_adc.mapped || s->dma_adc.count <= + (signed) (s->dma_adc.dmasize - 2 * s->dma_adc.fragsize)) + && s->dma_adc.ready +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) { + // + // now only use 16 bit capture, due to truncation issue + // in the chip, noticable distortion occurs. + // allocate buffer and then convert from 16 bit to + // 8 bit for the user buffer. + // + s->prop_adc.fmt_original = s->prop_adc.fmt; + if (s->prop_adc.fmt & AFMT_S8) { + s->prop_adc.fmt &= ~AFMT_S8; + s->prop_adc.fmt |= AFMT_S16_LE; + } + if (s->prop_adc.fmt & AFMT_U8) { + s->prop_adc.fmt &= ~AFMT_U8; + s->prop_adc.fmt |= AFMT_U16_LE; + } + // + // prog_dmabuf_adc performs a stop_adc() but that is + // ok since we really haven't started the DMA yet. + // + prog_codec(s, CS_TYPE_ADC); + + if (prog_dmabuf_adc(s) != 0) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: start_adc(): error in prog_dmabuf_adc\n")); + } + s->conversion = 1; + } + spin_lock_irqsave(&s->lock, flags); + s->ena |= FMODE_READ; + temp1 = readl(s->pBA0 + BA0_DCR1) & ~DCRn_MSK; // Clear DMA1 channel mask bit. + writel(temp1, s->pBA0 + BA0_DCR1); // Start recording + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + spin_unlock_irqrestore(&s->lock, flags); + + CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO + "cs4281: start_adc(): writel 0x%x \n", temp1)); + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()-\n")); + +} + + +// --------------------------------------------------------------------- + +#define DMABUF_MINORDER 1 // ==> min buffer size = 8K. + + +extern void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + struct page *map, *mapend; + + if (db->rawbuf) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - + 1); + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf(s, db); + } + if (s->tmpbuff && (db->type == CS_TYPE_ADC)) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf2(s, db); + } + s->tmpbuff = NULL; + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + int order; + unsigned bytespersec, temp1; + unsigned bufs, sample_shift = 0; + struct page *map, *mapend; + unsigned long df; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()+\n")); + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = + db->endcleared = db->blocks = db->wakeup = db->underrun = 0; +/* +* check for order within limits, but do not overwrite value, check +* later for a fractional defaultorder (i.e. 100+). +*/ + if((defaultorder > 0) && (defaultorder < 12)) + df = defaultorder; + else + df = 1; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = df; order >= DMABUF_MINORDER; order--) + if ( (db->rawbuf = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, &db-> dmaaddr))) + break; + if (!db->rawbuf) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate rawbuf\n")); + return -ENOMEM; + } + db->buforder = order; + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + cs4x_mem_map_reserve(map); + } + if (!s->tmpbuff && (db->type == CS_TYPE_ADC)) { + for (order = df; order >= DMABUF_MINORDER; + order--) + if ( (s->tmpbuff = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, + &s->dmaaddr_tmpbuff))) + break; + if (!s->tmpbuff) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate tmpbuff\n")); + return -ENOMEM; + } + s->buforder_tmpbuff = order; + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + cs4x_mem_map_reserve(map); + } + if (db->type == CS_TYPE_DAC) { + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_dac.channels > 1) + sample_shift++; + bytespersec = s->prop_dac.rate << sample_shift; + } else // CS_TYPE_ADC + { + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_adc.channels > 1) + sample_shift++; + bytespersec = s->prop_adc.rate << sample_shift; + } + bufs = PAGE_SIZE << db->buforder; + +/* +* added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +*/ + if(defaultorder >= 100) + { + bufs = 1 << (defaultorder-100); + } + +#define INTERRUPT_RATE_MS 100 // Interrupt rate in milliseconds. + db->numfrag = 2; +/* +* Nominal frag size(bytes/interrupt) +*/ + temp1 = bytespersec / (1000 / INTERRUPT_RATE_MS); + db->fragshift = 8; // Min 256 bytes. + while (1 << db->fragshift < temp1) // Calc power of 2 frag size. + db->fragshift += 1; + db->fragsize = 1 << db->fragshift; + db->dmasize = db->fragsize * 2; + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + +// If the calculated size is larger than the allocated +// buffer, divide the allocated buffer into 2 fragments. + if (db->dmasize > bufs) { + + db->numfrag = 2; // Two fragments. + db->fragsize = bufs >> 1; // Each 1/2 the alloc'ed buffer. + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + db->dmasize = bufs; // Use all the alloc'ed buffer. + + db->fragshift = 0; // Calculate 'fragshift'. + temp1 = db->fragsize; // update_ptr() uses it + while ((temp1 >>= 1) > 1) // to calc 'total-bytes' + db->fragshift += 1; // returned in DSP_GETI/OPTR. + } + CS_DBGOUT(CS_PARMS, 3, printk(KERN_INFO + "cs4281: prog_dmabuf(): numfrag=%d fragsize=%d fragsamples=%d fragshift=%d bufs=%d fmt=0x%x ch=%d\n", + db->numfrag, db->fragsize, db->fragsamples, + db->fragshift, bufs, + (db->type == CS_TYPE_DAC) ? s->prop_dac.fmt : + s->prop_adc.fmt, + (db->type == CS_TYPE_DAC) ? s->prop_dac.channels : + s->prop_adc.channels)); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()-\n")); + return 0; +} + + +static int prog_dmabuf_adc(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_adc(s); + s->dma_adc.type = CS_TYPE_ADC; + if ((c = prog_dmabuf(s, &s->dma_adc))) + return c; + + if (s->dma_adc.rawbuf) { + memset(s->dma_adc.rawbuf, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_adc.dmasize); + } + if (s->tmpbuff) { + memset(s->tmpbuff, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + PAGE_SIZE << s->buforder_tmpbuff); + } + + va = virt_to_bus(s->dma_adc.rawbuf); + + count = s->dma_adc.dmasize; + + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_adc.channels > 1) + count /= 2; // Assume stereo. + + CS_DBGOUT(CS_WAVE_READ, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_adc(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + writel(va, s->pBA0 + BA0_DBA1); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC1); // Set count. + s->dma_adc.ready = 1; + return 0; +} + + +static int prog_dmabuf_dac(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_dac(s); + s->dma_dac.type = CS_TYPE_DAC; + if ((c = prog_dmabuf(s, &s->dma_dac))) + return c; + memset(s->dma_dac.rawbuf, + (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_dac.dmasize); + + va = virt_to_bus(s->dma_dac.rawbuf); + + count = s->dma_dac.dmasize; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_dac.channels > 1) + count /= 2; // Assume stereo. + + writel(va, s->pBA0 + BA0_DBA0); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC0); // Set count. + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + s->dma_dac.ready = 1; + return 0; +} + + +static void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *) buf) + bptr, c, x); + bptr = 0; + len -= x; + } + CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO + "cs4281: clear_advance(): memset %d at 0x%.8x for %d size \n", + (unsigned)c, (unsigned)((char *) buf) + bptr, len)); + memset(((char *) buf) + bptr, c, len); +} + + + +// call with spinlock held! +static void cs4281_update_ptr(struct cs4281_state *s, int intflag) +{ + int diff; + unsigned hwptr, va; + + // update ADC pointer + if (s->ena & FMODE_READ) { + hwptr = readl(s->pBA0 + BA0_DCA1); // Read capture DMA address. + va = virt_to_bus(s->dma_adc.rawbuf); + hwptr -= (unsigned) va; + diff = + (s->dma_adc.dmasize + hwptr - + s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count > s->dma_adc.dmasize) + s->dma_adc.count = s->dma_adc.dmasize; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= + (signed) s->dma_adc.fragsize) wake_up(&s-> + dma_adc. + wait); + } else { + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned)s, s->dma_adc.hwptr, + s->dma_adc.total_bytes, s->dma_adc.count)); + } + // update DAC pointer + // + // check for end of buffer, means that we are going to wait for another interrupt + // to allow silence to fill the fifos on the part, to keep pops down to a minimum. + // + if (s->ena & FMODE_WRITE) { + hwptr = readl(s->pBA0 + BA0_DCA0); // Read play DMA address. + va = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) va; + diff = (s->dma_dac.dmasize + hwptr - + s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= s->dma_dac.fragsize) { + s->dma_dac.wakeup = 1; + wake_up(&s->dma_dac.wait); + if (s->dma_dac.count > s->dma_dac.dmasize) + s->dma_dac.count &= + s->dma_dac.dmasize - 1; + } + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + // + // fill with silence, and do not shut down the DAC. + // Continue to play silence until the _release. + // + CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): memset %d at 0x%.8x for %d size \n", + (unsigned)(s->prop_dac.fmt & + (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + (unsigned)s->dma_dac.rawbuf, + s->dma_dac.dmasize)); + memset(s->dma_dac.rawbuf, + (s->prop_dac. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? + 0x80 : 0, s->dma_dac.dmasize); + if (s->dma_dac.count < 0) { + s->dma_dac.underrun = 1; + s->dma_dac.count = 0; + CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): underrun\n")); + } + } else if (s->dma_dac.count <= + (signed) s->dma_dac.fragsize + && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, + s->dma_dac.dmasize, + s->dma_dac.swptr, + s->dma_dac.fragsize, + (s->prop_dac. + fmt & (AFMT_U8 | + AFMT_U16_LE)) ? 0x80 + : 0); + s->dma_dac.endcleared = 1; + } + if ( (s->dma_dac.count <= (signed) s->dma_dac.dmasize/2) || + intflag) + { + wake_up(&s->dma_dac.wait); + } + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", + (unsigned) s, s->dma_dac.hwptr, + s->dma_dac.total_bytes, s->dma_dac.count)); + } +} + + +// --------------------------------------------------------------------- + +static void prog_codec(struct cs4281_state *s, unsigned type) +{ + unsigned long flags; + unsigned temp1, format; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()+ \n")); + + spin_lock_irqsave(&s->lock, flags); + if (type == CS_TYPE_ADC) { + temp1 = readl(s->pBA0 + BA0_DCR1); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1); // Stop capture DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_record_rate(s, s->prop_adc.rate); + + // program ADC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE; + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE)) // Big-endian? + format |= DMRn_BEND; + if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + if (s->prop_adc.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR1); + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_adc.rate, format)); + + s->ena &= ~FMODE_READ; // not capturing data yet + } + + + if (type == CS_TYPE_DAC) { + temp1 = readl(s->pBA0 + BA0_DCR0); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0); // Stop play DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_play_rate(s, s->prop_dac.rate); + + // program DAC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE)) + format |= DMRn_BEND; // Big Endian. + if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + + if (s->prop_dac.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR0); + + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_dac.rate, format)); + + s->ena &= ~FMODE_WRITE; // not capturing data yet + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()- \n")); +} + + +static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd, + unsigned long arg) +{ + // Index to mixer_src[] is value of AC97 Input Mux Select Reg. + // Value of array member is recording source Device ID Mask. + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, + SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 + }; + + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + + static const unsigned mixreg[] = { + BA0_AC97_PCM_OUT_VOLUME, + BA0_AC97_AUX_VOLUME, + BA0_AC97_CD_VOLUME, + BA0_AC97_LINE_IN_VOLUME + }; + unsigned char l, r, rl, rr, vidx; + unsigned char attentbl[11] = + { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; + unsigned temp1; + int i, val; + + VALIDATE_STATE(s); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4281: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n", + (unsigned) s, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif +#if CSDEBUG_INTERFACE + + if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || + (cmd == SOUND_MIXER_CS_SETDBGMASK) || + (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_APM)) + { + switch (cmd) { + + case SOUND_MIXER_CS_GETDBGMASK: + return put_user(cs_debugmask, + (unsigned long *) arg); + + case SOUND_MIXER_CS_GETDBGLEVEL: + return put_user(cs_debuglevel, + (unsigned long *) arg); + + case SOUND_MIXER_CS_SETDBGMASK: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + cs_debugmask = val; + return 0; + + case SOUND_MIXER_CS_SETDBGLEVEL: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + cs_debuglevel = val; + return 0; +#ifndef NOT_CS4281_PM + case SOUND_MIXER_CS_APM: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + if(val == CS_IOCTL_CMD_SUSPEND) + cs4281_suspend(s); + else if(val == CS_IOCTL_CMD_RESUME) + cs4281_resume(s); + else + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n", + val)); + } + return 0; +#endif + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): ERROR unknown debug cmd\n")); + return 0; + } + } +#endif + + if (cmd == SOUND_MIXER_PRIVATE1) { + // enable/disable/query mixer preamp + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != -1) { + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf); + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + val = (temp1 & 0x40) ? 1 : 0; + return put_user(val, (int *) arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + // enable/disable/query spatializer + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != -1) { + temp1 = (val & 0x3f) >> 2; + cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, + &temp1); + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, + temp1 | 0x2000); + } + cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1); + return put_user((temp1 << 2) | 3, (int *) arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "CS4281", sizeof(info.id)); + strncpy(info.name, "Crystal CS4281", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "CS4281", sizeof(info.id)); + strncpy(info.name, "Crystal CS4281", sizeof(info.name)); + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *) arg); + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + // If ioctl has only the SIOC_READ bit(bit 31) + // on, process the only-read commands. + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + cs4281_read_ac97(s, BA0_AC97_RECORD_SELECT, + &temp1); + return put_user(mixer_src[temp1 & 7], (int *) arg); + + case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | + SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, (int *) arg); + + case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_CD | SOUND_MASK_VOLUME | + SOUND_MASK_LINE1, (int *) arg); + + case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | + SOUND_MASK_RECLEV, (int *) arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx - 1], (int *) arg); + } + } + // If ioctl doesn't have both the SIOC_READ and + // the SIOC_WRITE bit set, return invalid. + if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE)) + return -EINVAL; + + // Increment the count of volume writes. + s->mix.modcnt++; + + // Isolate the command; it must be a write. + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + if (get_user(val, (int *) arg)) + return -EFAULT; + i = hweight32(val); // i = # bits on in val. + if (i != 1) // One & only 1 bit must be on. + return 0; + for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { + if (val == mixer_src[i]) { + temp1 = (i << 8) | i; + cs4281_write_ac97(s, + BA0_AC97_RECORD_SELECT, + temp1); + return 0; + } + } + return 0; + + case SOUND_MIXER_VOLUME: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; // Max soundcard.h vol is 100. + if (l < 6) { + rl = 63; + l = 0; + } else + rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten. + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; // Max right volume is 100, too + if (r < 6) { + rr = 63; + r = 0; + } else + rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation. + + if ((rl > 60) && (rr > 60)) // If both l & r are 'low', + temp1 = 0x8000; // turn on the mute bit. + else + temp1 = 0; + + temp1 |= (rl << 8) | rr; + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, temp1); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int *) arg); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 3) { + rl = 0; + l = 0; + } else { + rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15. + l = (rl * 13 + 5) / 2; + } + + if (rl < 3) { + temp1 = 0x8000; + rl = 0; + } else + temp1 = 0; + rl = 15 - rl; // Convert volume to attenuation. + temp1 |= rl << 1; + cs4281_write_ac97(s, BA0_AC97_PC_BEEP_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[6] = l << 8; +#else + s->mix.vol[6] = val; +#endif + return put_user(s->mix.vol[6], (int *) arg); + + case SOUND_MIXER_RECLEV: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15. + rr = (r * 2 - 5) / 13; + if (rl < 3 && rr < 3) + temp1 = 0x8000; + else + temp1 = 0; + + temp1 = temp1 | (rl << 8) | rr; + cs4281_write_ac97(s, BA0_AC97_RECORD_GAIN, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int *) arg); + + case SOUND_MIXER_MIC: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 0; + } else { + rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31. + l = (rl * 16 + 4) / 5; + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 &= 0x40; // Isolate 20db gain bit. + if (rl < 3) { + temp1 |= 0x8000; + rl = 0; + } + rl = 31 - rl; // Convert volume to attenuation. + temp1 |= rl; + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[5] = val << 8; +#else + s->mix.vol[5] = val; +#endif + return put_user(s->mix.vol[5], (int *) arg); + + + case SOUND_MIXER_SYNTH: + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (get_user(val, (int *) arg)) + return -EFAULT; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63. + rr = (r * 2 - 11) / 3; + if (rl < 3) // If l is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + + rl = 63 - rl; // Convert vol to attenuation. + writel(temp1 | rl, s->pBA0 + BA0_FMLVC); + if (rr < 3) // If rr is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + rr = 63 - rr; // Convert vol to attenuation. + writel(temp1 | rr, s->pBA0 + BA0_FMRVC); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[4] = (r << 8) | l; +#else + s->mix.vol[4] = val; +#endif + return put_user(s->mix.vol[4], (int *) arg); + + + default: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: mixer_ioctl(): default\n")); + + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 31; + } else + rl = (attentbl[(l * 10) / 100]) >> 1; + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (r < 1) { + r = 0; + rr = 31; + } else + rr = (attentbl[(r * 10) / 100]) >> 1; + if ((rl > 30) && (rr > 30)) + temp1 = 0x8000; + else + temp1 = 0; + temp1 = temp1 | (rl << 8) | rr; + cs4281_write_ac97(s, mixreg[vidx - 1], temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[vidx - 1] = val; +#endif +#ifndef NOT_CS4281_PM + CS_DBGOUT(CS_PM, 9, printk(KERN_INFO + "write ac97 mixreg[%d]=0x%x mix.vol[]=0x%x\n", + vidx-1,temp1,s->mix.vol[vidx-1])); +#endif + return put_user(s->mix.vol[vidx - 1], (int *) arg); + } +} + + +// --------------------------------------------------------------------- + +static int cs4281_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()+\n")); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + if(s->dev_mixer == minor) + break; + } + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()- 0\n")); + + return 0; +} + + +static int cs4281_release_mixdev(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + + +static int cs4281_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cs4281_state *) file->private_data, cmd, + arg); +} + + +// ****************************************************************************************** +// Mixer file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_mixer_fops = { + llseek:no_llseek, + ioctl:cs4281_ioctl_mixdev, + open:cs4281_open_mixdev, + release:cs4281_release_mixdev, +}; + +// --------------------------------------------------------------------- + + +static int drain_adc(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_adc.mapped) + return 0; + add_wait_queue(&s->dma_adc.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_adc() %d\n", count)); + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: drain_adc() count<0\n")); + break; + } + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_adc.fragsize) / 2 / s->prop_adc.rate; + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_adc.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_dac.fragsize) / 2 / s->prop_dac.rate; + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_dac.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +//**************************************************************************** +// +// CopySamples copies 16-bit stereo samples from the source to the +// destination, possibly converting down to either 8-bit or mono or both. +// count specifies the number of output bytes to write. +// +// Arguments: +// +// dst - Pointer to a destination buffer. +// src - Pointer to a source buffer +// count - The number of bytes to copy into the destination buffer. +// iChannels - Stereo - 2 +// Mono - 1 +// fmt - AFMT_xxx (soundcard.h formats) +// +// NOTES: only call this routine for conversion to 8bit from 16bit +// +//**************************************************************************** +static void CopySamples(char *dst, char *src, int count, int iChannels, + unsigned fmt) +{ + + unsigned short *psSrc; + long lAudioSample; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: CopySamples()+ ")); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " dst=0x%x src=0x%x count=%d iChannels=%d fmt=0x%x\n", + (unsigned) dst, (unsigned) src, (unsigned) count, + (unsigned) iChannels, (unsigned) fmt)); + + // Gershwin does format conversion in hardware so normally + // we don't do any host based coversion. The data formatter + // truncates 16 bit data to 8 bit and that causes some hiss. + // We have already forced the HW to do 16 bit sampling and + // 2 channel so that we can use software to round instead + // of truncate + + // + // See if the data should be output as 8-bit unsigned stereo. + // or if the data should be output at 8-bit unsigned mono. + // + if ( ((iChannels == 2) && (fmt & AFMT_U8)) || + ((iChannels == 1) && (fmt & AFMT_U8)) ) { + // + // Convert each 16-bit unsigned stereo sample to 8-bit unsigned + // stereo using rounding. + // + psSrc = (unsigned short *) src; + count = count / 2; + while (count--) { + lAudioSample = (long) psSrc[count] + (long) 0x80; + if (lAudioSample > 0xffff) { + lAudioSample = 0xffff; + } + dst[count] = (char) (lAudioSample >> 8); + } + } + // + // check for 8-bit signed stereo. + // + else if ((iChannels == 2) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit stereo sample to 8-bit stereo using rounding. + // + psSrc = (short *) src; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } + // + // Otherwise, the data should be output as 8-bit signed mono. + // + else if ((iChannels == 1) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit signed mono sample to 8-bit signed mono + // using rounding. + // + psSrc = (short *) src; + count = count / 2; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + if (lAudioSample > 0x7fff) { + lAudioSample = 0x7fff; + } + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } +} + +// +// cs_copy_to_user() +// replacement for the standard copy_to_user, to allow for a conversion from +// 16 bit to 8 bit if the record conversion is active. the cs4281 has some +// issues with 8 bit capture, so the driver always captures data in 16 bit +// and then if the user requested 8 bit, converts from 16 to 8 bit. +// +static unsigned cs_copy_to_user(struct cs4281_state *s, void *dest, + unsigned *hwsrc, unsigned cnt, + unsigned *copied) +{ + void *src = hwsrc; //default to the standard destination buffer addr + + CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO + "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=0x%.8x\n", + s->prop_adc.fmt, s->prop_adc.fmt_original, + (unsigned) cnt, (unsigned) dest)); + + if (cnt > s->dma_adc.dmasize) { + cnt = s->dma_adc.dmasize; + } + if (!cnt) { + *copied = 0; + return 0; + } + if (s->conversion) { + if (!s->tmpbuff) { + *copied = cnt / 2; + return 0; + } + CopySamples(s->tmpbuff, (void *) hwsrc, cnt, + (unsigned) s->prop_adc.channels, + s->prop_adc.fmt_original); + src = s->tmpbuff; + cnt = cnt / 2; + } + + if (copy_to_user(dest, src, cnt)) { + *copied = 0; + return -EFAULT; + } + *copied = cnt; + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs_copy_to_user()- copied bytes is %d \n", cnt)); + return 0; +} + +// --------------------------------------------------------------------- + +static ssize_t cs4281_read(struct file *file, char *buffer, size_t count, + loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned copied = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4281: cs4281_read()+ %d \n", count)); + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +// +// "count" is the amount of bytes to read (from app), is decremented each loop +// by the amount of bytes that have been returned to the user buffer. +// "cnt" is the running total of each read from the buffer (changes each loop) +// "buffer" points to the app's buffer +// "ret" keeps a running total of the amount of bytes that have been copied +// to the user buffer. +// "copied" is the total bytes copied into the user buffer for each loop. +// + while (count > 0) { + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + "_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n", + count, s->dma_adc.count, + s->dma_adc.swptr, s->dma_adc.hwptr)); + spin_lock_irqsave(&s->lock, flags); + + // get the current copy point of the sw buffer + swptr = s->dma_adc.swptr; + + // cnt is the amount of unread bytes from the end of the + // hw buffer to the current sw pointer + cnt = s->dma_adc.dmasize - swptr; + + // dma_adc.count is the current total bytes that have not been read. + // if the amount of unread bytes from the current sw pointer to the + // end of the buffer is greater than the current total bytes that + // have not been read, then set the "cnt" (unread bytes) to the + // amount of unread bytes. + + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + // + // if we are converting from 8/16 then we need to copy + // twice the number of 16 bit bytes then 8 bit bytes. + // + if (s->conversion) { + if (cnt > (count * 2)) + cnt = (count * 2); + } else { + if (cnt > count) + cnt = count; + } + // + // "cnt" NOW is the smaller of the amount that will be read, + // and the amount that is requested in this read (or partial). + // if there are no bytes in the buffer to read, then start the + // ADC and wait for the interrupt handler to wake us up. + // + if (cnt <= 0) { + + // start up the dma engine and then continue back to the top of + // the loop when wake up occurs. + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + // there are bytes in the buffer to read. + // copy from the hw buffer over to the user buffer. + // user buffer is designated by "buffer" + // virtual address to copy from is rawbuf+swptr + // the "cnt" is the number of bytes to read. + + CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO + "_read() copy_to cnt=%d count=%d ", cnt, count)); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", + s->dma_adc.dmasize, s->dma_adc.count, + (unsigned) buffer, ret)); + + if (cs_copy_to_user + (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= copied; + buffer += copied; + ret += copied; + start_adc(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4281: cs4281_read()- %d\n", ret)); + return ret; +} + + +static ssize_t cs4281_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr, hwptr, busaddr; + int cnt; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4281: cs4281_write()+ count=%d\n", + count)); + VALIDATE_STATE(s); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + if (s->dma_dac.underrun) { + s->dma_dac.underrun = 0; + hwptr = readl(s->pBA0 + BA0_DCA0); + busaddr = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) busaddr; + s->dma_dac.swptr = s->dma_dac.hwptr = hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize - swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4281: cs4281_write()- %d\n", ret)); + return ret; +} + + +static unsigned int cs4281_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4281: cs4281_poll()+\n")); + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4281: cs4281_poll() wait on FMODE_WRITE\n")); + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4281: cs4281_poll() wait on FMODE_READ\n")); + if(!s->dma_dac.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed) s->dma_dac.fragsize) { + if (s->dma_dac.wakeup) + mask |= POLLOUT | POLLWRNORM; + else + mask = 0; + s->dma_dac.wakeup = 0; + } + } else { + if ((signed) (s->dma_dac.dmasize/2) >= s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } else if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4281: cs4281_poll()- 0x%.8x\n", + mask)); + return mask; +} + + +static int cs4281_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()+\n")); + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; +// +// only support PLAYBACK for now +// + db = &s->dma_dac; + + if (cs4x_pgoff(vma) != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range + (vma->vm_start, virt_to_phys(db->rawbuf), size, + vma->vm_page_prot)) return -EAGAIN; + db->mapped = 1; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()- 0 size=%d\n", + (unsigned) size)); + + return 0; +} + + +static int cs4281_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): file=0x%.8x cmd=0x%.8x\n", + (unsigned) file, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): SOUND_VERSION=0x%.8x\n", + SOUND_VERSION)); + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SYNC\n")); + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, + 0 /*file->f_flags & O_NONBLOCK */ + ); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, + (int *) arg); + + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_RESET\n")); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = + s->dma_dac.count = s->dma_dac.total_bytes = + s->dma_dac.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_DAC); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = + s->dma_adc.count = s->dma_adc.total_bytes = + s->dma_adc.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_ADC); + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SPEED val=%d\n", val)); + // + // support independent capture and playback channels + // assume that the file mode bit determines the + // direction of the data flow. + // + if (file->f_mode & FMODE_READ) { + if (val >= 0) { + stop_adc(s); + s->dma_adc.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_adc.rate = val; + prog_codec(s, CS_TYPE_ADC); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val >= 0) { + stop_dac(s); + s->dma_dac.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_dac.rate = val; + prog_codec(s, CS_TYPE_DAC); + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.rate; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.rate; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_STEREO val=%d\n", val)); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + s->prop_adc.channels = val ? 2 : 1; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + s->prop_dac.channels = val ? 2 : 1; + prog_codec(s, CS_TYPE_DAC); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_CHANNELS val=%d\n", + val)); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + s->prop_adc.channels = 2; + else + s->prop_adc.channels = 1; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + s->prop_dac.channels = 2; + else + s->prop_dac.channels = 1; + prog_codec(s, CS_TYPE_DAC); + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.channels; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.channels; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETFMTS: // Returns a mask + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_GETFMT val=0x%.8x\n", + AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8)); + return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8, (int *) arg); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *) arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SETFMT val=0x%.8x\n", + val)); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_adc.fmt = val; + s->prop_adc.fmt_original = s->prop_adc.fmt; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_dac.fmt = val; + s->prop_dac.fmt_original = s->prop_dac.fmt; + prog_codec(s, CS_TYPE_DAC); + } + } else { + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.fmt_original; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.fmt_original; + } + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SETFMT return val=0x%.8x\n", + val)); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_POST\n")); + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready + && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready + && (ret = prog_dmabuf_dac(s))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + abinfo.fragsize = s->dma_dac.fragsize; + if (s->dma_dac.mapped) + abinfo.bytes = s->dma_dac.dmasize; + else + abinfo.bytes = + s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n", + abinfo.fragsize,abinfo.bytes,abinfo.fragstotal, + abinfo.fragments)); + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + if (s->conversion) { + abinfo.fragsize = s->dma_adc.fragsize / 2; + abinfo.bytes = s->dma_adc.count / 2; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> (s->dma_adc.fragshift - 1); + } else { + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> s->dma_adc.fragshift; + } + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if(!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_adc.total_bytes; + if (s->dma_adc.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_adc.fragshift) - + s->dma_adc.blocks; + s->dma_adc.blocks = + cinfo.bytes >> s->dma_adc.fragshift; + } else { + if (s->conversion) { + cinfo.blocks = + s->dma_adc.count / + 2 >> (s->dma_adc.fragshift - 1); + } else + cinfo.blocks = + s->dma_adc.count >> s->dma_adc. + fragshift; + } + if (s->conversion) + cinfo.ptr = s->dma_adc.hwptr / 2; + else + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_dac.total_bytes; + if (s->dma_dac.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_dac.fragshift) - + s->dma_dac.blocks; + s->dma_dac.blocks = + cinfo.bytes >> s->dma_dac.fragshift; + } else { + cinfo.blocks = + s->dma_dac.count >> s->dma_dac.fragshift; + } + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *) arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + if (s->conversion) + return put_user(s->dma_adc.fragsize / 2, + (int *) arg); + else + return put_user(s->dma_adc.fragsize, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + return 0; // Say OK, but do nothing. + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) + || (file->f_mode & FMODE_WRITE + && s->dma_dac.subdivision)) return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + else if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.rate, (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.rate, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.channels, (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.channels, (int *) arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return + put_user( + (s->prop_adc. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + (int *) arg); + else if (file->f_mode & FMODE_WRITE) + return + put_user( + (s->prop_dac. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + (int *) arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return mixer_ioctl(s, cmd, arg); +} + + +static int cs4281_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO + "cs4281: cs4281_release(): inode=0x%.8x file=0x%.8x f_mode=%d\n", + (unsigned) inode, (unsigned) file, file->f_mode)); + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_dac); + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + s->open_mode &= ~FMODE_WRITE; + up(&s->open_sem_dac); + wake_up(&s->open_wait_dac); + MOD_DEC_USE_COUNT; + } + if (file->f_mode & FMODE_READ) { + drain_adc(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_adc); + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + s->open_mode &= ~FMODE_READ; + up(&s->open_sem_adc); + wake_up(&s->open_wait_adc); + MOD_DEC_USE_COUNT; + } + return 0; +} + +static int cs4281_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", + (unsigned) inode, (unsigned) file, file->f_mode)); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + + // wait for device to become free + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - must open READ and/or WRITE\n")); + return -ENODEV; + } + if (file->f_mode & FMODE_WRITE) { + down(&s->open_sem_dac); + while (s->open_mode & FMODE_WRITE) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_dac); + return -EBUSY; + } + up(&s->open_sem_dac); + interruptible_sleep_on(&s->open_wait_dac); + + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem_dac); + } + } + if (file->f_mode & FMODE_READ) { + down(&s->open_sem_adc); + while (s->open_mode & FMODE_READ) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_adc); + return -EBUSY; + } + up(&s->open_sem_adc); + interruptible_sleep_on(&s->open_wait_adc); + + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem_adc); + } + } + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + if (file->f_mode & FMODE_READ) { + s->prop_adc.fmt = AFMT_U8; + s->prop_adc.fmt_original = s->prop_adc.fmt; + s->prop_adc.channels = 1; + s->prop_adc.rate = 8000; + s->prop_adc.clkdiv = 96 | 0x80; + s->conversion = 0; + s->ena &= ~FMODE_READ; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = 0; + up(&s->open_sem_adc); + MOD_INC_USE_COUNT; + + if (prog_dmabuf_adc(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4281: adc Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + s->prop_dac.fmt = AFMT_U8; + s->prop_dac.fmt_original = s->prop_dac.fmt; + s->prop_dac.channels = 1; + s->prop_dac.rate = 8000; + s->prop_dac.clkdiv = 96 | 0x80; + s->conversion = 0; + s->ena &= ~FMODE_WRITE; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = 0; + up(&s->open_sem_dac); + MOD_INC_USE_COUNT; + + if (prog_dmabuf_dac(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4281: dac Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_DAC); + } + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, + printk(KERN_INFO "cs4281: cs4281_open()- 0\n")); + return 0; +} + + +// ****************************************************************************************** +// Wave (audio) file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_audio_fops = { + llseek:no_llseek, + read:cs4281_read, + write:cs4281_write, + poll:cs4281_poll, + ioctl:cs4281_ioctl, + mmap:cs4281_mmap, + open:cs4281_open, + release:cs4281_release, +}; + +// --------------------------------------------------------------------- + +// hold spinlock for the following! +static void cs4281_handle_midi(struct cs4281_state *s) +{ + unsigned char ch; + int wake; + unsigned temp1; + + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x80)) { + ch = readl(s->pBA0 + BA0_MIDRP); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x40) && s->midi.ocnt > 0) { + temp1 = (s->midi.obuf[s->midi.ord]) & 0x000000ff; + writel(temp1, s->pBA0 + BA0_MIDWP); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF - 16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + + + +static void cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs4281_state *s = (struct cs4281_state *) dev_id; + unsigned int temp1; + + // fastpath out, to ease interrupt sharing + temp1 = readl(s->pBA0 + BA0_HISR); // Get Int Status reg. + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4281: cs4281_interrupt() BA0_HISR=0x%.8x\n", temp1)); +/* +* If not DMA or MIDI interrupt, then just return. +*/ + if (!(temp1 & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) { + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + CS_DBGOUT(CS_INTERRUPT, 9, printk(KERN_INFO + "cs4281: cs4281_interrupt(): returning not cs4281 interrupt.\n")); + return; + } + + if (temp1 & HISR_DMA0) // If play interrupt, + readl(s->pBA0 + BA0_HDSR0); // clear the source. + + if (temp1 & HISR_DMA1) // Same for play. + readl(s->pBA0 + BA0_HDSR1); + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Local EOI + + spin_lock(&s->lock); + cs4281_update_ptr(s,CS_TRUE); + cs4281_handle_midi(s); + spin_unlock(&s->lock); +} + +// ************************************************************************** + +static void cs4281_midi_timer(unsigned long data) +{ + struct cs4281_state *s = (struct cs4281_state *) data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies + 1; + add_timer(&s->midi.timer); +} + + +// --------------------------------------------------------------------- + +static ssize_t cs4281_midi_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + + +static ssize_t cs4281_midi_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + + +static unsigned int cs4281_midi_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int cs4281_midi_open(struct inode *inode, struct file *file) +{ + unsigned long flags, temp1; + unsigned int minor = minor(inode->i_rdev); + struct cs4281_state *s=NULL; + struct list_head *entry; + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (s->dev_midi == minor) + break; + } + + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + // wait for device to become free + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + writel(1, s->pBA0 + BA0_MIDCR); // Reset the interface. + writel(0, s->pBA0 + BA0_MIDCR); // Return to normal mode. + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + writel(0x0000000f, s->pBA0 + BA0_MIDCR); // Enable transmit, record, ints. + temp1 = readl(s->pBA0 + BA0_HIMR); + writel(temp1 & 0xffbfffff, s->pBA0 + BA0_HIMR); // Enable midi int. recognition. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies + 1; + s->midi.timer.data = (unsigned long) s; + s->midi.timer.function = cs4281_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= + (file-> + f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + + +static int cs4281_midi_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG + "cs4281: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= + (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + writel(0, s->pBA0 + BA0_MIDCR); // Disable Midi interrupts. + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +// ****************************************************************************************** +// Midi file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_midi_fops = { + llseek:no_llseek, + read:cs4281_midi_read, + write:cs4281_midi_write, + poll:cs4281_midi_poll, + open:cs4281_midi_open, + release:cs4281_midi_release, +}; + + +// --------------------------------------------------------------------- + +// maximum number of devices +#define NR_DEVICE 8 // Only eight devices supported currently. + +// --------------------------------------------------------------------- + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + + { + SOUND_MIXER_WRITE_VOLUME, 0x4040}, { + SOUND_MIXER_WRITE_PCM, 0x4040}, { + SOUND_MIXER_WRITE_SYNTH, 0x4040}, { + SOUND_MIXER_WRITE_CD, 0x4040}, { + SOUND_MIXER_WRITE_LINE, 0x4040}, { + SOUND_MIXER_WRITE_LINE1, 0x4040}, { + SOUND_MIXER_WRITE_RECLEV, 0x0000}, { + SOUND_MIXER_WRITE_SPEAKER, 0x4040}, { + SOUND_MIXER_WRITE_MIC, 0x0000} +}; + + +#ifndef NOT_CS4281_PM +void __devinit cs4281_BuildFIFO( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ + switch(p->number) + { + case 0: /* playback */ + { + p->u32FCRnAddress = BA0_FCR0; + p->u32FSICnAddress = BA0_FSIC0; + p->u32FPDRnAddress = BA0_FPDR0; + break; + } + case 1: /* capture */ + { + p->u32FCRnAddress = BA0_FCR1; + p->u32FSICnAddress = BA0_FSIC1; + p->u32FPDRnAddress = BA0_FPDR1; + break; + } + + case 2: + { + p->u32FCRnAddress = BA0_FCR2; + p->u32FSICnAddress = BA0_FSIC2; + p->u32FPDRnAddress = BA0_FPDR2; + break; + } + case 3: + { + p->u32FCRnAddress = BA0_FCR3; + p->u32FSICnAddress = BA0_FSIC3; + p->u32FPDRnAddress = BA0_FPDR3; + break; + } + default: + break; + } + // + // first read the hardware to initialize the member variables + // + p->u32FCRnValue = readl(s->pBA0 + p->u32FCRnAddress); + p->u32FSICnValue = readl(s->pBA0 + p->u32FSICnAddress); + p->u32FPDRnValue = readl(s->pBA0 + p->u32FPDRnAddress); + +} + +void __devinit cs4281_BuildDMAengine( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ +/* +* initialize all the addresses of this pipeline dma info. +*/ + switch(p->number) + { + case 0: /* playback */ + { + p->u32DBAnAddress = BA0_DBA0; + p->u32DCAnAddress = BA0_DCA0; + p->u32DBCnAddress = BA0_DBC0; + p->u32DCCnAddress = BA0_DCC0; + p->u32DMRnAddress = BA0_DMR0; + p->u32DCRnAddress = BA0_DCR0; + p->u32HDSRnAddress = BA0_HDSR0; + break; + } + + case 1: /* capture */ + { + p->u32DBAnAddress = BA0_DBA1; + p->u32DCAnAddress = BA0_DCA1; + p->u32DBCnAddress = BA0_DBC1; + p->u32DCCnAddress = BA0_DCC1; + p->u32DMRnAddress = BA0_DMR1; + p->u32DCRnAddress = BA0_DCR1; + p->u32HDSRnAddress = BA0_HDSR1; + break; + } + + case 2: + { + p->u32DBAnAddress = BA0_DBA2; + p->u32DCAnAddress = BA0_DCA2; + p->u32DBCnAddress = BA0_DBC2; + p->u32DCCnAddress = BA0_DCC2; + p->u32DMRnAddress = BA0_DMR2; + p->u32DCRnAddress = BA0_DCR2; + p->u32HDSRnAddress = BA0_HDSR2; + break; + } + + case 3: + { + p->u32DBAnAddress = BA0_DBA3; + p->u32DCAnAddress = BA0_DCA3; + p->u32DBCnAddress = BA0_DBC3; + p->u32DCCnAddress = BA0_DCC3; + p->u32DMRnAddress = BA0_DMR3; + p->u32DCRnAddress = BA0_DCR3; + p->u32HDSRnAddress = BA0_HDSR3; + break; + } + default: + break; + } + +// +// Initialize the dma values for this pipeline +// + p->u32DBAnValue = readl(s->pBA0 + p->u32DBAnAddress); + p->u32DBCnValue = readl(s->pBA0 + p->u32DBCnAddress); + p->u32DMRnValue = readl(s->pBA0 + p->u32DMRnAddress); + p->u32DCRnValue = readl(s->pBA0 + p->u32DCRnAddress); + +} + +void __devinit cs4281_InitPM(struct cs4281_state *s) +{ + int i; + struct cs4281_pipeline *p; + + for(i=0;ipl[i]; + p->number = i; + cs4281_BuildDMAengine(p,s); + cs4281_BuildFIFO(p,s); + /* + * currently only 2 pipelines are used + * so, only set the valid bit on the playback and capture. + */ + if( (i == CS4281_PLAYBACK_PIPELINE_NUMBER) || + (i == CS4281_CAPTURE_PIPELINE_NUMBER)) + p->flags |= CS4281_PIPELINE_VALID; + } + s->pm.u32SSPM_BITS = 0x7e; /* rev c, use 0x7c for rev a or b */ +} +#endif + +static int __devinit cs4281_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ +#ifndef NOT_CS4281_PM + struct pm_dev *pmdev; +#endif + struct cs4281_state *s; + dma_addr_t dma_mask; + mm_segment_t fs; + int i, val; + unsigned int temp1, temp2; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, + printk(KERN_INFO "cs4281: probe()+\n")); + + if (pci_enable_device(pcidev)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: pci_enable_device() failed\n")); + return -1; + } + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) || + !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe()- Memory region not assigned\n")); + return -ENODEV; + } + if (pcidev->irq == 0) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() IRQ not assigned\n")); + return -ENODEV; + } + dma_mask = 0xffffffff; /* this enables playback and recording */ + i = pci_set_dma_mask(pcidev, dma_mask); + if (i) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n")); + return i; + } + if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() no memory for state struct.\n")); + return -1; + } + memset(s, 0, sizeof(struct cs4281_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->open_wait_adc); + init_waitqueue_head(&s->open_wait_dac); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + init_MUTEX(&s->open_sem_adc); + init_MUTEX(&s->open_sem_dac); + spin_lock_init(&s->lock); + s->pBA0phys = pci_resource_start(pcidev, 0); + s->pBA1phys = pci_resource_start(pcidev, 1); + + /* Convert phys to linear. */ + s->pBA0 = ioremap_nocache(s->pBA0phys, 4096); + if (!s->pBA0) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA0 I/O mapping failed. Skipping part.\n")); + goto err_free; + } + s->pBA1 = ioremap_nocache(s->pBA1phys, 65536); + if (!s->pBA1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA1 I/O mapping failed. Skipping part.\n")); + goto err_unmap; + } + + temp1 = readl(s->pBA0 + BA0_PCICFG00); + temp2 = readl(s->pBA0 + BA0_PCICFG04); + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=0x%.8x pBA1=0x%.8x \n", + (unsigned) temp1, (unsigned) temp2, + (unsigned) s->pBA0, (unsigned) s->pBA1)); + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n", + (unsigned) s->pBA0phys, (unsigned) s->pBA1phys)); + +#ifndef NOT_CS4281_PM + s->pm.flags = CS4281_PM_IDLE; +#endif + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: cs4281_hw_init() failed. Skipping part.\n")); + goto err_irq; + } + s->magic = CS4281_MAGIC; + s->pcidev = pcidev; + s->irq = pcidev->irq; + if (request_irq + (s->irq, cs4281_interrupt, SA_SHIRQ, "Crystal CS4281", s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, + printk(KERN_ERR "cs4281: irq %u in use\n", s->irq)); + goto err_irq; + } + if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_dsp() failed.\n")); + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_mixer() failed.\n")); + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_midi() failed.\n")); + goto err_dev3; + } +#ifndef NOT_CS4281_PM + cs4281_InitPM(s); + pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), cs4281_pm_callback); + if (pmdev) + { + CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO + "cs4281: probe() pm_register() succeeded (0x%x).\n", + (unsigned)pmdev)); + pmdev->data = s; + } + else + { + CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 0, printk(KERN_INFO + "cs4281: probe() pm_register() failed (0x%x).\n", + (unsigned)pmdev)); + s->pm.flags |= CS4281_PM_NOT_REGISTERED; + } +#endif + + pci_set_master(pcidev); // enable bus mastering + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val); + for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val); + } + val = 1; // enable mic preamp + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long) &val); + set_fs(fs); + + pci_set_drvdata(pcidev, s); + list_add(&s->list, &cs4281_devs); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: probe()- device allocated successfully\n")); + return 0; + + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_irq(s->irq, s); + err_irq: + iounmap(s->pBA1); + err_unmap: + iounmap(s->pBA0); + err_free: + kfree(s); + + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: probe()- no device allocated\n")); + return -ENODEV; +} // probe_cs4281 + + +// --------------------------------------------------------------------- + +static void __devinit cs4281_remove(struct pci_dev *pci_dev) +{ + struct cs4281_state *s = pci_get_drvdata(pci_dev); + // stop DMA controller + synchronize_irq(); + free_irq(s->irq, s); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + iounmap(s->pBA1); + iounmap(s->pBA0); + pci_set_drvdata(pci_dev,NULL); + list_del(&s->list); + kfree(s); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_remove()-: remove successful\n")); +} + +static struct pci_device_id cs4281_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CRYSTAL_CS4281, + PCI_ANY_ID, PCI_ANY_ID, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cs4281_pci_tbl); + +struct pci_driver cs4281_pci_driver = { + name:"cs4281", + id_table:cs4281_pci_tbl, + probe:cs4281_probe, + remove:cs4281_remove, + suspend:CS4281_SUSPEND_TBL, + resume:CS4281_RESUME_TBL, +}; + +int __init cs4281_init_module(void) +{ + int rtn = 0; + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_init_module()+ \n")); + if (!pci_present()) { /* No PCI bus in this machine! */ + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_init_module()- no pci bus found\n")); + return -ENODEV; + } + printk(KERN_INFO "cs4281: version v%d.%02d.%d time " __TIME__ " " + __DATE__ "\n", CS4281_MAJOR_VERSION, CS4281_MINOR_VERSION, + CS4281_ARCH); + rtn = pci_module_init(&cs4281_pci_driver); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn)); + return rtn; +} + +void __exit cs4281_cleanup_module(void) +{ + pci_unregister_driver(&cs4281_pci_driver); +#ifndef NOT_CS4281_PM + cs_pm_unregister_all(cs4281_pm_callback); +#endif + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cleanup_cs4281() finished\n")); +} +// --------------------------------------------------------------------- + +MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com"); +MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver"); +MODULE_LICENSE("GPL"); + +// --------------------------------------------------------------------- + +module_init(cs4281_init_module); +module_exit(cs4281_cleanup_module); + +#ifndef MODULE +int __init init_cs4281(void) +{ + return cs4281_init_module(); +} +#endif diff -Nru linux/sound/oss/cs4281/cs4281pm-24.c linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm-24.c --- linux/sound/oss/cs4281/cs4281pm-24.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm-24.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,84 @@ +/******************************************************************************* +* +* "cs4281pm.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ + +#ifndef NOT_CS4281_PM +#include + +#define cs_pm_register(a, b, c) pm_register((a), (b), (c)); +#define cs_pm_unregister_all(a) pm_unregister_all((a)); + +int cs4281_suspend(struct cs4281_state *s); +int cs4281_resume(struct cs4281_state *s); +/* +* for now (12/22/00) only enable the pm_register PM support. +* allow these table entries to be null. +#define CS4281_SUSPEND_TBL cs4281_suspend_tbl +#define CS4281_RESUME_TBL cs4281_resume_tbl +*/ +#define CS4281_SUSPEND_TBL cs4281_null +#define CS4281_RESUME_TBL cs4281_null + +int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct cs4281_state *state; + + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: cs4281_pm_callback dev=0x%x rqst=0x%x state=%d\n", + (unsigned)dev,(unsigned)rqst,(unsigned)data)); + state = (struct cs4281_state *) dev->data; + if (state) { + switch(rqst) { + case PM_SUSPEND: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: PM suspend request\n")); + if(cs4281_suspend(state)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: PM suspend request refused\n")); + return 1; + } + break; + case PM_RESUME: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: PM resume request\n")); + if(cs4281_resume(state)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: PM resume request refused\n")); + return 1; + } + break; + } + } + + return 0; +} + +#else /* CS4281_PM */ +#define CS4281_SUSPEND_TBL cs4281_null +#define CS4281_RESUME_TBL cs4281_null +#endif /* CS4281_PM */ + diff -Nru linux/sound/oss/cs4281/cs4281pm.h linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm.h --- linux/sound/oss/cs4281/cs4281pm.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs4281/cs4281pm.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,74 @@ +#ifndef NOT_CS4281_PM +/******************************************************************************* +* +* "cs4281pm.h" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +/* general pm definitions */ +#define CS4281_AC97_HIGHESTREGTORESTORE 0x26 +#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1) + +/* pipeline definitions */ +#define CS4281_NUMBER_OF_PIPELINES 4 +#define CS4281_PIPELINE_VALID 0x0001 +#define CS4281_PLAYBACK_PIPELINE_NUMBER 0x0000 +#define CS4281_CAPTURE_PIPELINE_NUMBER 0x0001 + +/* PM state defintions */ +#define CS4281_PM_NOT_REGISTERED 0x1000 +#define CS4281_PM_IDLE 0x0001 +#define CS4281_PM_SUSPENDING 0x0002 +#define CS4281_PM_SUSPENDED 0x0004 +#define CS4281_PM_RESUMING 0x0008 +#define CS4281_PM_RESUMED 0x0010 + +struct cs4281_pm { + unsigned long flags; + u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; + u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; + u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; + u32 u32SSPM_BITS; + u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS]; + u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; + u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; + u32 u32hwptr_playback,u32hwptr_capture; +}; + +struct cs4281_pipeline { + unsigned flags; + unsigned number; + u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue; + u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress; + u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress; + u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save; + u32 u32DCCn_Save,u32DCAn_Save; +/* +* technically, these are fifo variables, but just map the +* first fifo with the first pipeline and then use the fifo +* variables inside of the pipeline struct. +*/ + u32 u32FCRn_Save,u32FSICn_Save; + u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress; + u32 u32FPDRnValue,u32FPDRnAddress; +}; +#endif diff -Nru linux/sound/oss/cs461x.h linux-2.4.19-pre5-mjc/sound/oss/cs461x.h --- linux/sound/oss/cs461x.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs461x.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1691 @@ +#ifndef __CS461X_H +#define __CS461X_H + +/* + * Copyright (c) by Cirrus Logic Corporation + * Copyright (c) by Jaroslav Kysela + * Definitions for Cirrus Logic CS461x chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4610 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4612 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4615 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 +#endif + +/* + * Direct registers + */ + +/* + * The following define the offsets of the registers accessed via base address + * register zero on the CS461x part. + */ +#define BA0_HISR 0x00000000 +#define BA0_HSR0 0x00000004 +#define BA0_HICR 0x00000008 +#define BA0_DMSR 0x00000100 +#define BA0_HSAR 0x00000110 +#define BA0_HDAR 0x00000114 +#define BA0_HDMR 0x00000118 +#define BA0_HDCR 0x0000011C +#define BA0_PFMC 0x00000200 +#define BA0_PFCV1 0x00000204 +#define BA0_PFCV2 0x00000208 +#define BA0_PCICFG00 0x00000300 +#define BA0_PCICFG04 0x00000304 +#define BA0_PCICFG08 0x00000308 +#define BA0_PCICFG0C 0x0000030C +#define BA0_PCICFG10 0x00000310 +#define BA0_PCICFG14 0x00000314 +#define BA0_PCICFG18 0x00000318 +#define BA0_PCICFG1C 0x0000031C +#define BA0_PCICFG20 0x00000320 +#define BA0_PCICFG24 0x00000324 +#define BA0_PCICFG28 0x00000328 +#define BA0_PCICFG2C 0x0000032C +#define BA0_PCICFG30 0x00000330 +#define BA0_PCICFG34 0x00000334 +#define BA0_PCICFG38 0x00000338 +#define BA0_PCICFG3C 0x0000033C +#define BA0_CLKCR1 0x00000400 +#define BA0_CLKCR2 0x00000404 +#define BA0_PLLM 0x00000408 +#define BA0_PLLCC 0x0000040C +#define BA0_FRR 0x00000410 +#define BA0_CFL1 0x00000414 +#define BA0_CFL2 0x00000418 +#define BA0_SERMC1 0x00000420 +#define BA0_SERMC2 0x00000424 +#define BA0_SERC1 0x00000428 +#define BA0_SERC2 0x0000042C +#define BA0_SERC3 0x00000430 +#define BA0_SERC4 0x00000434 +#define BA0_SERC5 0x00000438 +#define BA0_SERBSP 0x0000043C +#define BA0_SERBST 0x00000440 +#define BA0_SERBCM 0x00000444 +#define BA0_SERBAD 0x00000448 +#define BA0_SERBCF 0x0000044C +#define BA0_SERBWP 0x00000450 +#define BA0_SERBRP 0x00000454 +#ifndef NO_CS4612 +#define BA0_ASER_FADDR 0x00000458 +#endif +#define BA0_ACCTL 0x00000460 +#define BA0_ACSTS 0x00000464 +#define BA0_ACOSV 0x00000468 +#define BA0_ACCAD 0x0000046C +#define BA0_ACCDA 0x00000470 +#define BA0_ACISV 0x00000474 +#define BA0_ACSAD 0x00000478 +#define BA0_ACSDA 0x0000047C +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_MIDCR 0x00000490 +#define BA0_MIDSR 0x00000494 +#define BA0_MIDWP 0x00000498 +#define BA0_MIDRP 0x0000049C +#define BA0_JSIO 0x000004A0 +#ifndef NO_CS4612 +#define BA0_ASER_MASTER 0x000004A4 +#endif +#define BA0_CFGI 0x000004B0 +#define BA0_SSVID 0x000004B4 +#define BA0_GPIOR 0x000004B8 +#ifndef NO_CS4612 +#define BA0_EGPIODR 0x000004BC +#define BA0_EGPIOPTR 0x000004C0 +#define BA0_EGPIOTR 0x000004C4 +#define BA0_EGPIOWR 0x000004C8 +#define BA0_EGPIOSR 0x000004CC +#define BA0_SERC6 0x000004D0 +#define BA0_SERC7 0x000004D4 +#define BA0_SERACC 0x000004D8 +#define BA0_ACCTL2 0x000004E0 +#define BA0_ACSTS2 0x000004E4 +#define BA0_ACOSV2 0x000004E8 +#define BA0_ACCAD2 0x000004EC +#define BA0_ACCDA2 0x000004F0 +#define BA0_ACISV2 0x000004F4 +#define BA0_ACSAD2 0x000004F8 +#define BA0_ACSDA2 0x000004FC +#define BA0_IOTAC0 0x00000500 +#define BA0_IOTAC1 0x00000504 +#define BA0_IOTAC2 0x00000508 +#define BA0_IOTAC3 0x0000050C +#define BA0_IOTAC4 0x00000510 +#define BA0_IOTAC5 0x00000514 +#define BA0_IOTAC6 0x00000518 +#define BA0_IOTAC7 0x0000051C +#define BA0_IOTAC8 0x00000520 +#define BA0_IOTAC9 0x00000524 +#define BA0_IOTAC10 0x00000528 +#define BA0_IOTAC11 0x0000052C +#define BA0_IOTFR0 0x00000540 +#define BA0_IOTFR1 0x00000544 +#define BA0_IOTFR2 0x00000548 +#define BA0_IOTFR3 0x0000054C +#define BA0_IOTFR4 0x00000550 +#define BA0_IOTFR5 0x00000554 +#define BA0_IOTFR6 0x00000558 +#define BA0_IOTFR7 0x0000055C +#define BA0_IOTFIFO 0x00000580 +#define BA0_IOTRRD 0x00000584 +#define BA0_IOTFP 0x00000588 +#define BA0_IOTCR 0x0000058C +#define BA0_DPCID 0x00000590 +#define BA0_DPCIA 0x00000594 +#define BA0_DPCIC 0x00000598 +#define BA0_PCPCIR 0x00000600 +#define BA0_PCPCIG 0x00000604 +#define BA0_PCPCIEN 0x00000608 +#define BA0_EPCIPMC 0x00000610 +#endif + +/* + * The following define the offsets of the registers and memories accessed via + * base address register one on the CS461x part. + */ +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 +#define BA1_SPCR 0x00030000 +#define BA1_DREG 0x00030004 +#define BA1_DSRWP 0x00030008 +#define BA1_TWPR 0x0003000C +#define BA1_SPWR 0x00030010 +#define BA1_SPIR 0x00030014 +#define BA1_FGR1 0x00030020 +#define BA1_SPCS 0x00030028 +#define BA1_SDSR 0x0003002C +#define BA1_FRMT 0x00030030 +#define BA1_FRCC 0x00030034 +#define BA1_FRSC 0x00030038 +#define BA1_OMNI_MEM 0x000E0000 + +/* + * The following defines are for the flags in the host interrupt status + * register. + */ +#define HISR_VC_MASK 0x0000FFFF +#define HISR_VC0 0x00000001 +#define HISR_VC1 0x00000002 +#define HISR_VC2 0x00000004 +#define HISR_VC3 0x00000008 +#define HISR_VC4 0x00000010 +#define HISR_VC5 0x00000020 +#define HISR_VC6 0x00000040 +#define HISR_VC7 0x00000080 +#define HISR_VC8 0x00000100 +#define HISR_VC9 0x00000200 +#define HISR_VC10 0x00000400 +#define HISR_VC11 0x00000800 +#define HISR_VC12 0x00001000 +#define HISR_VC13 0x00002000 +#define HISR_VC14 0x00004000 +#define HISR_VC15 0x00008000 +#define HISR_INT0 0x00010000 +#define HISR_INT1 0x00020000 +#define HISR_DMAI 0x00040000 +#define HISR_FROVR 0x00080000 +#define HISR_MIDI 0x00100000 +#ifdef NO_CS4612 +#define HISR_RESERVED 0x0FE00000 +#else +#define HISR_SBINT 0x00200000 +#define HISR_RESERVED 0x0FC00000 +#endif +#define HISR_H0P 0x40000000 +#define HISR_INTENA 0x80000000 + +/* + * The following defines are for the flags in the host signal register 0. + */ +#define HSR0_VC_MASK 0xFFFFFFFF +#define HSR0_VC16 0x00000001 +#define HSR0_VC17 0x00000002 +#define HSR0_VC18 0x00000004 +#define HSR0_VC19 0x00000008 +#define HSR0_VC20 0x00000010 +#define HSR0_VC21 0x00000020 +#define HSR0_VC22 0x00000040 +#define HSR0_VC23 0x00000080 +#define HSR0_VC24 0x00000100 +#define HSR0_VC25 0x00000200 +#define HSR0_VC26 0x00000400 +#define HSR0_VC27 0x00000800 +#define HSR0_VC28 0x00001000 +#define HSR0_VC29 0x00002000 +#define HSR0_VC30 0x00004000 +#define HSR0_VC31 0x00008000 +#define HSR0_VC32 0x00010000 +#define HSR0_VC33 0x00020000 +#define HSR0_VC34 0x00040000 +#define HSR0_VC35 0x00080000 +#define HSR0_VC36 0x00100000 +#define HSR0_VC37 0x00200000 +#define HSR0_VC38 0x00400000 +#define HSR0_VC39 0x00800000 +#define HSR0_VC40 0x01000000 +#define HSR0_VC41 0x02000000 +#define HSR0_VC42 0x04000000 +#define HSR0_VC43 0x08000000 +#define HSR0_VC44 0x10000000 +#define HSR0_VC45 0x20000000 +#define HSR0_VC46 0x40000000 +#define HSR0_VC47 0x80000000 + +/* + * The following defines are for the flags in the host interrupt control + * register. + */ +#define HICR_IEV 0x00000001 +#define HICR_CHGM 0x00000002 + +/* + * The following defines are for the flags in the DMA status register. + */ +#define DMSR_HP 0x00000001 +#define DMSR_HR 0x00000002 +#define DMSR_SP 0x00000004 +#define DMSR_SR 0x00000008 + +/* + * The following defines are for the flags in the host DMA source address + * register. + */ +#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HSAR_DSP_ADDR_MASK 0x0000FFFF +#define HSAR_MEMID_MASK 0x000F0000 +#define HSAR_MEMID_SP_DMEM0 0x00000000 +#define HSAR_MEMID_SP_DMEM1 0x00010000 +#define HSAR_MEMID_SP_PMEM 0x00020000 +#define HSAR_MEMID_SP_DEBUG 0x00030000 +#define HSAR_MEMID_OMNI_MEM 0x000E0000 +#define HSAR_END 0x40000000 +#define HSAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA destination address + * register. + */ +#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HDAR_DSP_ADDR_MASK 0x0000FFFF +#define HDAR_MEMID_MASK 0x000F0000 +#define HDAR_MEMID_SP_DMEM0 0x00000000 +#define HDAR_MEMID_SP_DMEM1 0x00010000 +#define HDAR_MEMID_SP_PMEM 0x00020000 +#define HDAR_MEMID_SP_DEBUG 0x00030000 +#define HDAR_MEMID_OMNI_MEM 0x000E0000 +#define HDAR_END 0x40000000 +#define HDAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDMR_AC_MASK 0x0000F000 +#define HDMR_AC_8_16 0x00001000 +#define HDMR_AC_M_S 0x00002000 +#define HDMR_AC_B_L 0x00004000 +#define HDMR_AC_S_U 0x00008000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDCR_COUNT_MASK 0x000003FF +#define HDCR_DONE 0x00004000 +#define HDCR_OPT 0x00008000 +#define HDCR_WBD 0x00400000 +#define HDCR_WBS 0x00800000 +#define HDCR_DMS_MASK 0x07000000 +#define HDCR_DMS_LINEAR 0x00000000 +#define HDCR_DMS_16_DWORDS 0x01000000 +#define HDCR_DMS_32_DWORDS 0x02000000 +#define HDCR_DMS_64_DWORDS 0x03000000 +#define HDCR_DMS_128_DWORDS 0x04000000 +#define HDCR_DMS_256_DWORDS 0x05000000 +#define HDCR_DMS_512_DWORDS 0x06000000 +#define HDCR_DMS_1024_DWORDS 0x07000000 +#define HDCR_DH 0x08000000 +#define HDCR_SMS_MASK 0x70000000 +#define HDCR_SMS_LINEAR 0x00000000 +#define HDCR_SMS_16_DWORDS 0x10000000 +#define HDCR_SMS_32_DWORDS 0x20000000 +#define HDCR_SMS_64_DWORDS 0x30000000 +#define HDCR_SMS_128_DWORDS 0x40000000 +#define HDCR_SMS_256_DWORDS 0x50000000 +#define HDCR_SMS_512_DWORDS 0x60000000 +#define HDCR_SMS_1024_DWORDS 0x70000000 +#define HDCR_SH 0x80000000 +#define HDCR_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the performance monitor control + * register. + */ +#define PFMC_C1SS_MASK 0x0000001F +#define PFMC_C1EV 0x00000020 +#define PFMC_C1RS 0x00008000 +#define PFMC_C2SS_MASK 0x001F0000 +#define PFMC_C2EV 0x00200000 +#define PFMC_C2RS 0x80000000 +#define PFMC_C1SS_SHIFT 0 +#define PFMC_C2SS_SHIFT 16 +#define PFMC_BUS_GRANT 0 +#define PFMC_GRANT_AFTER_REQ 1 +#define PFMC_TRANSACTION 2 +#define PFMC_DWORD_TRANSFER 3 +#define PFMC_SLAVE_READ 4 +#define PFMC_SLAVE_WRITE 5 +#define PFMC_PREEMPTION 6 +#define PFMC_DISCONNECT_RETRY 7 +#define PFMC_INTERRUPT 8 +#define PFMC_BUS_OWNERSHIP 9 +#define PFMC_TRANSACTION_LAG 10 +#define PFMC_PCI_CLOCK 11 +#define PFMC_SERIAL_CLOCK 12 +#define PFMC_SP_CLOCK 13 + +/* + * The following defines are for the flags in the performance counter value 1 + * register. + */ +#define PFCV1_PC1V_MASK 0xFFFFFFFF +#define PFCV1_PC1V_SHIFT 0 + +/* + * The following defines are for the flags in the performance counter value 2 + * register. + */ +#define PFCV2_PC2V_MASK 0xFFFFFFFF +#define PFCV2_PC2V_SHIFT 0 + +/* + * The following defines are for the flags in the clock control register 1. + */ +#define CLKCR1_OSCS 0x00000001 +#define CLKCR1_OSCP 0x00000002 +#define CLKCR1_PLLSS_MASK 0x0000000C +#define CLKCR1_PLLSS_SERIAL 0x00000000 +#define CLKCR1_PLLSS_CRYSTAL 0x00000004 +#define CLKCR1_PLLSS_PCI 0x00000008 +#define CLKCR1_PLLSS_RESERVED 0x0000000C +#define CLKCR1_PLLP 0x00000010 +#define CLKCR1_SWCE 0x00000020 +#define CLKCR1_PLLOS 0x00000040 + +/* + * The following defines are for the flags in the clock control register 2. + */ +#define CLKCR2_PDIVS_MASK 0x0000000F +#define CLKCR2_PDIVS_1 0x00000001 +#define CLKCR2_PDIVS_2 0x00000002 +#define CLKCR2_PDIVS_4 0x00000004 +#define CLKCR2_PDIVS_7 0x00000007 +#define CLKCR2_PDIVS_8 0x00000008 +#define CLKCR2_PDIVS_16 0x00000000 + +/* + * The following defines are for the flags in the PLL multiplier register. + */ +#define PLLM_MASK 0x000000FF +#define PLLM_SHIFT 0 + +/* + * The following defines are for the flags in the PLL capacitor coefficient + * register. + */ +#define PLLCC_CDR_MASK 0x00000007 +#ifndef NO_CS4610 +#define PLLCC_CDR_240_350_MHZ 0x00000000 +#define PLLCC_CDR_184_265_MHZ 0x00000001 +#define PLLCC_CDR_144_205_MHZ 0x00000002 +#define PLLCC_CDR_111_160_MHZ 0x00000003 +#define PLLCC_CDR_87_123_MHZ 0x00000004 +#define PLLCC_CDR_67_96_MHZ 0x00000005 +#define PLLCC_CDR_52_74_MHZ 0x00000006 +#define PLLCC_CDR_45_58_MHZ 0x00000007 +#endif +#ifndef NO_CS4612 +#define PLLCC_CDR_271_398_MHZ 0x00000000 +#define PLLCC_CDR_227_330_MHZ 0x00000001 +#define PLLCC_CDR_167_239_MHZ 0x00000002 +#define PLLCC_CDR_150_215_MHZ 0x00000003 +#define PLLCC_CDR_107_154_MHZ 0x00000004 +#define PLLCC_CDR_98_140_MHZ 0x00000005 +#define PLLCC_CDR_73_104_MHZ 0x00000006 +#define PLLCC_CDR_63_90_MHZ 0x00000007 +#endif +#define PLLCC_LPF_MASK 0x000000F8 +#ifndef NO_CS4610 +#define PLLCC_LPF_23850_60000_KHZ 0x00000000 +#define PLLCC_LPF_7960_26290_KHZ 0x00000008 +#define PLLCC_LPF_4160_10980_KHZ 0x00000018 +#define PLLCC_LPF_1740_4580_KHZ 0x00000038 +#define PLLCC_LPF_724_1910_KHZ 0x00000078 +#define PLLCC_LPF_317_798_KHZ 0x000000F8 +#endif +#ifndef NO_CS4612 +#define PLLCC_LPF_25580_64530_KHZ 0x00000000 +#define PLLCC_LPF_14360_37270_KHZ 0x00000008 +#define PLLCC_LPF_6100_16020_KHZ 0x00000018 +#define PLLCC_LPF_2540_6690_KHZ 0x00000038 +#define PLLCC_LPF_1050_2780_KHZ 0x00000078 +#define PLLCC_LPF_450_1160_KHZ 0x000000F8 +#endif + +/* + * The following defines are for the flags in the feature reporting register. + */ +#define FRR_FAB_MASK 0x00000003 +#define FRR_MASK_MASK 0x0000001C +#ifdef NO_CS4612 +#define FRR_CFOP_MASK 0x000000E0 +#else +#define FRR_CFOP_MASK 0x00000FE0 +#endif +#define FRR_CFOP_NOT_DVD 0x00000020 +#define FRR_CFOP_A3D 0x00000040 +#define FRR_CFOP_128_PIN 0x00000080 +#ifndef NO_CS4612 +#define FRR_CFOP_CS4280 0x00000800 +#endif +#define FRR_FAB_SHIFT 0 +#define FRR_MASK_SHIFT 2 +#define FRR_CFOP_SHIFT 5 + +/* + * The following defines are for the flags in the configuration load 1 + * register. + */ +#define CFL1_CLOCK_SOURCE_MASK 0x00000003 +#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 +#define CFL1_CLOCK_SOURCE_AC97 0x00000001 +#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 +#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 +#define CFL1_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the configuration load 2 + * register. + */ +#define CFL2_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the serial port master control + * register 1. + */ +#define SERMC1_MSPE 0x00000001 +#define SERMC1_PTC_MASK 0x0000000E +#define SERMC1_PTC_CS423X 0x00000000 +#define SERMC1_PTC_AC97 0x00000002 +#define SERMC1_PTC_DAC 0x00000004 +#define SERMC1_PLB 0x00000010 +#define SERMC1_XLB 0x00000020 + +/* + * The following defines are for the flags in the serial port master control + * register 2. + */ +#define SERMC2_LROE 0x00000001 +#define SERMC2_MCOE 0x00000002 +#define SERMC2_MCDIV 0x00000004 + +/* + * The following defines are for the flags in the serial port 1 configuration + * register. + */ +#define SERC1_SO1EN 0x00000001 +#define SERC1_SO1F_MASK 0x0000000E +#define SERC1_SO1F_CS423X 0x00000000 +#define SERC1_SO1F_AC97 0x00000002 +#define SERC1_SO1F_DAC 0x00000004 +#define SERC1_SO1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 2 configuration + * register. + */ +#define SERC2_SI1EN 0x00000001 +#define SERC2_SI1F_MASK 0x0000000E +#define SERC2_SI1F_CS423X 0x00000000 +#define SERC2_SI1F_AC97 0x00000002 +#define SERC2_SI1F_ADC 0x00000004 +#define SERC2_SI1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 3 configuration + * register. + */ +#define SERC3_SO2EN 0x00000001 +#define SERC3_SO2F_MASK 0x00000006 +#define SERC3_SO2F_DAC 0x00000000 +#define SERC3_SO2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 4 configuration + * register. + */ +#define SERC4_SO3EN 0x00000001 +#define SERC4_SO3F_MASK 0x00000006 +#define SERC4_SO3F_DAC 0x00000000 +#define SERC4_SO3F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 5 configuration + * register. + */ +#define SERC5_SI2EN 0x00000001 +#define SERC5_SI2F_MASK 0x00000006 +#define SERC5_SI2F_ADC 0x00000000 +#define SERC5_SI2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor sample + * pointer register. + */ +#define SERBSP_FSP_MASK 0x0000000F +#define SERBSP_FSP_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor status + * register. + */ +#define SERBST_RRDY 0x00000001 +#define SERBST_WBSY 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor command + * register. + */ +#define SERBCM_RDC 0x00000001 +#define SERBCM_WRC 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor address + * register. + */ +#ifdef NO_CS4612 +#define SERBAD_FAD_MASK 0x000000FF +#else +#define SERBAD_FAD_MASK 0x000001FF +#endif +#define SERBAD_FAD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor + * configuration register. + */ +#define SERBCF_HBP 0x00000001 + +/* + * The following defines are for the flags in the serial port backdoor write + * port register. + */ +#define SERBWP_FWD_MASK 0x000FFFFF +#define SERBWP_FWD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor read + * port register. + */ +#define SERBRP_FRD_MASK 0x000FFFFF +#define SERBRP_FRD_SHIFT 0 + +/* + * The following defines are for the flags in the async FIFO address register. + */ +#ifndef NO_CS4612 +#define ASER_FADDR_A1_MASK 0x000001FF +#define ASER_FADDR_EN1 0x00008000 +#define ASER_FADDR_A2_MASK 0x01FF0000 +#define ASER_FADDR_EN2 0x80000000 +#define ASER_FADDR_A1_SHIFT 0 +#define ASER_FADDR_A2_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the AC97 control register. + */ +#define ACCTL_RSTN 0x00000001 +#define ACCTL_ESYN 0x00000002 +#define ACCTL_VFRM 0x00000004 +#define ACCTL_DCV 0x00000008 +#define ACCTL_CRW 0x00000010 +#define ACCTL_ASYN 0x00000020 +#ifndef NO_CS4612 +#define ACCTL_TC 0x00000040 +#endif + +/* + * The following defines are for the flags in the AC97 status register. + */ +#define ACSTS_CRDY 0x00000001 +#define ACSTS_VSTS 0x00000002 +#ifndef NO_CS4612 +#define ACSTS_WKUP 0x00000004 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register. + */ +#define ACOSV_SLV3 0x00000001 +#define ACOSV_SLV4 0x00000002 +#define ACOSV_SLV5 0x00000004 +#define ACOSV_SLV6 0x00000008 +#define ACOSV_SLV7 0x00000010 +#define ACOSV_SLV8 0x00000020 +#define ACOSV_SLV9 0x00000040 +#define ACOSV_SLV10 0x00000080 +#define ACOSV_SLV11 0x00000100 +#define ACOSV_SLV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 command address + * register. + */ +#define ACCAD_CI_MASK 0x0000007F +#define ACCAD_CI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 command data register. + */ +#define ACCDA_CD_MASK 0x0000FFFF +#define ACCDA_CD_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 input slot valid + * register. + */ +#define ACISV_ISV3 0x00000001 +#define ACISV_ISV4 0x00000002 +#define ACISV_ISV5 0x00000004 +#define ACISV_ISV6 0x00000008 +#define ACISV_ISV7 0x00000010 +#define ACISV_ISV8 0x00000020 +#define ACISV_ISV9 0x00000040 +#define ACISV_ISV10 0x00000080 +#define ACISV_ISV11 0x00000100 +#define ACISV_ISV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 status address + * register. + */ +#define ACSAD_SI_MASK 0x0000007F +#define ACSAD_SI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 status data register. + */ +#define ACSDA_SD_MASK 0x0000FFFF +#define ACSDA_SD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick poll/trigger + * register. + */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* + * The following defines are for the flags in the joystick control register. + */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* + * The following defines are for the flags in the joystick coordinate pair 1 + * readback register. + */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 + +/* + * The following defines are for the flags in the joystick coordinate pair 2 + * readback register. + */ +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* + * The following defines are for the flags in the MIDI control register. + */ +#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ +#define MIDCR_RXE 0x00000002 /* Enable receiving. */ +#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ +#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ +#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ +#define MIDCR_MRST 0x00000020 /* Reset interface. */ + +/* + * The following defines are for the flags in the MIDI status register. + */ +#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ +#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ + +/* + * The following defines are for the flags in the MIDI write port register. + */ +#define MIDWP_MWD_MASK 0x000000FF +#define MIDWP_MWD_SHIFT 0 + +/* + * The following defines are for the flags in the MIDI read port register. + */ +#define MIDRP_MRD_MASK 0x000000FF +#define MIDRP_MRD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick GPIO register. + */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * The following defines are for the flags in the master async/sync serial + * port enable register. + */ +#ifndef NO_CS4612 +#define ASER_MASTER_ME 0x00000001 +#endif + +/* + * The following defines are for the flags in the configuration interface + * register. + */ +#define CFGI_CLK 0x00000001 +#define CFGI_DOUT 0x00000002 +#define CFGI_DIN_EEN 0x00000004 +#define CFGI_EELD 0x00000008 + +/* + * The following defines are for the flags in the subsystem ID and vendor ID + * register. + */ +#define SSVID_VID_MASK 0x0000FFFF +#define SSVID_SID_MASK 0xFFFF0000 +#define SSVID_VID_SHIFT 0 +#define SSVID_SID_SHIFT 16 + +/* + * The following defines are for the flags in the GPIO pin interface register. + */ +#define GPIOR_VOLDN 0x00000001 +#define GPIOR_VOLUP 0x00000002 +#define GPIOR_SI2D 0x00000004 +#define GPIOR_SI2OE 0x00000008 + +/* + * The following defines are for the flags in the extended GPIO pin direction + * register. + */ +#ifndef NO_CS4612 +#define EGPIODR_GPOE0 0x00000001 +#define EGPIODR_GPOE1 0x00000002 +#define EGPIODR_GPOE2 0x00000004 +#define EGPIODR_GPOE3 0x00000008 +#define EGPIODR_GPOE4 0x00000010 +#define EGPIODR_GPOE5 0x00000020 +#define EGPIODR_GPOE6 0x00000040 +#define EGPIODR_GPOE7 0x00000080 +#define EGPIODR_GPOE8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin polarity/ + * type register. + */ +#ifndef NO_CS4612 +#define EGPIOPTR_GPPT0 0x00000001 +#define EGPIOPTR_GPPT1 0x00000002 +#define EGPIOPTR_GPPT2 0x00000004 +#define EGPIOPTR_GPPT3 0x00000008 +#define EGPIOPTR_GPPT4 0x00000010 +#define EGPIOPTR_GPPT5 0x00000020 +#define EGPIOPTR_GPPT6 0x00000040 +#define EGPIOPTR_GPPT7 0x00000080 +#define EGPIOPTR_GPPT8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin sticky + * register. + */ +#ifndef NO_CS4612 +#define EGPIOTR_GPS0 0x00000001 +#define EGPIOTR_GPS1 0x00000002 +#define EGPIOTR_GPS2 0x00000004 +#define EGPIOTR_GPS3 0x00000008 +#define EGPIOTR_GPS4 0x00000010 +#define EGPIOTR_GPS5 0x00000020 +#define EGPIOTR_GPS6 0x00000040 +#define EGPIOTR_GPS7 0x00000080 +#define EGPIOTR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO ping wakeup + * register. + */ +#ifndef NO_CS4612 +#define EGPIOWR_GPW0 0x00000001 +#define EGPIOWR_GPW1 0x00000002 +#define EGPIOWR_GPW2 0x00000004 +#define EGPIOWR_GPW3 0x00000008 +#define EGPIOWR_GPW4 0x00000010 +#define EGPIOWR_GPW5 0x00000020 +#define EGPIOWR_GPW6 0x00000040 +#define EGPIOWR_GPW7 0x00000080 +#define EGPIOWR_GPW8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin status + * register. + */ +#ifndef NO_CS4612 +#define EGPIOSR_GPS0 0x00000001 +#define EGPIOSR_GPS1 0x00000002 +#define EGPIOSR_GPS2 0x00000004 +#define EGPIOSR_GPS3 0x00000008 +#define EGPIOSR_GPS4 0x00000010 +#define EGPIOSR_GPS5 0x00000020 +#define EGPIOSR_GPS6 0x00000040 +#define EGPIOSR_GPS7 0x00000080 +#define EGPIOSR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the serial port 6 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC6_ASDO2EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the serial port 7 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC7_ASDI2EN 0x00000001 +#define SERC7_POSILB 0x00000002 +#define SERC7_SIPOLB 0x00000004 +#define SERC7_SOSILB 0x00000008 +#define SERC7_SISOLB 0x00000010 +#endif + +/* + * The following defines are for the flags in the serial port AC link + * configuration register. + */ +#ifndef NO_CS4612 +#define SERACC_CODEC_TYPE_MASK 0x00000001 +#define SERACC_CODEC_TYPE_1_03 0x00000000 +#define SERACC_CODEC_TYPE_2_0 0x00000001 +#define SERACC_TWO_CODECS 0x00000002 +#define SERACC_MDM 0x00000004 +#define SERACC_HSP 0x00000008 +#endif + +/* + * The following defines are for the flags in the AC97 control register 2. + */ +#ifndef NO_CS4612 +#define ACCTL2_RSTN 0x00000001 +#define ACCTL2_ESYN 0x00000002 +#define ACCTL2_VFRM 0x00000004 +#define ACCTL2_DCV 0x00000008 +#define ACCTL2_CRW 0x00000010 +#define ACCTL2_ASYN 0x00000020 +#endif + +/* + * The following defines are for the flags in the AC97 status register 2. + */ +#ifndef NO_CS4612 +#define ACSTS2_CRDY 0x00000001 +#define ACSTS2_VSTS 0x00000002 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACOSV2_SLV3 0x00000001 +#define ACOSV2_SLV4 0x00000002 +#define ACOSV2_SLV5 0x00000004 +#define ACOSV2_SLV6 0x00000008 +#define ACOSV2_SLV7 0x00000010 +#define ACOSV2_SLV8 0x00000020 +#define ACOSV2_SLV9 0x00000040 +#define ACOSV2_SLV10 0x00000080 +#define ACOSV2_SLV11 0x00000100 +#define ACOSV2_SLV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 command address + * register 2. + */ +#ifndef NO_CS4612 +#define ACCAD2_CI_MASK 0x0000007F +#define ACCAD2_CI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 command data register + * 2. + */ +#ifndef NO_CS4612 +#define ACCDA2_CD_MASK 0x0000FFFF +#define ACCDA2_CD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 input slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACISV2_ISV3 0x00000001 +#define ACISV2_ISV4 0x00000002 +#define ACISV2_ISV5 0x00000004 +#define ACISV2_ISV6 0x00000008 +#define ACISV2_ISV7 0x00000010 +#define ACISV2_ISV8 0x00000020 +#define ACISV2_ISV9 0x00000040 +#define ACISV2_ISV10 0x00000080 +#define ACISV2_ISV11 0x00000100 +#define ACISV2_ISV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 status address + * register 2. + */ +#ifndef NO_CS4612 +#define ACSAD2_SI_MASK 0x0000007F +#define ACSAD2_SI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 status data register 2. + */ +#ifndef NO_CS4612 +#define ACSDA2_SD_MASK 0x0000FFFF +#define ACSDA2_SD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap address and control + * registers (all 12). + */ +#ifndef NO_CS4612 +#define IOTAC_SA_MASK 0x0000FFFF +#define IOTAC_MSK_MASK 0x000F0000 +#define IOTAC_IODC_MASK 0x06000000 +#define IOTAC_IODC_16_BIT 0x00000000 +#define IOTAC_IODC_10_BIT 0x02000000 +#define IOTAC_IODC_12_BIT 0x04000000 +#define IOTAC_WSPI 0x08000000 +#define IOTAC_RSPI 0x10000000 +#define IOTAC_WSE 0x20000000 +#define IOTAC_WE 0x40000000 +#define IOTAC_RE 0x80000000 +#define IOTAC_SA_SHIFT 0 +#define IOTAC_MSK_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap fast read registers + * (all 8). + */ +#ifndef NO_CS4612 +#define IOTFR_D_MASK 0x0000FFFF +#define IOTFR_A_MASK 0x000F0000 +#define IOTFR_R_MASK 0x0F000000 +#define IOTFR_ALL 0x40000000 +#define IOTFR_VL 0x80000000 +#define IOTFR_D_SHIFT 0 +#define IOTFR_A_SHIFT 16 +#define IOTFR_R_SHIFT 24 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO register. + */ +#ifndef NO_CS4612 +#define IOTFIFO_BA_MASK 0x00003FFF +#define IOTFIFO_S_MASK 0x00FF0000 +#define IOTFIFO_OF 0x40000000 +#define IOTFIFO_SPIOF 0x80000000 +#define IOTFIFO_BA_SHIFT 0 +#define IOTFIFO_S_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap retry read data + * register. + */ +#ifndef NO_CS4612 +#define IOTRRD_D_MASK 0x0000FFFF +#define IOTRRD_RDV 0x80000000 +#define IOTRRD_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO pointer + * register. + */ +#ifndef NO_CS4612 +#define IOTFP_CA_MASK 0x00003FFF +#define IOTFP_PA_MASK 0x3FFF0000 +#define IOTFP_CA_SHIFT 0 +#define IOTFP_PA_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap control register. + */ +#ifndef NO_CS4612 +#define IOTCR_ITD 0x00000001 +#define IOTCR_HRV 0x00000002 +#define IOTCR_SRV 0x00000004 +#define IOTCR_DTI 0x00000008 +#define IOTCR_DFI 0x00000010 +#define IOTCR_DDP 0x00000020 +#define IOTCR_JTE 0x00000040 +#define IOTCR_PPE 0x00000080 +#endif + +/* + * The following defines are for the flags in the direct PCI data register. + */ +#ifndef NO_CS4612 +#define DPCID_D_MASK 0xFFFFFFFF +#define DPCID_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI address register. + */ +#ifndef NO_CS4612 +#define DPCIA_A_MASK 0xFFFFFFFF +#define DPCIA_A_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI command register. + */ +#ifndef NO_CS4612 +#define DPCIC_C_MASK 0x0000000F +#define DPCIC_C_IOREAD 0x00000002 +#define DPCIC_C_IOWRITE 0x00000003 +#define DPCIC_BE_MASK 0x000000F0 +#endif + +/* + * The following defines are for the flags in the PC/PCI request register. + */ +#ifndef NO_CS4612 +#define PCPCIR_RDC_MASK 0x00000007 +#define PCPCIR_C_MASK 0x00007000 +#define PCPCIR_REQ 0x00008000 +#define PCPCIR_RDC_SHIFT 0 +#define PCPCIR_C_SHIFT 12 +#endif + +/* + * The following defines are for the flags in the PC/PCI grant register. + */ +#ifndef NO_CS4612 +#define PCPCIG_GDC_MASK 0x00000007 +#define PCPCIG_VL 0x00008000 +#define PCPCIG_GDC_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the PC/PCI master enable + * register. + */ +#ifndef NO_CS4612 +#define PCPCIEN_EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the extended PCI power + * management control register. + */ +#ifndef NO_CS4612 +#define EPCIPMC_GWU 0x00000001 +#define EPCIPMC_FSPC 0x00000002 +#endif + +/* + * The following defines are for the flags in the SP control register. + */ +#define SPCR_RUN 0x00000001 +#define SPCR_STPFR 0x00000002 +#define SPCR_RUNFR 0x00000004 +#define SPCR_TICK 0x00000008 +#define SPCR_DRQEN 0x00000020 +#define SPCR_RSTSP 0x00000040 +#define SPCR_OREN 0x00000080 +#ifndef NO_CS4612 +#define SPCR_PCIINT 0x00000100 +#define SPCR_OINTD 0x00000200 +#define SPCR_CRE 0x00008000 +#endif + +/* + * The following defines are for the flags in the debug index register. + */ +#define DREG_REGID_MASK 0x0000007F +#define DREG_DEBUG 0x00000080 +#define DREG_RGBK_MASK 0x00000700 +#define DREG_TRAP 0x00000800 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000 +#endif +#endif +#define DREG_REGID_SHIFT 0 +#define DREG_RGBK_SHIFT 8 +#define DREG_RGBK_REGID_MASK 0x0000077F +#define DREG_REGID_R0 0x00000010 +#define DREG_REGID_R1 0x00000011 +#define DREG_REGID_R2 0x00000012 +#define DREG_REGID_R3 0x00000013 +#define DREG_REGID_R4 0x00000014 +#define DREG_REGID_R5 0x00000015 +#define DREG_REGID_R6 0x00000016 +#define DREG_REGID_R7 0x00000017 +#define DREG_REGID_R8 0x00000018 +#define DREG_REGID_R9 0x00000019 +#define DREG_REGID_RA 0x0000001A +#define DREG_REGID_RB 0x0000001B +#define DREG_REGID_RC 0x0000001C +#define DREG_REGID_RD 0x0000001D +#define DREG_REGID_RE 0x0000001E +#define DREG_REGID_RF 0x0000001F +#define DREG_REGID_RA_BUS_LOW 0x00000020 +#define DREG_REGID_RA_BUS_HIGH 0x00000038 +#define DREG_REGID_YBUS_LOW 0x00000050 +#define DREG_REGID_YBUS_HIGH 0x00000058 +#define DREG_REGID_TRAP_0 0x00000100 +#define DREG_REGID_TRAP_1 0x00000101 +#define DREG_REGID_TRAP_2 0x00000102 +#define DREG_REGID_TRAP_3 0x00000103 +#define DREG_REGID_TRAP_4 0x00000104 +#define DREG_REGID_TRAP_5 0x00000105 +#define DREG_REGID_TRAP_6 0x00000106 +#define DREG_REGID_TRAP_7 0x00000107 +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E +#define DREG_REGID_TOP_OF_STACK 0x0000010F +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110 +#define DREG_REGID_TRAP_9 0x00000111 +#define DREG_REGID_TRAP_10 0x00000112 +#define DREG_REGID_TRAP_11 0x00000113 +#define DREG_REGID_TRAP_12 0x00000114 +#define DREG_REGID_TRAP_13 0x00000115 +#define DREG_REGID_TRAP_14 0x00000116 +#define DREG_REGID_TRAP_15 0x00000117 +#define DREG_REGID_TRAP_16 0x00000118 +#define DREG_REGID_TRAP_17 0x00000119 +#define DREG_REGID_TRAP_18 0x0000011A +#define DREG_REGID_TRAP_19 0x0000011B +#define DREG_REGID_TRAP_20 0x0000011C +#define DREG_REGID_TRAP_21 0x0000011D +#define DREG_REGID_TRAP_22 0x0000011E +#define DREG_REGID_TRAP_23 0x0000011F +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200 +#define DREG_REGID_RSA0_HIGH 0x00000201 +#define DREG_REGID_RSA1_LOW 0x00000202 +#define DREG_REGID_RSA1_HIGH 0x00000203 +#define DREG_REGID_RSA2 0x00000204 +#define DREG_REGID_RSA3 0x00000205 +#define DREG_REGID_RSI0_LOW 0x00000206 +#define DREG_REGID_RSI0_HIGH 0x00000207 +#define DREG_REGID_RSI1 0x00000208 +#define DREG_REGID_RSI2 0x00000209 +#define DREG_REGID_SAGUSTATUS 0x0000020A +#define DREG_REGID_RSCONFIG01_LOW 0x0000020B +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C +#define DREG_REGID_RSCONFIG23_LOW 0x0000020D +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E +#define DREG_REGID_RSDMA01E 0x0000020F +#define DREG_REGID_RSDMA23E 0x00000210 +#define DREG_REGID_RSD0_LOW 0x00000211 +#define DREG_REGID_RSD0_HIGH 0x00000212 +#define DREG_REGID_RSD1_LOW 0x00000213 +#define DREG_REGID_RSD1_HIGH 0x00000214 +#define DREG_REGID_RSD2_LOW 0x00000215 +#define DREG_REGID_RSD2_HIGH 0x00000216 +#define DREG_REGID_RSD3_LOW 0x00000217 +#define DREG_REGID_RSD3_HIGH 0x00000218 +#define DREG_REGID_SRAR_HIGH 0x0000021A +#define DREG_REGID_SRAR_LOW 0x0000021B +#define DREG_REGID_DMA_STATE 0x0000021C +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E +#define DREG_REGID_CPU_STATUS 0x00000300 +#define DREG_REGID_MAC_MODE 0x00000301 +#define DREG_REGID_STACK_AND_REPEAT 0x00000302 +#define DREG_REGID_INDEX0 0x00000304 +#define DREG_REGID_INDEX1 0x00000305 +#define DREG_REGID_DMA_STATE_0_3 0x00000400 +#define DREG_REGID_DMA_STATE_4_7 0x00000404 +#define DREG_REGID_DMA_STATE_8_11 0x00000408 +#define DREG_REGID_DMA_STATE_12_15 0x0000040C +#define DREG_REGID_DMA_STATE_16_19 0x00000410 +#define DREG_REGID_DMA_STATE_20_23 0x00000414 +#define DREG_REGID_DMA_STATE_24_27 0x00000418 +#define DREG_REGID_DMA_STATE_28_31 0x0000041C +#define DREG_REGID_DMA_STATE_32_35 0x00000420 +#define DREG_REGID_DMA_STATE_36_39 0x00000424 +#define DREG_REGID_DMA_STATE_40_43 0x00000428 +#define DREG_REGID_DMA_STATE_44_47 0x0000042C +#define DREG_REGID_DMA_STATE_48_51 0x00000430 +#define DREG_REGID_DMA_STATE_52_55 0x00000434 +#define DREG_REGID_DMA_STATE_56_59 0x00000438 +#define DREG_REGID_DMA_STATE_60_63 0x0000043C +#define DREG_REGID_DMA_STATE_64_67 0x00000440 +#define DREG_REGID_DMA_STATE_68_71 0x00000444 +#define DREG_REGID_DMA_STATE_72_75 0x00000448 +#define DREG_REGID_DMA_STATE_76_79 0x0000044C +#define DREG_REGID_DMA_STATE_80_83 0x00000450 +#define DREG_REGID_DMA_STATE_84_87 0x00000454 +#define DREG_REGID_DMA_STATE_88_91 0x00000458 +#define DREG_REGID_DMA_STATE_92_95 0x0000045C +#define DREG_REGID_TRAP_SELECT 0x00000500 +#define DREG_REGID_TRAP_WRITE_0 0x00000500 +#define DREG_REGID_TRAP_WRITE_1 0x00000501 +#define DREG_REGID_TRAP_WRITE_2 0x00000502 +#define DREG_REGID_TRAP_WRITE_3 0x00000503 +#define DREG_REGID_TRAP_WRITE_4 0x00000504 +#define DREG_REGID_TRAP_WRITE_5 0x00000505 +#define DREG_REGID_TRAP_WRITE_6 0x00000506 +#define DREG_REGID_TRAP_WRITE_7 0x00000507 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510 +#define DREG_REGID_TRAP_WRITE_9 0x00000511 +#define DREG_REGID_TRAP_WRITE_10 0x00000512 +#define DREG_REGID_TRAP_WRITE_11 0x00000513 +#define DREG_REGID_TRAP_WRITE_12 0x00000514 +#define DREG_REGID_TRAP_WRITE_13 0x00000515 +#define DREG_REGID_TRAP_WRITE_14 0x00000516 +#define DREG_REGID_TRAP_WRITE_15 0x00000517 +#define DREG_REGID_TRAP_WRITE_16 0x00000518 +#define DREG_REGID_TRAP_WRITE_17 0x00000519 +#define DREG_REGID_TRAP_WRITE_18 0x0000051A +#define DREG_REGID_TRAP_WRITE_19 0x0000051B +#define DREG_REGID_TRAP_WRITE_20 0x0000051C +#define DREG_REGID_TRAP_WRITE_21 0x0000051D +#define DREG_REGID_TRAP_WRITE_22 0x0000051E +#define DREG_REGID_TRAP_WRITE_23 0x0000051F +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 +#define DREG_REGID_MAC0_ACC0_MID 0x00000608 +#define DREG_REGID_MAC0_ACC1_MID 0x00000609 +#define DREG_REGID_MAC0_ACC2_MID 0x0000060A +#define DREG_REGID_MAC0_ACC3_MID 0x0000060B +#define DREG_REGID_MAC1_ACC0_MID 0x0000060C +#define DREG_REGID_MAC1_ACC1_MID 0x0000060D +#define DREG_REGID_MAC1_ACC2_MID 0x0000060E +#define DREG_REGID_MAC1_ACC3_MID 0x0000060F +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 +#define DREG_REGID_RSHOUT_LOW 0x00000620 +#define DREG_REGID_RSHOUT_MID 0x00000628 +#define DREG_REGID_RSHOUT_HIGH 0x00000630 + +/* + * The following defines are for the flags in the DMA stream requestor write + */ +#define DSRWP_DSR_MASK 0x0000000F +#define DSRWP_DSR_BG_RQ 0x00000001 +#define DSRWP_DSR_PRIORITY_MASK 0x00000006 +#define DSRWP_DSR_PRIORITY_0 0x00000000 +#define DSRWP_DSR_PRIORITY_1 0x00000002 +#define DSRWP_DSR_PRIORITY_2 0x00000004 +#define DSRWP_DSR_PRIORITY_3 0x00000006 +#define DSRWP_DSR_RQ_PENDING 0x00000008 + +/* + * The following defines are for the flags in the trap write port register. + */ +#define TWPR_TW_MASK 0x0000FFFF +#define TWPR_TW_SHIFT 0 + +/* + * The following defines are for the flags in the stack pointer write + * register. + */ +#define SPWR_STKP_MASK 0x0000000F +#define SPWR_STKP_SHIFT 0 + +/* + * The following defines are for the flags in the SP interrupt register. + */ +#define SPIR_FRI 0x00000001 +#define SPIR_DOI 0x00000002 +#define SPIR_GPI2 0x00000004 +#define SPIR_GPI3 0x00000008 +#define SPIR_IP0 0x00000010 +#define SPIR_IP1 0x00000020 +#define SPIR_IP2 0x00000040 +#define SPIR_IP3 0x00000080 + +/* + * The following defines are for the flags in the functional group 1 register. + */ +#define FGR1_F1S_MASK 0x0000FFFF +#define FGR1_F1S_SHIFT 0 + +/* + * The following defines are for the flags in the SP clock status register. + */ +#define SPCS_FRI 0x00000001 +#define SPCS_DOI 0x00000002 +#define SPCS_GPI2 0x00000004 +#define SPCS_GPI3 0x00000008 +#define SPCS_IP0 0x00000010 +#define SPCS_IP1 0x00000020 +#define SPCS_IP2 0x00000040 +#define SPCS_IP3 0x00000080 +#define SPCS_SPRUN 0x00000100 +#define SPCS_SLEEP 0x00000200 +#define SPCS_FG 0x00000400 +#define SPCS_ORUN 0x00000800 +#define SPCS_IRQ 0x00001000 +#define SPCS_FGN_MASK 0x0000E000 +#define SPCS_FGN_SHIFT 13 + +/* + * The following defines are for the flags in the SP DMA requestor status + * register. + */ +#define SDSR_DCS_MASK 0x000000FF +#define SDSR_DCS_SHIFT 0 +#define SDSR_DCS_NONE 0x00000007 + +/* + * The following defines are for the flags in the frame timer register. + */ +#define FRMT_FTV_MASK 0x0000FFFF +#define FRMT_FTV_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer current count + * register. + */ +#define FRCC_FCC_MASK 0x0000FFFF +#define FRCC_FCC_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer save count + * register. + */ +#define FRSC_FCS_MASK 0x0000FFFF +#define FRSC_FCS_SHIFT 0 + +/* + * The following define the various flags stored in the scatter/gather + * descriptors. + */ +#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 +#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 +#define DMA_SG_SAMPLE_END_FLAG 0x10000000 +#define DMA_SG_LOOP_END_FLAG 0x20000000 +#define DMA_SG_SIGNAL_END_FLAG 0x40000000 +#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 +#define DMA_SG_NEXT_ENTRY_SHIFT 3 +#define DMA_SG_SAMPLE_END_SHIFT 16 + +/* + * The following define the offsets of the fields within the on-chip generic + * DMA requestor. + */ +#define DMA_RQ_CONTROL1 0x00000000 +#define DMA_RQ_CONTROL2 0x00000004 +#define DMA_RQ_SOURCE_ADDR 0x00000008 +#define DMA_RQ_DESTINATION_ADDR 0x0000000C +#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 +#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 +#define DMA_RQ_LOOP_START_ADDR 0x00000018 +#define DMA_RQ_POST_LOOP_ADDR 0x0000001C +#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 + +/* + * The following defines are for the flags in the first control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C1_COUNT_MASK 0x000003FF +#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 +#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 +#define DMA_RQ_C1_DONE_FLAG 0x00004000 +#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 +#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 +#define DMA_RQ_C1_FULL_PAGE 0x00000000 +#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 +#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 +#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 +#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 +#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 +#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 +#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 +#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 +#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 +#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 +#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 +#define DMA_RQ_C1_PM_RESERVED 0x00200000 +#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 +#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 +#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 +#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 +#define DMA_RQ_C1_DEST_LINEAR 0x00000000 +#define DMA_RQ_C1_DEST_MOD16 0x01000000 +#define DMA_RQ_C1_DEST_MOD32 0x02000000 +#define DMA_RQ_C1_DEST_MOD64 0x03000000 +#define DMA_RQ_C1_DEST_MOD128 0x04000000 +#define DMA_RQ_C1_DEST_MOD256 0x05000000 +#define DMA_RQ_C1_DEST_MOD512 0x06000000 +#define DMA_RQ_C1_DEST_MOD1024 0x07000000 +#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 +#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 +#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 +#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 +#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 +#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 +#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 +#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 +#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 +#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 +#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 +#define DMA_RQ_C1_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the second control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F +#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 +#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 +#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 +#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 +#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 +#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 +#define DMA_RQ_C2_AC_NONE 0x00000000 +#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 +#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 +#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 +#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 +#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 +#define DMA_RQ_C2_LOOP_MASK 0x30000000 +#define DMA_RQ_C2_NO_LOOP 0x00000000 +#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 +#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 +#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 +#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 +#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 +#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 +#define DMA_RQ_C2_LOOP_END_SHIFT 16 + +/* + * The following defines are for the flags in the source and destination words + * of the on-chip generic DMA requestor. + */ +#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF +#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 +#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 +#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 +#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 +#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 +#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 +#define DMA_RQ_SD_END_FLAG 0x40000000 +#define DMA_RQ_SD_ERROR_FLAG 0x80000000 +#define DMA_RQ_SD_ADDRESS_SHIFT 0 + +/* + * The following defines are for the flags in the page map address word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 +#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 +#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 + +#define BA1_VARIDEC_BUF_1 0x000 + +#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ +#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ +#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ +#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ +#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ +#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ + +#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ +#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ +#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ +#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ +#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ +#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ +#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ + +#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ +#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ +#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ +#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ + +/* + * + */ + +#define CS461X_MODE_OUTPUT (1<<0) /* MIDI UART - output */ +#define CS461X_MODE_INPUT (1<<1) /* MIDI UART - input */ + +//**************************************************************************** +// +// The following define the offsets of the AC97 shadow registers, which appear +// as a virtual extension to the base address register zero memory range. +// +//**************************************************************************** +#define AC97_REG_OFFSET_MASK 0x0000007EL +#define AC97_CODEC_NUMBER_MASK 0x00003000L + +#define BA0_AC97_RESET 0x00001000L +#define BA0_AC97_MASTER_VOLUME 0x00001002L +#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L +#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L +#define BA0_AC97_MASTER_TONE 0x00001008L +#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL +#define BA0_AC97_PHONE_VOLUME 0x0000100CL +#define BA0_AC97_MIC_VOLUME 0x0000100EL +#define BA0_AC97_LINE_IN_VOLUME 0x00001010L +#define BA0_AC97_CD_VOLUME 0x00001012L +#define BA0_AC97_VIDEO_VOLUME 0x00001014L +#define BA0_AC97_AUX_VOLUME 0x00001016L +#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L +#define BA0_AC97_RECORD_SELECT 0x0000101AL +#define BA0_AC97_RECORD_GAIN 0x0000101CL +#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL +#define BA0_AC97_GENERAL_PURPOSE 0x00001020L +#define BA0_AC97_3D_CONTROL 0x00001022L +#define BA0_AC97_MODEM_RATE 0x00001024L +#define BA0_AC97_POWERDOWN 0x00001026L +#define BA0_AC97_EXT_AUDIO_ID 0x00001028L +#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL +#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL +#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL +#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L +#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L +#define BA0_AC97_MIC_ADC_RATE 0x00001034L +#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L +#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L +#define BA0_AC97_RESERVED_3A 0x0000103AL +#define BA0_AC97_EXT_MODEM_ID 0x0000103CL +#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL +#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L +#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L +#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L +#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L +#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L +#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL +#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL +#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL +#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L +#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L +#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L +#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L +#define BA0_AC97_RESERVED_58 0x00001058L +#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL +#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL +#define BA0_AC97_AC_MODE 0x0000105EL +#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L +#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L +#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L +#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L +#define BA0_AC97_SPDIF_CONTROL 0x00001068L +#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL +#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL +#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL +#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L +#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L +#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L +#define BA0_AC97_CAL_ADDRESS 0x00001076L +#define BA0_AC97_CAL_DATA 0x00001078L +#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL +#define BA0_AC97_VENDOR_ID1 0x0000107CL +#define BA0_AC97_VENDOR_ID2 0x0000107EL +#endif /* __CS461X_H */ diff -Nru linux/sound/oss/cs461x_image.h linux-2.4.19-pre5-mjc/sound/oss/cs461x_image.h --- linux/sound/oss/cs461x_image.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs461x_image.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,322 @@ +/**************************************************************************** + * "CWCIMAGE.H"-- For CS46XX. Ver 1.04 + * Copyright 1998-2001 (c) Cirrus Logic Corp. + * Version 1.04 + **************************************************************************** + */ +#ifndef __CS_IMAGE_H +#define __CS_IMAGE_H + +#define CLEAR__COUNT 3 +#define FILL__COUNT 4 +#define BA1__DWORD_SIZE 13*1024+512 + +static struct +{ + unsigned BA1__DestByteOffset; + unsigned BA1__SourceSize; +} ClrStat[CLEAR__COUNT] ={ {0x00000000, 0x00003000 }, + {0x00010000, 0x00003800 }, + {0x00020000, 0x00007000 } }; + +static u32 FillArray1[]={ +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00200040,0x00008010,0x00000000, +0x00000000,0x80000001,0x00000001,0x00060000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00900080,0x00000173,0x00000000, +0x00000000,0x00000010,0x00800000,0x00900000, +0xf2c0000f,0x00000200,0x00000000,0x00010600, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x330300c2, +0x06000000,0x00000000,0x80008000,0x80008000, +0x3fc0000f,0x00000301,0x00010400,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00b00000,0x00d0806d,0x330480c3, +0x04800000,0x00000001,0x00800001,0x0000ffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x066a0600,0x06350070,0x0000929d,0x929d929d, +0x00000000,0x0000735a,0x00000600,0x00000000, +0x929d735a,0x00000000,0x00010000,0x735a735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000804f,0x000000c3, +0x05000000,0x00a00010,0x00000000,0x80008000, +0x00000000,0x00000000,0x00000700,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000080,0x00a00000,0x0000809a,0x000000c2, +0x07400000,0x00000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x000107a0, +0x00c80028,0x000000c2,0x06800000,0x00000000, +0x06e00080,0x00300000,0x000080bb,0x000000c9, +0x07a00000,0x04000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x00000780, +0x00c80028,0x000000c5,0xff800000,0x00000000, +0x00640080,0x00c00000,0x00008197,0x000000c9, +0x07800000,0x04000000,0x80008000,0xffffffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000805e,0x000000c1, +0x00000000,0x00800000,0x80008000,0x80008000, +0x00020000,0x0000ffff,0x00000000,0x00000000}; + +static u32 FillArray2[]={ +0x929d0600,0x929d929d,0x929d929d,0x929d0000, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x00100635,0x060b013f,0x00000004, +0x00000001,0x007a0002,0x00000000,0x066e0610, +0x0105929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0xa431ac75,0x0001735a,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051, +0x00000000,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x00000000,0x06400136, +0x0000270f,0x00010000,0x007a0000,0x00000000, +0x068e0645,0x0105929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0xa431ac75,0x0001735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x735a0100,0x00000000,0x00000000,0x00000000}; + +static u32 FillArray3[]={ +0x00000000,0x00000000,0x00000000,0x00010004}; + +static u32 FillArray4[]={ +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00001705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00009705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00011705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00019705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00021705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00029705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00031705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00039705,0x00001400,0x000a411e,0x00001003, +0x000fe19e,0x00001003,0x0009c730,0x00001003, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00009705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00011705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00019705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00021705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00029705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00031705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00039705,0x00001400,0x000a211e,0x00001003, +0x0000a730,0x00001008,0x000e2730,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x00000000,0x00000000,0x000f619c,0x00001003, +0x0007f801,0x000c0000,0x00000037,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0000373c,0x00001000,0x00000000,0x00000000, +0x000ee19c,0x00001003,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000273c,0x00001000, +0x00000033,0x00001000,0x000e679e,0x00001003, +0x00007705,0x00001400,0x000ac71e,0x00001003, +0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000a730,0x00001003, +0x00000033,0x00001000,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x000c0000, +0x00000032,0x00001000,0x0000273d,0x00001000, +0x0004a730,0x00001003,0x00000f41,0x00097140, +0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +0x00000000,0x00000000,0x0001bf05,0x0003fc40, +0x00002725,0x000aa400,0x00013705,0x00093a00, +0x0000002e,0x0009d6c0,0x00038630,0x00001004, +0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000, +0x00000000,0x000c70e0,0x0007d182,0x0002c640, +0x00000630,0x00001004,0x000799b8,0x0002c6c0, +0x00031705,0x00092240,0x00039f05,0x000932c0, +0x0003520a,0x00000000,0x00040731,0x0000100b, +0x00010705,0x000b20c0,0x00000000,0x000eba44, +0x00032108,0x000c60c4,0x00065208,0x000c2917, +0x000406b0,0x00001007,0x00012f05,0x00036880, +0x0002818e,0x000c0000,0x0004410a,0x00000000, +0x00040630,0x00001007,0x00029705,0x000c0000, +0x00000000,0x00000000,0x00003fc1,0x0003fc40, +0x000037c1,0x00091b40,0x00003fc1,0x000911c0, +0x000037c1,0x000957c0,0x00003fc1,0x000951c0, +0x000037c1,0x00000000,0x00003fc1,0x000991c0, +0x000037c1,0x00000000,0x00003fc1,0x0009d1c0, +0x000037c1,0x00000000,0x0001ccc1,0x000915c0, +0x0001c441,0x0009d800,0x0009cdc1,0x00091240, +0x0001c541,0x00091d00,0x0009cfc1,0x00095240, +0x0001c741,0x00095c80,0x000e8ca9,0x00099240, +0x000e85ad,0x00095640,0x00069ca9,0x00099d80, +0x000e952d,0x00099640,0x000eaca9,0x0009d6c0, +0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80, +0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0, +0x000ec5ad,0x0009da40,0x000edca9,0x0009d300, +0x000a6e0a,0x00001000,0x000ed52d,0x00091e40, +0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40, +0x0006fca9,0x00002500,0x000fb208,0x000c59a0, +0x000ef52d,0x0009de40,0x00068ca9,0x000912c1, +0x000683ad,0x00095241,0x00020f05,0x000991c1, +0x00000000,0x00000000,0x00086f88,0x00001000, +0x0009cf81,0x000b5340,0x0009c701,0x000b92c0, +0x0009de81,0x000bd300,0x0009d601,0x000b1700, +0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0, +0x000a0f81,0x000bd740,0x00020701,0x000b5c80, +0x000a1681,0x000b97c0,0x00021601,0x00002500, +0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0, +0x00021681,0x00002d00,0x00020f81,0x000bd800, +0x000a0701,0x000b5bc0,0x00021601,0x00003500, +0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0, +0x00021681,0x00003d00,0x00020f81,0x000b1d00, +0x000a0701,0x000b1fc0,0x00021601,0x00020500, +0x00020f81,0x000b1341,0x000a0701,0x000b9fc0, +0x00021681,0x00020d00,0x00020f81,0x000bde80, +0x000a0701,0x000bdfc0,0x00021601,0x00021500, +0x00020f81,0x000b9341,0x00020701,0x000b53c1, +0x00021681,0x00021d00,0x000a0f81,0x000d0380, +0x0000b601,0x000b15c0,0x00007b01,0x00000000, +0x00007b81,0x000bd1c0,0x00007b01,0x00000000, +0x00007b81,0x000b91c0,0x00007b01,0x000b57c0, +0x00007b81,0x000b51c0,0x00007b01,0x000b1b40, +0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0, +0x0007e488,0x000d7e45,0x00000000,0x000d7a44, +0x0007e48a,0x00000000,0x00011f05,0x00084080, +0x00000000,0x00000000,0x00001705,0x000b3540, +0x00008a01,0x000bf040,0x00007081,0x000bb5c0, +0x00055488,0x00000000,0x0000d482,0x0003fc40, +0x0003fc88,0x00000000,0x0001e401,0x000b3a00, +0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784, +0x000c86b0,0x00001007,0x00008281,0x000bb240, +0x0000b801,0x000b7140,0x00007888,0x00000000, +0x0000073c,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x00055288,0x000c555c, +0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, +0x0000fa88,0x00000000,0x00000032,0x00001000, +0x0000073d,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x0008c01c,0x00001003, +0x00002705,0x00001008,0x0008b201,0x000c1392, +0x0000ba01,0x00000000,0x00008731,0x00001400, +0x0004c108,0x000fe0c4,0x00057488,0x00000000, +0x000a6388,0x00001001,0x0008b334,0x000bc141, +0x0003020e,0x00000000,0x000886b0,0x00001008, +0x00003625,0x000c5dfa,0x000a638a,0x00001001, +0x0008020e,0x00001002,0x0008a6b0,0x00001008, +0x0007f301,0x00000000,0x00000000,0x00000000, +0x00002725,0x000a8c40,0x000000ae,0x00000000, +0x000d8630,0x00001008,0x00000000,0x000c74e0, +0x0007d182,0x0002d640,0x000a8630,0x00001008, +0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5, +0x0007420a,0x000c0000,0x00062208,0x000c4117, +0x00070630,0x00001009,0x00000000,0x000c0000, +0x0001022e,0x00000000,0x0003a630,0x00001009, +0x00000000,0x000c0000,0x00000036,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x0002a730,0x00001008,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0002a730,0x00001008, +0x00000033,0x00001000,0x0002a705,0x00001008, +0x00007a01,0x000c0000,0x000e6288,0x000d550a, +0x0006428a,0x00000000,0x00060730,0x0000100a, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0007aab0,0x00034880,0x00078fb0,0x0000100b, +0x00057488,0x00000000,0x00033b94,0x00081140, +0x000183ae,0x00000000,0x000786b0,0x0000100b, +0x00022f05,0x000c3545,0x0000eb8a,0x00000000, +0x00042731,0x00001003,0x0007aab0,0x00034880, +0x00048fb0,0x0000100a,0x00057488,0x00000000, +0x00033b94,0x00081140,0x000183ae,0x00000000, +0x000806b0,0x0000100b,0x00022f05,0x00000000, +0x00007401,0x00091140,0x00048f05,0x000951c0, +0x00042731,0x00001003,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x000fe19e,0x00001003,0x00000000,0x00000000, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00000f41,0x00097140,0x0000a841,0x0009b240, +0x0000a0c1,0x0009f040,0x0001c641,0x00093540, +0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44, +0x00055208,0x00000000,0x00010705,0x000a2880, +0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5, +0x0004ef0a,0x000c0000,0x00012f05,0x00036880, +0x00065308,0x000c2997,0x000d86b0,0x0000100a, +0x0004410a,0x000d40c7,0x00000000,0x00000000, +0x00080730,0x00001004,0x00056f0a,0x000ea105, +0x00000000,0x00000000,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x0000273d,0x00001000,0x00000000,0x000eba44, +0x00048f05,0x0000f440,0x00007401,0x0000f7c0, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x00000000,0x000e5084, +0x00000000,0x000eba44,0x00087401,0x000e4782, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x0007c108,0x000c0000, +0x0007e721,0x000bed40,0x00005f25,0x000badc0, +0x0003ba97,0x000beb80,0x00065590,0x000b2e00, +0x00033217,0x00003ec0,0x00065590,0x000b8e40, +0x0003ed80,0x000491c0,0x00073fb0,0x00074c80, +0x000283a0,0x0000100c,0x000ee388,0x00042970, +0x00008301,0x00021ef2,0x000b8f14,0x0000000f, +0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6, +0x000032ac,0x000c3916,0x0004edc2,0x00074c80, +0x00078898,0x00001000,0x00038894,0x00000032, +0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6, +0x0004edc2,0x000c1956,0x0000722c,0x00034a00, +0x00041705,0x0009ed40,0x00058730,0x00001400, +0x000d7488,0x000c3a00,0x00048f05,0x00000000}; + +static struct +{ u32 Offset; + u32 Size; + u32 *pFill; +} FillStat[FILL__COUNT] = { + {0x00000000, sizeof(FillArray1), FillArray1}, + {0x00001800, sizeof(FillArray2), FillArray2}, + {0x000137f0, sizeof(FillArray3), FillArray3}, + {0x00020000, sizeof(FillArray4), FillArray4} + }; + + +#endif diff -Nru linux/sound/oss/cs46xx.c linux-2.4.19-pre5-mjc/sound/oss/cs46xx.c --- linux/sound/oss/cs46xx.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs46xx.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,5727 @@ +/* + * Crystal SoundFusion CS46xx driver + * + * Copyright 1998-2001 Cirrus Logic Corporation + * + * Copyright 1999-2000 Jaroslav Kysela + * Copyright 2000 Alan Cox + * + * The core of this code is taken from the ALSA project driver by + * Jaroslav. Please send Jaroslav the credit for the driver and + * report bugs in this port to + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * Current maintainers: + * Cirrus Logic Corporation, Thomas Woller (tw) + * + * Nils Faerber (nf) + * + * Thanks to David Pollard for testing. + * + * Changes: + * 20000909-nf Changed cs_read, cs_write and drain_dac + * 20001025-tw Separate Playback/Capture structs and buffers. + * Added Scatter/Gather support for Playback. + * Added Capture. + * 20001027-nf Port to kernel 2.4.0-test9, some clean-ups + * Start of powermanagement support (CS46XX_PM). + * 20001128-tw Add module parm for default buffer order. + * added DMA_GFP flag to kmalloc dma buffer allocs. + * backfill silence to eliminate stuttering on + * underruns. + * 20001201-tw add resyncing of swptr on underruns. + * 20001205-tw-nf fixed GETOSPACE ioctl() after open() + * 20010113-tw patch from Hans Grobler general cleanup. + * 20010117-tw 2.4.0 pci cleanup, wrapper code for 2.2.16-2.4.0 + * 20010118-tw basic PM support for 2.2.16+ and 2.4.0/2.4.2. + * 20010228-dh patch from David Huggins - cs_update_ptr recursion. + * 20010409-tw add hercules game theatre XP amp code. + * 20010420-tw cleanup powerdown/up code. + * 20010521-tw eliminate pops, and fixes for powerdown. + * 20010525-tw added fixes for thinkpads with powerdown logic. + * 20010723-sh patch from Horms (Simon Horman) - + * SOUND_PCM_READ_BITS returns bits as set in driver + * rather than a logical or of the possible values. + * Various ioctls handle the case where the device + * is open for reading or writing but not both better. + * + * Status: + * Playback/Capture supported from 8k-48k. + * 16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. + * + * APM/PM - 2.2.x APM is enabled and functioning fine. APM can also + * be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro + * definition. + * + * Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, + * so, use the drain/polarity to enable. + * hercules_egpio_disable set to 1, will force a 0 to EGPIODR. + * + * VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control + * the external amplifier for the "back" speakers, since we do not + * support the secondary codec then this external amp is also not + * turned on. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs46xxpm-24.h" +#include "cs46xx_wrapper-24.h" + +#include "cs461x.h" + +/* MIDI buffer sizes */ +#define CS_MIDIINBUF 500 +#define CS_MIDIOUTBUF 500 + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 + +#define CS_FMT_16BIT 1 /* These are fixed in fact */ +#define CS_FMT_STEREO 2 +#define CS_FMT_MASK 3 + +#define CS_TYPE_ADC 1 +#define CS_TYPE_DAC 2 + +#define CS_TRUE 1 +#define CS_FALSE 0 + +#define CS_INC_USE_COUNT(m) (atomic_inc(m)) +#define CS_DEC_USE_COUNT(m) (atomic_dec(m)) +#define CS_DEC_AND_TEST(m) (atomic_dec_and_test(m)) +#define CS_IN_USE(m) (atomic_read(m) != 0) + +#define CS_DBGBREAKPOINT {__asm__("INT $3");} +/* + * CS461x definitions + */ + +#define CS461X_BA0_SIZE 0x2000 +#define CS461X_BA1_DATA0_SIZE 0x3000 +#define CS461X_BA1_DATA1_SIZE 0x3800 +#define CS461X_BA1_PRG_SIZE 0x7000 +#define CS461X_BA1_REG_SIZE 0x0100 + +#define GOF_PER_SEC 200 + +#define CSDEBUG_INTERFACE 1 +#define CSDEBUG 1 +/* + * Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG + * + * + * CSDEBUG is usual mode is set to 1, then use the + * cs_debuglevel and cs_debugmask to turn on or off debugging. + * Debug level of 1 has been defined to be kernel errors and info + * that should be printed on any released driver. + */ +#if CSDEBUG +#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask)) {x;} +#else +#define CS_DBGOUT(mask,level,x) +#endif +/* + * cs_debugmask areas + */ +#define CS_INIT 0x00000001 /* initialization and probe functions */ +#define CS_ERROR 0x00000002 /* tmp debugging bit placeholder */ +#define CS_INTERRUPT 0x00000004 /* interrupt handler (separate from all other) */ +#define CS_FUNCTION 0x00000008 /* enter/leave functions */ +#define CS_WAVE_WRITE 0x00000010 /* write information for wave */ +#define CS_WAVE_READ 0x00000020 /* read information for wave */ +#define CS_MIDI_WRITE 0x00000040 /* write information for midi */ +#define CS_MIDI_READ 0x00000080 /* read information for midi */ +#define CS_MPU401_WRITE 0x00000100 /* write information for mpu401 */ +#define CS_MPU401_READ 0x00000200 /* read information for mpu401 */ +#define CS_OPEN 0x00000400 /* all open functions in the driver */ +#define CS_RELEASE 0x00000800 /* all release functions in the driver */ +#define CS_PARMS 0x00001000 /* functional and operational parameters */ +#define CS_IOCTL 0x00002000 /* ioctl (non-mixer) */ +#define CS_PM 0x00004000 /* PM */ +#define CS_TMP 0x10000000 /* tmp debug mask bit */ + +#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend +#define CS_IOCTL_CMD_RESUME 0x2 // resume + +#if CSDEBUG +static unsigned long cs_debuglevel=1; /* levels range from 1-9 */ +MODULE_PARM(cs_debuglevel, "i"); +static unsigned long cs_debugmask=CS_INIT | CS_ERROR; /* use CS_DBGOUT with various mask values */ +MODULE_PARM(cs_debugmask, "i"); +#endif +static unsigned long hercules_egpio_disable=0; /* if non-zero set all EGPIO to 0 */ +MODULE_PARM(hercules_egpio_disable, "i"); +static unsigned long initdelay=700; /* PM delay in millisecs */ +MODULE_PARM(initdelay, "i"); +static unsigned long powerdown=-1; /* turn on/off powerdown processing in driver */ +MODULE_PARM(powerdown, "i"); +#define DMABUF_DEFAULTORDER 3 +static unsigned long defaultorder=DMABUF_DEFAULTORDER; +MODULE_PARM(defaultorder, "i"); + +static int external_amp; +MODULE_PARM(external_amp, "i"); +static int thinkpad; +MODULE_PARM(thinkpad, "i"); + +/* +* set the powerdown module parm to 0 to disable all +* powerdown. also set thinkpad to 1 to disable powerdown, +* but also to enable the clkrun functionality. +*/ +static unsigned cs_powerdown=1; +static unsigned cs_laptop_wait=1; + +/* An instance of the 4610 channel */ +struct cs_channel +{ + int used; + int num; + void *state; +}; + +#define CS46XX_MAJOR_VERSION "1" +#define CS46XX_MINOR_VERSION "28" + +#ifdef __ia64__ +#define CS46XX_ARCH "64" //architecture key +#else +#define CS46XX_ARCH "32" //architecture key +#endif + +struct list_head cs46xx_devs = { &cs46xx_devs, &cs46xx_devs }; + +/* magic numbers to protect our data structures */ +#define CS_CARD_MAGIC 0x43525553 /* "CRUS" */ +#define CS_STATE_MAGIC 0x4c4f4749 /* "LOGI" */ +#define NR_HW_CH 3 + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct cs_state { + unsigned int magic; + struct cs_card *card; /* Card info */ + + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + wait_queue_head_t open_wait; + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable; + + /* hardware channel */ + struct cs_channel *channel; + int pringbuf; /* Software ring slot */ + void *pbuf; /* 4K hardware DMA buffer */ + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned divisor; + unsigned type; + void *tmpbuff; /* tmp buffer for sample conversions */ + dma_addr_t dmaaddr; + dma_addr_t dmaaddr_tmpbuff; + unsigned buforder_tmpbuff; /* Log base 2 of size in bytes.. */ + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, updated by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be comsumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + unsigned blocks; /* total blocks */ + + unsigned error; /* number of over/underruns */ + unsigned underrun; /* underrun pending before next write has occurred */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned SGok:1; + unsigned update_flag; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dmabuf; + /* Guard against mmap/write/read races */ + struct semaphore sem; +}; + +struct cs_card { + struct cs_channel channel[2]; + unsigned int magic; + + /* We keep cs461x cards in a linked list */ + struct cs_card *next; + + /* The cs461x has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + + /* mixer use count */ + atomic_t mixer_use_cnt; + + /* PCI device stuff */ + struct pci_dev * pci_dev; + struct list_head list; + + unsigned int pctl, cctl; /* Hardware DMA flag sets */ + + /* soundcore stuff */ + int dev_audio; + int dev_midi; + + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct cs_state *states[2]; + + u16 ac97_features; + + int amplifier; /* Amplifier control */ + void (*amplifier_ctrl)(struct cs_card *, int); + void (*amp_init)(struct cs_card *); + + int active; /* Active clocking */ + void (*active_ctrl)(struct cs_card *, int); + + /* hardware resources */ + unsigned long ba0_addr; + unsigned long ba1_addr; + u32 irq; + + /* mappings */ + void *ba0; + union + { + struct + { + u8 *data0; + u8 *data1; + u8 *pmem; + u8 *reg; + } name; + u8 *idx[4]; + } ba1; + + /* Function support */ + struct cs_channel *(*alloc_pcm_channel)(struct cs_card *); + struct cs_channel *(*alloc_rec_pcm_channel)(struct cs_card *); + void (*free_pcm_channel)(struct cs_card *, int chan); + + /* /dev/midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t open_wait; + wait_queue_head_t iwait; + wait_queue_head_t owait; + spinlock_t lock; + unsigned char ibuf[CS_MIDIINBUF]; + unsigned char obuf[CS_MIDIOUTBUF]; + mode_t open_mode; + struct semaphore open_sem; + } midi; + struct cs46xx_pm pm; +}; + +static int cs_open_mixdev(struct inode *inode, struct file *file); +static int cs_release_mixdev(struct inode *inode, struct file *file); +static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg); +static int cs_hardware_init(struct cs_card *card); +static int cs46xx_powerup(struct cs_card *card, unsigned int type); +static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag); +static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type); +static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state); +static int cs46xx_resume_tbl(struct pci_dev *pcidev); + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +#if CSDEBUG + +/* DEBUG ROUTINES */ + +#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) +#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) +#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) +#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) +#define SOUND_MIXER_CS_APM _SIOWR('M',124, int) + +void printioctl(unsigned int x) +{ + unsigned int i; + unsigned char vidx; + /* these values are incorrect for the ac97 driver, fix. + * Index of mixtable1[] member is Device ID + * and must be <= SOUND_MIXER_NRDEVICES. + * Value of array member is index into s->mix.vol[] + */ + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, /* voice */ + [SOUND_MIXER_LINE1] = 2, /* AUX */ + [SOUND_MIXER_CD] = 3, /* CD */ + [SOUND_MIXER_LINE] = 4, /* Line */ + [SOUND_MIXER_SYNTH] = 5, /* FM */ + [SOUND_MIXER_MIC] = 6, /* Mic */ + [SOUND_MIXER_SPEAKER] = 7, /* Speaker */ + [SOUND_MIXER_RECLEV] = 8, /* Recording level */ + [SOUND_MIXER_VOLUME] = 9 /* Master Volume */ + }; + + switch(x) + { + case SOUND_MIXER_CS_GETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGMASK: ") ); + break; + case SOUND_MIXER_CS_GETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGLEVEL: ") ); + break; + case SOUND_MIXER_CS_SETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGMASK: ") ); + break; + case SOUND_MIXER_CS_SETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_SETDBGLEVEL: ") ); + break; + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION: ") ); + break; + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC: ") ); + break; + case SNDCTL_DSP_SETDUPLEX: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX: ") ); + break; + case SNDCTL_DSP_GETCAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS: ") ); + break; + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET: ") ); + break; + case SNDCTL_DSP_SPEED: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED: ") ); + break; + case SNDCTL_DSP_STEREO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO: ") ); + break; + case SNDCTL_DSP_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS: ") ); + break; + case SNDCTL_DSP_GETFMTS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS: ") ); + break; + case SNDCTL_DSP_SETFMT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT: ") ); + break; + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST: ") ); + break; + case SNDCTL_DSP_GETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER: ") ); + break; + case SNDCTL_DSP_SETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER: ") ); + break; + case SNDCTL_DSP_GETOSPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE: ") ); + break; + case SNDCTL_DSP_GETISPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE: ") ); + break; + case SNDCTL_DSP_NONBLOCK: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK: ") ); + break; + case SNDCTL_DSP_GETODELAY: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY: ") ); + break; + case SNDCTL_DSP_GETIPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR: ") ); + break; + case SNDCTL_DSP_GETOPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR: ") ); + break; + case SNDCTL_DSP_GETBLKSIZE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE: ") ); + break; + case SNDCTL_DSP_SETFRAGMENT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFRAGMENT: ") ); + break; + case SNDCTL_DSP_SUBDIVIDE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE: ") ); + break; + case SOUND_PCM_READ_RATE: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE: ") ); + break; + case SOUND_PCM_READ_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_CHANNELS: ") ); + break; + case SOUND_PCM_READ_BITS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS: ") ); + break; + case SOUND_PCM_WRITE_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_WRITE_FILTER: ") ); + break; + case SNDCTL_DSP_SETSYNCRO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO: ") ); + break; + case SOUND_PCM_READ_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER: ") ); + break; + + case SOUND_MIXER_PRIVATE1: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1: ") ); + break; + case SOUND_MIXER_PRIVATE2: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2: ") ); + break; + case SOUND_MIXER_PRIVATE3: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3: ") ); + break; + case SOUND_MIXER_PRIVATE4: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4: ") ); + break; + case SOUND_MIXER_PRIVATE5: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5: ") ); + break; + case SOUND_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO: ") ); + break; + case SOUND_OLD_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO: ") ); + break; + + default: + switch (_IOC_NR(x)) + { + case SOUND_MIXER_VOLUME: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_VOLUME: ") ); + break; + case SOUND_MIXER_SPEAKER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SPEAKER: ") ); + break; + case SOUND_MIXER_RECLEV: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECLEV: ") ); + break; + case SOUND_MIXER_MIC: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_MIC: ") ); + break; + case SOUND_MIXER_SYNTH: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_SYNTH: ") ); + break; + case SOUND_MIXER_RECSRC: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECSRC: ") ); + break; + case SOUND_MIXER_DEVMASK: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_DEVMASK: ") ); + break; + case SOUND_MIXER_RECMASK: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_RECMASK: ") ); + break; + case SOUND_MIXER_STEREODEVS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_STEREODEVS: ") ); + break; + case SOUND_MIXER_CAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:") ); + break; + default: + i = _IOC_NR(x); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + { + CS_DBGOUT(CS_IOCTL, 4, printk("UNKNOWN IOCTL: 0x%.8x NR=%d ",x,i) ); + } + else + { + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d ", + x,i) ); + } + break; + } + } + CS_DBGOUT(CS_IOCTL, 4, printk("command = 0x%x IOC_NR=%d\n",x, _IOC_NR(x)) ); +} +#endif + +/* + * common I/O routines + */ + +static void cs461x_poke(struct cs_card *codec, unsigned long reg, unsigned int val) +{ + writel(val, codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); +} + +static unsigned int cs461x_peek(struct cs_card *codec, unsigned long reg) +{ + return readl(codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); +} + +static void cs461x_pokeBA0(struct cs_card *codec, unsigned long reg, unsigned int val) +{ + writel(val, codec->ba0+reg); +} + +static unsigned int cs461x_peekBA0(struct cs_card *codec, unsigned long reg) +{ + return readl(codec->ba0+reg); +} + + +static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg); +static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct cs_channel *cs_alloc_pcm_channel(struct cs_card *card) +{ + if(card->channel[1].used==1) + return NULL; + card->channel[1].used=1; + card->channel[1].num=1; + return &card->channel[1]; +} + +static struct cs_channel *cs_alloc_rec_pcm_channel(struct cs_card *card) +{ + if(card->channel[0].used==1) + return NULL; + card->channel[0].used=1; + card->channel[0].num=0; + return &card->channel[0]; +} + +static void cs_free_pcm_channel(struct cs_card *card, int channel) +{ + card->channel[channel].state = NULL; + card->channel[channel].used=0; +} + +/* + * setup a divisor value to help with conversion from + * 16bit Stereo, down to 8bit stereo/mono or 16bit mono. + * assign a divisor of 1 if using 16bit Stereo as that is + * the only format that the static image will capture. + */ +static void cs_set_divisor(struct dmabuf *dmabuf) +{ + if(dmabuf->type == CS_TYPE_DAC) + dmabuf->divisor = 1; + else if( !(dmabuf->fmt & CS_FMT_STEREO) && + (dmabuf->fmt & CS_FMT_16BIT)) + dmabuf->divisor = 2; + else if( (dmabuf->fmt & CS_FMT_STEREO) && + !(dmabuf->fmt & CS_FMT_16BIT)) + dmabuf->divisor = 2; + else if( !(dmabuf->fmt & CS_FMT_STEREO) && + !(dmabuf->fmt & CS_FMT_16BIT)) + dmabuf->divisor = 4; + else + dmabuf->divisor = 1; + + CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, printk( + "cs46xx: cs_set_divisor()- %s %d\n", + (dmabuf->type == CS_TYPE_ADC) ? "ADC" : "DAC", + dmabuf->divisor) ); +} + +/* +* mute some of the more prevalent registers to avoid popping. +*/ +static void cs_mute(struct cs_card *card, int state) +{ + struct ac97_codec *dev=card->ac97_codec[0]; + + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()+ %s\n", + (state == CS_TRUE) ? "Muting" : "UnMuting") ); + + if(state == CS_TRUE) + { + /* + * fix pops when powering up on thinkpads + */ + card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, + (u8)BA0_AC97_MASTER_VOLUME); + card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_HEADPHONE_VOLUME); + card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_MASTER_VOLUME_MONO); + card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_PCM_OUT_VOLUME); + + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000); + } + else + { + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, card->pm.u32AC97_master_volume); + cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, card->pm.u32AC97_headphone_volume); + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, card->pm.u32AC97_master_volume_mono); + cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, card->pm.u32AC97_pcm_out_volume); + } + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()-\n")); +} + +/* set playback sample rate */ +static unsigned int cs_set_dac_rate(struct cs_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int tmp1, tmp2; + unsigned int phiIncr; + unsigned int correctionPerGOF, correctionPerSec; + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()+ %d\n",rate) ); + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M + * GOF_PER_SEC * correctionPerGOF + * + * i.e. + * + * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + */ + tmp1 = rate << 16; + phiIncr = tmp1 / 48000; + tmp1 -= phiIncr * 48000; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / 48000; + phiIncr += tmp2; + tmp1 -= tmp2 * 48000; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + + /* + * Fill in the SampleRateConverter control block. + */ + + spin_lock_irqsave(&state->card->lock, flags); + cs461x_poke(state->card, BA1_PSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + cs461x_poke(state->card, BA1_PPI, phiIncr); + spin_unlock_irqrestore(&state->card->lock, flags); + dmabuf->rate = rate; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_dac_rate()- %d\n",rate) ); + return rate; +} + +/* set recording sample rate */ +static unsigned int cs_set_adc_rate(struct cs_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int phiIncr, coeffIncr, tmp1, tmp2; + unsigned int correctionPerGOF, correctionPerSec, initialDelay; + unsigned int frameGroupLength, cnt; + unsigned long flags; + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()+ %d\n",rate) ); + + /* + * We can only decimate by up to a factor of 1/9th the hardware rate. + * Correct the value if an attempt is made to stray outside that limit. + */ + if ((rate * 9) < 48000) + rate = 48000 / 9; + + /* + * We can not capture at at rate greater than the Input Rate (48000). + * Return an error if an attempt is made to stray outside that limit. + */ + if (rate > 48000) + rate = 48000; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * coeffIncr = -floor((Fs,out * 2^23) / Fs,in) + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - + * GOF_PER_SEC * correctionPerGOF + * initialDelay = ceil((24 * Fs,in) / Fs,out) + * + * i.e. + * + * coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) + * phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) + */ + + tmp1 = rate << 16; + coeffIncr = tmp1 / 48000; + tmp1 -= coeffIncr * 48000; + tmp1 <<= 7; + coeffIncr <<= 7; + coeffIncr += tmp1 / 48000; + coeffIncr ^= 0xFFFFFFFF; + coeffIncr++; + tmp1 = 48000 << 16; + phiIncr = tmp1 / rate; + tmp1 -= phiIncr * rate; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / rate; + phiIncr += tmp2; + tmp1 -= tmp2 * rate; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + initialDelay = ((48000 * 24) + rate - 1) / rate; + + /* + * Fill in the VariDecimate control block. + */ + spin_lock_irqsave(&card->lock, flags); + cs461x_poke(card, BA1_CSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + cs461x_poke(card, BA1_CCI, coeffIncr); + cs461x_poke(card, BA1_CD, + (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); + cs461x_poke(card, BA1_CPI, phiIncr); + spin_unlock_irqrestore(&card->lock, flags); + + /* + * Figure out the frame group length for the write back task. Basically, + * this is just the factors of 24000 (2^6*3*5^3) that are not present in + * the output sample rate. + */ + frameGroupLength = 1; + for (cnt = 2; cnt <= 64; cnt *= 2) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 2; + } + if (((rate / 3) * 3) != rate) { + frameGroupLength *= 3; + } + for (cnt = 5; cnt <= 125; cnt *= 5) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 5; + } + + /* + * Fill in the WriteBack control block. + */ + spin_lock_irqsave(&card->lock, flags); + cs461x_poke(card, BA1_CFG1, frameGroupLength); + cs461x_poke(card, BA1_CFG2, (0x00800000 | frameGroupLength)); + cs461x_poke(card, BA1_CCST, 0x0000FFFF); + cs461x_poke(card, BA1_CSPB, ((65536 * rate) / 24000)); + cs461x_poke(card, (BA1_CSPB + 4), 0x0000FFFF); + spin_unlock_irqrestore(&card->lock, flags); + dmabuf->rate = rate; + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_set_adc_rate()- %d\n",rate) ); + return rate; +} + +/* prepare channel attributes for playback */ +static void cs_play_setup(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int tmp, Count, playFormat; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()+\n") ); + cs461x_poke(card, BA1_PVOL, 0x80008000); + if(!dmabuf->SGok) + cs461x_poke(card, BA1_PBA, virt_to_bus(dmabuf->pbuf)); + + Count = 4; + playFormat=cs461x_peek(card, BA1_PFIE); + if ((dmabuf->fmt & CS_FMT_STEREO)) { + playFormat &= ~DMA_RQ_C2_AC_MONO_TO_STEREO; + Count *= 2; + } + else + playFormat |= DMA_RQ_C2_AC_MONO_TO_STEREO; + + if ((dmabuf->fmt & CS_FMT_16BIT)) { + playFormat &= ~(DMA_RQ_C2_AC_8_TO_16_BIT + | DMA_RQ_C2_AC_SIGNED_CONVERT); + Count *= 2; + } + else + playFormat |= (DMA_RQ_C2_AC_8_TO_16_BIT + | DMA_RQ_C2_AC_SIGNED_CONVERT); + + cs461x_poke(card, BA1_PFIE, playFormat); + + tmp = cs461x_peek(card, BA1_PDTC); + tmp &= 0xfffffe00; + cs461x_poke(card, BA1_PDTC, tmp | --Count); + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()-\n") ); + +} + +struct InitStruct +{ + u32 long off; + u32 long val; +} InitArray[] = { {0x00000040, 0x3fc0000f}, + {0x0000004c, 0x04800000}, + + {0x000000b3, 0x00000780}, + {0x000000b7, 0x00000000}, + {0x000000bc, 0x07800000}, + + {0x000000cd, 0x00800000}, + }; + +/* + * "SetCaptureSPValues()" -- Initialize record task values before each + * capture startup. + */ +void SetCaptureSPValues(struct cs_card *card) +{ + unsigned i, offset; + CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()+\n") ); + for(i=0; icard; + struct dmabuf *dmabuf = &state->dmabuf; + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()+\n") ); + + SetCaptureSPValues(card); + + /* + * set the attenuation to 0dB + */ + cs461x_poke(card, BA1_CVOL, 0x80008000); + + /* + * set the physical address of the capture buffer into the SP + */ + cs461x_poke(card, BA1_CBA, virt_to_bus(dmabuf->rawbuf)); + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()-\n") ); +} + + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +static inline unsigned cs_get_dma_addr(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 offset; + + if ( (!(dmabuf->enable & DAC_RUNNING)) && + (!(dmabuf->enable & ADC_RUNNING) ) ) + { + CS_DBGOUT(CS_ERROR, 2, printk( + "cs46xx: ERROR cs_get_dma_addr(): not enabled \n") ); + return 0; + } + + /* + * ganularity is byte boundry, good part. + */ + if(dmabuf->enable & DAC_RUNNING) + { + offset = cs461x_peek(state->card, BA1_PBA); + } + else /* ADC_RUNNING must be set */ + { + offset = cs461x_peek(state->card, BA1_CBA); + } + CS_DBGOUT(CS_PARMS | CS_FUNCTION, 9, + printk("cs46xx: cs_get_dma_addr() %d\n",offset) ); + offset = (u32)bus_to_virt((unsigned long)offset) - (u32)dmabuf->rawbuf; + CS_DBGOUT(CS_PARMS | CS_FUNCTION, 8, + printk("cs46xx: cs_get_dma_addr()- %d\n",offset) ); + return offset; +} + +static void resync_dma_ptrs(struct cs_state *state) +{ + struct dmabuf *dmabuf; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()+ \n") ); + if(state) + { + dmabuf = &state->dmabuf; + dmabuf->hwptr=dmabuf->swptr = 0; + dmabuf->pringbuf = 0; + } + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()- \n") ); +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int tmp; + + dmabuf->enable &= ~ADC_RUNNING; + + tmp = cs461x_peek(card, BA1_CCTL); + tmp &= 0xFFFF0000; + cs461x_poke(card, BA1_CCTL, tmp ); +} + +static void stop_adc(struct cs_state *state) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()+ \n") ); + spin_lock_irqsave(&state->card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&state->card->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_adc()- \n") ); +} + +static void start_adc(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned long flags; + unsigned int tmp; + + spin_lock_irqsave(&card->lock, flags); + if (!(dmabuf->enable & ADC_RUNNING) && + ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) + && dmabuf->ready) && + ((card->pm.flags & CS46XX_PM_IDLE) || + (card->pm.flags & CS46XX_PM_RESUMED)) ) + { + dmabuf->enable |= ADC_RUNNING; + cs_set_divisor(dmabuf); + tmp = cs461x_peek(card, BA1_CCTL); + tmp &= 0xFFFF0000; + tmp |= card->cctl; + CS_DBGOUT(CS_FUNCTION, 2, printk( + "cs46xx: start_adc() poke 0x%x \n",tmp) ); + cs461x_poke(card, BA1_CCTL, tmp); + } + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned int tmp; + + dmabuf->enable &= ~DAC_RUNNING; + + tmp=cs461x_peek(card, BA1_PCTL); + tmp&=0xFFFF; + cs461x_poke(card, BA1_PCTL, tmp); +} + +static void stop_dac(struct cs_state *state) +{ + unsigned long flags; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()+ \n") ); + spin_lock_irqsave(&state->card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&state->card->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: stop_dac()- \n") ); +} + +static void start_dac(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card = state->card; + unsigned long flags; + int tmp; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()+ \n") ); + spin_lock_irqsave(&card->lock, flags); + if (!(dmabuf->enable & DAC_RUNNING) && + ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) && + ((card->pm.flags & CS46XX_PM_IDLE) || + (card->pm.flags & CS46XX_PM_RESUMED)) ) + { + dmabuf->enable |= DAC_RUNNING; + tmp = cs461x_peek(card, BA1_PCTL); + tmp &= 0xFFFF; + tmp |= card->pctl; + CS_DBGOUT(CS_PARMS, 6, printk( + "cs46xx: start_dac() poke card=0x%.08x tmp=0x%.08x addr=0x%.08x \n", + (unsigned)card, (unsigned)tmp, + (unsigned)card->ba1.idx[(BA1_PCTL >> 16) & 3]+(BA1_PCTL&0xffff) ) ); + cs461x_poke(card, BA1_PCTL, tmp); + } + spin_unlock_irqrestore(&card->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: start_dac()- \n") ); +} + +#define DMABUF_MINORDER 1 + +/* + * allocate DMA buffer, playback and recording buffers are separate. + */ +static int alloc_dmabuf(struct cs_state *state) +{ + + struct cs_card *card=state->card; + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf = NULL; + void *tmpbuff = NULL; + int order; + struct page *map, *mapend; + unsigned long df; + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->SGok = 0; +/* +* check for order within limits, but do not overwrite value. +*/ + if((defaultorder > 1) && (defaultorder < 12)) + df = defaultorder; + else + df = 2; + + for (order = df; order >= DMABUF_MINORDER; order--) + if ( (rawbuf = (void *) pci_alloc_consistent( + card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr))) + break; + if (!rawbuf) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: alloc_dmabuf(): unable to allocate rawbuf\n")); + return -ENOMEM; + } + dmabuf->buforder = order; + dmabuf->rawbuf = rawbuf; + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs46xx_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(dmabuf->rawbuf + + (PAGE_SIZE << dmabuf->buforder) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) + cs4x_mem_map_reserve(map); + + CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: alloc_dmabuf(): allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf) ); + +/* +* only allocate the conversion buffer for the ADC +*/ + if(dmabuf->type == CS_TYPE_DAC) + { + dmabuf->tmpbuff = NULL; + dmabuf->buforder_tmpbuff = 0; + return 0; + } +/* + * now the temp buffer for 16/8 conversions + */ + + tmpbuff = (void *) pci_alloc_consistent( + card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr_tmpbuff); + + if (!tmpbuff) + return -ENOMEM; + CS_DBGOUT(CS_PARMS, 9, printk("cs46xx: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, tmpbuff) ); + + dmabuf->tmpbuff = tmpbuff; + dmabuf->buforder_tmpbuff = order; + + // Now mark the pages as reserved; otherwise the + // remap_page_range() in cs46xx_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(dmabuf->tmpbuff + + (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) + cs4x_mem_map_reserve(map); + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct page *map, *mapend; + + if (dmabuf->rawbuf) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = virt_to_page(dmabuf->rawbuf + + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf(state->card, dmabuf); + } + + if (dmabuf->tmpbuff) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = virt_to_page(dmabuf->tmpbuff + + (PAGE_SIZE << dmabuf->buforder_tmpbuff) - 1); + for (map = virt_to_page(dmabuf->tmpbuff); map <= mapend; map++) + cs4x_mem_map_unreserve(map); + free_dmabuf2(state->card, dmabuf); + } + + dmabuf->rawbuf = NULL; + dmabuf->tmpbuff = NULL; + dmabuf->mapped = dmabuf->ready = 0; + dmabuf->SGok = 0; +} + +static int __prog_dmabuf(struct cs_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long allocated_pages, allocated_bytes; + unsigned long tmp1, tmp2, fmt=0; + unsigned long *ptmp = (unsigned long *) dmabuf->pbuf; + unsigned long SGarray[9], nSGpages=0; + int ret; + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()+ \n")); +/* + * check for CAPTURE and use only non-sg for initial release + */ + if(dmabuf->type == CS_TYPE_ADC) + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() ADC\n")); + /* + * add in non-sg support for capture. + */ + spin_lock_irqsave(&state->card->lock, flags); + /* add code to reset the rawbuf memory. TRW */ + resync_dma_ptrs(state); + dmabuf->total_bytes = dmabuf->blocks = 0; + dmabuf->count = dmabuf->error = dmabuf->underrun = 0; + + dmabuf->SGok = 0; + + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf || !dmabuf->tmpbuff) + if ((ret = alloc_dmabuf(state))) + return ret; + /* + * static image only supports 16Bit signed, stereo - hard code fmt + */ + fmt = CS_FMT_16BIT | CS_FMT_STEREO; + + dmabuf->numfrag = 2; + dmabuf->fragsize = 2048; + dmabuf->fragsamples = 2048 >> sample_shift[fmt]; + dmabuf->dmasize = 4096; + dmabuf->fragshift = 11; + + memset(dmabuf->rawbuf, (fmt & CS_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); + memset(dmabuf->tmpbuff, (fmt & CS_FMT_16BIT) ? 0 : 0x80, + PAGE_SIZE<buforder_tmpbuff); + + /* + * Now set up the ring + */ + + spin_lock_irqsave(&state->card->lock, flags); + cs_rec_setup(state); + spin_unlock_irqrestore(&state->card->lock, flags); + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + + CS_DBGOUT(CS_PARMS, 4, printk( + "cs46xx: prog_dmabuf(): CAPTURE rate=%d fmt=0x%x numfrag=%d " + "fragsize=%d dmasize=%d\n", + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize) ); + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- 0 \n")); + return 0; + } + else if (dmabuf->type == CS_TYPE_DAC) + { + /* + * Must be DAC + */ + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() DAC\n")); + spin_lock_irqsave(&state->card->lock, flags); + resync_dma_ptrs(state); + dmabuf->total_bytes = dmabuf->blocks = 0; + dmabuf->count = dmabuf->error = dmabuf->underrun = 0; + + dmabuf->SGok = 0; + + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) + if ((ret = alloc_dmabuf(state))) + return ret; + + allocated_pages = 1 << dmabuf->buforder; + allocated_bytes = allocated_pages*PAGE_SIZE; + + if(allocated_pages < 2) + { + CS_DBGOUT(CS_FUNCTION, 4, printk( + "cs46xx: prog_dmabuf() Error: allocated_pages too small (%d)\n", + (unsigned)allocated_pages)); + return -ENOMEM; + } + + /* Use all the pages allocated, fragsize 4k. */ + /* Use 'pbuf' for S/G page map table. */ + dmabuf->SGok = 1; /* Use S/G. */ + + nSGpages = allocated_bytes/4096; /* S/G pages always 4k. */ + + /* Set up S/G variables. */ + *ptmp = virt_to_bus(dmabuf->rawbuf); + *(ptmp+1) = 0x00000008; + for(tmp1= 1; tmp1 < nSGpages; tmp1++) { + *(ptmp+2*tmp1) = virt_to_bus( (dmabuf->rawbuf)+4096*tmp1); + if( tmp1 == nSGpages-1) + tmp2 = 0xbfff0000; + else + tmp2 = 0x80000000+8*(tmp1+1); + *(ptmp+2*tmp1+1) = tmp2; + } + SGarray[0] = 0x82c0200d; + SGarray[1] = 0xffff0000; + SGarray[2] = *ptmp; + SGarray[3] = 0x00010600; + SGarray[4] = *(ptmp+2); + SGarray[5] = 0x80000010; + SGarray[6] = *ptmp; + SGarray[7] = *(ptmp+2); + SGarray[8] = (virt_to_bus(dmabuf->pbuf) & 0xffff000) | 0x10; + + if (dmabuf->SGok) { + dmabuf->numfrag = nSGpages; + dmabuf->fragsize = 4096; + dmabuf->fragsamples = 4096 >> sample_shift[dmabuf->fmt]; + dmabuf->fragshift = 12; + dmabuf->dmasize = dmabuf->numfrag*4096; + } + else { + SGarray[0] = 0xf2c0000f; + SGarray[1] = 0x00000200; + SGarray[2] = 0; + SGarray[3] = 0x00010600; + SGarray[4]=SGarray[5]=SGarray[6]=SGarray[7]=SGarray[8] = 0; + dmabuf->numfrag = 2; + dmabuf->fragsize = 2048; + dmabuf->fragsamples = 2048 >> sample_shift[dmabuf->fmt]; + dmabuf->dmasize = 4096; + dmabuf->fragshift = 11; + } + for(tmp1 = 0; tmp1 < sizeof(SGarray)/4; tmp1++) + cs461x_poke( state->card, BA1_PDTC+tmp1*4, SGarray[tmp1]); + + memset(dmabuf->rawbuf, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); + + /* + * Now set up the ring + */ + + spin_lock_irqsave(&state->card->lock, flags); + cs_play_setup(state); + spin_unlock_irqrestore(&state->card->lock, flags); + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + + CS_DBGOUT(CS_PARMS, 4, printk( + "cs46xx: prog_dmabuf(): PLAYBACK rate=%d fmt=0x%x numfrag=%d " + "fragsize=%d dmasize=%d\n", + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize) ); + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- \n")); + return 0; + } + else + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- Invalid Type %d\n", + dmabuf->type)); + } + return 1; +} + +static int prog_dmabuf(struct cs_state *state) +{ + int ret; + + down(&state->sem); + ret = __prog_dmabuf(state); + up(&state->sem); + + return ret; +} + +static void cs_clear_tail(struct cs_state *state) +{ +} + +static int drain_dac(struct cs_state *state, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + struct cs_card *card=state->card; + unsigned long flags; + unsigned long tmo; + int count; + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()+ \n")); + if (dmabuf->mapped || !dmabuf->ready) + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0, not ready\n")); + return 0; + } + + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + /* It seems that we have to set the current state to TASK_INTERRUPTIBLE + every time to make the process really go to sleep */ + current->state = TASK_INTERRUPTIBLE; + + spin_lock_irqsave(&state->card->lock, flags); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + if (signal_pending(current)) + break; + + if (nonblock) { + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + + tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; + tmo >>= sample_shift[dmabuf->fmt]; + tmo += (2048*HZ)/dmabuf->rate; + + if (!schedule_timeout(tmo ? tmo : 1) && tmo){ + printk(KERN_ERR "cs46xx: drain_dac, dma timeout? %d\n", count); + break; + } + } + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + { + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- -ERESTARTSYS\n")); + /* + * set to silence and let that clear the fifos. + */ + cs461x_clear_serial_FIFOs(card, CS_TYPE_DAC); + return -ERESTARTSYS; + } + + CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- 0\n")); + return 0; +} + + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void cs_update_ptr(struct cs_card *card, int wake) +{ + struct cs_state *state; + struct dmabuf *dmabuf; + unsigned hwptr; + int diff; + + /* error handling and process wake up for ADC */ + state = card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->enable & ADC_RUNNING) { + /* update hardware pointer */ + hwptr = cs_get_dma_addr(state); + + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + CS_DBGOUT(CS_PARMS, 9, printk( + "cs46xx: cs_update_ptr()+ ADC hwptr=%d diff=%d\n", + hwptr,diff) ); + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + if (dmabuf->count > dmabuf->dmasize) + dmabuf->count = dmabuf->dmasize; + + if(dmabuf->mapped) + { + if (wake && dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else + { + if (wake && dmabuf->count > 0) + wake_up(&dmabuf->wait); + } + } + } + +/* + * Now the DAC + */ + state = card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + /* error handling and process wake up for DAC */ + if (dmabuf->enable & DAC_RUNNING) { + /* update hardware pointer */ + hwptr = cs_get_dma_addr(state); + + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + CS_DBGOUT(CS_PARMS, 9, printk( + "cs46xx: cs_update_ptr()+ DAC hwptr=%d diff=%d\n", + hwptr,diff) ); + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + if (dmabuf->mapped) { + dmabuf->count += diff; + if (wake && dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + /* + * other drivers use fragsize, but don't see any sense + * in that, since dmasize is the buffer asked for + * via mmap. + */ + if( dmabuf->count > dmabuf->dmasize) + dmabuf->count &= dmabuf->dmasize-1; + } else { + dmabuf->count -= diff; + /* + * backfill with silence and clear out the last + * "diff" number of bytes. + */ + if(hwptr >= diff) + { + memset(dmabuf->rawbuf + hwptr - diff, + (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, diff); + } + else + { + memset(dmabuf->rawbuf, + (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, + (unsigned)hwptr); + memset((void *)((unsigned)dmabuf->rawbuf + + dmabuf->dmasize + hwptr - diff), + (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, + diff - hwptr); + } + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: ERROR DAC count<0 or count > dmasize (%d)\n", + dmabuf->count)); + /* + * buffer underrun or buffer overrun, reset the + * count of bytes written back to 0. + */ + if(dmabuf->count < 0) + dmabuf->underrun=1; + dmabuf->count = 0; + dmabuf->error++; + } + if (wake && dmabuf->count < (signed)dmabuf->dmasize/2) + wake_up(&dmabuf->wait); + } + } + } +} + + +/* hold spinlock for the following! */ +static void cs_handle_midi(struct cs_card *card) +{ + unsigned char ch; + int wake; + unsigned temp1; + + wake = 0; + while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_RBE)) { + ch = cs461x_peekBA0(card, BA0_MIDRP); + if (card->midi.icnt < CS_MIDIINBUF) { + card->midi.ibuf[card->midi.iwr] = ch; + card->midi.iwr = (card->midi.iwr + 1) % CS_MIDIINBUF; + card->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&card->midi.iwait); + wake = 0; + while (!(cs461x_peekBA0(card, BA0_MIDSR) & MIDSR_TBF) && card->midi.ocnt > 0) { + temp1 = ( card->midi.obuf[card->midi.ord] ) & 0x000000ff; + cs461x_pokeBA0(card, BA0_MIDWP,temp1); + card->midi.ord = (card->midi.ord + 1) % CS_MIDIOUTBUF; + card->midi.ocnt--; + if (card->midi.ocnt < CS_MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&card->midi.owait); +} + +static void cs_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs_card *card = (struct cs_card *)dev_id; + /* Single channel card */ + struct cs_state *recstate = card->channel[0].state; + struct cs_state *playstate = card->channel[1].state; + u32 status; + + CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()+ \n")); + + spin_lock(&card->lock); + + status = cs461x_peekBA0(card, BA0_HISR); + + if ((status & 0x7fffffff) == 0) + { + cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV); + spin_unlock(&card->lock); + return; + } + + /* + * check for playback or capture interrupt only + */ + if( ((status & HISR_VC0) && playstate && playstate->dmabuf.ready) || + (((status & HISR_VC1) && recstate && recstate->dmabuf.ready)) ) + { + CS_DBGOUT(CS_INTERRUPT, 8, printk( + "cs46xx: cs_interrupt() interrupt bit(s) set (0x%x)\n",status)); + cs_update_ptr(card, CS_TRUE); + } + + if( status & HISR_MIDI ) + cs_handle_midi(card); + + /* clear 'em */ + cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV); + spin_unlock(&card->lock); + CS_DBGOUT(CS_INTERRUPT, 9, printk("cs46xx: cs_interrupt()- \n")); +} + + +/**********************************************************************/ + +static ssize_t cs_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&card->lock, flags); + ptr = card->midi.ird; + cnt = CS_MIDIINBUF - ptr; + if (card->midi.icnt < cnt) + cnt = card->midi.icnt; + spin_unlock_irqrestore(&card->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&card->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, card->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % CS_MIDIINBUF; + spin_lock_irqsave(&card->lock, flags); + card->midi.ird = ptr; + card->midi.icnt -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + + +static ssize_t cs_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&card->lock, flags); + ptr = card->midi.owr; + cnt = CS_MIDIOUTBUF - ptr; + if (card->midi.ocnt + cnt > CS_MIDIOUTBUF) + cnt = CS_MIDIOUTBUF - card->midi.ocnt; + if (cnt <= 0) + cs_handle_midi(card); + spin_unlock_irqrestore(&card->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&card->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(card->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % CS_MIDIOUTBUF; + spin_lock_irqsave(&card->lock, flags); + card->midi.owr = ptr; + card->midi.ocnt += cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&card->lock, flags); + cs_handle_midi(card); + spin_unlock_irqrestore(&card->lock, flags); + } + return ret; +} + + +static unsigned int cs_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &card->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &card->midi.iwait, wait); + spin_lock_irqsave(&card->lock, flags); + if (file->f_flags & FMODE_READ) { + if (card->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (card->midi.ocnt < CS_MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&card->lock, flags); + return mask; +} + + +static int cs_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs_card *card=NULL; + unsigned long flags; + struct list_head *entry; + + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + if (card->dev_midi == minor) + break; + } + + if (entry == &cs46xx_devs) + return -ENODEV; + if (!card) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs46xx: cs46xx_midi_open(): Error - unable to find card struct\n")); + return -ENODEV; + } + + file->private_data = card; + /* wait for device to become free */ + down(&card->midi.open_sem); + while (card->midi.open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&card->midi.open_sem); + return -EBUSY; + } + up(&card->midi.open_sem); + interruptible_sleep_on(&card->midi.open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&card->midi.open_sem); + } + spin_lock_irqsave(&card->midi.lock, flags); + if (!(card->midi.open_mode & (FMODE_READ | FMODE_WRITE))) { + card->midi.ird = card->midi.iwr = card->midi.icnt = 0; + card->midi.ord = card->midi.owr = card->midi.ocnt = 0; + card->midi.ird = card->midi.iwr = card->midi.icnt = 0; + cs461x_pokeBA0(card, BA0_MIDCR, 0x0000000f); /* Enable xmit, rcv. */ + cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); /* Enable interrupts */ + } + if (file->f_mode & FMODE_READ) { + card->midi.ird = card->midi.iwr = card->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + card->midi.ord = card->midi.owr = card->midi.ocnt = 0; + } + spin_unlock_irqrestore(&card->midi.lock, flags); + card->midi.open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); + up(&card->midi.open_sem); + MOD_INC_USE_COUNT; /* for 2.2 */ + return 0; +} + + +static int cs_midi_release(struct inode *inode, struct file *file) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&card->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&card->midi.lock, flags); + count = card->midi.ocnt; + spin_unlock_irqrestore(&card->midi.lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&card->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "cs46xx: midi timed out??\n"); + } + remove_wait_queue(&card->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&card->midi.open_sem); + card->midi.open_mode &= (~(file->f_mode & (FMODE_READ | FMODE_WRITE))); + up(&card->midi.open_sem); + wake_up(&card->midi.open_wait); + MOD_DEC_USE_COUNT; /* for 2.2 */ + return 0; +} + +/* + * Midi file operations struct. + */ +static /*const*/ struct file_operations cs_midi_fops = { + CS_OWNER CS_THIS_MODULE + llseek: no_llseek, + read: cs_midi_read, + write: cs_midi_write, + poll: cs_midi_poll, + open: cs_midi_open, + release: cs_midi_release, +}; + +/* + * + * CopySamples copies 16-bit stereo signed samples from the source to the + * destination, possibly converting down to unsigned 8-bit and/or mono. + * count specifies the number of output bytes to write. + * + * Arguments: + * + * dst - Pointer to a destination buffer. + * src - Pointer to a source buffer + * count - The number of bytes to copy into the destination buffer. + * fmt - CS_FMT_16BIT and/or CS_FMT_STEREO bits + * dmabuf - pointer to the dma buffer structure + * + * NOTES: only call this routine if the output desired is not 16 Signed Stereo + * + * + */ +static void CopySamples(char *dst, char *src, int count, unsigned fmt, + struct dmabuf *dmabuf) +{ + + s32 s32AudioSample; + s16 *psSrc=(s16 *)src; + s16 *psDst=(s16 *)dst; + u8 *pucDst=(u8 *)dst; + + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") ); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " dst=0x%x src=0x%x count=%d fmt=0x%x\n", + (unsigned)dst,(unsigned)src,(unsigned)count,(unsigned)fmt) ); + + /* + * See if the data should be output as 8-bit unsigned stereo. + */ + if((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) + { + /* + * Convert each 16-bit signed stereo sample to 8-bit unsigned + * stereo using rounding. + */ + psSrc = (s16 *)src; + count = count/2; + while(count--) + { + *(pucDst++) = (u8)(((s16)(*psSrc++) + (s16)0x8000) >> 8); + } + } + /* + * See if the data should be output at 8-bit unsigned mono. + */ + else if(!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) + { + /* + * Convert each 16-bit signed stereo sample to 8-bit unsigned + * mono using averaging and rounding. + */ + psSrc = (s16 *)src; + count = count/2; + while(count--) + { + s32AudioSample = ((*psSrc)+(*(psSrc + 1)))/2 + (s32)0x80; + if(s32AudioSample > 0x7fff) + s32AudioSample = 0x7fff; + *(pucDst++) = (u8)(((s16)s32AudioSample + (s16)0x8000) >> 8); + psSrc += 2; + } + } + /* + * See if the data should be output at 16-bit signed mono. + */ + else if(!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT)) + { + /* + * Convert each 16-bit signed stereo sample to 16-bit signed + * mono using averaging. + */ + psSrc = (s16 *)src; + count = count/2; + while(count--) + { + *(psDst++) = (s16)((*psSrc)+(*(psSrc + 1)))/2; + psSrc += 2; + } + } +} + +/* + * cs_copy_to_user() + * replacement for the standard copy_to_user, to allow for a conversion from + * 16 bit to 8 bit and from stereo to mono, if the record conversion is active. + * The current CS46xx/CS4280 static image only records in 16bit unsigned Stereo, + * so we convert from any of the other format combinations. + */ +static unsigned cs_copy_to_user( + struct cs_state *s, + void *dest, + void *hwsrc, + unsigned cnt, + unsigned *copied) +{ + struct dmabuf *dmabuf = &s->dmabuf; + void *src = hwsrc; /* default to the standard destination buffer addr */ + + CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO + "cs_copy_to_user()+ fmt=0x%x cnt=%d dest=0x%.8x\n", + dmabuf->fmt,(unsigned)cnt,(unsigned)dest) ); + + if(cnt > dmabuf->dmasize) + { + cnt = dmabuf->dmasize; + } + if(!cnt) + { + *copied = 0; + return 0; + } + if(dmabuf->divisor != 1) + { + if(!dmabuf->tmpbuff) + { + *copied = cnt/dmabuf->divisor; + return 0; + } + + CopySamples((char *)dmabuf->tmpbuff, (char *)hwsrc, cnt, + dmabuf->fmt, dmabuf); + src = dmabuf->tmpbuff; + cnt = cnt/dmabuf->divisor; + } + if (copy_to_user(dest, src, cnt)) + { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs46xx: cs_copy_to_user()- fault dest=0x%x src=0x%x cnt=%d\n", + (unsigned)dest,(unsigned)src,cnt) ); + *copied = 0; + return -EFAULT; + } + *copied = cnt; + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt) ); + return 0; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to + the user's buffer. it is filled by the dma machine and drained by this loop. */ +static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *) file->private_data; + struct cs_state *state; + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned copied=0; + + CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, + printk("cs46xx: cs_read()+ %d\n",count) ); + state = (struct cs_state *)card->states[0]; + if(!state) + return -ENODEV; + dmabuf = &state->dmabuf; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + down(&state->sem); + if (!dmabuf->ready && (ret = __prog_dmabuf(state))) + goto out; + + add_wait_queue(&state->dmabuf.wait, &wait); + while (count > 0) { + while(!(card->pm.flags & CS46XX_PM_IDLE)) + { + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + } + spin_lock_irqsave(&state->card->lock, flags); + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > (count * dmabuf->divisor)) + cnt = count * dmabuf->divisor; + if (cnt <= 0) { + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + start_adc(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + up(&state->sem); + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if (dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + + CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO + "_read() copy_to cnt=%d count=%d ", cnt,count) ); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", + dmabuf->dmasize,dmabuf->count,(unsigned)buffer,ret) ); + + if (cs_copy_to_user(state, buffer, + (void *)((unsigned)dmabuf->rawbuf + swptr), cnt, &copied)) + { + if (!ret) ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= copied; + buffer += copied; + ret += copied; + start_adc(state); + } +out: + up(&state->sem); + remove_wait_queue(&state->dmabuf.wait, &wait); + set_current_state(TASK_RUNNING); + CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, + printk("cs46xx: cs_read()- %d\n",ret) ); + return ret; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to + the soundcard. it is drained by the dma machine and filled by this loop. */ +static ssize_t cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct cs_card *card = (struct cs_card *) file->private_data; + struct cs_state *state; + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4, + printk("cs46xx: cs_write called, count = %d\n", count) ); + state = (struct cs_state *)card->states[1]; + if(!state) + return -ENODEV; + dmabuf = &state->dmabuf; + + if (ppos != &file->f_pos) + return -ESPIPE; + + down(&state->sem); + if (dmabuf->mapped) + { + ret = -ENXIO; + goto out; + } + + if (!dmabuf->ready && (ret = __prog_dmabuf(state))) + goto out; + if (!access_ok(VERIFY_READ, buffer, count)) + { + ret = -EFAULT; + goto out; + } + add_wait_queue(&state->dmabuf.wait, &wait); + ret = 0; +/* +* Start the loop to read from the user's buffer and write to the dma buffer. +* check for PM events and underrun/overrun in the loop. +*/ + while (count > 0) { + while(!(card->pm.flags & CS46XX_PM_IDLE)) + { + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + } + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count < 0) { + /* buffer underrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr */ + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + if (dmabuf->underrun) + { + dmabuf->underrun = 0; + dmabuf->hwptr = cs_get_dma_addr(state); + dmabuf->swptr = dmabuf->hwptr; + } + + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize) + cnt = dmabuf->dmasize - dmabuf->count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + /* buffer is full, start the dma machine and wait for data to be + played */ + start_dac(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + up(&state->sem); + schedule(); + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if (dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto out; + } + spin_lock_irqsave(&state->card->lock, flags); + swptr = (swptr + cnt) % dmabuf->dmasize; + dmabuf->swptr = swptr; + dmabuf->count += cnt; + if(dmabuf->count > dmabuf->dmasize) + { + CS_DBGOUT(CS_WAVE_WRITE | CS_ERROR, 2, printk( + "cs46xx: cs_write() d->count > dmasize - resetting\n")); + dmabuf->count = dmabuf->dmasize; + } + dmabuf->endcleared = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(state); + } +out: + up(&state->sem); + remove_wait_queue(&state->dmabuf.wait, &wait); + set_current_state(TASK_RUNNING); + + CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 2, + printk("cs46xx: cs_write()- ret=0x%x\n", ret) ); + return ret; +} + +static unsigned int cs_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct dmabuf *dmabuf; + struct cs_state *state; + + unsigned long flags; + unsigned int mask = 0; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()+ \n")); + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) + { + return -EINVAL; + } + if (file->f_mode & FMODE_WRITE) + { + state = card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + } + if (file->f_mode & FMODE_READ) + { + state = card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + poll_wait(file, &dmabuf->wait, wait); + } + } + + spin_lock_irqsave(&card->lock, flags); + cs_update_ptr(card, CS_FALSE); + if (file->f_mode & FMODE_READ) { + state = card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_mode & FMODE_WRITE) { + state = card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)dmabuf->dmasize >= dmabuf->count + + (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + } + spin_unlock_irqrestore(&card->lock, flags); + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()- (0x%x) \n", + mask)); + return mask; +} + +/* + * We let users mmap the ring buffer. Its not the real DMA buffer but + * that side of the code is hidden in the IRQ handling. We do a software + * emulation of DMA from a 64K or so buffer into a 2K FIFO. + * (the hardware probably deserves a moan here but Crystal send me nice + * toys ;)). + */ + +static int cs_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_state *state; + struct dmabuf *dmabuf; + int ret = 0; + unsigned long size; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=0x%x %s %s\n", + (unsigned)file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "", + vma->vm_flags & VM_READ ? "VM_READ" : "") ); + + if (vma->vm_flags & VM_WRITE) { + state = card->states[1]; + if(state) + { + CS_DBGOUT(CS_OPEN, 2, printk( + "cs46xx: cs_mmap() VM_WRITE - state TRUE prog_dmabuf DAC\n") ); + if ((ret = prog_dmabuf(state)) != 0) + return ret; + } + } else if (vma->vm_flags & VM_READ) { + state = card->states[0]; + if(state) + { + CS_DBGOUT(CS_OPEN, 2, printk( + "cs46xx: cs_mmap() VM_READ - state TRUE prog_dmabuf ADC\n") ); + if ((ret = prog_dmabuf(state)) != 0) + return ret; + } + } else { + CS_DBGOUT(CS_ERROR, 2, printk( + "cs46xx: cs_mmap() return -EINVAL\n") ); + return -EINVAL; + } + +/* + * For now ONLY support playback, but seems like the only way to use + * mmap() is to open an FD with RDWR, just read or just write access + * does not function, get an error back from the kernel. + * Also, QuakeIII opens with RDWR! So, there must be something + * to needing read/write access mapping. So, allow read/write but + * use the DAC only. + */ + state = card->states[1]; + if(!(unsigned)state) + { + ret = -EINVAL; + goto out; + } + + down(&state->sem); + dmabuf = &state->dmabuf; + if (cs4x_pgoff(vma) != 0) + { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + + CS_DBGOUT(CS_PARMS, 2, printk("cs46xx: cs_mmap(): size=%d\n",(unsigned)size) ); + + if (size > (PAGE_SIZE << dmabuf->buforder)) + { + ret = -EINVAL; + goto out; + } + if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) + { + ret = -EAGAIN; + goto out; + } + dmabuf->mapped = 1; + + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_mmap()-\n") ); +out: + up(&state->sem); + return ret; +} + +static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_state *state; + struct dmabuf *dmabuf=0; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, valsave, mapped, ret; + + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + mapped = (file->f_mode & FMODE_READ) && dmabuf->mapped; + } + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + mapped |= (file->f_mode & FMODE_WRITE) && dmabuf->mapped; + } + +#if CSDEBUG + printioctl(cmd); +#endif + + switch (cmd) + { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + /* FIXME: spin_lock ? */ + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + synchronize_irq(); + dmabuf->ready = 0; + resync_dma_ptrs(state); + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + dmabuf->blocks = 0; + dmabuf->SGok = 0; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + synchronize_irq(); + resync_dma_ptrs(state); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + dmabuf->blocks = 0; + dmabuf->SGok = 0; + } + } + CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_RESET()-\n") ); + return 0; + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(state, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SPEED: /* set sample rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + cs_set_adc_rate(state, val); + cs_set_divisor(dmabuf); + } + } + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + cs_set_dac_rate(state, val); + cs_set_divisor(dmabuf); + } + } + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: cs_ioctl() DSP_SPEED %s %s %d\n", + file->f_mode & FMODE_WRITE ? "DAC" : "", + file->f_mode & FMODE_READ ? "ADC" : "", + dmabuf->rate ) ); + return put_user(dmabuf->rate, (int *)arg); + } + return put_user(0, (int *)arg); + + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: DSP_STEREO() DAC %s\n", + (dmabuf->fmt & CS_FMT_STEREO) ? + "STEREO":"MONO") ); + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: DSP_STEREO() ADC %s\n", + (dmabuf->fmt & CS_FMT_STEREO) ? + "STEREO":"MONO") ); + } + } + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if ((val = prog_dmabuf(state))) + return val; + return put_user(dmabuf->fragsize, (int *)arg); + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if ((val = prog_dmabuf(state))) + return val; + return put_user(dmabuf->fragsize/dmabuf->divisor, + (int *)arg); + } + } + return put_user(0, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + return put_user(AFMT_S16_LE | AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk( + "cs46xx: cs_ioctl() DSP_SETFMT %s %s %s %s\n", + file->f_mode & FMODE_WRITE ? "DAC" : "", + file->f_mode & FMODE_READ ? "ADC" : "", + val == AFMT_S16_LE ? "16Bit Signed" : "", + val == AFMT_U8 ? "8Bit Unsigned" : "") ); + valsave = val; + if (val != AFMT_QUERY) { + if(val==AFMT_S16_LE || val==AFMT_U8) + { + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val==AFMT_S16_LE) + dmabuf->fmt |= CS_FMT_16BIT; + else + dmabuf->fmt &= ~CS_FMT_16BIT; + cs_set_divisor(dmabuf); + if((ret = prog_dmabuf(state))) + return ret; + } + } + if (file->f_mode & FMODE_READ) { + val = valsave; + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val==AFMT_S16_LE) + dmabuf->fmt |= CS_FMT_16BIT; + else + dmabuf->fmt &= ~CS_FMT_16BIT; + cs_set_divisor(dmabuf); + if((ret = prog_dmabuf(state))) + return ret; + } + } + } + else + { + CS_DBGOUT(CS_IOCTL | CS_ERROR, 2, printk( + "cs46xx: DSP_SETFMT() Unsupported format (0x%x)\n", + valsave) ); + } + } + else + { + if(file->f_mode & FMODE_WRITE) + { + state = (struct cs_state *)card->states[1]; + if(state) + dmabuf = &state->dmabuf; + } + else if(file->f_mode & FMODE_READ) + { + state = (struct cs_state *)card->states[0]; + if(state) + dmabuf = &state->dmabuf; + } + } + if(dmabuf) + { + if(dmabuf->fmt & CS_FMT_16BIT) + return put_user(AFMT_S16_LE, (int *)arg); + else + return put_user(AFMT_U8, (int *)arg); + } + return put_user(0, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + stop_dac(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val>1) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + if (prog_dmabuf(state)) + return 0; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + stop_adc(state); + dmabuf->ready = 0; + dmabuf->SGok = 0; + if(val>1) + dmabuf->fmt |= CS_FMT_STEREO; + else + dmabuf->fmt &= ~CS_FMT_STEREO; + cs_set_divisor(dmabuf); + if (prog_dmabuf(state)) + return 0; + } + } + } + return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, + (int *)arg); + + case SNDCTL_DSP_POST: + /* + * There will be a longer than normal pause in the data. + * so... do nothing, because there is nothing that we can do. + */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2) + return -EINVAL; + dmabuf->subdivision = val; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2) + return -EINVAL; + dmabuf->subdivision = val; + } + } + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + } + } + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + abinfo.fragsize = dmabuf->fragsize; + abinfo.fragstotal = dmabuf->numfrag; + /* + * for mmap we always have total space available + */ + if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; + else + abinfo.bytes = dmabuf->dmasize - dmabuf->count; + + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + return -ENODEV; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + abinfo.fragsize = dmabuf->fragsize/dmabuf->divisor; + abinfo.bytes = dmabuf->count/dmabuf->divisor; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + return -ENODEV; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, + (int *)arg); + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()+\n") ); + if (file->f_mode & FMODE_WRITE) + { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if(dmabuf->enable & DAC_RUNNING) + val |= PCM_ENABLE_INPUT; + } + } + if (file->f_mode & FMODE_READ) + { + if(state) + { + state = (struct cs_state *)card->states[0]; + dmabuf = &state->dmabuf; + if(dmabuf->enable & ADC_RUNNING) + val |= PCM_ENABLE_OUTPUT; + } + } + CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()- val=0x%x\n",val) ); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + if (val & PCM_ENABLE_INPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state))) + return ret; + start_adc(state); + } else + stop_adc(state); + } + } + if (file->f_mode & FMODE_WRITE) { + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + if (val & PCM_ENABLE_OUTPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state))) + return ret; + start_dac(state); + } else + stop_dac(state); + } + } + return 0; + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + state = (struct cs_state *)card->states[0]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + cinfo.bytes = dmabuf->total_bytes/dmabuf->divisor; + cinfo.blocks = dmabuf->count/dmabuf->divisor >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr/dmabuf->divisor; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + } + return -ENODEV; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + cinfo.bytes = dmabuf->total_bytes; + if (dmabuf->mapped) + { + cinfo.blocks = (cinfo.bytes >> dmabuf->fragshift) + - dmabuf->blocks; + CS_DBGOUT(CS_PARMS, 8, + printk("total_bytes=%d blocks=%d dmabuf->blocks=%d\n", + cinfo.bytes,cinfo.blocks,dmabuf->blocks) ); + dmabuf->blocks = cinfo.bytes >> dmabuf->fragshift; + } + else + { + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + } + cinfo.ptr = dmabuf->hwptr; + + CS_DBGOUT(CS_PARMS, 4, printk( + "cs46xx: GETOPTR bytes=%d blocks=%d ptr=%d\n", + cinfo.bytes,cinfo.blocks,cinfo.ptr) ); + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + } + return -ENODEV; + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + spin_lock_irqsave(&state->card->lock, flags); + cs_update_ptr(card, CS_TRUE); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + } + else + val = 0; + return put_user(val, (int *)arg); + + case SOUND_PCM_READ_RATE: + if(file->f_mode & FMODE_READ) + state = (struct cs_state *)card->states[0]; + else + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + return put_user(dmabuf->rate, (int *)arg); + } + return put_user(0, (int *)arg); + + + case SOUND_PCM_READ_CHANNELS: + if(file->f_mode & FMODE_READ) + state = (struct cs_state *)card->states[0]; + else + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, + (int *)arg); + } + return put_user(0, (int *)arg); + + case SOUND_PCM_READ_BITS: + if(file->f_mode & FMODE_READ) + state = (struct cs_state *)card->states[0]; + else + state = (struct cs_state *)card->states[1]; + if(state) + { + dmabuf = &state->dmabuf; + return put_user((dmabuf->fmt & CS_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + } + return put_user(0, (int *)arg); + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + + +/* + * AMP control - null AMP + */ + +static void amp_none(struct cs_card *card, int change) +{ +} + +/* + * Crystal EAPD mode + */ + +static void amp_voyetra(struct cs_card *card, int change) +{ + /* Manage the EAPD bit on the Crystal 4297 + and the Analog AD1885 */ + + int old=card->amplifier; + + card->amplifier+=change; + if(card->amplifier && !old) + { + /* Turn the EAPD amp on */ + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, + cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) | + 0x8000); + } + else if(old && !card->amplifier) + { + /* Turn the EAPD amp off */ + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, + cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + ~0x8000); + } +} + + +/* + * Game Theatre XP card - EGPIO[2] is used to enable the external amp. + */ + +static void amp_hercules(struct cs_card *card, int change) +{ + int old=card->amplifier; + if(!card) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: amp_hercules() called before initialized.\n")); + return; + } + card->amplifier+=change; + if( (card->amplifier && !old) && !(hercules_egpio_disable)) + { + CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO + "cs46xx: amp_hercules() external amp enabled\n")); + cs461x_pokeBA0(card, BA0_EGPIODR, + EGPIODR_GPOE2); /* enable EGPIO2 output */ + cs461x_pokeBA0(card, BA0_EGPIOPTR, + EGPIOPTR_GPPT2); /* open-drain on output */ + } + else if(old && !card->amplifier) + { + CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO + "cs46xx: amp_hercules() external amp disabled\n")); + cs461x_pokeBA0(card, BA0_EGPIODR, 0); /* disable */ + cs461x_pokeBA0(card, BA0_EGPIOPTR, 0); /* disable */ + } +} + +/* + * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support + * whenever we need to beat on the chip. + * + * The original idea and code for this hack comes from David Kaiser at + * Linuxcare. Perhaps one day Crystal will document their chips well + * enough to make them useful. + */ + +static void clkrun_hack(struct cs_card *card, int change) +{ + struct pci_dev *acpi_dev; + u16 control; + u8 pp; + unsigned long port; + int old=card->active; + + card->active+=change; + + acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + if(acpi_dev == NULL) + return; /* Not a thinkpad thats for sure */ + + /* Find the control port */ + pci_read_config_byte(acpi_dev, 0x41, &pp); + port=pp<<8; + + /* Read ACPI port */ + control=inw(port+0x10); + + /* Flip CLKRUN off while running */ + if(!card->active && old) + { + CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO + "cs46xx: clkrun() enable clkrun - change=%d active=%d\n", + change,card->active)); + outw(control|0x2000, port+0x10); + } + else + { + /* + * sometimes on a resume the bit is set, so always reset the bit. + */ + CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO + "cs46xx: clkrun() disable clkrun - change=%d active=%d\n", + change,card->active)); + outw(control&~0x2000, port+0x10); + } +} + + +static int cs_open(struct inode *inode, struct file *file) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_state *state = NULL; + struct dmabuf *dmabuf = NULL; + struct list_head *entry; + unsigned int minor = minor(inode->i_rdev); + int ret=0; + unsigned int tmp; + + CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=0x%x %s %s\n", + (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", + file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); + + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + + if (!((card->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs46xx_devs) + return -ENODEV; + if (!card) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs46xx: cs_open(): Error - unable to find audio card struct\n")); + return -ENODEV; + } + + /* + * hardcode state[0] for capture, [1] for playback + */ + if(file->f_mode & FMODE_READ) + { + CS_DBGOUT(CS_WAVE_READ, 2, printk("cs46xx: cs_open() FMODE_READ\n") ); + if (card->states[0] == NULL) { + state = card->states[0] = (struct cs_state *) + kmalloc(sizeof(struct cs_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct cs_state)); + init_MUTEX(&state->sem); + dmabuf = &state->dmabuf; + dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA); + if(dmabuf->pbuf==NULL) + { + kfree(state); + card->states[0]=NULL; + return -ENOMEM; + } + } + else + { + state = card->states[0]; + if(state->open_mode & FMODE_READ) + return -EBUSY; + } + dmabuf->channel = card->alloc_rec_pcm_channel(card); + + if (dmabuf->channel == NULL) { + kfree (card->states[0]); + card->states[0] = NULL;; + return -ENODEV; + } + + /* Now turn on external AMP if needed */ + state->card = card; + state->card->active_ctrl(state->card,1); + state->card->amplifier_ctrl(state->card,1); + + if( (tmp = cs46xx_powerup(card, CS_POWER_ADC)) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs46xx_powerup of ADC failed (0x%x)\n",tmp) ); + return -EIO; + } + + dmabuf->channel->state = state; + /* initialize the virtual channel */ + state->virt = 0; + state->magic = CS_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = card; + + down(&state->open_sem); + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample */ + + /* Default input is 8bit mono */ + dmabuf->fmt &= ~CS_FMT_MASK; + dmabuf->type = CS_TYPE_ADC; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + cs_set_adc_rate(state, 8000); + cs_set_divisor(dmabuf); + + state->open_mode |= FMODE_READ; + up(&state->open_sem); + } + if(file->f_mode & FMODE_WRITE) + { + CS_DBGOUT(CS_OPEN, 2, printk("cs46xx: cs_open() FMODE_WRITE\n") ); + if (card->states[1] == NULL) { + state = card->states[1] = (struct cs_state *) + kmalloc(sizeof(struct cs_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct cs_state)); + init_MUTEX(&state->sem); + dmabuf = &state->dmabuf; + dmabuf->pbuf = (void *)get_free_page(GFP_KERNEL | GFP_DMA); + if(dmabuf->pbuf==NULL) + { + kfree(state); + card->states[1]=NULL; + return -ENOMEM; + } + } + else + { + state = card->states[1]; + if(state->open_mode & FMODE_WRITE) + return -EBUSY; + } + dmabuf->channel = card->alloc_pcm_channel(card); + + if (dmabuf->channel == NULL) { + kfree (card->states[1]); + card->states[1] = NULL;; + return -ENODEV; + } + + /* Now turn on external AMP if needed */ + state->card = card; + state->card->active_ctrl(state->card,1); + state->card->amplifier_ctrl(state->card,1); + + if( (tmp = cs46xx_powerup(card, CS_POWER_DAC)) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs46xx_powerup of DAC failed (0x%x)\n",tmp) ); + return -EIO; + } + + dmabuf->channel->state = state; + /* initialize the virtual channel */ + state->virt = 1; + state->magic = CS_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = card; + + down(&state->open_sem); + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample */ + + /* Default output is 8bit mono. */ + dmabuf->fmt &= ~CS_FMT_MASK; + dmabuf->type = CS_TYPE_DAC; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + cs_set_dac_rate(state, 8000); + cs_set_divisor(dmabuf); + + state->open_mode |= FMODE_WRITE; + up(&state->open_sem); + if((ret = prog_dmabuf(state))) + return ret; + } + MOD_INC_USE_COUNT; /* for 2.2 */ + CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n") ); + return 0; +} + +static int cs_release(struct inode *inode, struct file *file) +{ + struct cs_card *card = (struct cs_card *)file->private_data; + struct dmabuf *dmabuf; + struct cs_state *state; + unsigned int tmp; + CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=0x%x %s %s\n", + (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", + file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); + + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) + { + return -EINVAL; + } + state = card->states[1]; + if(state) + { + if ( (state->open_mode & FMODE_WRITE) & (file->f_mode & FMODE_WRITE) ) + { + CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_WRITE\n") ); + dmabuf = &state->dmabuf; + cs_clear_tail(state); + drain_dac(state, file->f_flags & O_NONBLOCK); + /* stop DMA state machine and free DMA buffers/channels */ + down(&state->open_sem); + stop_dac(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + free_page((unsigned long)state->dmabuf.pbuf); + + /* we're covered by the open_sem */ + up(&state->open_sem); + state->card->states[state->virt] = NULL; + state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: cs_release_mixdev() powerdown DAC failure (0x%x)\n",tmp) ); + } + + /* Now turn off external AMP if needed */ + state->card->amplifier_ctrl(state->card, -1); + state->card->active_ctrl(state->card, -1); + + kfree(state); + } + } + + state = card->states[0]; + if(state) + { + if ( (state->open_mode & FMODE_READ) & (file->f_mode & FMODE_READ) ) + { + CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_READ\n") ); + dmabuf = &state->dmabuf; + down(&state->open_sem); + stop_adc(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + free_page((unsigned long)state->dmabuf.pbuf); + + /* we're covered by the open_sem */ + up(&state->open_sem); + state->card->states[state->virt] = NULL; + state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + if( (tmp = cs461x_powerdown(card, CS_POWER_ADC, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: cs_release_mixdev() powerdown ADC failure (0x%x)\n",tmp) ); + } + + /* Now turn off external AMP if needed */ + state->card->amplifier_ctrl(state->card, -1); + state->card->active_ctrl(state->card, -1); + + kfree(state); + } + } + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk("cs46xx: cs_release()- 0\n") ); + MOD_DEC_USE_COUNT; /* For 2.2 */ + return 0; +} + +static void printpm(struct cs_card *s) +{ + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", + (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); + CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", + s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", + s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", + s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", + s->pm.u32SSCR,s->pm.u32SRCSA)); + CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", + s->pm.u32DacASR,s->pm.u32AdcASR)); + CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", + s->pm.u32DacSR,s->pm.u32AdcSR)); + CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", + s->pm.u32MIDCR_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_powerdown: 0x%x _general_purpose 0x%x\n", + s->pm.u32AC97_powerdown,s->pm.u32AC97_general_purpose)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume: 0x%x\n", + s->pm.u32AC97_master_volume)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_headphone_volume: 0x%x\n", + s->pm.u32AC97_headphone_volume)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_master_volume_mono: 0x%x\n", + s->pm.u32AC97_master_volume_mono)); + CS_DBGOUT(CS_PM, 9, printk("u32AC97_pcm_out_volume: 0x%x\n", + s->pm.u32AC97_pcm_out_volume)); + CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_play: 0x%x dmabuf_count_play: %d\n", + s->pm.dmabuf_swptr_play,s->pm.dmabuf_count_play)); + CS_DBGOUT(CS_PM, 9, printk("dmabuf_swptr_capture: 0x%x dmabuf_count_capture: %d\n", + s->pm.dmabuf_swptr_capture,s->pm.dmabuf_count_capture)); + +} + +/**************************************************************************** +* +* Suspend - save the ac97 regs, mute the outputs and power down the part. +* +****************************************************************************/ +void cs46xx_ac97_suspend(struct cs_card *card) +{ + int Count,i; + struct ac97_codec *dev=card->ac97_codec[0]; + unsigned int tmp; + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()+\n")); + + if(card->states[1]) + { + stop_dac(card->states[1]); + resync_dma_ptrs(card->states[1]); + } + if(card->states[0]) + { + stop_adc(card->states[0]); + resync_dma_ptrs(card->states[0]); + } + + for(Count = 0x2, i=0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) + && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + card->pm.ac97[i] = cs_ac97_get(dev, BA0_AC97_RESET + Count); + } +/* +* Save the ac97 volume registers as well as the current powerdown state. +* Now, mute the all the outputs (master, headphone, and mono), as well +* as the PCM volume, in preparation for powering down the entire part. + card->pm.u32AC97_master_volume = (u32)cs_ac97_get( dev, + (u8)BA0_AC97_MASTER_VOLUME); + card->pm.u32AC97_headphone_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_HEADPHONE_VOLUME); + card->pm.u32AC97_master_volume_mono = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_MASTER_VOLUME_MONO); + card->pm.u32AC97_pcm_out_volume = (u32)cs_ac97_get(dev, + (u8)BA0_AC97_PCM_OUT_VOLUME); +*/ +/* +* mute the outputs +*/ + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000); + +/* +* save the registers that cause pops +*/ + card->pm.u32AC97_powerdown = (u32)cs_ac97_get(dev, (u8)AC97_POWER_CONTROL); + card->pm.u32AC97_general_purpose = (u32)cs_ac97_get(dev, (u8)BA0_AC97_GENERAL_PURPOSE); +/* +* And power down everything on the AC97 codec. +* well, for now, only power down the DAC/ADC and MIXER VREFON components. +* trouble with removing VREF. +*/ + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_TRUE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp) ); + } + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()-\n")); +} + +/**************************************************************************** +* +* Resume - power up the part and restore its registers.. +* +****************************************************************************/ +void cs46xx_ac97_resume(struct cs_card *card) +{ + int Count,i; + struct ac97_codec *dev=card->ac97_codec[0]; + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()+\n")); + +/* +* First, we restore the state of the general purpose register. This +* contains the mic select (mic1 or mic2) and if we restore this after +* we restore the mic volume/boost state and mic2 was selected at +* suspend time, we will end up with a brief period of time where mic1 +* is selected with the volume/boost settings for mic2, causing +* acoustic feedback. So we restore the general purpose register +* first, thereby getting the correct mic selected before we restore +* the mic volume/boost. +*/ + cs_ac97_set(dev, (u8)BA0_AC97_GENERAL_PURPOSE, + (u16)card->pm.u32AC97_general_purpose); +/* +* Now, while the outputs are still muted, restore the state of power +* on the AC97 part. +*/ + cs_ac97_set(dev, (u8)BA0_AC97_POWERDOWN, (u16)card->pm.u32AC97_powerdown); + mdelay(5 * cs_laptop_wait); +/* +* Restore just the first set of registers, from register number +* 0x02 to the register number that ulHighestRegToRestore specifies. +*/ + for( Count = 0x2, i=0; + (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) + && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs_ac97_set(dev, (u8)(BA0_AC97_RESET + Count), (u16)card->pm.ac97[i]); + } + + /* Check if we have to init the amplifier */ + if(card->amp_init) + card->amp_init(card); + + CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()-\n")); +} + + +static int cs46xx_restart_part(struct cs_card *card) +{ + struct dmabuf *dmabuf; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs46xx: cs46xx_restart_part()+\n")); + if(card->states[1]) + { + dmabuf = &card->states[1]->dmabuf; + dmabuf->ready = 0; + resync_dma_ptrs(card->states[1]); + cs_set_divisor(dmabuf); + if(__prog_dmabuf(card->states[1])) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, + printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() dac error\n")); + return -1; + } + cs_set_dac_rate(card->states[1], dmabuf->rate); + } + if(card->states[0]) + { + dmabuf = &card->states[0]->dmabuf; + dmabuf->ready = 0; + resync_dma_ptrs(card->states[0]); + cs_set_divisor(dmabuf); + if(__prog_dmabuf(card->states[0])) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, + printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() adc error\n")); + return -1; + } + cs_set_adc_rate(card->states[0], dmabuf->rate); + } + card->pm.flags |= CS46XX_PM_RESUMED; + if(card->states[0]) + start_adc(card->states[0]); + if(card->states[1]) + start_dac(card->states[1]); + + card->pm.flags |= CS46XX_PM_IDLE; + card->pm.flags &= ~(CS46XX_PM_SUSPENDING | CS46XX_PM_SUSPENDED + | CS46XX_PM_RESUMING | CS46XX_PM_RESUMED); + if(card->states[0]) + wake_up(&card->states[0]->dmabuf.wait); + if(card->states[1]) + wake_up(&card->states[1]->dmabuf.wait); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs46xx: cs46xx_restart_part()-\n")); + return 0; +} + + +static void cs461x_reset(struct cs_card *card); +static void cs461x_proc_stop(struct cs_card *card); +static int cs46xx_suspend(struct cs_card *card, u32 state) +{ + unsigned int tmp; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk("cs46xx: cs46xx_suspend()+ flags=0x%x s=0x%x\n", + (unsigned)card->pm.flags,(unsigned)card)); +/* +* check the current state, only suspend if IDLE +*/ + if(!(card->pm.flags & CS46XX_PM_IDLE)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs46xx: cs46xx_suspend() unable to suspend, not IDLE\n")); + return 1; + } + card->pm.flags &= ~CS46XX_PM_IDLE; + card->pm.flags |= CS46XX_PM_SUSPENDING; + + card->active_ctrl(card,1); + + tmp = cs461x_peek(card, BA1_PFIE); + tmp &= ~0x0000f03f; + tmp |= 0x00000010; + cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt disable */ + + tmp = cs461x_peek(card, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000011; + cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt disable */ + + /* + * Stop playback DMA. + */ + tmp = cs461x_peek(card, BA1_PCTL); + cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = cs461x_peek(card, BA1_CCTL); + cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); + + if(card->states[1]) + { + card->pm.dmabuf_swptr_play = card->states[1]->dmabuf.swptr; + card->pm.dmabuf_count_play = card->states[1]->dmabuf.count; + } + if(card->states[0]) + { + card->pm.dmabuf_swptr_capture = card->states[0]->dmabuf.swptr; + card->pm.dmabuf_count_capture = card->states[0]->dmabuf.count; + } + + cs46xx_ac97_suspend(card); + + /* + * Reset the processor. + */ + cs461x_reset(card); + + cs461x_proc_stop(card); + + /* + * Power down the DAC and ADC. For now leave the other areas on. + */ + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, 0x0300); + + /* + * Power down the PLL. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, 0); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; + cs461x_pokeBA0(card, BA0_CLKCR1, tmp); + + card->active_ctrl(card,-1); + + card->pm.flags &= ~CS46XX_PM_SUSPENDING; + card->pm.flags |= CS46XX_PM_SUSPENDED; + + printpm(card); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk("cs46xx: cs46xx_suspend()- flags=0x%x\n", + (unsigned)card->pm.flags)); + return 0; +} + +static int cs46xx_resume(struct cs_card *card) +{ + int i; + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs46xx: cs46xx_resume()+ flags=0x%x\n", + (unsigned)card->pm.flags)); + if(!(card->pm.flags & CS46XX_PM_SUSPENDED)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs46xx: cs46xx_resume() unable to resume, not SUSPENDED\n")); + return 1; + } + card->pm.flags |= CS46XX_PM_RESUMING; + card->pm.flags &= ~CS46XX_PM_SUSPENDED; + printpm(card); + card->active_ctrl(card, 1); + + for(i=0;i<5;i++) + { + if (cs_hardware_init(card) != 0) + { + CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( + "cs46xx: cs46xx_resume()- ERROR in cs_hardware_init()\n")); + mdelay(10 * cs_laptop_wait); + cs461x_reset(card); + continue; + } + break; + } + if(i>=4) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( + "cs46xx: cs46xx_resume()- cs_hardware_init() failed, retried %d times.\n",i)); + return 0; + } + + if(cs46xx_restart_part(card)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( + "cs46xx: cs46xx_resume(): cs46xx_restart_part() returned error\n")); + } + + card->active_ctrl(card, -1); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk("cs46xx: cs46xx_resume()- flags=0x%x\n", + (unsigned)card->pm.flags)); + return 0; +} + +static /*const*/ struct file_operations cs461x_fops = { + CS_OWNER CS_THIS_MODULE + llseek: no_llseek, + read: cs_read, + write: cs_write, + poll: cs_poll, + ioctl: cs_ioctl, + mmap: cs_mmap, + open: cs_open, + release: cs_release, +}; + +/* Write AC97 codec registers */ + + +static u16 cs_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct cs_card *card = dev->private_data; + int count,loopcnt; + unsigned int tmp; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + + cs461x_peekBA0(card, BA0_ACSDA); + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + cs461x_pokeBA0(card, BA0_ACCAD, reg); + cs461x_pokeBA0(card, BA0_ACCDA, 0); + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | + ACCTL_VFRM | ACCTL_ESYN | + ACCTL_RSTN); + + + /* + * Wait for the read to occur. + */ + if(!(card->pm.flags & CS46XX_PM_IDLE)) + loopcnt = 2000; + else + loopcnt = 500 * cs_laptop_wait; + loopcnt *= cs_laptop_wait; + for (count = 0; count < loopcnt; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10 * cs_laptop_wait); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)) + break; + } + + /* + * Make sure the read completed. + */ + if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: AC'97 read problem (ACCTL_DCV), reg = 0x%x returning 0xffff\n", reg)); + return 0xffff; + } + + /* + * Wait for the valid status bit to go active. + */ + + if(!(card->pm.flags & CS46XX_PM_IDLE)) + loopcnt = 2000; + else + loopcnt = 1000; + loopcnt *= cs_laptop_wait; + for (count = 0; count < loopcnt; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_VSTS) + break; + udelay(10 * cs_laptop_wait); + } + + /* + * Make sure we got valid status. + */ + if (!( (tmp=cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_WARNING + "cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x 0xffff \n", + reg, tmp)); + return 0xffff; + } + + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ + CS_DBGOUT(CS_FUNCTION, 9, printk(KERN_INFO + "cs46xx: cs_ac97_get() reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", + reg, cs461x_peekBA0(card, BA0_ACSDA), + cs461x_peekBA0(card, BA0_ACCAD))); + return(cs461x_peekBA0(card, BA0_ACSDA)); +} + +static void cs_ac97_set(struct ac97_codec *dev, u8 reg, u16 val) +{ + struct cs_card *card = dev->private_data; + int count; + int val2 = 0; + + if(reg == AC97_CD_VOL) + { + val2 = cs_ac97_get(dev, AC97_CD_VOL); + } + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + cs461x_pokeBA0(card, BA0_ACCAD, reg); + cs461x_pokeBA0(card, BA0_ACCDA, val); + cs461x_peekBA0(card, BA0_ACCTL); + cs461x_pokeBA0(card, BA0_ACCTL, 0 | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | + ACCTL_ESYN | ACCTL_RSTN); + for (count = 0; count < 1000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10 * cs_laptop_wait); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV)) + break; + } + /* + * Make sure the write completed. + */ + if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val)); + } + + /* + * Adjust power if the mixer is selected/deselected according + * to the CD. + * + * IF the CD is a valid input source (mixer or direct) AND + * the CD is not muted THEN power is needed + * + * We do two things. When record select changes the input to + * add/remove the CD we adjust the power count if the CD is + * unmuted. + * + * When the CD mute changes we adjust the power level if the + * CD was a valid input. + * + * We also check for CD volume != 0, as the CD mute isn't + * normally tweaked from userspace. + */ + + /* CD mute change ? */ + + if(reg==AC97_CD_VOL) + { + /* Mute bit change ? */ + if((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) + { + /* This is a hack but its cleaner than the alternatives. + Right now card->ac97_codec[0] might be NULL as we are + still doing codec setup. This does an early assignment + to avoid the problem if it occurs */ + + if(card->ac97_codec[0]==NULL) + card->ac97_codec[0]=dev; + + /* Mute on */ + if(val&0x8000 || val == 0x1f1f) + card->amplifier_ctrl(card, -1); + else /* Mute off power on */ + { + if(card->amp_init) + card->amp_init(card); + card->amplifier_ctrl(card, 1); + } + } + } +} + + +/* OSS /dev/mixer file operation methods */ + +static int cs_open_mixdev(struct inode *inode, struct file *file) +{ + int i=0; + unsigned int minor = minor(inode->i_rdev); + struct cs_card *card=NULL; + struct list_head *entry; + unsigned int tmp; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs46xx: cs_open_mixdev()+\n")); + + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + } + if (!card) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } + match: + if(!card->ac97_codec[i]) + return -ENODEV; + file->private_data = card->ac97_codec[i]; + + card->active_ctrl(card,1); + if(!CS_IN_USE(&card->mixer_use_cnt)) + { + if( (tmp = cs46xx_powerup(card, CS_POWER_MIXVON )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs_open_mixdev() powerup failure (0x%x)\n",tmp) ); + return -EIO; + } + } + card->amplifier_ctrl(card, 1); + CS_INC_USE_COUNT(&card->mixer_use_cnt); + MOD_INC_USE_COUNT; /* for 2.2 */ + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs46xx: cs_open_mixdev()- 0\n")); + return 0; +} + +static int cs_release_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct cs_card *card=NULL; + struct list_head *entry; + int i; + unsigned int tmp; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, + printk(KERN_INFO "cs46xx: cs_release_mixdev()+\n")); + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + } + if (!card) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } +match: + MOD_DEC_USE_COUNT; /* for 2.2 */ + if(!CS_DEC_AND_TEST(&card->mixer_use_cnt)) + { + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, + printk(KERN_INFO "cs46xx: cs_release_mixdev()- no powerdown, usecnt>0\n")); + card->active_ctrl(card, -1); + card->amplifier_ctrl(card, -1); + return 0; + } +/* +* ok, no outstanding mixer opens, so powerdown. +*/ + if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n",tmp) ); + card->active_ctrl(card, -1); + card->amplifier_ctrl(card, -1); + return -EIO; + } + card->active_ctrl(card, -1); + card->amplifier_ctrl(card, -1); + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, + printk(KERN_INFO "cs46xx: cs_release_mixdev()- 0\n")); + return 0; +} + +void __exit cs46xx_cleanup_module(void); +static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + struct cs_card *card=NULL; + struct list_head *entry; + +#if CSDEBUG_INTERFACE + int val; + + if( (cmd == SOUND_MIXER_CS_GETDBGMASK) || + (cmd == SOUND_MIXER_CS_SETDBGMASK) || + (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_APM)) + { + switch(cmd) + { + + case SOUND_MIXER_CS_GETDBGMASK: + return put_user(cs_debugmask, (unsigned long *)arg); + + case SOUND_MIXER_CS_GETDBGLEVEL: + return put_user(cs_debuglevel, (unsigned long *)arg); + + case SOUND_MIXER_CS_SETDBGMASK: + if (get_user(val, (unsigned long *)arg)) + return -EFAULT; + cs_debugmask = val; + return 0; + + case SOUND_MIXER_CS_SETDBGLEVEL: + if (get_user(val, (unsigned long *)arg)) + return -EFAULT; + cs_debuglevel = val; + return 0; + + case SOUND_MIXER_CS_APM: + if (get_user(val, (unsigned long *) arg)) + return -EFAULT; + if(val == CS_IOCTL_CMD_SUSPEND) + { + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + cs46xx_suspend(card, 0); + } + + } + else if(val == CS_IOCTL_CMD_RESUME) + { + list_for_each(entry, &cs46xx_devs) + { + card = list_entry(entry, struct cs_card, list); + cs46xx_resume(card); + } + } + else + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: mixer_ioctl(): invalid APM cmd (%d)\n", + val)); + } + return 0; + + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs46xx: mixer_ioctl(): ERROR unknown debug cmd\n") ); + return 0; + } + } +#endif + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations cs_mixer_fops = { + CS_OWNER CS_THIS_MODULE + llseek: no_llseek, + ioctl: cs_ioctl_mixdev, + open: cs_open_mixdev, + release: cs_release_mixdev, +}; + +/* AC97 codec initialisation. */ +static int __init cs_ac97_init(struct cs_card *card) +{ + int num_ac97 = 0; + int ready_2nd = 0; + struct ac97_codec *codec; + u16 eid; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init()+\n") ); + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + + codec->codec_read = cs_ac97_get; + codec->codec_write = cs_ac97_set; + + if (ac97_probe_codec(codec) == 0) + { + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init()- codec number %d not found\n", + num_ac97) ); + card->ac97_codec[num_ac97] = 0; + break; + } + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init() found codec %d\n",num_ac97) ); + + eid = cs_ac97_get(codec, AC97_EXTENDED_ID); + + if(eid==0xFFFFFF) + { + printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97); + kfree(codec); + break; + } + + card->ac97_features = eid; + + if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) { + printk(KERN_ERR "cs46xx: couldn't register mixer!\n"); + kfree(codec); + break; + } + card->ac97_codec[num_ac97] = codec; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init() ac97_codec[%d] set to 0x%x\n", + (unsigned int)num_ac97, + (unsigned int)codec)); + /* if there is no secondary codec at all, don't probe any more */ + if (!ready_2nd) + { + num_ac97 += 1; + break; + } + } + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_ac97_init()- %d\n", (unsigned int)num_ac97)); + return num_ac97; +} + +/* + * load the static image into the DSP + */ +#include "cs461x_image.h" +static void cs461x_download_image(struct cs_card *card) +{ + unsigned i, j, temp1, temp2, offset, count; + unsigned char *pBA1 = ioremap(card->ba1_addr, 0x40000); + for( i=0; i < CLEAR__COUNT; i++) + { + offset = ClrStat[i].BA1__DestByteOffset; + count = ClrStat[i].BA1__SourceSize; + for( temp1 = offset; temp1<(offset+count); temp1+=4 ); + writel(0, pBA1+temp1); + } + + for(i=0; iac97_codec[0], AC97_POWER_CONTROL); + CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO + "cs46xx: cs461x_powerdown() powerdown reg=0x%x\n",tmp)); +/* +* if powering down only the VREF, and not powering down the DAC/ADC, +* then do not power down the VREF, UNLESS both the DAC and ADC are not +* currently powered down. If powering down DAC and ADC, then +* it is possible to power down the VREF (ON). +*/ + if ( ((type & CS_POWER_MIXVON) && + (!(type & CS_POWER_ADC) || (!(type & CS_POWER_DAC))) ) + && + ((tmp & CS_AC97_POWER_CONTROL_ADC_ON) || + (tmp & CS_AC97_POWER_CONTROL_DAC_ON) ) ) + { + CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO + "cs46xx: cs461x_powerdown()- 0 unable to powerdown. tmp=0x%x\n",tmp)); + return 0; + } +/* +* for now, always keep power to the mixer block. +* not sure why it's a problem but it seems to be if we power off. +*/ + type &= ~CS_POWER_MIXVON; + type &= ~CS_POWER_MIXVOFF; + + /* + * Power down indicated areas. + */ + if(type & CS_POWER_MIXVOFF) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVOFF\n")); + /* + * Power down the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_MIXVOFF; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown MIXVOFF failed\n")); + return 1; + } + } + } + if(type & CS_POWER_MIXVON) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVON\n")); + /* + * Power down the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_MIXVON; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown MIXVON failed\n")); + return 1; + } + } + } + if(type & CS_POWER_ADC) + { + /* + * Power down the ADC on the AC97 card. + */ + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ ADC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_ADC_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_ADC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown ADC failed\n")); + return 1; + } + } + } + if(type & CS_POWER_DAC) + { + /* + * Power down the DAC on the AC97 card. + */ + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs461x_powerdown()+ DAC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (tmp & CS_AC97_POWER_CONTROL_DAC_ON) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp |= CS_AC97_POWER_CONTROL_DAC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON)) + break; + } + + /* + * Check the status.. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerdown DAC failed\n")); + return 1; + } + } + } + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if(muted) + cs_mute(card, CS_FALSE); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs46xx: cs461x_powerdown()- 0 tmp=0x%x\n",tmp)); + return 0; +} + +static int cs46xx_powerup(struct cs_card *card, unsigned int type) +{ + int count; + unsigned int tmp=0,muted=0; + + CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO + "cs46xx: cs46xx_powerup()+ type=0x%x\n",type)); + /* + * check for VREF and powerup if need to. + */ + if(type & CS_POWER_MIXVON) + type |= CS_POWER_MIXVOFF; + if(type & (CS_POWER_DAC | CS_POWER_ADC)) + type |= CS_POWER_MIXVON | CS_POWER_MIXVOFF; + + /* + * Power up indicated areas. + */ + if(type & CS_POWER_MIXVOFF) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVOFF\n")); + /* + * Power up the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_MIXVOFF; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVOFF_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup MIXVOFF failed\n")); + return 1; + } + } + } + if(type & CS_POWER_MIXVON) + { + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVON\n")); + /* + * Power up the MIXER (VREF ON) on the AC97 card. + */ + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_MIXVON; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_MIXVON_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup MIXVON failed\n")); + return 1; + } + } + } + if(type & CS_POWER_ADC) + { + /* + * Power up the ADC on the AC97 card. + */ + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ ADC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_ADC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_ADC_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup ADC failed\n")); + return 1; + } + } + } + if(type & CS_POWER_DAC) + { + /* + * Power up the DAC on the AC97 card. + */ + + CS_DBGOUT(CS_FUNCTION, 4, + printk(KERN_INFO "cs46xx: cs46xx_powerup()+ DAC\n")); + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON)) + { + if(!muted) + { + cs_mute(card, CS_TRUE); + muted=1; + } + tmp &= ~CS_AC97_POWER_CONTROL_DAC; + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + /* + * Now, we wait until we sample a ready state. + */ + for (count = 0; count < 32; count++) { + /* + * First, lets wait a short while to let things settle out a + * bit, and to prevent retrying the read too quickly. + */ + udelay(500); + + /* + * Read the current state of the power control register. + */ + if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON) + break; + } + + /* + * Check the status.. + */ + if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & + CS_AC97_POWER_CONTROL_DAC_ON)) + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING + "cs46xx: powerup DAC failed\n")); + return 1; + } + } + } + tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); + if(muted) + cs_mute(card, CS_FALSE); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs46xx: cs46xx_powerup()- 0 tmp=0x%x\n",tmp)); + return 0; +} + + +static void cs461x_proc_start(struct cs_card *card) +{ + int cnt; + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + cs461x_poke(card, BA1_FRMT, 0xadf); + /* + * Turn on the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + cs461x_poke(card, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); + /* + * Wait until the run at frame bit resets itself in the SP control + * register. + */ + for (cnt = 0; cnt < 25; cnt++) { + udelay(50); + if (!(cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR)) + break; + } + + if (cs461x_peek(card, BA1_SPCR) & SPCR_RUNFR) + printk(KERN_WARNING "cs46xx: SPCR_RUNFR never reset\n"); +} + +static void cs461x_proc_stop(struct cs_card *card) +{ + /* + * Turn off the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + cs461x_poke(card, BA1_SPCR, 0); +} + +static int cs_hardware_init(struct cs_card *card) +{ + unsigned long end_time; + unsigned int tmp,count; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_hardware_init()+\n") ); + /* + * First, blast the clock control register to zero so that the PLL starts + * out in a known state, and blast the master serial port control register + * to zero so that the serial ports also start out in a known state. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, 0); + cs461x_pokeBA0(card, BA0_SERMC1, 0); + + /* + * If we are in AC97 mode, then we must set the part to a host controlled + * AC-link. Otherwise, we won't be able to bring up the link. + */ + cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03); /* 1.03 card */ + /* cs461x_pokeBA0(card, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_2_0); */ /* 2.00 card */ + + /* + * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS461x that uses the ARST# line + * for a reset. + */ + cs461x_pokeBA0(card, BA0_ACCTL, 1); + udelay(50); + cs461x_pokeBA0(card, BA0_ACCTL, 0); + udelay(50); + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_RSTN); + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); + + /* + * Now wait for a short while to allow the AC97 part to start + * generating bit clock (so we don't try to start the PLL without an + * input clock). + */ + mdelay(5 * cs_laptop_wait); /* 1 should be enough ?? (and pigs might fly) */ + + /* + * Set the serial port timing configuration, so that + * the clock control circuit gets its clock from the correct place. + */ + cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97); + + /* + * The part seems to not be ready for a while after a resume. + * so, if we are resuming, then wait for 700 mils. Note that 600 mils + * is not enough for some platforms! tested on an IBM Thinkpads and + * reference cards. + */ + if(!(card->pm.flags & CS46XX_PM_IDLE)) + mdelay(initdelay); + /* + * Write the selected clock control setup to the hardware. Do not turn on + * SWCE yet (if requested), so that the devices clocked by the output of + * PLL are not clocked until the PLL is stable. + */ + cs461x_pokeBA0(card, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); + cs461x_pokeBA0(card, BA0_PLLM, 0x3a); + cs461x_pokeBA0(card, BA0_CLKCR2, CLKCR2_PDIVS_8); + + /* + * Power up the PLL. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, CLKCR1_PLLP); + + /* + * Wait until the PLL has stabilized. + */ + mdelay(5 * cs_laptop_wait); /* Again 1 should be enough ?? */ + + /* + * Turn on clocking of the core so that we can setup the serial ports. + */ + tmp = cs461x_peekBA0(card, BA0_CLKCR1) | CLKCR1_SWCE; + cs461x_pokeBA0(card, BA0_CLKCR1, tmp); + + /* + * Fill the serial port FIFOs with silence. + */ + cs461x_clear_serial_FIFOs(card,CS_TYPE_DAC | CS_TYPE_ADC); + + /* + * Set the serial port FIFO pointer to the first sample in the FIFO. + */ + /* cs461x_pokeBA0(card, BA0_SERBSP, 0); */ + + /* + * Write the serial port configuration to the part. The master + * enable bit is not set until all other values have been written. + */ + cs461x_pokeBA0(card, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); + cs461x_pokeBA0(card, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); + cs461x_pokeBA0(card, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); + + + mdelay(5 * cs_laptop_wait); /* Shouldnt be needed ?? */ + +/* +* If we are resuming under 2.2.x then we can not schedule a timeout. +* so, just spin the CPU. +*/ + if(card->pm.flags & CS46XX_PM_IDLE) + { + /* + * Wait for the card ready signal from the AC97 card. + */ + end_time = jiffies + 3 * (HZ >> 2); + do { + /* + * Read the AC97 status register to see if we've seen a CODEC READY + * signal from the AC97 card. + */ + if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY) + break; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1); + } while (time_before(jiffies, end_time)); + } + else + { + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25 * cs_laptop_wait); + + if (cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY) + break; + } + } + + /* + * Make sure CODEC is READY. + */ + if (!(cs461x_peekBA0(card, BA0_ACSTS) & ACSTS_CRDY)) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING + "cs46xx: create - never read card ready from AC'97\n")); + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING + "cs46xx: probably not a bug, try using the CS4232 driver,\n")); + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_WARNING + "cs46xx: or turn off any automatic Power Management support in the BIOS.\n")); + return -EIO; + } + + /* + * Assert the vaid frame signal so that we can start sending commands + * to the AC97 card. + */ + cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); + + if(card->pm.flags & CS46XX_PM_IDLE) + { + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the card is pumping ADC data across the AC-link. + */ + end_time = jiffies + 3 * (HZ >> 2); + do { + /* + * Read the input slot valid register and see if input slots 3 and + * 4 are valid yet. + */ + if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) + break; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1); + } while (time_before(jiffies, end_time)); + } + else + { + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25 * cs_laptop_wait); + + if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) + break; + } + } + /* + * Make sure input slots 3 and 4 are valid. If not, then return + * an error. + */ + if ((cs461x_peekBA0(card, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4)) { + printk(KERN_WARNING "cs46xx: create - never read ISV3 & ISV4 from AC'97\n"); + return -EIO; + } + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 card. + */ + cs461x_pokeBA0(card, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + /* tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; */ + /* cs461x_pokeBA0(card, BA0_CLKCR1, tmp); */ + + /* + * Reset the processor. + */ + cs461x_reset(card); + + /* + * Download the image to the processor. + */ + + cs461x_download_image(card); + + /* + * Stop playback DMA. + */ + tmp = cs461x_peek(card, BA1_PCTL); + card->pctl = tmp & 0xffff0000; + cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = cs461x_peek(card, BA1_CCTL); + card->cctl = tmp & 0x0000ffff; + cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); + + /* initialize AC97 codec and register /dev/mixer */ + if(card->pm.flags & CS46XX_PM_IDLE) + { + if (cs_ac97_init(card) <= 0) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs_ac97_init() failure\n") ); + return -EIO; + } + } + else + { + cs46xx_ac97_resume(card); + } + + cs461x_proc_start(card); + + /* + * Enable interrupts on the part. + */ + cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); + + tmp = cs461x_peek(card, BA1_PFIE); + tmp &= ~0x0000f03f; + cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt enable */ + + tmp = cs461x_peek(card, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000001; + cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt enable */ + + /* + * If IDLE then Power down the part. We will power components up + * when we need them. + */ + if(card->pm.flags & CS46XX_PM_IDLE) + { + if(!cs_powerdown) + { + if( (tmp = cs46xx_powerup(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs461x_powerup() failure (0x%x)\n",tmp) ); + return -EIO; + } + } + else + { + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_FALSE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); + return -EIO; + } + } + } + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO + "cs46xx: cs_hardware_init()- 0\n")); + return 0; +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ + +/* + * Card subid table + */ + +struct cs_card_type +{ + u16 vendor; + u16 id; + char *name; + void (*amp)(struct cs_card *, int); + void (*amp_init)(struct cs_card *); + void (*active)(struct cs_card *, int); +}; + +static struct cs_card_type cards[]={ + {0x1489, 0x7001, "Genius Soundmaker 128 value", amp_none, NULL, NULL}, + {0x5053, 0x3357, "Voyetra", amp_voyetra, NULL, NULL}, + {0x1071, 0x6003, "Mitac MI6020/21", amp_voyetra, NULL, NULL}, + {0x14AF, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0050, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0051, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0052, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0053, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + {0x1681, 0x0054, "Hercules Game Theatre XP", amp_hercules, NULL, NULL}, + /* Not sure if the 570 needs the clkrun hack */ + {PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL}, + {0, 0, "Card without SSID set", NULL, NULL, NULL }, + {0, 0, NULL, NULL, NULL} +}; + +MODULE_AUTHOR("Alan Cox , Jaroslav Kysela, "); +MODULE_DESCRIPTION("Crystal SoundFusion Audio Support"); +MODULE_LICENSE("GPL"); + + +static const char cs46xx_banner[] = KERN_INFO "Crystal 4280/46xx + AC97 Audio, version " CS46XX_MAJOR_VERSION "." CS46XX_MINOR_VERSION "." CS46XX_ARCH ", " __TIME__ " " __DATE__ "\n"; +static const char fndmsg[] = KERN_INFO "cs46xx: Found %d audio device(s).\n"; + +static int __devinit cs46xx_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pciid) +{ + struct pm_dev *pmdev; + int i,j; + u16 ss_card, ss_vendor; + struct cs_card *card; + dma_addr_t dma_mask; + struct cs_card_type *cp = &cards[0]; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, + printk(KERN_INFO "cs46xx: probe()+\n")); + + dma_mask = 0xffffffff; /* this enables playback and recording */ + if (pci_enable_device(pci_dev)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs46xx: pci_enable_device() failed\n")); + return -1; + } + if (!RSRCISMEMORYREGION(pci_dev, 0) || + !RSRCISMEMORYREGION(pci_dev, 1)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: probe()- Memory region not assigned\n")); + return -1; + } + if (pci_dev->irq == 0) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: probe() IRQ not assigned\n")); + return -1; + } + if (!pci_dma_supported(pci_dev, 0xffffffff)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs46xx: probe() architecture does not support 32bit PCI busmaster DMA\n")); + return -1; + } + pci_read_config_word(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); + pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &ss_card); + + if ((card = kmalloc(sizeof(struct cs_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "cs46xx: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + card->ba0_addr = RSRCADDRESS(pci_dev, 0); + card->ba1_addr = RSRCADDRESS(pci_dev, 1); + card->pci_dev = pci_dev; + card->irq = pci_dev->irq; + card->magic = CS_CARD_MAGIC; + spin_lock_init(&card->lock); + + pci_set_master(pci_dev); + + printk(cs46xx_banner); + printk(KERN_INFO "cs46xx: Card found at 0x%08lx and 0x%08lx, IRQ %d\n", + card->ba0_addr, card->ba1_addr, card->irq); + + card->alloc_pcm_channel = cs_alloc_pcm_channel; + card->alloc_rec_pcm_channel = cs_alloc_rec_pcm_channel; + card->free_pcm_channel = cs_free_pcm_channel; + card->amplifier_ctrl = amp_none; + card->active_ctrl = amp_none; + + while (cp->name) + { + if(cp->vendor == ss_vendor && cp->id == ss_card) + { + card->amplifier_ctrl = cp->amp; + if(cp->active) + card->active_ctrl = cp->active; + if(cp->amp_init) + card->amp_init = cp->amp_init; + break; + } + cp++; + } + if (cp->name==NULL) + { + printk(KERN_INFO "cs46xx: Unknown card (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", + ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); + } + else + { + printk(KERN_INFO "cs46xx: %s (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", + cp->name, ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); + } + + if (card->amplifier_ctrl==NULL) + { + card->amplifier_ctrl = amp_none; + card->active_ctrl = clkrun_hack; + } + + if (external_amp == 1) + { + printk(KERN_INFO "cs46xx: Crystal EAPD support forced on.\n"); + card->amplifier_ctrl = amp_voyetra; + } + + if (thinkpad == 1) + { + printk(KERN_INFO "cs46xx: Activating CLKRUN hack for Thinkpad.\n"); + card->active_ctrl = clkrun_hack; + } +/* +* The thinkpads don't work well without runtime updating on their kernel +* delay values (or any laptop with variable CPU speeds really). +* so, just to be safe set the init delay to 2100. Eliminates +* failures on T21 Thinkpads. remove this code when the udelay +* and mdelay kernel code is replaced by a pm timer, or the delays +* work well for battery and/or AC power both. +*/ + if(card->active_ctrl == clkrun_hack) + { + initdelay = 2100; + cs_laptop_wait = 5; + } + if((card->active_ctrl == clkrun_hack) && !(powerdown == 1)) + { +/* +* for some currently unknown reason, powering down the DAC and ADC component +* blocks on thinkpads causes some funky behavior... distoorrrtion and ac97 +* codec access problems. probably the serial clock becomes unsynced. +* added code to sync the chips back up, but only helped about 70% the time. +*/ + cs_powerdown = 0; + } + if(powerdown == 0) + cs_powerdown = 0; + card->active_ctrl(card, 1); + + /* claim our iospace and irq */ + + card->ba0 = ioremap_nocache(card->ba0_addr, CS461X_BA0_SIZE); + card->ba1.name.data0 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE); + card->ba1.name.data1 = ioremap_nocache(card->ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE); + card->ba1.name.pmem = ioremap_nocache(card->ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE); + card->ba1.name.reg = ioremap_nocache(card->ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE); + + CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO + "cs46xx: card=0x%x card->ba0=0x%.08x\n",(unsigned)card,(unsigned)card->ba0) ); + CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO + "cs46xx: card->ba1=0x%.08x 0x%.08x 0x%.08x 0x%.08x\n", + (unsigned)card->ba1.name.data0, + (unsigned)card->ba1.name.data1, + (unsigned)card->ba1.name.pmem, + (unsigned)card->ba1.name.reg) ); + + if(card->ba0 == 0 || card->ba1.name.data0 == 0 || + card->ba1.name.data1 == 0 || card->ba1.name.pmem == 0 || + card->ba1.name.reg == 0) + goto fail2; + + if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs46xx", card)) { + printk(KERN_ERR "cs46xx: unable to allocate irq %d\n", card->irq); + goto fail2; + } + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1)) < 0) { + printk(KERN_ERR "cs46xx: unable to register dsp\n"); + goto fail; + } + + /* register /dev/midi */ + if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0) + printk(KERN_ERR "cs46xx: unable to register midi\n"); + + card->pm.flags |= CS46XX_PM_IDLE; + for(i=0;i<5;i++) + { + if (cs_hardware_init(card) != 0) + { + CS_DBGOUT(CS_ERROR, 4, printk( + "cs46xx: ERROR in cs_hardware_init()... retrying\n")); + for (j = 0; j < NR_AC97; j++) + if (card->ac97_codec[j] != NULL) { + unregister_sound_mixer(card->ac97_codec[j]->dev_mixer); + kfree (card->ac97_codec[j]); + } + mdelay(10 * cs_laptop_wait); + continue; + } + break; + } + if(i>=4) + { + CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( + "cs46xx: cs46xx_probe()- cs_hardware_init() failed, retried %d times.\n",i)); + unregister_sound_dsp(card->dev_audio); + if(card->dev_midi) + unregister_sound_midi(card->dev_midi); + goto fail; + } + + init_waitqueue_head(&card->midi.open_wait); + init_MUTEX(&card->midi.open_sem); + init_waitqueue_head(&card->midi.iwait); + init_waitqueue_head(&card->midi.owait); + cs461x_pokeBA0(card, BA0_MIDCR, MIDCR_MRST); + cs461x_pokeBA0(card, BA0_MIDCR, 0); + + /* + * Check if we have to init the amplifier, but probably already done + * since the CD logic in the ac97 init code will turn on the ext amp. + */ + if(cp->amp_init) + cp->amp_init(card); + card->active_ctrl(card, -1); + + PCI_SET_DRIVER_DATA(pci_dev, card); + PCI_SET_DMA_MASK(pci_dev, dma_mask); + list_add(&card->list, &cs46xx_devs); + + pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), cs46xx_pm_callback); + if (pmdev) + { + CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO + "cs46xx: probe() pm_register() succeeded (0x%x).\n", + (unsigned)pmdev)); + pmdev->data = card; + } + else + { + CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 2, printk(KERN_INFO + "cs46xx: probe() pm_register() failed (0x%x).\n", + (unsigned)pmdev)); + card->pm.flags |= CS46XX_PM_NOT_REGISTERED; + } + + CS_DBGOUT(CS_PM, 9, printk(KERN_INFO "cs46xx: pm.flags=0x%x card=0x%x\n", + (unsigned)card->pm.flags,(unsigned)card)); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: probe()- device allocated successfully\n")); + return 0; + +fail: + free_irq(card->irq, card); +fail2: + if(card->ba0) + iounmap(card->ba0); + if(card->ba1.name.data0) + iounmap(card->ba1.name.data0); + if(card->ba1.name.data1) + iounmap(card->ba1.name.data1); + if(card->ba1.name.pmem) + iounmap(card->ba1.name.pmem); + if(card->ba1.name.reg) + iounmap(card->ba1.name.reg); + kfree(card); + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs46xx: probe()- no device allocated\n")); + return -ENODEV; +} // probe_cs46xx + +// --------------------------------------------------------------------- + +static void __devinit cs46xx_remove(struct pci_dev *pci_dev) +{ + struct cs_card *card = PCI_GET_DRIVER_DATA(pci_dev); + int i; + unsigned int tmp; + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_remove()+\n")); + + card->active_ctrl(card,1); + + tmp = cs461x_peek(card, BA1_PFIE); + tmp &= ~0x0000f03f; + tmp |= 0x00000010; + cs461x_poke(card, BA1_PFIE, tmp); /* playback interrupt disable */ + + tmp = cs461x_peek(card, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000011; + cs461x_poke(card, BA1_CIE, tmp); /* capture interrupt disable */ + + /* + * Stop playback DMA. + */ + tmp = cs461x_peek(card, BA1_PCTL); + cs461x_poke(card, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = cs461x_peek(card, BA1_CCTL); + cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); + + /* + * Reset the processor. + */ + cs461x_reset(card); + + cs461x_proc_stop(card); + + /* + * Power down the DAC and ADC. We will power them up (if) when we need + * them. + */ + if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_TRUE )) ) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO + "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); + } + + /* + * Power down the PLL. + */ + cs461x_pokeBA0(card, BA0_CLKCR1, 0); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + tmp = cs461x_peekBA0(card, BA0_CLKCR1) & ~CLKCR1_SWCE; + cs461x_pokeBA0(card, BA0_CLKCR1, tmp); + + card->active_ctrl(card,-1); + + /* free hardware resources */ + free_irq(card->irq, card); + iounmap(card->ba0); + iounmap(card->ba1.name.data0); + iounmap(card->ba1.name.data1); + iounmap(card->ba1.name.pmem); + iounmap(card->ba1.name.reg); + + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree (card->ac97_codec[i]); + } + unregister_sound_dsp(card->dev_audio); + if(card->dev_midi) + unregister_sound_midi(card->dev_midi); + list_del(&card->list); + kfree(card); + PCI_SET_DRIVER_DATA(pci_dev,NULL); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_remove()-: remove successful\n")); +} + +enum { + CS46XX_4610 = 0, + CS46XX_4612, /* same as 4630 */ + CS46XX_4615, /* same as 4624 */ +}; + +static struct pci_device_id cs46xx_pci_tbl[] __devinitdata = { + + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4610}, + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4612}, + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_4615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CS46XX_4615}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cs46xx_pci_tbl); + +struct pci_driver cs46xx_pci_driver = { + name:"cs46xx", + id_table:cs46xx_pci_tbl, + probe:cs46xx_probe, + remove:cs46xx_remove, + suspend:CS46XX_SUSPEND_TBL, + resume:CS46XX_RESUME_TBL, +}; + +int __init cs46xx_init_module(void) +{ + int rtn = 0; + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_init_module()+ \n")); + if (!pci_present()) { /* No PCI bus in this machine! */ + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs46xx: cs46xx_init_module()- no pci bus found\n")); + return -ENODEV; + } + rtn = pci_module_init(&cs46xx_pci_driver); + + if(rtn == -ENODEV) + { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk( + "cs46xx: Unable to detect valid cs46xx device\n")); + } + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cs46xx_init_module()- (%d)\n",rtn)); + return rtn; +} + +void __exit cs46xx_cleanup_module(void) +{ + pci_unregister_driver(&cs46xx_pci_driver); + cs_pm_unregister_all(cs46xx_pm_callback); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cleanup_cs46xx() finished\n")); +} + +module_init(cs46xx_init_module); +module_exit(cs46xx_cleanup_module); + +int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct cs_card *card; + + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs46xx: cs46xx_pm_callback dev=0x%x rqst=0x%x card=%d\n", + (unsigned)dev,(unsigned)rqst,(unsigned)data)); + card = (struct cs_card *) dev->data; + if (card) { + switch(rqst) { + case PM_SUSPEND: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs46xx: PM suspend request\n")); + if(cs46xx_suspend(card, 0)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: PM suspend request refused\n")); + return 1; + } + break; + case PM_RESUME: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs46xx: PM resume request\n")); + if(cs46xx_resume(card)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs46xx: PM resume request refused\n")); + return 1; + } + break; + } + } + + return 0; +} + +#if CS46XX_ACPI_SUPPORT +static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state) +{ + struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev); + CS_DBGOUT(CS_PM | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cs46xx_suspend_tbl request\n")); + cs46xx_suspend(s, 0); + return 0; +} + +static int cs46xx_resume_tbl(struct pci_dev *pcidev) +{ + struct cs_card *s = PCI_GET_DRIVER_DATA(pcidev); + CS_DBGOUT(CS_PM | CS_FUNCTION, 2, + printk(KERN_INFO "cs46xx: cs46xx_resume_tbl request\n")); + cs46xx_resume(s); + return 0; +} +#endif diff -Nru linux/sound/oss/cs46xx_wrapper-24.h linux-2.4.19-pre5-mjc/sound/oss/cs46xx_wrapper-24.h --- linux/sound/oss/cs46xx_wrapper-24.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs46xx_wrapper-24.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,56 @@ +/******************************************************************************* +* +* "cs46xx_wrapper.c" -- Cirrus Logic-Crystal CS46XX linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (pcaudio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 01/11/2001 trw - new file from cs4281 wrapper code. +* +*******************************************************************************/ +#ifndef __CS46XX_WRAPPER24_H +#define __CS46XX_WRAPPER24_H + +#include + +#define CS_OWNER owner: +#define CS_THIS_MODULE THIS_MODULE, +void cs46xx_null(struct pci_dev *pcidev) { return; } +#define cs4x_mem_map_reserve(page) mem_map_reserve(page) +#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page) + +#define free_dmabuf(card, dmabuf) \ + pci_free_consistent((card)->pci_dev, \ + PAGE_SIZE << (dmabuf)->buforder, \ + (dmabuf)->rawbuf, (dmabuf)->dmaaddr); +#define free_dmabuf2(card, dmabuf) \ + pci_free_consistent((card)->pci_dev, \ + PAGE_SIZE << (dmabuf)->buforder_tmpbuff, \ + (dmabuf)->tmpbuff, (dmabuf)->dmaaddr_tmpbuff); +#define cs4x_pgoff(vma) ((vma)->vm_pgoff) + +#define RSRCISIOREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ + ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) +#define RSRCISMEMORYREGION(dev,num) ((dev)->resource[(num)].start != 0 && \ + ((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) +#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start) +#define PCI_GET_DRIVER_DATA pci_get_drvdata +#define PCI_SET_DRIVER_DATA pci_set_drvdata +#define PCI_SET_DMA_MASK(pcidev,mask) pcidev->dma_mask = mask + +#endif diff -Nru linux/sound/oss/cs46xxpm-24.h linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm-24.h --- linux/sound/oss/cs46xxpm-24.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm-24.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,53 @@ +/******************************************************************************* +* +* "cs46xxpm-24.h" -- Cirrus Logic-Crystal CS46XX linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (pcaudio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +#ifndef __CS46XXPM24_H +#define __CS46XXPM24_H + +#include +#include "cs46xxpm.h" + + +#define CS46XX_ACPI_SUPPORT 1 +#ifdef CS46XX_ACPI_SUPPORT +/* +* for now (12/22/00) only enable the pm_register PM support. +* allow these table entries to be null. +*/ +static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state); +static int cs46xx_resume_tbl(struct pci_dev *pcidev); +#define cs_pm_register(a, b, c) 0 +#define cs_pm_unregister_all(a) +#define CS46XX_SUSPEND_TBL cs46xx_suspend_tbl +#define CS46XX_RESUME_TBL cs46xx_resume_tbl +#else +#define cs_pm_register(a, b, c) pm_register((a), (b), (c)); +#define cs_pm_unregister_all(a) pm_unregister_all((a)); +#define CS46XX_SUSPEND_TBL cs46xx_null +#define CS46XX_RESUME_TBL cs46xx_null +#endif +int cs46xx_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); + +#endif diff -Nru linux/sound/oss/cs46xxpm.h linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm.h --- linux/sound/oss/cs46xxpm.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/cs46xxpm.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,70 @@ +/******************************************************************************* +* +* "cs46xxpm.h" -- Cirrus Logic-Crystal CS46XX linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (pcaudio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +#ifndef __CS46XXPM_H +#define __CS46XXPM_H + +#define CS46XX_AC97_HIGHESTREGTORESTORE 0x26 +#define CS46XX_AC97_NUMBER_RESTORE_REGS (CS46XX_AC97_HIGHESTREGTORESTORE/2-1) + +/* PM state defintions */ +#define CS46XX_PM_NOT_REGISTERED 0x1000 +#define CS46XX_PM_IDLE 0x0001 +#define CS46XX_PM_SUSPENDING 0x0002 +#define CS46XX_PM_SUSPENDED 0x0004 +#define CS46XX_PM_RESUMING 0x0008 +#define CS46XX_PM_RESUMED 0x0010 + +#define CS_POWER_DAC 0x0001 +#define CS_POWER_ADC 0x0002 +#define CS_POWER_MIXVON 0x0004 +#define CS_POWER_MIXVOFF 0x0008 +#define CS_AC97_POWER_CONTROL_ON 0xf000 /* always on bits (inverted) */ +#define CS_AC97_POWER_CONTROL_ADC 0x0100 +#define CS_AC97_POWER_CONTROL_DAC 0x0200 +#define CS_AC97_POWER_CONTROL_MIXVON 0x0400 +#define CS_AC97_POWER_CONTROL_MIXVOFF 0x0800 +#define CS_AC97_POWER_CONTROL_ADC_ON 0x0001 +#define CS_AC97_POWER_CONTROL_DAC_ON 0x0002 +#define CS_AC97_POWER_CONTROL_MIXVON_ON 0x0004 +#define CS_AC97_POWER_CONTROL_MIXVOFF_ON 0x0008 + +struct cs46xx_pm { + unsigned long flags; + u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; + u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; + u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; + u32 u32SSPM_BITS; + u32 ac97[CS46XX_AC97_NUMBER_RESTORE_REGS]; + u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; + u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; + u32 u32hwptr_playback,u32hwptr_capture; + unsigned dmabuf_swptr_play; + int dmabuf_count_play; + unsigned dmabuf_swptr_capture; + int dmabuf_count_capture; +}; + +#endif diff -Nru linux/sound/oss/dev_table.c linux-2.4.19-pre5-mjc/sound/oss/dev_table.c --- linux/sound/oss/dev_table.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dev_table.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,221 @@ +/* + * sound/dev_table.c + * + * Device call tables. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include + +#define _DEV_TABLE_C_ +#include "sound_config.h" + +int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, + int driver_size, int flags, unsigned int format_mask, + void *devc, int dma1, int dma2) +{ + struct audio_driver *d; + struct audio_operations *op; + int l, num; + + if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) { + printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name); + return -(EINVAL); + } + num = sound_alloc_audiodev(); + + if (num == -1) { + printk(KERN_ERR "sound: Too many audio drivers\n"); + return -(EBUSY); + } + d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver))); + + if (sound_nblocks < 1024) + sound_nblocks++; + + op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations))); + + if (sound_nblocks < 1024) + sound_nblocks++; + if (d == NULL || op == NULL) { + printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); + sound_unload_audiodev(num); + return -(ENOMEM); + } + memset((char *) op, 0, sizeof(struct audio_operations)); + init_waitqueue_head(&op->in_sleeper); + init_waitqueue_head(&op->out_sleeper); + init_waitqueue_head(&op->poll_sleeper); + if (driver_size < sizeof(struct audio_driver)) + memset((char *) d, 0, sizeof(struct audio_driver)); + + memcpy((char *) d, (char *) driver, driver_size); + + op->d = d; + l = strlen(name) + 1; + if (l > sizeof(op->name)) + l = sizeof(op->name); + strncpy(op->name, name, l); + op->name[l - 1] = 0; + op->flags = flags; + op->format_mask = format_mask; + op->devc = devc; + + /* + * Hardcoded defaults + */ + audio_devs[num] = op; + + DMAbuf_init(num, dma1, dma2); + + audio_init_devices(); + return num; +} + +int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, + int driver_size, void *devc) +{ + struct mixer_operations *op; + int l; + + int n = sound_alloc_mixerdev(); + + if (n == -1) { + printk(KERN_ERR "Sound: Too many mixer drivers\n"); + return -EBUSY; + } + if (vers != MIXER_DRIVER_VERSION || + driver_size > sizeof(struct mixer_operations)) { + printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name); + return -EINVAL; + } + + /* FIXME: This leaks a mixer_operations struct every time its called + until you unload sound! */ + + op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations))); + + if (sound_nblocks < 1024) + sound_nblocks++; + if (op == NULL) { + printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name); + return -ENOMEM; + } + memset((char *) op, 0, sizeof(struct mixer_operations)); + memcpy((char *) op, (char *) driver, driver_size); + + l = strlen(name) + 1; + if (l > sizeof(op->name)) + l = sizeof(op->name); + strncpy(op->name, name, l); + op->name[l - 1] = 0; + op->devc = devc; + + mixer_devs[n] = op; + return n; +} + +void sound_unload_audiodev(int dev) +{ + if (dev != -1) { + DMAbuf_deinit(dev); + audio_devs[dev] = NULL; + unregister_sound_dsp((dev<<4)+3); + } +} + +int sound_alloc_audiodev(void) +{ + int i = register_sound_dsp(&oss_sound_fops, -1); + if(i==-1) + return i; + i>>=4; + if(i>=num_audiodevs) + num_audiodevs = i + 1; + return i; +} + +int sound_alloc_mididev(void) +{ + int i = register_sound_midi(&oss_sound_fops, -1); + if(i==-1) + return i; + i>>=4; + if(i>=num_midis) + num_midis = i + 1; + return i; +} + +int sound_alloc_synthdev(void) +{ + int i; + + for (i = 0; i < MAX_SYNTH_DEV; i++) { + if (synth_devs[i] == NULL) { + if (i >= num_synths) + num_synths++; + return i; + } + } + return -1; +} + +int sound_alloc_mixerdev(void) +{ + int i = register_sound_mixer(&oss_sound_fops, -1); + if(i==-1) + return -1; + i>>=4; + if(i>=num_mixers) + num_mixers = i + 1; + return i; +} + +int sound_alloc_timerdev(void) +{ + int i; + + for (i = 0; i < MAX_TIMER_DEV; i++) { + if (sound_timer_devs[i] == NULL) { + if (i >= num_sound_timers) + num_sound_timers++; + return i; + } + } + return -1; +} + +void sound_unload_mixerdev(int dev) +{ + if (dev != -1) { + mixer_devs[dev] = NULL; + unregister_sound_mixer(dev<<4); + num_mixers--; + } +} + +void sound_unload_mididev(int dev) +{ + if (dev != -1) { + midi_devs[dev] = NULL; + unregister_sound_midi((dev<<4)+2); + } +} + +void sound_unload_synthdev(int dev) +{ + if (dev != -1) + synth_devs[dev] = NULL; +} + +void sound_unload_timerdev(int dev) +{ + if (dev != -1) + sound_timer_devs[dev] = NULL; +} diff -Nru linux/sound/oss/dev_table.h linux-2.4.19-pre5-mjc/sound/oss/dev_table.h --- linux/sound/oss/dev_table.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dev_table.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,403 @@ +/* + * dev_table.h + * + * Global definitions for device call tables + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + + +#ifndef _DEV_TABLE_H_ +#define _DEV_TABLE_H_ + +/* + * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) + * Numbers 1000 to N are reserved for driver's internal use. + */ + +#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */ +#define SNDCARD_VIDC 28 /* ARMs VIDC */ +#define SNDCARD_SBPNP 29 +#define SNDCARD_SOFTOSS 36 +#define SNDCARD_VMIDI 37 +#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */ +#define SNDCARD_OPL3SA1_SB 39 +#define SNDCARD_OPL3SA1_MPU 40 +#define SNDCARD_WAVEFRONT 41 +#define SNDCARD_OPL3SA2 42 +#define SNDCARD_OPL3SA2_MPU 43 +#define SNDCARD_WAVEARTIST 44 /* Waveartist */ +#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */ +#define SNDCARD_AD1816 88 + +/* + * NOTE! NOTE! NOTE! NOTE! + * + * If you modify this file, please check the dev_table.c also. + * + * NOTE! NOTE! NOTE! NOTE! + */ + +struct driver_info +{ + char *driver_id; + int card_subtype; /* Driver specific. Usually 0 */ + int card_type; /* From soundcard.h */ + char *name; + void (*attach) (struct address_info *hw_config); + int (*probe) (struct address_info *hw_config); + void (*unload) (struct address_info *hw_config); +}; + +struct card_info +{ + int card_type; /* Link (search key) to the driver list */ + struct address_info config; + int enabled; + void *for_driver_use; +}; + + +/* + * Device specific parameters (used only by dmabuf.c) + */ +#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR) + +#define DMODE_NONE 0 +#define DMODE_OUTPUT PCM_ENABLE_OUTPUT +#define DMODE_INPUT PCM_ENABLE_INPUT + +struct dma_buffparms +{ + int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ + int closing; + + /* + * Pointers to raw buffers + */ + + char *raw_buf; + unsigned long raw_buf_phys; + int buffsize; + + /* + * Device state tables + */ + + unsigned long flags; +#define DMA_BUSY 0x00000001 +#define DMA_RESTART 0x00000002 +#define DMA_ACTIVE 0x00000004 +#define DMA_STARTED 0x00000008 +#define DMA_EMPTY 0x00000010 +#define DMA_ALLOC_DONE 0x00000020 +#define DMA_SYNCING 0x00000040 +#define DMA_DIRTY 0x00000080 +#define DMA_POST 0x00000100 +#define DMA_NODMA 0x00000200 +#define DMA_NOTIMEOUT 0x00000400 + + int open_mode; + + /* + * Queue parameters. + */ + int qlen; + int qhead; + int qtail; + int cfrag; /* Current incomplete fragment (write) */ + + int nbufs; + int counts[MAX_SUB_BUFFERS]; + int subdivision; + + int fragment_size; + int needs_reorg; + int max_fragments; + + int bytes_in_use; + + int underrun_count; + unsigned long byte_counter; + unsigned long user_counter; + unsigned long max_byte_counter; + int data_rate; /* Bytes/second */ + + int mapping_flags; +#define DMA_MAP_MAPPED 0x00000001 + char neutral_byte; + int dma; /* DMA channel */ + + int applic_profile; /* Application profile (APF_*) */ + /* Interrupt callback stuff */ + void (*audio_callback) (int dev, int parm); + int callback_parm; + + int buf_flags[MAX_SUB_BUFFERS]; +#define BUFF_EOF 0x00000001 /* Increment eof count */ +#define BUFF_DIRTY 0x00000002 /* Buffer written */ +}; + +/* + * Structure for use with various microcontrollers and DSP processors + * in the recent sound cards. + */ +typedef struct coproc_operations +{ + char name[64]; + struct module *owner; + int (*open) (void *devc, int sub_device); + void (*close) (void *devc, int sub_device); + int (*ioctl) (void *devc, unsigned int cmd, caddr_t arg, int local); + void (*reset) (void *devc); + + void *devc; /* Driver specific info */ +} coproc_operations; + +struct audio_driver +{ + struct module *owner; + int (*open) (int dev, int mode); + void (*close) (int dev); + void (*output_block) (int dev, unsigned long buf, + int count, int intrflag); + void (*start_input) (int dev, unsigned long buf, + int count, int intrflag); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + int (*prepare_for_input) (int dev, int bufsize, int nbufs); + int (*prepare_for_output) (int dev, int bufsize, int nbufs); + void (*halt_io) (int dev); + int (*local_qlen)(int dev); + void (*copy_user) (int dev, + char *localbuf, int localoffs, + const char *userbuf, int useroffs, + int max_in, int max_out, + int *used, int *returned, + int len); + void (*halt_input) (int dev); + void (*halt_output) (int dev); + void (*trigger) (int dev, int bits); + int (*set_speed)(int dev, int speed); + unsigned int (*set_bits)(int dev, unsigned int bits); + short (*set_channels)(int dev, short channels); + void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */ + void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */ + void (*mmap)(int dev); +}; + +struct audio_operations +{ + char name[128]; + int flags; +#define NOTHING_SPECIAL 0x00 +#define NEEDS_RESTART 0x01 +#define DMA_AUTOMODE 0x02 +#define DMA_DUPLEX 0x04 +#define DMA_PSEUDO_AUTOMODE 0x08 +#define DMA_HARDSTOP 0x10 +#define DMA_EXACT 0x40 +#define DMA_NORESET 0x80 + int format_mask; /* Bitmask for supported audio formats */ + void *devc; /* Driver specific info */ + struct audio_driver *d; + void *portc; /* Driver spesific info */ + struct dma_buffparms *dmap_in, *dmap_out; + struct coproc_operations *coproc; + int mixer_dev; + int enable_bits; + int open_mode; + int go; + int min_fragment; /* 0 == unlimited */ + int max_fragment; /* 0 == unlimited */ + int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */ + + /* fields formerly in dmabuf.c */ + wait_queue_head_t in_sleeper; + wait_queue_head_t out_sleeper; + wait_queue_head_t poll_sleeper; + + /* fields formerly in audio.c */ + int audio_mode; + +#define AM_NONE 0 +#define AM_WRITE OPEN_WRITE +#define AM_READ OPEN_READ + + int local_format; + int audio_format; + int local_conversion; +#define CNV_MU_LAW 0x00000001 + + /* large structures at the end to keep offsets small */ + struct dma_buffparms dmaps[2]; +}; + +int *load_mixer_volumes(char *name, int *levels, int present); + +struct mixer_operations +{ + struct module *owner; + char id[16]; + char name[64]; + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + + void *devc; + int modify_counter; +}; + +struct synth_operations +{ + struct module *owner; + char *id; /* Unique identifier (ASCII) max 29 char */ + struct synth_info *info; + int midi_dev; + int synth_type; + int synth_subtype; + + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + int (*kill_note) (int dev, int voice, int note, int velocity); + int (*start_note) (int dev, int voice, int note, int velocity); + int (*set_instr) (int dev, int voice, int instr); + void (*reset) (int dev); + void (*hw_control) (int dev, unsigned char *event); + int (*load_patch) (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag); + void (*aftertouch) (int dev, int voice, int pressure); + void (*controller) (int dev, int voice, int ctrl_num, int value); + void (*panning) (int dev, int voice, int value); + void (*volume_method) (int dev, int mode); + void (*bender) (int dev, int chn, int value); + int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc); + void (*setup_voice) (int dev, int voice, int chn); + int (*send_sysex)(int dev, unsigned char *bytes, int len); + + struct voice_alloc_info alloc; + struct channel_info chn_info[16]; + int emulation; +#define EMU_GM 1 /* General MIDI */ +#define EMU_XG 2 /* Yamaha XG */ +#define MAX_SYSEX_BUF 64 + unsigned char sysex_buf[MAX_SYSEX_BUF]; + int sysex_ptr; +}; + +struct midi_input_info +{ + /* MIDI input scanner variables */ +#define MI_MAX 10 + int m_busy; + unsigned char m_buf[MI_MAX]; + unsigned char m_prev_status; /* For running status */ + int m_ptr; +#define MST_INIT 0 +#define MST_DATA 1 +#define MST_SYSEX 2 + int m_state; + int m_left; +}; + +struct midi_operations +{ + struct module *owner; + struct midi_info info; + struct synth_operations *converter; + struct midi_input_info in_info; + int (*open) (int dev, int mode, + void (*inputintr)(int dev, unsigned char data), + void (*outputintr)(int dev) + ); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + int (*outputc) (int dev, unsigned char data); + int (*start_read) (int dev); + int (*end_read) (int dev); + void (*kick)(int dev); + int (*command) (int dev, unsigned char *data); + int (*buffer_status) (int dev); + int (*prefix_cmd) (int dev, unsigned char status); + struct coproc_operations *coproc; + void *devc; +}; + +struct sound_lowlev_timer +{ + int dev; + int priority; + unsigned int (*tmr_start)(int dev, unsigned int usecs); + void (*tmr_disable)(int dev); + void (*tmr_restart)(int dev); +}; + +struct sound_timer_operations +{ + struct module *owner; + struct sound_timer_info info; + int priority; + int devlink; + int (*open)(int dev, int mode); + void (*close)(int dev); + int (*event)(int dev, unsigned char *ev); + unsigned long (*get_time)(int dev); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); + void (*arm_timer)(int dev, long time); +}; + +#ifdef _DEV_TABLE_C_ +struct audio_operations *audio_devs[MAX_AUDIO_DEV]; +int num_audiodevs; +struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; +int num_mixers; +struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; +int num_synths; +struct midi_operations *midi_devs[MAX_MIDI_DEV]; +int num_midis; + +extern struct sound_timer_operations default_sound_timer; +struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { + &default_sound_timer, NULL +}; +int num_sound_timers = 1; +#else +extern struct audio_operations *audio_devs[MAX_AUDIO_DEV]; +extern int num_audiodevs; +extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; +extern int num_mixers; +extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; +extern int num_synths; +extern struct midi_operations *midi_devs[MAX_MIDI_DEV]; +extern int num_midis; +extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; +extern int num_sound_timers; +#endif /* _DEV_TABLE_C_ */ + +extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); +void sound_timer_init (struct sound_lowlev_timer *t, char *name); +void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan); + +#define AUDIO_DRIVER_VERSION 2 +#define MIXER_DRIVER_VERSION 2 +int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, + int driver_size, int flags, unsigned int format_mask, + void *devc, int dma1, int dma2); +int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, + int driver_size, void *devc); + +void sound_unload_audiodev(int dev); +void sound_unload_mixerdev(int dev); +void sound_unload_mididev(int dev); +void sound_unload_synthdev(int dev); +void sound_unload_timerdev(int dev); +int sound_alloc_audiodev(void); +int sound_alloc_mixerdev(void); +int sound_alloc_timerdev(void); +int sound_alloc_synthdev(void); +int sound_alloc_mididev(void); +#endif /* _DEV_TABLE_H_ */ + diff -Nru linux/sound/oss/dm.h linux-2.4.19-pre5-mjc/sound/oss/dm.h --- linux/sound/oss/dm.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dm.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,79 @@ +#ifndef _DRIVERS_SOUND_DM_H +#define _DRIVERS_SOUND_DM_H + +/* + * Definitions of the 'direct midi sound' interface used + * by the newer commercial OSS package. We should export + * this to userland somewhere in glibc later. + */ + +/* + * Data structure composing an FM "note" or sound event. + */ + +struct dm_fm_voice +{ + u8 op; + u8 voice; + u8 am; + u8 vibrato; + u8 do_sustain; + u8 kbd_scale; + u8 harmonic; + u8 scale_level; + u8 volume; + u8 attack; + u8 decay; + u8 sustain; + u8 release; + u8 feedback; + u8 connection; + u8 left; + u8 right; + u8 waveform; +}; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +struct dm_fm_note +{ + u8 voice; + u8 octave; + u32 fnum; + u8 key_on; +}; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +struct dm_fm_params +{ + u8 am_depth; + u8 vib_depth; + u8 kbd_split; + u8 rhythm; + + /* This block is the percussion instrument data */ + u8 bass; + u8 snare; + u8 tomtom; + u8 cymbal; + u8 hihat; +}; + +/* + * FM mode ioctl settings + */ + +#define FM_IOCTL_RESET 0x20 +#define FM_IOCTL_PLAY_NOTE 0x21 +#define FM_IOCTL_SET_VOICE 0x22 +#define FM_IOCTL_SET_PARAMS 0x23 +#define FM_IOCTL_SET_MODE 0x24 +#define FM_IOCTL_SET_OPL 0x25 + +#endif diff -Nru linux/sound/oss/dmabuf.c linux-2.4.19-pre5-mjc/sound/oss/dmabuf.c --- linux/sound/oss/dmabuf.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmabuf.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1296 @@ +/* + * sound/dmabuf.c + * + * The DMA buffer manager for digitized voice applications + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Thomas Sailer : moved several static variables into struct audio_operations + * (which is grossly misnamed btw.) because they have the same + * lifetime as the rest in there and dynamic allocation saves + * 12k or so + * Thomas Sailer : remove {in,out}_sleep_flag. It was used for the sleeper to + * determine if it was woken up by the expiring timeout or by + * an explicit wake_up. The return value from schedule_timeout + * can be used instead; if 0, the wakeup was due to the timeout. + * + * Rob Riggs Added persistent DMA buffers (1998/10/17) + */ + +#define BE_CONSERVATIVE +#define SAMPLE_ROUNDUP 0 + +#include "sound_config.h" +#include + +#define DMAP_FREE_ON_CLOSE 0 +#define DMAP_KEEP_ON_CLOSE 1 +extern int sound_dmap_flag; + +static void dma_reset_output(int dev); +static void dma_reset_input(int dev); +static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode); + + + +static int debugmem = 0; /* switched off by default */ +static int dma_buffsize = DSP_BUFFSIZE; + +static long dmabuf_timeout(struct dma_buffparms *dmap) +{ + long tmout; + + tmout = (dmap->fragment_size * HZ) / dmap->data_rate; + tmout += HZ / 5; /* Some safety distance */ + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; + return tmout; +} + +static int sound_alloc_dmap(struct dma_buffparms *dmap) +{ + char *start_addr, *end_addr; + int dma_pagesize; + int sz, size; + struct page *page; + + dmap->mapping_flags &= ~DMA_MAP_MAPPED; + + if (dmap->raw_buf != NULL) + return 0; /* Already done */ + if (dma_buffsize < 4096) + dma_buffsize = 4096; + dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024); + + /* + * Now check for the Cyrix problem. + */ + + if(isa_dma_bridge_buggy==2) + dma_pagesize=32768; + + dmap->raw_buf = NULL; + dmap->buffsize = dma_buffsize; + if (dmap->buffsize > dma_pagesize) + dmap->buffsize = dma_pagesize; + start_addr = NULL; + /* + * Now loop until we get a free buffer. Try to get smaller buffer if + * it fails. Don't accept smaller than 8k buffer for performance + * reasons. + */ + while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) { + for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); + dmap->buffsize = PAGE_SIZE * (1 << sz); + start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); + if (start_addr == NULL) + dmap->buffsize /= 2; + } + + if (start_addr == NULL) { + printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n"); + return -ENOMEM; + } else { + /* make some checks */ + end_addr = start_addr + dmap->buffsize - 1; + + if (debugmem) + printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr); + + /* now check if it fits into the same dma-pagesize */ + + if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) + || end_addr >= (char *) (MAX_DMA_ADDRESS)) { + printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize); + return -EFAULT; + } + } + dmap->raw_buf = start_addr; + dmap->raw_buf_phys = virt_to_bus(start_addr); + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_reserve(page); + return 0; +} + +static void sound_free_dmap(struct dma_buffparms *dmap) +{ + int sz, size; + struct page *page; + unsigned long start_addr, end_addr; + + if (dmap->raw_buf == NULL) + return; + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return; /* Don't free mmapped buffer. Will use it next time */ + for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); + + start_addr = (unsigned long) dmap->raw_buf; + end_addr = start_addr + dmap->buffsize; + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_unreserve(page); + + free_pages((unsigned long) dmap->raw_buf, sz); + dmap->raw_buf = NULL; +} + + +/* Intel version !!!!!!!!! */ + +static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode) +{ + unsigned long flags; + int chan = dmap->dma; + + /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, dma_mode); + set_dma_addr(chan, physaddr); + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); + + return 0; +} + +static void dma_init_buffers(struct dma_buffparms *dmap) +{ + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->bytes_in_use = dmap->buffsize; + + dmap->dma_mode = DMODE_NONE; + dmap->mapping_flags = 0; + dmap->neutral_byte = 0x80; + dmap->data_rate = 8000; + dmap->cfrag = -1; + dmap->closing = 0; + dmap->nbufs = 1; + dmap->flags = DMA_BUSY; /* Other flags off */ +} + +static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap) +{ + int err; + + if (dmap->flags & DMA_BUSY) + return -EBUSY; + if ((err = sound_alloc_dmap(dmap)) < 0) + return err; + + if (dmap->raw_buf == NULL) { + printk(KERN_WARNING "Sound: DMA buffers not available\n"); + return -ENOSPC; /* Memory allocation failed during boot */ + } + if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) { + printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma); + return -EBUSY; + } + dma_init_buffers(dmap); + dmap->open_mode = mode; + dmap->subdivision = dmap->underrun_count = 0; + dmap->fragment_size = 0; + dmap->max_fragments = 65536; /* Just a large value */ + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->applic_profile = APF_NORMAL; + dmap->needs_reorg = 1; + dmap->audio_callback = NULL; + dmap->callback_parm = 0; + return 0; +} + +static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap) +{ + unsigned long flags; + + if (dmap->dma >= 0) { + sound_close_dma(dmap->dma); + flags=claim_dma_lock(); + disable_dma(dmap->dma); + release_dma_lock(flags); + } + if (dmap->flags & DMA_BUSY) + dmap->dma_mode = DMODE_NONE; + dmap->flags &= ~DMA_BUSY; + + if (sound_dmap_flag == DMAP_FREE_ON_CLOSE) + sound_free_dmap(dmap); +} + + +static unsigned int default_set_bits(int dev, unsigned int bits) +{ + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t)&bits); + set_fs(fs); + return bits; +} + +static int default_set_speed(int dev, int speed) +{ + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t)&speed); + set_fs(fs); + return speed; +} + +static short default_set_channels(int dev, short channels) +{ + int c = channels; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t)&c); + set_fs(fs); + return c; +} + +static void check_driver(struct audio_driver *d) +{ + if (d->set_speed == NULL) + d->set_speed = default_set_speed; + if (d->set_bits == NULL) + d->set_bits = default_set_bits; + if (d->set_channels == NULL) + d->set_channels = default_set_channels; +} + +int DMAbuf_open(int dev, int mode) +{ + struct audio_operations *adev = audio_devs[dev]; + int retval; + struct dma_buffparms *dmap_in = NULL; + struct dma_buffparms *dmap_out = NULL; + + if (!adev) + return -ENXIO; + if (!(adev->flags & DMA_DUPLEX)) + adev->dmap_in = adev->dmap_out; + check_driver(adev->d); + + if ((retval = adev->d->open(dev, mode)) < 0) + return retval; + dmap_out = adev->dmap_out; + dmap_in = adev->dmap_in; + if (dmap_in == dmap_out) + adev->flags &= ~DMA_DUPLEX; + + if (mode & OPEN_WRITE) { + if ((retval = open_dmap(adev, mode, dmap_out)) < 0) { + adev->d->close(dev); + return retval; + } + } + adev->enable_bits = mode; + + if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) { + if ((retval = open_dmap(adev, mode, dmap_in)) < 0) { + adev->d->close(dev); + if (mode & OPEN_WRITE) + close_dmap(adev, dmap_out); + return retval; + } + } + adev->open_mode = mode; + adev->go = 1; + + adev->d->set_bits(dev, 8); + adev->d->set_channels(dev, 1); + adev->d->set_speed(dev, DSP_DEFAULT_SPEED); + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, + adev->dmap_out->bytes_in_use); + return 0; +} + +void DMAbuf_reset(int dev) +{ + if (audio_devs[dev]->open_mode & OPEN_WRITE) + dma_reset_output(dev); + + if (audio_devs[dev]->open_mode & OPEN_READ) + dma_reset_input(dev); +} + +static void dma_reset_output(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags,f ; + struct dma_buffparms *dmap = adev->dmap_out; + + if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ + return; + + /* + * First wait until the current fragment has been played completely + */ + save_flags(flags); + cli(); + adev->dmap_out->flags |= DMA_SYNCING; + + adev->dmap_out->underrun_count = 0; + if (!signal_pending(current) && adev->dmap_out->qlen && + adev->dmap_out->underrun_count == 0) + interruptible_sleep_on_timeout(&adev->out_sleeper, + dmabuf_timeout(dmap)); + adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); + + /* + * Finally shut the device off + */ + if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output) + adev->d->halt_io(dev); + else + adev->d->halt_output(dev); + adev->dmap_out->flags &= ~DMA_STARTED; + + f=claim_dma_lock(); + clear_dma_ff(dmap->dma); + disable_dma(dmap->dma); + release_dma_lock(f); + + restore_flags(flags); + dmap->byte_counter = 0; + reorganize_buffers(dev, adev->dmap_out, 0); + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; +} + +static void dma_reset_input(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_in; + + save_flags(flags); + cli(); + if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input) + adev->d->halt_io(dev); + else + adev->d->halt_input(dev); + adev->dmap_in->flags &= ~DMA_STARTED; + restore_flags(flags); + + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + reorganize_buffers(dev, adev->dmap_in, 1); +} + +void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap) +{ + struct audio_operations *adev = audio_devs[dev]; + + if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT)) + return; /* Don't start DMA yet */ + dmap->dma_mode = DMODE_OUTPUT; + + if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { + if (!(dmap->flags & DMA_STARTED)) { + reorganize_buffers(dev, dmap, 0); + if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs)) + return; + if (!(dmap->flags & DMA_NODMA)) + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE); + dmap->flags |= DMA_STARTED; + } + if (dmap->counts[dmap->qhead] == 0) + dmap->counts[dmap->qhead] = dmap->fragment_size; + dmap->dma_mode = DMODE_OUTPUT; + adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; +} + +int DMAbuf_sync(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int n = 0; + struct dma_buffparms *dmap; + + if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT)) + return 0; + + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) { + dmap = adev->dmap_out; + save_flags(flags); + cli(); + if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE)) + DMAbuf_launch_output(dev, dmap); + adev->dmap_out->flags |= DMA_SYNCING; + adev->dmap_out->underrun_count = 0; + while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs && + adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) { + long t = dmabuf_timeout(dmap); + t = interruptible_sleep_on_timeout(&adev->out_sleeper, + t); + if (!t) { + adev->dmap_out->flags &= ~DMA_SYNCING; + restore_flags(flags); + return adev->dmap_out->qlen; + } + } + adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); + restore_flags(flags); + + /* + * Some devices such as GUS have huge amount of on board RAM for the + * audio data. We have to wait until the device has finished playing. + */ + + save_flags(flags); + cli(); + if (adev->d->local_qlen) { /* Device has hidden buffers */ + while (!signal_pending(current) && + adev->d->local_qlen(dev)) + interruptible_sleep_on_timeout(&adev->out_sleeper, + dmabuf_timeout(dmap)); + } + restore_flags(flags); + } + adev->dmap_out->dma_mode = DMODE_NONE; + return adev->dmap_out->qlen; +} + +int DMAbuf_release(int dev, int mode) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + + if (adev->open_mode & OPEN_WRITE) + adev->dmap_out->closing = 1; + if (adev->open_mode & OPEN_READ) + adev->dmap_in->closing = 1; + + if (adev->open_mode & OPEN_WRITE) + if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED)) + if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT)) + DMAbuf_sync(dev); + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use); + save_flags(flags); + cli(); + + DMAbuf_reset(dev); + adev->d->close(dev); + + if (adev->open_mode & OPEN_WRITE) + close_dmap(adev, adev->dmap_out); + + if (adev->open_mode == OPEN_READ || + (adev->open_mode != OPEN_WRITE && + (adev->flags & DMA_DUPLEX))) + close_dmap(adev, adev->dmap_in); + adev->open_mode = 0; + restore_flags(flags); + return 0; +} + +int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap) +{ + struct audio_operations *adev = audio_devs[dev]; + int err; + + if (!(adev->open_mode & OPEN_READ)) + return 0; + if (!(adev->enable_bits & PCM_ENABLE_INPUT)) + return 0; + if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */ + DMAbuf_sync(dev); + DMAbuf_reset(dev); + dmap->dma_mode = DMODE_NONE; + } + if (!dmap->dma_mode) { + reorganize_buffers(dev, dmap, 1); + if ((err = adev->d->prepare_for_input(dev, + dmap->fragment_size, dmap->nbufs)) < 0) + return err; + dmap->dma_mode = DMODE_INPUT; + } + if (!(dmap->flags & DMA_ACTIVE)) { + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 0); + dmap->flags |= DMA_ACTIVE; + if (adev->d->trigger) + adev->d->trigger(dev, adev->enable_bits * adev->go); + } + return 0; +} + +int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int err = 0, n = 0; + struct dma_buffparms *dmap = adev->dmap_in; + int go; + + if (!(adev->open_mode & OPEN_READ)) + return -EIO; + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + save_flags(flags); + cli(); + if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) { +/* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/ + restore_flags(flags); + return -EINVAL; + } else while (dmap->qlen <= 0 && n++ < 10) { + long timeout = MAX_SCHEDULE_TIMEOUT; + if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) { + restore_flags(flags); + return -EAGAIN; + } + if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) { + restore_flags(flags); + return err; + } + /* Wait for the next block */ + + if (dontblock) { + restore_flags(flags); + return -EAGAIN; + } + if ((go = adev->go)) + timeout = dmabuf_timeout(dmap); + timeout = interruptible_sleep_on_timeout(&adev->in_sleeper, + timeout); + if (!timeout) { + /* FIXME: include device name */ + err = -EIO; + printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); + dma_reset_input(dev); + } else + err = -EINTR; + } + restore_flags(flags); + + if (dmap->qlen <= 0) + return err ? err : -EINTR; + *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; + *len = dmap->fragment_size - dmap->counts[dmap->qhead]; + + return dmap->qhead; +} + +int DMAbuf_rmchars(int dev, int buff_no, int c) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + int p = dmap->counts[dmap->qhead] + c; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { +/* printk("Sound: Can't read from mmapped device (2)\n");*/ + return -EINVAL; + } + else if (dmap->qlen <= 0) + return -EIO; + else if (p >= dmap->fragment_size) { /* This buffer is completely empty */ + dmap->counts[dmap->qhead] = 0; + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + } + else dmap->counts[dmap->qhead] = p; + + return 0; +} + +int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction) +{ + /* + * Try to approximate the active byte position of the DMA pointer within the + * buffer area as well as possible. + */ + + int pos; + unsigned long flags; + unsigned long f; + + save_flags(flags); + cli(); + if (!(dmap->flags & DMA_ACTIVE)) + pos = 0; + else { + int chan = dmap->dma; + + f=claim_dma_lock(); + clear_dma_ff(chan); + + if(!isa_dma_bridge_buggy) + disable_dma(dmap->dma); + + pos = get_dma_residue(chan); + + pos = dmap->bytes_in_use - pos; + + if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) { + if (direction == DMODE_OUTPUT) { + if (dmap->qhead == 0) + if (pos > dmap->fragment_size) + pos = 0; + } else { + if (dmap->qtail == 0) + if (pos > dmap->fragment_size) + pos = 0; + } + } + if (pos < 0) + pos = 0; + if (pos >= dmap->bytes_in_use) + pos = 0; + + if(!isa_dma_bridge_buggy) + enable_dma(dmap->dma); + + release_dma_lock(f); + } + restore_flags(flags); + /* printk( "%04x ", pos); */ + + return pos; +} + +/* + * DMAbuf_start_devices() is called by the /dev/music driver to start + * one or more audio devices at desired moment. + */ + +void DMAbuf_start_devices(unsigned int devmask) +{ + struct audio_operations *adev; + int dev; + + for (dev = 0; dev < num_audiodevs; dev++) { + if (!(devmask & (1 << dev))) + continue; + if (!(adev = audio_devs[dev])) + continue; + if (adev->open_mode == 0) + continue; + if (adev->go) + continue; + /* OK to start the device */ + adev->go = 1; + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } +} + +int DMAbuf_space_in_queue(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + int len, max, tmp; + struct dma_buffparms *dmap = adev->dmap_out; + int lim = dmap->nbufs; + + if (lim < 2) + lim = 2; + + if (dmap->qlen >= lim) /* No space at all */ + return 0; + + /* + * Verify that there are no more pending buffers than the limit + * defined by the process. + */ + + max = dmap->max_fragments; + if (max > lim) + max = lim; + len = dmap->qlen; + + if (adev->d->local_qlen) { + tmp = adev->d->local_qlen(dev); + if (tmp && len) + tmp--; /* This buffer has been counted twice */ + len += tmp; + } + if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */ + len = len + 1; + + if (len >= max) + return 0; + return max - len; +} + +static int output_sleep(int dev, int dontblock) +{ + struct audio_operations *adev = audio_devs[dev]; + int err = 0; + struct dma_buffparms *dmap = adev->dmap_out; + long timeout; + long timeout_value; + + if (dontblock) + return -EAGAIN; + if (!(adev->enable_bits & PCM_ENABLE_OUTPUT)) + return -EAGAIN; + + /* + * Wait for free space + */ + if (signal_pending(current)) + return -EINTR; + timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT)); + if (timeout) + timeout_value = dmabuf_timeout(dmap); + else + timeout_value = MAX_SCHEDULE_TIMEOUT; + timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper, + timeout_value); + if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) { + printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); + dma_reset_output(dev); + } else { + if (signal_pending(current)) + err = -EINTR; + } + return err; +} + +static int find_output_space(int dev, char **buf, int *size) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + unsigned long flags; + unsigned long active_offs; + long len, offs; + int maxfrags; + int occupied_bytes = (dmap->user_counter % dmap->fragment_size); + + *buf = dmap->raw_buf; + if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes) + return 0; + save_flags(flags); + cli(); + +#ifdef BE_CONSERVATIVE + active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size; +#else + active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT); + /* Check for pointer wrapping situation */ + if (active_offs < 0 || active_offs >= dmap->bytes_in_use) + active_offs = 0; + active_offs += dmap->byte_counter; +#endif + + offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP; + if (offs < 0 || offs >= dmap->bytes_in_use) { + restore_flags(flags); + printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs); + printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); + return 0; + } + *buf = dmap->raw_buf + offs; + + len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ + + if ((offs + len) > dmap->bytes_in_use) + len = dmap->bytes_in_use - offs; + if (len < 0) { + restore_flags(flags); + return 0; + } + if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) + len = (maxfrags * dmap->fragment_size) - occupied_bytes; + *size = len & ~SAMPLE_ROUNDUP; + restore_flags(flags); + return (*size > 0); +} + +int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int err = -EIO; + struct dma_buffparms *dmap = adev->dmap_out; + + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + + if (dmap->mapping_flags & DMA_MAP_MAPPED) { +/* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/ + return -EINVAL; + } + if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */ + DMAbuf_reset(dev); + dmap->dma_mode = DMODE_NONE; + } + dmap->dma_mode = DMODE_OUTPUT; + + save_flags(flags); + cli(); + while (find_output_space(dev, buf, size) <= 0) { + if ((err = output_sleep(dev, dontblock)) < 0) { + restore_flags(flags); + return err; + } + } + restore_flags(flags); + + return 0; +} + +int DMAbuf_move_wrpointer(int dev, int l) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + unsigned long ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; + unsigned long end_ptr, p; + int post = (dmap->flags & DMA_POST); + + dmap->flags &= ~DMA_POST; + dmap->cfrag = -1; + dmap->user_counter += l; + dmap->flags |= DMA_DIRTY; + + if (dmap->byte_counter >= dmap->max_byte_counter) { + /* Wrap the byte counters */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; + + p = (dmap->user_counter - 1) % dmap->bytes_in_use; + dmap->neutral_byte = dmap->raw_buf[p]; + + /* Update the fragment based bookkeeping too */ + while (ptr < end_ptr) { + dmap->counts[dmap->qtail] = dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + ptr += dmap->fragment_size; + } + + dmap->counts[dmap->qtail] = dmap->user_counter - ptr; + + /* + * Let the low level driver to perform some postprocessing to + * the written data. + */ + if (adev->d->postprocess_write) + adev->d->postprocess_write(dev); + + if (!(dmap->flags & DMA_ACTIVE)) + if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1))) + DMAbuf_launch_output(dev, dmap); + return 0; +} + +int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "sound: DMA buffer(1) == NULL\n"); + printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in"); + return 0; + } + if (dmap->dma < 0) + return 0; + sound_start_dma(dmap, physaddr, count, dma_mode); + return count; +} + +static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode) +{ + struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "sound: DMA buffer(2) == NULL\n"); + printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in"); + return 0; + } + if (dmap->flags & DMA_NODMA) + return 1; + if (dmap->dma < 0) + return 0; + sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT); + dmap->flags |= DMA_STARTED; + return count; +} + +static void finish_output_interrupt(int dev, struct dma_buffparms *dmap) +{ + struct audio_operations *adev = audio_devs[dev]; + + if (dmap->audio_callback != NULL) + dmap->audio_callback(dev, dmap->callback_parm); + wake_up(&adev->out_sleeper); + wake_up(&adev->poll_sleeper); +} + +static void do_outputintr(int dev, int dummy) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_out; + int this_fragment; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev); + return; + } + if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* Virtual memory mapped access */ + /* mmapped access */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + if (dmap->qhead == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + dmap->qlen++; /* Yes increment it (don't decrement) */ + if (!(adev->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + dmap->counts[dmap->qhead] = dmap->fragment_size; + DMAbuf_launch_output(dev, dmap); + finish_output_interrupt(dev, dmap); + return; + } + save_flags(flags); + cli(); + + dmap->qlen--; + this_fragment = dmap->qhead; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + + if (dmap->qhead == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + if (!(adev->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + + /* + * This is dmap->qlen <= 0 except when closing when + * dmap->qlen < 0 + */ + + while (dmap->qlen <= -dmap->closing) { + dmap->underrun_count++; + dmap->qlen++; + if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) { + dmap->flags &= ~DMA_DIRTY; + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, + adev->dmap_out->buffsize); + } + dmap->user_counter += dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } + if (dmap->qlen > 0) + DMAbuf_launch_output(dev, dmap); + restore_flags(flags); + finish_output_interrupt(dev, dmap); +} + +void DMAbuf_outputintr(int dev, int notify_only) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_out; + + save_flags(flags); + cli(); + if (!(dmap->flags & DMA_NODMA)) { + int chan = dmap->dma, pos, n; + unsigned long f; + + f=claim_dma_lock(); + + if(!isa_dma_bridge_buggy) + disable_dma(dmap->dma); + clear_dma_ff(chan); + pos = dmap->bytes_in_use - get_dma_residue(chan); + if(!isa_dma_bridge_buggy) + enable_dma(dmap->dma); + release_dma_lock(f); + + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + n = 0; + while (dmap->qhead != pos && n++ < dmap->nbufs) + do_outputintr(dev, notify_only); + } + else + do_outputintr(dev, notify_only); + restore_flags(flags); +} + +static void do_inputintr(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n"); + return; + } + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + dmap->qlen++; + + if (!(adev->flags & DMA_AUTOMODE)) { + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 1); + if (adev->d->trigger) + adev->d->trigger(dev, adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; + } else if (dmap->qlen >= (dmap->nbufs - 1)) { + printk(KERN_WARNING "Sound: Recording overrun\n"); + dmap->underrun_count++; + + /* Just throw away the oldest fragment but keep the engine running */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) { + dmap->qlen++; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + } + if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1); + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; + if (dmap->qlen > 0) + { + wake_up(&adev->in_sleeper); + wake_up(&adev->poll_sleeper); + } +} + +void DMAbuf_inputintr(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + unsigned long flags; + + save_flags(flags); + cli(); + + if (!(dmap->flags & DMA_NODMA)) { + int chan = dmap->dma, pos, n; + unsigned long f; + + f=claim_dma_lock(); + if(!isa_dma_bridge_buggy) + disable_dma(dmap->dma); + clear_dma_ff(chan); + pos = dmap->bytes_in_use - get_dma_residue(chan); + if(!isa_dma_bridge_buggy) + enable_dma(dmap->dma); + release_dma_lock(f); + + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + + n = 0; + while (dmap->qtail != pos && ++n < dmap->nbufs) + do_inputintr(dev); + } else + do_inputintr(dev); + restore_flags(flags); +} + +int DMAbuf_open_dma(int dev) +{ + /* + * NOTE! This routine opens only the primary DMA channel (output). + */ + struct audio_operations *adev = audio_devs[dev]; + int err; + + if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0) + return -EBUSY; + dma_init_buffers(adev->dmap_out); + adev->dmap_out->flags |= DMA_ALLOC_DONE; + adev->dmap_out->fragment_size = adev->dmap_out->buffsize; + + if (adev->dmap_out->dma >= 0) { + unsigned long flags; + + flags=claim_dma_lock(); + clear_dma_ff(adev->dmap_out->dma); + disable_dma(adev->dmap_out->dma); + release_dma_lock(flags); + } + return 0; +} + +void DMAbuf_close_dma(int dev) +{ + close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out); +} + +void DMAbuf_init(int dev, int dma1, int dma2) +{ + struct audio_operations *adev = audio_devs[dev]; + /* + * NOTE! This routine could be called several times. + */ + + /* drag in audio_syms.o */ + { + extern char audio_syms_symbol; + audio_syms_symbol = 0; + } + + if (adev && adev->dmap_out == NULL) { + if (adev->d == NULL) + panic("OSS: audio_devs[%d]->d == NULL\n", dev); + + if (adev->parent_dev) { /* Use DMA map of the parent dev */ + int parent = adev->parent_dev - 1; + adev->dmap_out = audio_devs[parent]->dmap_out; + adev->dmap_in = audio_devs[parent]->dmap_in; + } else { + adev->dmap_out = adev->dmap_in = &adev->dmaps[0]; + adev->dmap_out->dma = dma1; + if (adev->flags & DMA_DUPLEX) { + adev->dmap_in = &adev->dmaps[1]; + adev->dmap_in->dma = dma2; + } + } + /* Persistent DMA buffers allocated here */ + if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { + if (adev->dmap_in->raw_buf == NULL) + sound_alloc_dmap(adev->dmap_in); + if (adev->dmap_out->raw_buf == NULL) + sound_alloc_dmap(adev->dmap_out); + } + } +} + +/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */ +static unsigned int poll_input(struct file * file, int dev, poll_table *wait) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + + if (!(adev->open_mode & OPEN_READ)) + return 0; + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + if (dmap->qlen) + return POLLIN | POLLRDNORM; + return 0; + } + if (dmap->dma_mode != DMODE_INPUT) { + if (dmap->dma_mode == DMODE_NONE && + adev->enable_bits & PCM_ENABLE_INPUT && + !dmap->qlen && adev->go) { + unsigned long flags; + + save_flags(flags); + cli(); + DMAbuf_activate_recording(dev, dmap); + restore_flags(flags); + } + return 0; + } + if (!dmap->qlen) + return 0; + return POLLIN | POLLRDNORM; +} + +static unsigned int poll_output(struct file * file, int dev, poll_table *wait) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + + if (!(adev->open_mode & OPEN_WRITE)) + return 0; + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + if (dmap->qlen) + return POLLOUT | POLLWRNORM; + return 0; + } + if (dmap->dma_mode == DMODE_INPUT) + return 0; + if (dmap->dma_mode == DMODE_NONE) + return POLLOUT | POLLWRNORM; + if (!DMAbuf_space_in_queue(dev)) + return 0; + return POLLOUT | POLLWRNORM; +} + +unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait) +{ + struct audio_operations *adev = audio_devs[dev]; + poll_wait(file, &adev->poll_sleeper, wait); + return poll_input(file, dev, wait) | poll_output(file, dev, wait); +} + +void DMAbuf_deinit(int dev) +{ + struct audio_operations *adev = audio_devs[dev]; + /* This routine is called when driver is being unloaded */ + if (!adev) + return; + + /* Persistent DMA buffers deallocated here */ + if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { + sound_free_dmap(adev->dmap_out); + if (adev->flags & DMA_DUPLEX) + sound_free_dmap(adev->dmap_in); + } +} diff -Nru linux/sound/oss/dmasound/Config.help linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.help --- linux/sound/oss/dmasound/Config.help Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.help Mon Apr 8 22:31:22 2002 @@ -0,0 +1,47 @@ +CONFIG_DMASOUND + Support built-in audio chips accessible by DMA on various machines + that have them. Note that this symbol does not affect the kernel + directly; rather, it controls whether configuration questions + enabling DMA sound drivers for various specific machine + architectures will be used. + +CONFIG_DMASOUND_ATARI + If you want to use the internal audio of your Atari in Linux, answer + Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + +CONFIG_DMASOUND_AWACS + If you want to use the internal audio of your PowerMac in Linux, + answer Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + +CONFIG_DMASOUND_PAULA + If you want to use the internal audio of your Amiga in Linux, answer + Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + +CONFIG_DMASOUND_Q40 + If you want to use the internal audio of your Q40 in Linux, answer + Y to this question. This will provide a Sun-like /dev/audio, + compatible with the Linux/i386 sound system. Otherwise, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + . + diff -Nru linux/sound/oss/dmasound/Config.in linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.in --- linux/sound/oss/dmasound/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/Config.in Mon Apr 8 22:31:22 2002 @@ -0,0 +1,27 @@ +# drivers/sound/dmasound/Config.in + +if [ "$CONFIG_ATARI" = "y" ]; then + dep_tristate ' Atari DMA sound support' CONFIG_DMASOUND_ATARI $CONFIG_SOUND_ALSA +fi +if [ "$CONFIG_ALL_PPC" = "y" ]; then + dep_tristate ' PowerMac DMA sound support' CONFIG_DMASOUND_AWACS $CONFIG_SOUND_ALSA +fi +if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APUS" = "y" ]; then + dep_tristate ' Amiga DMA sound support' CONFIG_DMASOUND_PAULA $CONFIG_SOUND_ALSA +fi +if [ "$CONFIG_Q40" = "y" ]; then + dep_tristate ' Q40 sound support' CONFIG_DMASOUND_Q40 $CONFIG_SOUND_ALSA +fi +if [ "$CONFIG_DMASOUND_ATARI" = "y" -o \ + "$CONFIG_DMASOUND_AWACS" = "y" -o \ + "$CONFIG_DMASOUND_PAULA" = "y" -o \ + "$CONFIG_DMASOUND_Q40" = "y" ]; then + define_tristate CONFIG_DMASOUND y +else + if [ "$CONFIG_DMASOUND_ATARI" = "m" -o \ + "$CONFIG_DMASOUND_AWACS" = "m" -o \ + "$CONFIG_DMASOUND_PAULA" = "m" -o \ + "$CONFIG_DMASOUND_Q40" = "m" ]; then + define_tristate CONFIG_DMASOUND m + fi +fi diff -Nru linux/sound/oss/dmasound/Makefile linux-2.4.19-pre5-mjc/sound/oss/dmasound/Makefile --- linux/sound/oss/dmasound/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,21 @@ +# +# Makefile for the DMA sound driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +export-objs := dmasound_core.o + +obj-$(CONFIG_DMASOUND_ATARI) += dmasound_core.o dmasound_atari.o +obj-$(CONFIG_DMASOUND_AWACS) += dmasound_core.o dmasound_awacs.o +obj-$(CONFIG_DMASOUND_PAULA) += dmasound_core.o dmasound_paula.o +obj-$(CONFIG_DMASOUND_Q40) += dmasound_core.o dmasound_q40.o + +ifeq ($(CONFIG_DMASOUND),y) + O_TARGET = dmasound.o +endif + +include $(TOPDIR)/Rules.make diff -Nru linux/sound/oss/dmasound/awacs_defs.h linux-2.4.19-pre5-mjc/sound/oss/dmasound/awacs_defs.h --- linux/sound/oss/dmasound/awacs_defs.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/awacs_defs.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,229 @@ +/*********************************************************/ +/* This file was written by someone, somewhere, sometime */ +/* And is released into the Public Domain */ +/*********************************************************/ + +#ifndef _AWACS_DEFS_H_ +#define _AWACS_DEFS_H_ + +/*******************************/ +/* AWACs Audio Register Layout */ +/*******************************/ + +struct awacs_regs { + unsigned control; /* Audio control register */ + unsigned pad0[3]; + unsigned codec_ctrl; /* Codec control register */ + unsigned pad1[3]; + unsigned codec_stat; /* Codec status register */ + unsigned pad2[3]; + unsigned clip_count; /* Clipping count register */ + unsigned pad3[3]; + unsigned byteswap; /* Data is little-endian if 1 */ +}; + +/*******************/ +/* Audio Bit Masks */ +/*******************/ + +/* Audio Control Reg Bit Masks */ +/* ----- ------- --- --- ----- */ +#define MASK_ISFSEL (0xf) /* Input SubFrame Select */ +#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */ +#define MASK_RATE (0x7 << 8) /* Sound Rate */ +#define MASK_CNTLERR (0x1 << 11) /* Error */ +#define MASK_PORTCHG (0x1 << 12) /* Port Change */ +#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */ +#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */ +#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */ + +/* Audio Codec Control Reg Bit Masks */ +/* ----- ----- ------- --- --- ----- */ +#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */ +#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */ +#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */ +#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */ + +/* Audio Codec Control Address Values / Masks */ +/* ----- ----- ------- ------- ------ - ----- */ +#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */ +#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */ +#define MASK_ADDR_GAIN MASK_ADDR0 + +#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */ +#define MASK_ADDR_MUTE MASK_ADDR1 +#define MASK_ADDR_RATE MASK_ADDR1 + +#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */ +#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */ +#define MASK_ADDR_VOLHD MASK_ADDR2 + +#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */ +#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */ +#define MASK_ADDR_VOLSPK MASK_ADDR4 + +/* additional registers of screamer */ +#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */ +#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */ +#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */ + +/* Address 0 Bit Masks & Macros */ +/* ------- - --- ----- - ------ */ +#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */ +#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */ +#define MASK_GAINLINE (0x1 << 8) /* Change Gain for Line??? */ +#define MASK_GAINMIC (0x0 << 8) /* Change Gain for Mic??? */ + +#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */ +#define MASK_MUX_AUDIN (0x1 << 10) /* Select Audio In in MUX */ +#define MASK_MUX_MIC (0x1 << 11) /* Select Mic in MUX */ +#define MASK_MUX_LINE MASK_MUX_AUDIN + +#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT) +#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT) + +/* Address 1 Bit Masks */ +/* ------- - --- ----- */ +#define MASK_ADDR1RES1 (0x3) /* Reserved */ +#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */ +#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */ +#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */ +#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ +#define MASK_SPKMUTE MASK_CMUTE +#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ +#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ +#define MASK_HDMUTE MASK_AMUTE +#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ + +#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ +#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ +#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */ +#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */ +#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */ +#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */ +#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */ +#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */ + +/* Address 2 & 4 Bit Masks & Macros */ +/* ------- - - - --- ----- - ------ */ +#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */ +#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */ +#define MASK_ADDR4RES1 MASK_ADDR2RES1 +#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */ +#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */ +#define MASK_ADDR4RES2 MASK_ADDR2RES2 + +#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT)) +#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) + +/* Audio Codec Status Reg Bit Masks */ +/* ----- ----- ------ --- --- ----- */ +#define MASK_EXTEND (0x1 << 23) /* Extend */ +#define MASK_VALID (0x1 << 22) /* Valid Data? */ +#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */ +#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */ +#define MASK_ERRCODE (0xf << 16) /* Error Code */ +#define MASK_REVISION (0xf << 12) /* Revision Number */ +#define MASK_MFGID (0xf << 8) /* Mfg. ID */ +#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ +#define MASK_INPPORT (0xf) /* Input Port */ +#define MASK_HDPCONN 8 /* headphone plugged in */ + +/* Clipping Count Reg Bit Masks */ +/* -------- ----- --- --- ----- */ +#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */ +#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */ + +/* DBDMA ChannelStatus Bit Masks */ +/* ----- ------------- --- ----- */ +#define MASK_CSERR (0x1 << 7) /* Error */ +#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ +#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ +#define MASK_WAIT (0x1) /* Wait */ + +/* Various Rates */ +/* ------- ----- */ +#define RATE_48000 (0x0 << 8) /* 48 kHz */ +#define RATE_44100 (0x0 << 8) /* 44.1 kHz */ +#define RATE_32000 (0x1 << 8) /* 32 kHz */ +#define RATE_29400 (0x1 << 8) /* 29.4 kHz */ +#define RATE_24000 (0x2 << 8) /* 24 kHz */ +#define RATE_22050 (0x2 << 8) /* 22.05 kHz */ +#define RATE_19200 (0x3 << 8) /* 19.2 kHz */ +#define RATE_17640 (0x3 << 8) /* 17.64 kHz */ +#define RATE_16000 (0x4 << 8) /* 16 kHz */ +#define RATE_14700 (0x4 << 8) /* 14.7 kHz */ +#define RATE_12000 (0x5 << 8) /* 12 kHz */ +#define RATE_11025 (0x5 << 8) /* 11.025 kHz */ +#define RATE_9600 (0x6 << 8) /* 9.6 kHz */ +#define RATE_8820 (0x6 << 8) /* 8.82 kHz */ +#define RATE_8000 (0x7 << 8) /* 8 kHz */ +#define RATE_7350 (0x7 << 8) /* 7.35 kHz */ + +#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ + + +/* Burgundy values */ + +#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) +#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) + +#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) + +#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) + +#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) + +#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) + +#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) +#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) +#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) +#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) +#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) +#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + +#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) +#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) +#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) +#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + + +/* These are all default values for the burgundy */ +#define DEF_BURGUNDY_INPSEL21 (0xAA) +#define DEF_BURGUNDY_INPSEL3 (0x0A) + +#define DEF_BURGUNDY_GAINCD (0x33) +#define DEF_BURGUNDY_GAINLINE (0x44) +#define DEF_BURGUNDY_GAINMIC (0x44) +#define DEF_BURGUNDY_GAINMODEM (0x06) + +/* Remember: lowest volume here is 0x9b */ +#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) +#define DEF_BURGUNDY_VOLLINE (0x00000000) +#define DEF_BURGUNDY_VOLMIC (0x00000000) +#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) + +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) + +#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) + +#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) + +#define DEF_BURGUNDY_ATTENSPEAKER (0x44) +#define DEF_BURGUNDY_ATTENLINEOUT (0xCC) +#define DEF_BURGUNDY_ATTENHP (0xCC) + +#endif /* _AWACS_DEFS_H_ */ diff -Nru linux/sound/oss/dmasound/dmasound.h linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound.h --- linux/sound/oss/dmasound/dmasound.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,248 @@ + +/* + * linux/drivers/sound/dmasound/dmasound.h + * + * + * Minor numbers for the sound driver. + * + * Unfortunately Creative called the codec chip of SB as a DSP. For this + * reason the /dev/dsp is reserved for digitized audio use. There is a + * device for true DSP processors but it will be called something else. + * In v3.0 it's /dev/sndproc but this could be a temporary solution. + */ + + +#include + + +#define SND_NDEVS 256 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +#define DSP_DEFAULT_SPEED 8000 + +#define ON 1 +#define OFF 0 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 2 +#define MAX_SYNTH_DEV 3 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 3 + + +#define MAX_CATCH_RADIUS 10 +#define MIN_BUFFERS 4 +#define MIN_BUFSIZE 4 /* in KB */ +#define MAX_BUFSIZE 128 /* Limit for Amiga in KB */ + + +#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) +#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) + +#define IOCTL_IN(arg, ret) \ + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) +#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) + +static inline int ioctl_return(int *addr, int value) +{ + return value < 0 ? value : put_user(value, addr); +} + + + /* + * Configuration + */ + +#undef HAS_8BIT_TABLES +#undef HAS_14BIT_TABLES +#undef HAS_16BIT_TABLES +#undef HAS_RECORD + +#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\ + defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\ + defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE) +#define HAS_8BIT_TABLES +#endif +#if defined(CONFIG_DMASOUND_AWACS) || defined(CONFIG_DMASOUND_AWACS_MODULE) +#define HAS_16BIT_TABLES +#define HAS_RECORD +#endif + + + /* + * Initialization + */ + +extern int dmasound_init(void); +#ifdef MODULE +extern void dmasound_deinit(void); +#else +#define dmasound_deinit() do { } while (0) +#endif + + + /* + * Machine definitions + */ + +typedef struct { + const char *name; + const char *name2; + void (*open)(void); + void (*release)(void); + void *(*dma_alloc)(unsigned int, int); + void (*dma_free)(void *, unsigned int); + int (*irqinit)(void); +#ifdef MODULE + void (*irqcleanup)(void); +#endif + void (*init)(void); + void (*silence)(void); + int (*setFormat)(int); + int (*setVolume)(int); + int (*setBass)(int); + int (*setTreble)(int); + int (*setGain)(int); + void (*play)(void); + void (*record)(void); /* optional */ + void (*mixer_init)(void); /* optional */ + int (*mixer_ioctl)(u_int, u_long); /* optional */ + void (*write_sq_setup)(void); /* optional */ + void (*read_sq_setup)(void); /* optional */ + void (*sq_open)(void); /* optional */ + int (*state_info)(char *); /* optional */ + void (*abort_read)(void); /* optional */ + int min_dsp_speed; +} MACHINE; + + + /* + * Low level stuff + */ + +typedef struct { + int format; /* AFMT_* */ + int stereo; /* 0 = mono, 1 = stereo */ + int size; /* 8/16 bit*/ + int speed; /* speed */ +} SETTINGS; + +typedef struct { + ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16be)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_s16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + ssize_t (*ct_u16le)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); +} TRANS; + +struct sound_settings { + MACHINE mach; /* machine dependent things */ + SETTINGS hard; /* hardware settings */ + SETTINGS soft; /* software settings */ + SETTINGS dsp; /* /dev/dsp default settings */ + TRANS *trans_write; /* supported translations */ +#ifdef HAS_RECORD + TRANS *trans_read; /* supported translations */ +#endif + int volume_left; /* volume (range is machine dependent) */ + int volume_right; + int bass; /* tone (range is machine dependent) */ + int treble; + int gain; + int minDev; /* minor device number currently open */ +}; + +extern struct sound_settings dmasound; + +extern char dmasound_ulaw2dma8[]; +extern char dmasound_alaw2dma8[]; +extern short dmasound_ulaw2dma16[]; +extern short dmasound_alaw2dma16[]; + + + /* + * Mid level stuff + */ + +static inline int dmasound_set_volume(int volume) +{ + return dmasound.mach.setVolume(volume); +} + +static inline int dmasound_set_bass(int bass) +{ + return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50; +} + +static inline int dmasound_set_treble(int treble) +{ + return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50; +} + +static inline int dmasound_set_gain(int gain) +{ + return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100; +} + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue { + /* buffers allocated for this queue */ + int numBufs; + int bufSize; /* in bytes */ + char **buffers; + + /* current parameters */ + int max_count; + int block_size; /* in bytes */ + int max_active; + + /* it shouldn't be necessary to declare any of these volatile */ + int front, rear, count; + int rear_size; + /* + * The use of the playing field depends on the hardware + * + * Atari, PMac: The number of frames that are loaded/playing + * + * Amiga: Bit 0 is set: a frame is loaded + * Bit 1 is set: a frame is playing + */ + int active; + wait_queue_head_t action_queue, open_queue, sync_queue; + int open_mode; + int busy, syncing; +}; + +#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ) +#define WAKE_UP(queue) (wake_up_interruptible(&queue)) + +extern struct sound_queue dmasound_write_sq; +extern struct sound_queue dmasound_read_sq; + +#define write_sq dmasound_write_sq +#define read_sq dmasound_read_sq + +extern int dmasound_catchRadius; + +#define catchRadius dmasound_catchRadius + diff -Nru linux/sound/oss/dmasound/dmasound_atari.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_atari.c --- linux/sound/oss/dmasound/dmasound_atari.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_atari.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1562 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_atari.c + * + * Atari TT and Falcon DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dmasound.h" + + +extern void atari_microwire_cmd(int cmd); + + +static int is_falcon; +static int write_sq_ignore_int = 0; /* ++TeSche: used for Falcon */ + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + + +static void AtaOpen(void); +static void AtaRelease(void); +static void *AtaAlloc(unsigned int size, int flags); +static void AtaFree(void *, unsigned int size); +static int AtaIrqInit(void); +#ifdef MODULE +static void AtaIrqCleanUp(void); +#endif /* MODULE */ +static int AtaSetBass(int bass); +static int AtaSetTreble(int treble); +static void TTSilence(void); +static void TTInit(void); +static int TTSetFormat(int format); +static int TTSetVolume(int volume); +static int TTSetGain(int gain); +static void FalconSilence(void); +static void FalconInit(void); +static int FalconSetFormat(int format); +static int FalconSetVolume(int volume); +static void AtaPlayNextFrame(int index); +static void AtaPlay(void); +static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp); + +/*** Mid level stuff *********************************************************/ + +static void TTMixerInit(void); +static void FalconMixerInit(void); +static int AtaMixerIoctl(u_int cmd, u_long arg); +static int TTMixerIoctl(u_int cmd, u_long arg); +static int FalconMixerIoctl(u_int cmd, u_long arg); +static void AtaWriteSqSetup(void); +static void AtaSqOpen(void); +static int TTStateInfo(char *buffer); +static int FalconStateInfo(char *buffer); + + +/*** Translations ************************************************************/ + + +static ssize_t ata_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 + : dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = &frame[*frameUsed]; + + count = min_t(unsigned long, userCount, frameLeft); + if (dmasound.soft.stereo) + count &= ~1; + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = table[data]; + count--; + } + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + void *p = &frame[*frameUsed]; + + count = min_t(unsigned long, userCount, frameLeft); + if (dmasound.soft.stereo) + count &= ~1; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft); + used = count; + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + *p++ = data ^ 0x80; + count--; + } + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x8080; + count--; + } + } + *frameUsed += used; + return used; +} + + +static ssize_t ata_ct_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + void *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft) & ~3; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + *p++ = data ^ 0x80008000; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + count = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + *p++ = data; + *p++ = data; + count--; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>2; + used = count*4; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + *p++ = data; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ct_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + + count = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>1; + used = count*2; + while (count > 0) { + u_short data; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + *p++ = data; + *p++ = data; + } + *frameUsed += used*2; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft)>>2; + used = count; + while (count > 0) { + u_long data; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + *p++ = data; + count--; + } + *frameUsed += used; + } + return used; +} + + +static ssize_t ata_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8 + : dmasound_alaw2dma8; + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (!userCount) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + u_char c; + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c] << 8; + if (get_user(c, userPtr++)) + return -EFAULT; + data |= table[c]; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + if (bal < 0) { + if (!userCount) + break; + if (get_user(data, userPtr++)) + return -EFAULT; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_char *p = &frame[*frameUsed]; + u_char data = expand_data; + while (frameLeft) { + if (bal < 0) { + if (!userCount) + break; + if (get_user(data, userPtr++)) + return -EFAULT; + data ^= 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_data = data; + } else { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 2) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8080; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 2; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u16be(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data ^= 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data ^= 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_s16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data); + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data); + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static ssize_t ata_ctx_u16le(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + /* this should help gcc to stuff everything into registers */ + long bal = expand_bal; + long hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + ssize_t used, usedf; + + used = userCount; + usedf = frameLeft; + if (!dmasound.soft.stereo) { + u_short *p = (u_short *)&frame[*frameUsed]; + u_short data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 2) + break; + if (get_user(data, ((u_short *)userPtr)++)) + return -EFAULT; + data = le2be16(data) ^ 0x8000; + userCount -= 2; + bal += hSpeed; + } + *p++ = data; + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } else { + u_long *p = (u_long *)&frame[*frameUsed]; + u_long data = expand_data; + while (frameLeft >= 4) { + if (bal < 0) { + if (userCount < 4) + break; + if (get_user(data, ((u_int *)userPtr)++)) + return -EFAULT; + data = le2be16dbl(data) ^ 0x80008000; + userCount -= 4; + bal += hSpeed; + } + *p++ = data; + frameLeft -= 4; + bal -= sSpeed; + } + expand_data = data; + } + expand_bal = bal; + used -= userCount; + *frameUsed += usedf-frameLeft; + return used; +} + + +static TRANS transTTNormal = { + ct_ulaw: ata_ct_law, + ct_alaw: ata_ct_law, + ct_s8: ata_ct_s8, + ct_u8: ata_ct_u8, +}; + +static TRANS transTTExpanding = { + ct_ulaw: ata_ctx_law, + ct_alaw: ata_ctx_law, + ct_s8: ata_ctx_s8, + ct_u8: ata_ctx_u8, +}; + +static TRANS transFalconNormal = { + ct_ulaw: ata_ct_law, + ct_alaw: ata_ct_law, + ct_s8: ata_ct_s8, + ct_u8: ata_ct_u8, + ct_s16be: ata_ct_s16be, + ct_u16be: ata_ct_u16be, + ct_s16le: ata_ct_s16le, + ct_u16le: ata_ct_u16le +}; + +static TRANS transFalconExpanding = { + ct_ulaw: ata_ctx_law, + ct_alaw: ata_ctx_law, + ct_s8: ata_ctx_s8, + ct_u8: ata_ctx_u8, + ct_s16be: ata_ctx_s16be, + ct_u16be: ata_ctx_u16be, + ct_s16le: ata_ctx_s16le, + ct_u16le: ata_ctx_u16le, +}; + + +/*** Low level stuff *********************************************************/ + + + +/* + * Atari (TT/Falcon) + */ + +static void AtaOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void AtaRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static void *AtaAlloc(unsigned int size, int flags) +{ + return atari_stram_alloc(size, "dmasound"); +} + +static void AtaFree(void *obj, unsigned int size) +{ + atari_stram_free( obj ); +} + +static int __init AtaIrqInit(void) +{ + /* Set up timer A. Timer A + will receive a signal upon end of playing from the sound + hardware. Furthermore Timer A is able to count events + and will cause an interrupt after a programmed number + of events. So all we need to keep the music playing is + to provide the sound hardware with new data upon + an interrupt from timer A. */ + mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */ + mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ + mfp.tim_ct_a = 8; /* Turn on event counting. */ + /* Register interrupt handler. */ + request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound", + AtaInterrupt); + mfp.int_en_a |= 0x20; /* Turn interrupt on. */ + mfp.int_mk_a |= 0x20; + return 1; +} + +#ifdef MODULE +static void AtaIrqCleanUp(void) +{ + mfp.tim_ct_a = 0; /* stop timer */ + mfp.int_en_a &= ~0x20; /* turn interrupt off */ + free_irq(IRQ_MFP_TIMA, AtaInterrupt); +} +#endif /* MODULE */ + + +#define TONE_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25) +#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50) + + +static int AtaSetBass(int bass) +{ + dmasound.bass = TONE_VOXWARE_TO_DB(bass); + atari_microwire_cmd(MW_LM1992_BASS(dmasound.bass)); + return TONE_DB_TO_VOXWARE(dmasound.bass); +} + + +static int AtaSetTreble(int treble) +{ + dmasound.treble = TONE_VOXWARE_TO_DB(treble); + atari_microwire_cmd(MW_LM1992_TREBLE(dmasound.treble)); + return TONE_DB_TO_VOXWARE(dmasound.treble); +} + + + +/* + * TT + */ + + +static void TTSilence(void) +{ + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */ +} + + +static void TTInit(void) +{ + int mode, i, idx; + const int freq[4] = {50066, 25033, 12517, 6258}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < ARRAY_SIZE(freq); i++) + /* this isn't as much useful for a TT than for a Falcon, but + * then it doesn't hurt very much to implement it for a TT too. + */ + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transTTNormal; + } else + dmasound.trans_write = &transTTExpanding; + + TTSilence(); + dmasound.hard = dmasound.soft; + + if (dmasound.hard.speed > 50066) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + dmasound.trans_write = &transTTNormal; + } else if (dmasound.hard.speed > 25033) { + dmasound.hard.speed = 50066; + mode = DMASND_MODE_50KHZ; + } else if (dmasound.hard.speed > 12517) { + dmasound.hard.speed = 25033; + mode = DMASND_MODE_25KHZ; + } else if (dmasound.hard.speed > 6258) { + dmasound.hard.speed = 12517; + mode = DMASND_MODE_12KHZ; + } else { + dmasound.hard.speed = 6258; + mode = DMASND_MODE_6KHZ; + } + + tt_dmasnd.mode = (dmasound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + DMASND_MODE_8BIT | mode; + + expand_bal = -dmasound.soft.speed; +} + + +static int TTSetFormat(int format) +{ + /* TT sound DMA supports only 8bit modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_S8: + case AFMT_U8: + break; + default: + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = 8; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = 8; + } + TTInit(); + + return format; +} + + +#define VOLUME_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -40 : ((v) > 100) ? 0 : ((v) * 2) / 5 - 40) +#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2) + + +static int TTSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff); + atari_microwire_cmd(MW_LM1992_BALLEFT(dmasound.volume_left)); + dmasound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8); + atari_microwire_cmd(MW_LM1992_BALRIGHT(dmasound.volume_right)); + return VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8); +} + + +#define GAIN_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80) +#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4) + +static int TTSetGain(int gain) +{ + dmasound.gain = GAIN_VOXWARE_TO_DB(gain); + atari_microwire_cmd(MW_LM1992_VOLUME(dmasound.gain)); + return GAIN_DB_TO_VOXWARE(dmasound.gain); +} + + + +/* + * Falcon + */ + + +static void FalconSilence(void) +{ + /* stop playback, set sample rate 50kHz for PSG sound */ + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT; + tt_dmasnd.int_div = 0; /* STE compatible divider */ + tt_dmasnd.int_ctrl = 0x0; + tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */ + tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */ + tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */ + tt_dmasnd.adc_src = 3; /* ADC Input = PSG */ +} + + +static void FalconInit(void) +{ + int divider, i, idx; + const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < ARRAY_SIZE(freq); i++) + /* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would + * be playable without expanding, but that now a kernel runtime + * option + */ + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) < catchRadius) + idx = i; + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transFalconNormal; + } else + dmasound.trans_write = &transFalconExpanding; + + FalconSilence(); + dmasound.hard = dmasound.soft; + + if (dmasound.hard.size == 16) { + /* the Falcon can play 16bit samples only in stereo */ + dmasound.hard.stereo = 1; + } + + if (dmasound.hard.speed > 49170) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 49170; + divider = 1; + dmasound.trans_write = &transFalconNormal; + } else if (dmasound.hard.speed > 32780) { + dmasound.hard.speed = 49170; + divider = 1; + } else if (dmasound.hard.speed > 24585) { + dmasound.hard.speed = 32780; + divider = 2; + } else if (dmasound.hard.speed > 19668) { + dmasound.hard.speed = 24585; + divider = 3; + } else if (dmasound.hard.speed > 16390) { + dmasound.hard.speed = 19668; + divider = 4; + } else if (dmasound.hard.speed > 12292) { + dmasound.hard.speed = 16390; + divider = 5; + } else if (dmasound.hard.speed > 9834) { + dmasound.hard.speed = 12292; + divider = 7; + } else if (dmasound.hard.speed > 8195) { + dmasound.hard.speed = 9834; + divider = 9; + } else { + dmasound.hard.speed = 8195; + divider = 11; + } + tt_dmasnd.int_div = divider; + + /* Setup Falcon sound DMA for playback */ + tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */ + tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */ + tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */ + tt_dmasnd.cbar_dst = 0x0000; + tt_dmasnd.rec_track_select = 0; + tt_dmasnd.dac_src = 2; /* connect matrix to DAC */ + tt_dmasnd.adc_src = 0; /* ADC Input = Mic */ + + tt_dmasnd.mode = (dmasound.hard.stereo ? + DMASND_MODE_STEREO : DMASND_MODE_MONO) | + ((dmasound.hard.size == 8) ? + DMASND_MODE_8BIT : DMASND_MODE_16BIT) | + DMASND_MODE_6KHZ; + + expand_bal = -dmasound.soft.speed; +} + + +static int FalconSetFormat(int format) +{ + int size; + /* Falcon sound DMA supports 8bit and 16bit modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + size = 8; + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = dmasound.soft.size; + } + + FalconInit(); + + return format; +} + + +/* This is for the Falcon output *attenuation* in 1.5dB steps, + * i.e. output level from 0 to -22.5dB in -1.5dB steps. + */ +#define VOLUME_VOXWARE_TO_ATT(v) \ + ((v) < 0 ? 15 : (v) > 100 ? 0 : 15 - (v) * 3 / 20) +#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3) + + +static int FalconSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff); + dmasound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8); + tt_dmasnd.output_atten = dmasound.volume_left << 8 | dmasound.volume_right << 4; + return VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | + VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8; +} + + +static void AtaPlayNextFrame(int index) +{ + char *start, *end; + + /* used by AtaPlay() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + end = start+((write_sq.count == index) ? write_sq.rear_size + : write_sq.block_size); + /* end might not be a legal virtual address. */ + DMASNDSetEnd(virt_to_phys(end - 1) + 1); + DMASNDSetBase(virt_to_phys(start)); + /* Since only an even number of samples per frame can + be played, we might lose one byte here. (TO DO) */ + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active++; + tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT; +} + + +static void AtaPlay(void) +{ + /* ++TeSche: Note that write_sq.active is no longer just a flag but + * holds the number of frames the DMA is currently programmed for + * instead, may be 0, 1 (currently being played) or 2 (pre-programmed). + * + * Changes done to write_sq.count and write_sq.active are a bit more + * subtle again so now I must admit I also prefer disabling the irq + * here rather than considering all possible situations. But the point + * is that disabling the irq doesn't have any bad influence on this + * version of the driver as we benefit from having pre-programmed the + * DMA wherever possible: There's no need to reload the DMA at the + * exact time of an interrupt but only at some time while the + * pre-programmed frame is playing! + */ + atari_disable_irq(IRQ_MFP_TIMA); + + if (write_sq.active == 2 || /* DMA is 'full' */ + write_sq.count <= 0) { /* nothing to do */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + + if (write_sq.active == 0) { + /* looks like there's nothing 'in' the DMA yet, so try + * to put two frames into it (at least one is available). + */ + if (write_sq.count == 1 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(1); + if (write_sq.count == 1) { + /* no more frames */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + if (write_sq.count == 2 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, there were two frames, but the second + * one is not yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(2); + } else { + /* there's already a frame being played so we may only stuff + * one new into the DMA, but even if this may be the last + * frame existing the previous one is still on write_sq.count. + */ + if (write_sq.count == 2 && + write_sq.rear_size < write_sq.block_size && + !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + atari_enable_irq(IRQ_MFP_TIMA); + return; + } + AtaPlayNextFrame(2); + } + atari_enable_irq(IRQ_MFP_TIMA); +} + + +static void AtaInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ +#if 0 + /* ++TeSche: if you should want to test this... */ + static int cnt = 0; + if (write_sq.active == 2) + if (++cnt == 10) { + /* simulate losing an interrupt */ + cnt = 0; + return; + } +#endif + + if (write_sq_ignore_int && is_falcon) { + /* ++TeSche: Falcon only: ignore first irq because it comes + * immediately after starting a frame. after that, irqs come + * (almost) like on the TT. + */ + write_sq_ignore_int = 0; + return; + } + + if (!write_sq.active) { + /* playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + return; + } + + /* Probably ;) one frame is finished. Well, in fact it may be that a + * pre-programmed one is also finished because there has been a long + * delay in interrupt delivery and we've completely lost one, but + * there's no way to detect such a situation. In such a case the last + * frame will be played more than once and the situation will recover + * as soon as the irq gets through. + */ + write_sq.count--; + write_sq.active--; + + if (!write_sq.active) { + tt_dmasnd.ctrl = DMASND_CTRL_OFF; + write_sq_ignore_int = 1; + } + + WAKE_UP(write_sq.action_queue); + /* At least one block of the queue is free now + so wake up a writing process blocked because + of a full queue. */ + + if ((write_sq.active != 1) || (write_sq.count != 1)) + /* We must be a bit carefully here: write_sq.count indicates the + * number of buffers used and not the number of frames to be + * played. If write_sq.count==1 and write_sq.active==1 that + * means the only remaining frame was already programmed + * earlier (and is currently running) so we mustn't call + * AtaPlay() here, otherwise we'll play one frame too much. + */ + AtaPlay(); + + if (!write_sq.active) WAKE_UP(write_sq.sync_queue); + /* We are not playing after AtaPlay(), so there + is nothing to play any more. Wake up a process + waiting for audio output to drain. */ +} + + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +#define RECLEVEL_VOXWARE_TO_GAIN(v) \ + ((v) < 0 ? 0 : (v) > 100 ? 15 : (v) * 3 / 20) +#define RECLEVEL_GAIN_TO_VOXWARE(v) (((v) * 20 + 2) / 3) + + +static void __init TTMixerInit(void) +{ + atari_microwire_cmd(MW_LM1992_VOLUME(0)); + dmasound.volume_left = 0; + atari_microwire_cmd(MW_LM1992_BALLEFT(0)); + dmasound.volume_right = 0; + atari_microwire_cmd(MW_LM1992_BALRIGHT(0)); + atari_microwire_cmd(MW_LM1992_TREBLE(0)); + atari_microwire_cmd(MW_LM1992_BASS(0)); +} + +static void __init FalconMixerInit(void) +{ + dmasound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8; + dmasound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4; +} + +static int AtaMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_SPEAKER: + if (is_falcon || MACH_IS_TT) { + int porta; + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = sound_ym.rd_data_reg_sel; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + break; + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_volume(data)); + case SOUND_MIXER_WRITE_SPEAKER: + if (is_falcon || MACH_IS_TT) { + int porta; + IOCTL_IN(arg, data); + cli(); + sound_ym.rd_data_reg_sel = 14; + porta = (sound_ym.rd_data_reg_sel & ~0x40) | + (data < 50 ? 0x40 : 0); + sound_ym.wd_data = porta; + sti(); + return IOCTL_OUT(arg, porta & 0x40 ? 0 : 100); + } + } + return -EINVAL; +} + + +static int TTMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, + SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS | + (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0)); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_DB_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_DB_TO_VOXWARE(dmasound.volume_right) << 8)); + case SOUND_MIXER_READ_BASS: + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.bass)); + case SOUND_MIXER_READ_TREBLE: + return IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(dmasound.treble)); + case SOUND_MIXER_READ_OGAIN: + return IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(dmasound.gain)); + case SOUND_MIXER_WRITE_BASS: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_bass(data)); + case SOUND_MIXER_WRITE_TREBLE: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_treble(data)); + case SOUND_MIXER_WRITE_OGAIN: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_gain(data)); + } + return AtaMixerIoctl(cmd, arg); +} + +static int FalconMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, SOUND_MASK_MIC); + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) | + VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + tt_dmasnd.input_gain = + RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 | + RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff); + /* fall thru, return set value */ + case SOUND_MIXER_READ_MIC: + return IOCTL_OUT(arg, + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) | + RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8); + } + return AtaMixerIoctl(cmd, arg); +} + +static void AtaWriteSqSetup(void) +{ + write_sq_ignore_int = 0; +} + +static void AtaSqOpen(void) +{ + write_sq_ignore_int = 1; +} + +static int TTStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n", + dmasound.volume_right); + len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n", + dmasound.bass); + len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n", + dmasound.treble); + return len; +} + +static int FalconStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n", + dmasound.volume_right); + return len; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machTT = { + name: "Atari", + name2: "TT", + open: AtaOpen, + release: AtaRelease, + dma_alloc: AtaAlloc, + dma_free: AtaFree, + irqinit: AtaIrqInit, +#ifdef MODULE + irqcleanup: AtaIrqCleanUp, +#endif /* MODULE */ + init: TTInit, + silence: TTSilence, + setFormat: TTSetFormat, + setVolume: TTSetVolume, + setBass: AtaSetBass, + setTreble: AtaSetTreble, + setGain: TTSetGain, + play: AtaPlay, + mixer_init: TTMixerInit, + mixer_ioctl: TTMixerIoctl, + write_sq_setup: AtaWriteSqSetup, + sq_open: AtaSqOpen, + state_info: TTStateInfo, + min_dsp_speed: 6258, +}; + +static MACHINE machFalcon = { + name: "Atari", + name2: "FALCON", + dma_alloc: AtaAlloc, + dma_free: AtaFree, + irqinit: AtaIrqInit, +#ifdef MODULE + irqcleanup: AtaIrqCleanUp, +#endif /* MODULE */ + init: FalconInit, + silence: FalconSilence, + setFormat: FalconSetFormat, + setVolume: FalconSetVolume, + setBass: AtaSetBass, + setTreble: AtaSetTreble, + play: AtaPlay, + mixer_init: FalconMixerInit, + mixer_ioctl: FalconMixerIoctl, + write_sq_setup: AtaWriteSqSetup, + sq_open: AtaSqOpen, + state_info: FalconStateInfo, + min_dsp_speed: 8195, +}; + + +/*** Config & Setup **********************************************************/ + + +static int __init dmasound_atari_init(void) +{ + if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) { + if (ATARIHW_PRESENT(CODEC)) { + dmasound.mach = machFalcon; + is_falcon = 1; + } else if (ATARIHW_PRESENT(MICROWIRE)) { + dmasound.mach = machTT; + is_falcon = 0; + } else + return -ENODEV; + if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0) + return dmasound_init(); + else { + printk("DMA sound driver: Timer A interrupt already in use\n"); + return -EBUSY; + } + } + return -ENODEV; +} + +static void __exit dmasound_atari_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_atari_init); +module_exit(dmasound_atari_cleanup); +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/dmasound/dmasound_awacs.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_awacs.c --- linux/sound/oss/dmasound/dmasound_awacs.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_awacs.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,2183 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_awacs.c + * + * PowerMac `AWACS' and `Burgundy' DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ADB_CUDA +#include +#endif +#ifdef CONFIG_ADB_PMU +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "awacs_defs.h" +#include "dmasound.h" + + +/* + * Interrupt numbers and addresses, obtained from the device tree. + */ +static int awacs_irq, awacs_tx_irq, awacs_rx_irq; +static volatile struct awacs_regs *awacs; +static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma; +static int awacs_rate_index; +static int awacs_subframe; +static int awacs_spkr_vol; +static struct device_node* awacs_node; + +static char awacs_name[64]; +static int awacs_revision; +int awacs_is_screamer = 0; +int awacs_device_id = 0; +int awacs_has_iic = 0; +#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */ + +/* + * Space for the DBDMA command blocks. + */ +static void *awacs_tx_cmd_space; +static volatile struct dbdma_cmd *awacs_tx_cmds; + +static void *awacs_rx_cmd_space; +static volatile struct dbdma_cmd *awacs_rx_cmds; + +/* + * Cached values of AWACS registers (we can't read them). + * Except on the burgundy. XXX + */ +int awacs_reg[8]; + +#define HAS_16BIT_TABLES +#undef HAS_8BIT_TABLES + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static int beep_volume = BEEP_VOLUME; +static int beep_playing = 0; +static int awacs_beep_state = 0; +static short *beep_buf; +static volatile struct dbdma_cmd *beep_dbdma_cmd; +static void (*orig_mksound)(unsigned int, unsigned int); +static int is_pbook_3400; +static unsigned char *latch_base; +static int is_pbook_G3; +static unsigned char *macio_base; + +/* Burgundy functions */ +static void awacs_burgundy_wcw(unsigned addr,unsigned newval); +static unsigned awacs_burgundy_rcw(unsigned addr); +static void awacs_burgundy_write_volume(unsigned address, int volume); +static int awacs_burgundy_read_volume(unsigned address); +static void awacs_burgundy_write_mvolume(unsigned address, int volume); +static int awacs_burgundy_read_mvolume(unsigned address); + +#ifdef CONFIG_PMAC_PBOOK +/* + * Stuff for restoring after a sleep. + */ +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); +struct pmu_sleep_notifier awacs_sleep_notifier = { + awacs_sleep_notify, SLEEP_LEVEL_SOUND, +}; +#endif /* CONFIG_PMAC_PBOOK */ + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Translations ************************************************************/ + + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); +static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft); + + +/*** Low level stuff *********************************************************/ + + +static void PMacOpen(void); +static void PMacRelease(void); +static void *PMacAlloc(unsigned int size, int flags); +static void PMacFree(void *ptr, unsigned int size); +static int PMacIrqInit(void); +#ifdef MODULE +static void PMacIrqCleanup(void); +#endif +static void PMacSilence(void); +static void PMacInit(void); +static int PMacSetFormat(int format); +static int PMacSetVolume(int volume); +static void PMacPlay(void); +static void PMacRecord(void); +static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs); +static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs); +static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs); +static void awacs_write(int val); +static int awacs_get_volume(int reg, int lshift); +static int awacs_volume_setter(int volume, int n, int mute, int lshift); +static void awacs_mksound(unsigned int hz, unsigned int ticks); +static void awacs_nosound(unsigned long xx); + + +/*** Mid level stuff **********************************************************/ + + +static int PMacMixerIoctl(u_int cmd, u_long arg); +static void PMacWriteSqSetup(void); +static void PMacReadSqSetup(void); +static void PMacAbortRead(void); + + +/*** Translations ************************************************************/ + + +static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + short *table = dmasound.soft.format == AFMT_MU_LAW + ? dmasound_ulaw2dma16 : dmasound_alaw2dma16; + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = (data ^ 0x80) << 8; + } + *p++ = val; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + if (get_user(data, up++)) + return -EFAULT; + *fp++ = data; + *fp++ = data; + count--; + } + } else { + if (copy_from_user(fp, userPtr, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + int data; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + *fp++ = data; + if (stereo) { + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + } + *fp++ = data; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned short *table = (unsigned short *) + (dmasound.soft.format == AFMT_MU_LAW + ? dmasound_ulaw2dma16 : dmasound_alaw2dma16); + unsigned int data = expand_data; + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + int stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + table[c]; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + (c << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = (c ^ 0x80) << 8; + if (stereo) { + if (get_user(c, userPtr++)) + return -EFAULT; + data = (data << 16) + ((c ^ 0x80) << 8); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 2: utotal; +} + + +static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + c; + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + + +static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + unsigned int *p = (unsigned int *) &frame[*frameUsed]; + unsigned int data = expand_data; + unsigned short *up = (unsigned short *) userPtr; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int stereo = dmasound.soft.stereo; + int utotal, ftotal; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + unsigned short c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(data, up++)) + return -EFAULT; + data ^= mask; + if (stereo) { + if (get_user(c, up++)) + return -EFAULT; + data = (data << 16) + (c ^ mask); + } else + data = (data << 16) + data; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) * 4; + utotal -= userCount; + return stereo? utotal * 4: utotal * 2; +} + +static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = val >> 8; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + short *p = (short *) &frame[*frameUsed]; + int val, stereo = dmasound.soft.stereo; + + frameLeft >>= 2; + if (stereo) + userCount >>= 1; + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + u_char data; + + val = *p++; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + if (stereo) { + val = *p; + data = (val >> 8) ^ 0x80; + if (put_user(data, (u_char *)userPtr++)) + return -EFAULT; + } + p++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 2: used; +} + + +static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + if (!stereo) { + short *up = (short *) userPtr; + while (count > 0) { + short data; + data = *fp; + if (put_user(data, up++)) + return -EFAULT; + fp+=2; + count--; + } + } else { + if (copy_to_user((u_char *)userPtr, fp, count * 4)) + return -EFAULT; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + +static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000); + int stereo = dmasound.soft.stereo; + short *fp = (short *) &frame[*frameUsed]; + short *up = (short *) userPtr; + + frameLeft >>= 2; + userCount >>= (stereo? 2: 1); + used = count = min_t(unsigned long, userCount, frameLeft); + while (count > 0) { + int data; + + data = *fp++; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + if (stereo) { + data = *fp; + data ^= mask; + if (put_user(data, up++)) + return -EFAULT; + } + fp++; + count--; + } + *frameUsed += used * 4; + return stereo? used * 4: used * 2; +} + + +static TRANS transAwacsNormal = { + ct_ulaw: pmac_ct_law, + ct_alaw: pmac_ct_law, + ct_s8: pmac_ct_s8, + ct_u8: pmac_ct_u8, + ct_s16be: pmac_ct_s16, + ct_u16be: pmac_ct_u16, + ct_s16le: pmac_ct_s16, + ct_u16le: pmac_ct_u16, +}; + +static TRANS transAwacsExpand = { + ct_ulaw: pmac_ctx_law, + ct_alaw: pmac_ctx_law, + ct_s8: pmac_ctx_s8, + ct_u8: pmac_ctx_u8, + ct_s16be: pmac_ctx_s16, + ct_u16be: pmac_ctx_u16, + ct_s16le: pmac_ctx_s16, + ct_u16le: pmac_ctx_u16, +}; + +static TRANS transAwacsNormalRead = { + ct_s8: pmac_ct_s8_read, + ct_u8: pmac_ct_u8_read, + ct_s16be: pmac_ct_s16_read, + ct_u16be: pmac_ct_u16_read, + ct_s16le: pmac_ct_s16_read, + ct_u16le: pmac_ct_u16_read, +}; + +/*** Low level stuff *********************************************************/ + + + +/* + * PCI PowerMac, with AWACS and DBDMA. + */ + +static void PMacOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void PMacRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static void *PMacAlloc(unsigned int size, int flags) +{ + return kmalloc(size, flags); +} + +static void PMacFree(void *ptr, unsigned int size) +{ + kfree(ptr); +} + +static int __init PMacIrqInit(void) +{ + if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0) + || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0) + || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0)) + return 0; + return 1; +} + +#ifdef MODULE +static void PMacIrqCleanup(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); + /* disable interrupts from awacs interface */ + out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff); +#ifdef CONFIG_PMAC_PBOOK + if (is_pbook_G3) { + feature_clear(awacs_node, FEATURE_Sound_power); + feature_clear(awacs_node, FEATURE_Sound_CLK_enable); + } +#endif + free_irq(awacs_irq, 0); + free_irq(awacs_tx_irq, 0); + free_irq(awacs_rx_irq, 0); + kfree(awacs_tx_cmd_space); + if (awacs_rx_cmd_space) + kfree(awacs_rx_cmd_space); + if (beep_buf) + kfree(beep_buf); + kd_mksound = orig_mksound; +#ifdef CONFIG_PMAC_PBOOK + pmu_unregister_sleep_notifier(&awacs_sleep_notifier); +#endif +} +#endif /* MODULE */ + +static void PMacSilence(void) +{ + /* turn off output dma */ + out_le32(&awacs_txdma->control, RUN<<16); +} + +static int awacs_freqs[8] = { + 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 +}; +static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +static void PMacInit(void) +{ + int i, tolerance; + + switch (dmasound.soft.format) { + case AFMT_S16_LE: + case AFMT_U16_LE: + dmasound.hard.format = AFMT_S16_LE; + break; + default: + dmasound.hard.format = AFMT_S16_BE; + break; + } + dmasound.hard.stereo = 1; + dmasound.hard.size = 16; + + /* + * If we have a sample rate which is within catchRadius percent + * of the requested value, we don't have to expand the samples. + * Otherwise choose the next higher rate. + * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz. + */ + i = 8; + do { + tolerance = catchRadius * awacs_freqs[--i] / 100; + if (awacs_freqs_ok[i] + && dmasound.soft.speed <= awacs_freqs[i] + tolerance) + break; + } while (i > 0); + if (dmasound.soft.speed >= awacs_freqs[i] - tolerance) + dmasound.trans_write = &transAwacsNormal; + else + dmasound.trans_write = &transAwacsExpand; + dmasound.trans_read = &transAwacsNormalRead; + dmasound.hard.speed = awacs_freqs[i]; + awacs_rate_index = i; + + /* XXX disable error interrupt on burgundy for now */ + out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); + awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3); + awacs_write(awacs_reg[1] | MASK_ADDR1); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + + /* We really want to execute a DMA stop command, after the AWACS + * is initialized. + * For reasons I don't understand, it stops the hissing noise + * common to many PowerBook G3 systems (like mine :-). + */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + + expand_bal = -dmasound.soft.speed; +} + +static int PMacSetFormat(int format) +{ + int size; + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n", + format); + size = 8; + format = AFMT_U8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = size; + } + + PMacInit(); + + return format; +} + +#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99)) +#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15)) + +static int awacs_get_volume(int reg, int lshift) +{ + int volume; + + volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf); + volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8; + return volume; +} + +static int awacs_volume_setter(int volume, int n, int mute, int lshift) +{ + int r1, rn; + + if (mute && volume == 0) { + r1 = awacs_reg[1] | mute; + } else { + r1 = awacs_reg[1] & ~mute; + rn = awacs_reg[n] & ~(0xf | (0xf << lshift)); + rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift); + rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf; + awacs_reg[n] = rn; + awacs_write((n << 12) | rn); + volume = awacs_get_volume(rn, lshift); + } + if (r1 != awacs_reg[1]) { + awacs_reg[1] = r1; + awacs_write(r1 | MASK_ADDR1); + } + return volume; +} + +static int PMacSetVolume(int volume) +{ + return awacs_volume_setter(volume, 2, MASK_AMUTE, 6); +} + +static void PMacPlay(void) +{ + volatile struct dbdma_cmd *cp; + int i, count; + unsigned long flags; + + save_flags(flags); cli(); + if (awacs_beep_state) { + /* sound takes precedence over beeps */ + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count]))); + + beep_playing = 0; + awacs_beep_state = 0; + } + i = write_sq.front + write_sq.active; + if (i >= write_sq.max_count) + i -= write_sq.max_count; + while (write_sq.active < 2 && write_sq.active < write_sq.count) { + count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size; + if (count < write_sq.block_size && !write_sq.syncing) + /* last block not yet filled, and we're not syncing. */ + break; + cp = &awacs_tx_cmds[i]; + st_le16(&cp->req_count, count); + st_le16(&cp->xfer_status, 0); + if (++i >= write_sq.max_count) + i = 0; + out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP); + out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS); + if (write_sq.active == 0) + out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp)); + out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + ++write_sq.active; + } + restore_flags(flags); +} + + +static void PMacRecord(void) +{ + unsigned long flags; + + if (read_sq.active) + return; + + save_flags(flags); cli(); + + /* This is all we have to do......Just start it up. + */ + out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + read_sq.active = 1; + + restore_flags(flags); +} + + +static void +pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + int i = write_sq.front; + int stat; + volatile struct dbdma_cmd *cp; + + while (write_sq.active > 0) { + cp = &awacs_tx_cmds[i]; + stat = ld_le16(&cp->xfer_status); + if ((stat & ACTIVE) == 0) + break; /* this frame is still going */ + --write_sq.count; + --write_sq.active; + if (++i >= write_sq.max_count) + i = 0; + } + if (i != write_sq.front) + WAKE_UP(write_sq.action_queue); + write_sq.front = i; + + PMacPlay(); + + if (!write_sq.active) + WAKE_UP(write_sq.sync_queue); +} + + +static void +pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs) +{ + + /* For some reason on my PowerBook G3, I get one interrupt + * when the interrupt vector is installed (like something is + * pending). This happens before the dbdma is initialize by + * us, so I just check the command pointer and if it is zero, + * just blow it off. + */ + if (in_le32(&awacs_rxdma->cmdptr) == 0) + return; + + /* We also want to blow 'em off when shutting down. + */ + if (read_sq.active == 0) + return; + + /* Check multiple buffers in case we were held off from + * interrupt processing for a long time. Geeze, I really hope + * this doesn't happen. + */ + while (awacs_rx_cmds[read_sq.rear].xfer_status) { + + /* Clear status and move on to next buffer. + */ + awacs_rx_cmds[read_sq.rear].xfer_status = 0; + read_sq.rear++; + + /* Wrap the buffer ring. + */ + if (read_sq.rear >= read_sq.max_active) + read_sq.rear = 0; + + /* If we have caught up to the front buffer, bump it. + * This will cause weird (but not fatal) results if the + * read loop is currently using this buffer. The user is + * behind in this case anyway, so weird things are going + * to happen. + */ + if (read_sq.rear == read_sq.front) { + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + + WAKE_UP(read_sq.action_queue); +} + + +static void +pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs) +{ + int ctrl = in_le32(&awacs->control); + + if (ctrl & MASK_PORTCHG) { + /* do something when headphone is plugged/unplugged? */ + } + if (ctrl & MASK_CNTLERR) { + int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16; + if (err != 0 && awacs_revision < AWACS_BURGUNDY) + printk(KERN_ERR "AWACS: error %x\n", err); + } + /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ + out_le32(&awacs->control, ctrl); +} + +static void +awacs_write(int val) +{ + if (awacs_revision >= AWACS_BURGUNDY) + return; + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; /* XXX should have timeout */ + out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22)); +} + +static void awacs_nosound(unsigned long xx) +{ + unsigned long flags; + + save_flags(flags); cli(); + if (beep_playing) { + st_le16(&beep_dbdma_cmd->command, DBDMA_STOP); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (awacs_rate_index << 8)); + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + beep_playing = 0; + } + restore_flags(flags); +} + +static struct timer_list beep_timer = { + function: awacs_nosound +}; + +static void awacs_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + int beep_speed = 0; + int srate; + int period, ncycles, nsamples; + int i, j, f; + short *p; + static int beep_hz_cache; + static int beep_nsamples_cache; + static int beep_volume_cache; + + for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i) + if (awacs_freqs_ok[i]) + beep_speed = i; + srate = awacs_freqs[beep_speed]; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { +#if 1 + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; +#else + /* cancel beep currently playing */ + awacs_nosound(0); + return; +#endif + } + save_flags(flags); cli(); + del_timer(&beep_timer); + if (ticks) { + beep_timer.expires = jiffies + ticks; + add_timer(&beep_timer); + } + if (beep_playing || write_sq.active || beep_buf == NULL) { + restore_flags(flags); + return; /* too hard, sorry :-( */ + } + beep_playing = 1; + st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS); + restore_flags(flags); + + if (hz == beep_hz_cache && beep_volume == beep_volume_cache) { + nsamples = beep_nsamples_cache; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep_buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep_volume; + j = (j + f) & 0xffff; + } + beep_hz_cache = hz; + beep_volume_cache = beep_volume; + beep_nsamples_cache = nsamples; + } + + st_le16(&beep_dbdma_cmd->req_count, nsamples*4); + st_le16(&beep_dbdma_cmd->xfer_status, 0); + st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd)); + st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf)); + awacs_beep_state = 1; + + save_flags(flags); cli(); + if (beep_playing) { /* i.e. haven't been terminated already */ + out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + out_le32(&awacs->control, + (in_le32(&awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&awacs->byteswap, 0); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd)); + out_le32(&awacs_txdma->control, RUN | (RUN << 16)); + } + restore_flags(flags); +} + +#ifdef CONFIG_PMAC_PBOOK +/* + * Save state when going to sleep, restore it afterwards. + */ +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + switch (when) { + case PBOOK_SLEEP_NOW: + /* XXX we should stop any dma in progress when going to sleep + and restart it when we wake. */ + PMacSilence(); + disable_irq(awacs_irq); + disable_irq(awacs_tx_irq); + if (is_pbook_G3) { + feature_clear(awacs_node, FEATURE_Sound_CLK_enable); + feature_clear(awacs_node, FEATURE_Sound_power); + } + break; + case PBOOK_WAKE: + /* There is still a problem on wake. Sound seems to work fine + if I launch mpg123 and resumes fine if mpg123 was playing, + but the console beep is dead until I do something with the + mixer. Probably yet another timing issue */ + if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable) + || !feature_test(awacs_node, FEATURE_Sound_power)) { + /* these aren't present on the 3400 AFAIK -- paulus */ + feature_set(awacs_node, FEATURE_Sound_CLK_enable); + feature_set(awacs_node, FEATURE_Sound_power); + mdelay(1000); + } + out_le32(&awacs->control, MASK_IEPC + | (awacs_rate_index << 8) | 0x11 + | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0)); + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + awacs_write(awacs_reg[2] | MASK_ADDR2); + awacs_write(awacs_reg[4] | MASK_ADDR4); + if (awacs_is_screamer) { + awacs_write(awacs_reg[5] + MASK_ADDR5); + awacs_write(awacs_reg[6] + MASK_ADDR6); + awacs_write(awacs_reg[7] + MASK_ADDR7); + } + out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE); + enable_irq(awacs_irq); + enable_irq(awacs_tx_irq); + if (awacs_revision == 3) { + mdelay(100); + awacs_write(0x6000); + mdelay(2); + awacs_write(awacs_reg[1] | MASK_ADDR1); + } + /* enable CD sound input */ + if (macio_base && is_pbook_G3) { + out_8(macio_base + 0x37, 3); + } else if (is_pbook_3400) { + feature_set(awacs_node, FEATURE_IOBUS_enable); + udelay(10); + in_8(latch_base + 0x190); + } + /* Resume pending sounds. */ + PMacPlay(); + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ + + +/* All the burgundy functions: */ + +/* Waits for busy flag to clear */ +inline static void +awacs_burgundy_busy_wait(void) +{ + while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) + ; +} + +inline static void +awacs_burgundy_extend_wait(void) +{ + while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) + ; + while (in_le32(&awacs->codec_stat) & MASK_EXTEND) + ; +} + +static void +awacs_burgundy_wcw(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); + awacs_burgundy_busy_wait(); + out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcw(unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + out_le32(&awacs->codec_ctrl, addr + 0x100100); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8; + + out_le32(&awacs->codec_ctrl, addr + 0x100200); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16; + + out_le32(&awacs->codec_ctrl, addr + 0x100300); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24; + + restore_flags(flags); + + return val; +} + + +static void +awacs_burgundy_wcb(unsigned addr, unsigned val) +{ + out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); + awacs_burgundy_busy_wait(); +} + +static unsigned +awacs_burgundy_rcb(unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + save_flags(flags); cli(); + + out_le32(&awacs->codec_ctrl, addr + 0x100000); + awacs_burgundy_busy_wait(); + awacs_burgundy_extend_wait(); + val += (in_le32(&awacs->codec_stat) >> 4) & 0xff; + + restore_flags(flags); + + return val; +} + +static int +awacs_burgundy_check(void) +{ + /* Checks to see the chip is alive and kicking */ + int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE; + + return error == 0xf0000; +} + +static int +awacs_burgundy_init(void) +{ + if (awacs_burgundy_check()) { + printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n"); + return 1; + } + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES, + DEF_BURGUNDY_OUTPUTENABLES); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + DEF_BURGUNDY_MORE_OUTPUTENABLES); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS, + DEF_BURGUNDY_OUTPUTSELECTS); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21, + DEF_BURGUNDY_INPSEL21); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3, + DEF_BURGUNDY_INPSEL3); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD, + DEF_BURGUNDY_GAINCD); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE, + DEF_BURGUNDY_GAINLINE); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC, + DEF_BURGUNDY_GAINMIC); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM, + DEF_BURGUNDY_GAINMODEM); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, + DEF_BURGUNDY_ATTENSPEAKER); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT, + DEF_BURGUNDY_ATTENLINEOUT); + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP, + DEF_BURGUNDY_ATTENHP); + + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME, + DEF_BURGUNDY_MASTER_VOLUME); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD, + DEF_BURGUNDY_VOLCD); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE, + DEF_BURGUNDY_VOLLINE); + awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC, + DEF_BURGUNDY_VOLMIC); + return 0; +} + +static void +awacs_burgundy_write_volume(unsigned address, int volume) +{ + int hardvolume,lvolume,rvolume; + + lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0; + rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0; + + hardvolume = lvolume + (rvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +static int +awacs_burgundy_read_volume(unsigned address) +{ + int softvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + softvolume = (wvolume & 0xff) - 155; + softvolume += (((wvolume >> 16) & 0xff) - 155)<<8; + + return softvolume > 0 ? softvolume : 0; +} + + + + +static int +awacs_burgundy_read_mvolume(unsigned address) +{ + int lvolume,rvolume,wvolume; + + wvolume = awacs_burgundy_rcw(address); + + wvolume &= 0xffff; + + rvolume = (wvolume & 0xff) - 155; + lvolume = ((wvolume & 0xff00)>>8) - 155; + + return lvolume + (rvolume << 8); +} + + +static void +awacs_burgundy_write_mvolume(unsigned address, int volume) +{ + int lvolume,rvolume,hardvolume; + + lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0; + rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0; + + hardvolume = lvolume + (rvolume << 8); + hardvolume += (hardvolume << 16); + + awacs_burgundy_wcw(address, hardvolume); +} + +/* End burgundy functions */ + + + + + +/* Turn on sound output, needed on G3 desktop powermacs */ +static void +awacs_enable_amp(int spkr_vol) +{ + struct adb_request req; + + awacs_spkr_vol = spkr_vol; + if (sys_ctrler != SYS_CTRLER_CUDA) + return; + +#ifdef CONFIG_ADB_CUDA + /* turn on headphones */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 4, 0); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 6, 0); + while (!req.complete) cuda_poll(); + + /* turn on speaker */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100); + while (!req.complete) cuda_poll(); + + cuda_request(&req, NULL, 5, CUDA_PACKET, + CUDA_GET_SET_IIC, 0x8a, 1, 0x29); + while (!req.complete) cuda_poll(); +#endif /* CONFIG_ADB_CUDA */ +} + + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +static int awacs_mixer_ioctl(u_int cmd, u_long arg) +{ + int data; + + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD | SOUND_MASK_RECLEV + | SOUND_MASK_ALTPCM + | SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + if (awacs_reg[1] & MASK_LOOPTHRU) + data |= SOUND_MASK_MONITOR; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD + | SOUND_MASK_MONITOR); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + awacs_reg[1] &= ~MASK_LOOPTHRU; + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + if (data & SOUND_MASK_MONITOR) + awacs_reg[1] |= MASK_LOOPTHRU; + awacs_write(awacs_reg[0] | MASK_ADDR0); + awacs_write(awacs_reg[1] | MASK_ADDR1); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_VOLUME: + data = (awacs_reg[1] & MASK_AMUTE)? 0: + awacs_get_volume(awacs_reg[2], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, PMacSetVolume(data)); + case SOUND_MIXER_READ_SPEAKER: + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + data = awacs_spkr_vol; + else + data = (awacs_reg[1] & MASK_CMUTE)? 0: + awacs_get_volume(awacs_reg[4], 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + if (awacs_revision == 3 + && sys_ctrler == SYS_CTRLER_CUDA) + awacs_enable_amp(data); + else + data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_AUDIN; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_AUDIN; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + data &= 0xff; + awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); + if (data >= 25) { + awacs_reg[0] |= MASK_MUX_MIC; + if (data >= 75) + awacs_reg[0] |= MASK_GAINLINE; + } + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = (awacs_reg[0] & MASK_MUX_MIC)? + (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_reg[0] &= ~MASK_MUX_CD; + if ((data & 0xff) >= 50) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(MASK_ADDR0 | awacs_reg[0]); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case MIXER_WRITE(SOUND_MIXER_MONITOR): + IOCTL_IN(arg, data); + awacs_reg[1] &= ~MASK_LOOPTHRU; + if ((data & 0xff) >= 50) + awacs_reg[1] |= MASK_LOOPTHRU; + awacs_write(MASK_ADDR1 | awacs_reg[1]); + /* fall through */ + case MIXER_READ(SOUND_MIXER_MONITOR): + data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0; + return IOCTL_OUT(arg, data); + } + return -EINVAL; +} + +static int burgundy_mixer_ioctl(u_int cmd, u_long arg) +{ + int data; + + /* We are, we are, we are... Burgundy or better */ + switch(cmd) { + case SOUND_MIXER_READ_DEVMASK: + data = SOUND_MASK_VOLUME | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECMASK: + data = SOUND_MASK_LINE | SOUND_MASK_MIC + | SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECSRC: + data = 0; + if (awacs_reg[0] & MASK_MUX_AUDIN) + data |= SOUND_MASK_LINE; + if (awacs_reg[0] & MASK_MUX_MIC) + data |= SOUND_MASK_MIC; + if (awacs_reg[0] & MASK_MUX_CD) + data |= SOUND_MASK_CD; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECSRC: + IOCTL_IN(arg, data); + data &= (SOUND_MASK_LINE + | SOUND_MASK_MIC | SOUND_MASK_CD); + awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC + | MASK_MUX_AUDIN); + if (data & SOUND_MASK_LINE) + awacs_reg[0] |= MASK_MUX_AUDIN; + if (data & SOUND_MASK_MIC) + awacs_reg[0] |= MASK_MUX_MIC; + if (data & SOUND_MASK_CD) + awacs_reg[0] |= MASK_MUX_CD; + awacs_write(awacs_reg[0] | MASK_ADDR0); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_STEREODEVS: + data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER + | SOUND_MASK_RECLEV | SOUND_MASK_CD + | SOUND_MASK_LINE; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_CAPS: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); + /* Fall through */ + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); + case SOUND_MIXER_WRITE_SPEAKER: + IOCTL_IN(arg, data); + + if (!(data & 0xff)) { + /* Mute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); + } else { + /* Unmute the left speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); + } + if (!(data & 0xff00)) { + /* Mute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); + } else { + /* Unmute the right speaker */ + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); + } + + data = (((data&0xff)*16)/100 > 0xf ? 0xf : + (((data&0xff)*16)/100)) + + ((((data>>8)*16)/100 > 0xf ? 0xf : + ((((data>>8)*16)/100)))<<4); + + awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); + /* Fall through */ + case SOUND_MIXER_READ_SPEAKER: + data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); + data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); + return IOCTL_OUT(arg, ~data); + case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ + IOCTL_IN(arg, data); + beep_volume = data & 0xff; + /* fall through */ + case SOUND_MIXER_READ_ALTPCM: + return IOCTL_OUT(arg, beep_volume); + case SOUND_MIXER_WRITE_LINE: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); + + /* fall through */ + case SOUND_MIXER_READ_LINE: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); + /* Mic is mono device */ + data = (data << 8) + (data << 24); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); + /* fall through */ + case SOUND_MIXER_READ_MIC: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); + data <<= 24; + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_CD: + IOCTL_IN(arg, data); + awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); + /* fall through */ + case SOUND_MIXER_READ_CD: + data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_WRITE_RECLEV: + IOCTL_IN(arg, data); + data = awacs_volume_setter(data, 0, 0, 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_READ_RECLEV: + data = awacs_get_volume(awacs_reg[0], 4); + return IOCTL_OUT(arg, data); + case SOUND_MIXER_OUTMASK: + break; + case SOUND_MIXER_OUTSRC: + break; + } + return -EINVAL; +} + +static int PMacMixerIoctl(u_int cmd, u_long arg) +{ + /* Different IOCTLS for burgundy*/ + if (awacs_revision >= AWACS_BURGUNDY) + return burgundy_mixer_ioctl(cmd, arg); + return awacs_mixer_ioctl(cmd, arg); +} + + +static void PMacWriteSqSetup(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_tx_cmds; + memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd)); + for (i = 0; i < write_sq.numBufs; ++i, ++cp) { + st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i])); + } + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds)); + out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds)); +} + +static void PMacReadSqSetup(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_rx_cmds; + memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd)); + + /* Set dma buffers up in a loop */ + for (i = 0; i < read_sq.numBufs; i++,cp++) { + st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i])); + st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS); + st_le16(&cp->req_count, read_sq.block_size); + st_le16(&cp->xfer_status, 0); + } + + /* The next two lines make the thing loop around. + */ + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds)); + + /* Don't start until the first read is done. + * This will also abort any operations in progress if the DMA + * happens to be running (and it shouldn't). + */ + out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds)); + +} + +static void PMacAbortRead(void) +{ + int i; + volatile struct dbdma_cmd *cp; + + cp = awacs_rx_cmds; + for (i = 0; i < read_sq.numBufs; i++,cp++) + st_le16(&cp->command, DBDMA_STOP); + /* + * We should probably wait for the thing to stop before we + * release the memory + */ +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machPMac = { + name: awacs_name, + name2: "AWACS", + open: PMacOpen, + release: PMacRelease, + dma_alloc: PMacAlloc, + dma_free: PMacFree, + irqinit: PMacIrqInit, +#ifdef MODULE + irqcleanup: PMacIrqCleanup, +#endif /* MODULE */ + init: PMacInit, + silence: PMacSilence, + setFormat: PMacSetFormat, + setVolume: PMacSetVolume, + play: PMacPlay, + record: PMacRecord, + mixer_ioctl: PMacMixerIoctl, + write_sq_setup: PMacWriteSqSetup, + read_sq_setup: PMacReadSqSetup, + abort_read: PMacAbortRead, + min_dsp_speed: 8000 +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_awacs_init(void) +{ + struct device_node *np; + + if (_machine != _MACH_Pmac) + return -ENODEV; + + awacs_subframe = 0; + awacs_revision = 0; + np = find_devices("awacs"); + if (np == 0) { + /* + * powermac G3 models have a node called "davbus" + * with a child called "sound". + */ + struct device_node *sound; + np = find_devices("davbus"); + sound = find_devices("sound"); + if (sound != 0 && sound->parent == np) { + unsigned int *prop, l, i; + prop = (unsigned int *) + get_property(sound, "sub-frame", 0); + if (prop != 0 && *prop >= 0 && *prop < 16) + awacs_subframe = *prop; + if (device_is_compatible(sound, "burgundy")) + awacs_revision = AWACS_BURGUNDY; + /* This should be verified on older screamers */ + if (device_is_compatible(sound, "screamer")) + awacs_is_screamer = 1; + prop = (unsigned int *)get_property(sound, "device-id", 0); + if (prop != 0) + awacs_device_id = *prop; + awacs_has_iic = (find_devices("perch") != NULL); + + /* look for a property saying what sample rates + are available */ + for (i = 0; i < 8; ++i) + awacs_freqs_ok[i] = 0; + prop = (unsigned int *) get_property + (sound, "sample-rates", &l); + if (prop == 0) + prop = (unsigned int *) get_property + (sound, "output-frame-rates", &l); + if (prop != 0) { + for (l /= sizeof(int); l > 0; --l) { + /* sometimes the rate is in the + high-order 16 bits (?) */ + unsigned int r = *prop++; + if (r >= 0x10000) + r >>= 16; + for (i = 0; i < 8; ++i) { + if (r == awacs_freqs[i]) { + awacs_freqs_ok[i] = 1; + break; + } + } + } + } else { + /* assume just 44.1k is OK */ + awacs_freqs_ok[0] = 1; + } + } + } + if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { + int vol; + dmasound.mach = machPMac; + + awacs = (volatile struct awacs_regs *) + ioremap(np->addrs[0].address, 0x80); + awacs_txdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[1].address, 0x100); + awacs_rxdma = (volatile struct dbdma_regs *) + ioremap(np->addrs[2].address, 0x100); + + awacs_irq = np->intrs[0].line; + awacs_tx_irq = np->intrs[1].line; + awacs_rx_irq = np->intrs[2].line; + + awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd), + GFP_KERNEL); + if (awacs_tx_cmd_space == NULL) { + printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n"); + return -ENOMEM; + } + awacs_node = np; +#ifdef CONFIG_PMAC_PBOOK + if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) { + pmu_suspend(); + feature_set(np, FEATURE_Sound_CLK_enable); + feature_set(np, FEATURE_Sound_power); + /* Shorter delay will not work */ + mdelay(1000); + pmu_resume(); + } +#endif + awacs_tx_cmds = (volatile struct dbdma_cmd *) + DBDMA_ALIGN(awacs_tx_cmd_space); + + + awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd), + GFP_KERNEL); + if (awacs_rx_cmd_space == NULL) { + printk("DMA sound driver: No memory for input"); + } + awacs_rx_cmds = (volatile struct dbdma_cmd *) + DBDMA_ALIGN(awacs_rx_cmd_space); + + + + awacs_reg[0] = MASK_MUX_CD; + /* FIXME: Only machines with external SRS module need MASK_PAROUT */ + awacs_reg[1] = MASK_LOOPTHRU; + if (awacs_has_iic || awacs_device_id == 0x5 || /*awacs_device_id == 0x8 + || */awacs_device_id == 0xb) + awacs_reg[1] |= MASK_PAROUT; + /* get default volume from nvram */ + vol = (~nvram_read_byte(0x1308) & 7) << 1; + awacs_reg[2] = vol + (vol << 6); + awacs_reg[4] = vol + (vol << 6); + awacs_reg[5] = 0; + awacs_reg[6] = 0; + awacs_reg[7] = 0; + out_le32(&awacs->control, 0x11); + awacs_write(awacs_reg[0] + MASK_ADDR0); + awacs_write(awacs_reg[1] + MASK_ADDR1); + awacs_write(awacs_reg[2] + MASK_ADDR2); + awacs_write(awacs_reg[4] + MASK_ADDR4); + if (awacs_is_screamer) { + awacs_write(awacs_reg[5] + MASK_ADDR5); + awacs_write(awacs_reg[6] + MASK_ADDR6); + awacs_write(awacs_reg[7] + MASK_ADDR7); + } + + /* Initialize recent versions of the awacs */ + if (awacs_revision == 0) { + awacs_revision = + (in_le32(&awacs->codec_stat) >> 12) & 0xf; + if (awacs_revision == 3) { + mdelay(100); + awacs_write(0x6000); + mdelay(2); + awacs_write(awacs_reg[1] + MASK_ADDR1); + awacs_enable_amp(100 * 0x101); + } + } + if (awacs_revision >= AWACS_BURGUNDY) + awacs_burgundy_init(); + + /* Initialize beep stuff */ + beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1); + orig_mksound = kd_mksound; + kd_mksound = awacs_mksound; + beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (beep_buf == NULL) + printk(KERN_WARNING "dmasound: no memory for " + "beep buffer\n"); +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&awacs_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + + /* Powerbooks have odd ways of enabling inputs such as + an expansion-bay CD or sound from an internal modem + or a PC-card modem. */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) { + is_pbook_3400 = 1; + /* + * Enable CD and PC-card sound inputs. + * This is done by reading from address + * f301a000, + 0x10 to enable the expansion-bay + * CD sound input, + 0x80 to enable the PC-card + * sound input. The 0x100 enables the SCSI bus + * terminator power. + */ + latch_base = (unsigned char *) ioremap + (0xf301a000, 0x1000); + in_8(latch_base + 0x190); + } else if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) { + struct device_node* mio; + macio_base = 0; + is_pbook_G3 = 1; + for (mio = np->parent; mio; mio = mio->parent) { + if (strcmp(mio->name, "mac-io") == 0 + && mio->n_addrs > 0) { + macio_base = (unsigned char *) ioremap + (mio->addrs[0].address, 0x40); + break; + } + } + /* + * Enable CD sound input. + * The relevant bits for writing to this byte are 0x8f. + * I haven't found out what the 0x80 bit does. + * For the 0xf bits, writing 3 or 7 enables the CD + * input, any other value disables it. Values + * 1, 3, 5, 7 enable the microphone. Values 0, 2, + * 4, 6, 8 - f enable the input from the modem. + */ + if (macio_base) + out_8(macio_base + 0x37, 3); + } + sprintf(awacs_name, "PowerMac (AWACS rev %d) ", + awacs_revision); + return dmasound_init(); + } + return -ENODEV; +} + +static void __exit dmasound_awacs_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_awacs_init); +module_exit(dmasound_awacs_cleanup); +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/dmasound/dmasound_core.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_core.c --- linux/sound/oss/dmasound/dmasound_core.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_core.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1313 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_core.c + * + * + * OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for + * Linux/m68k + * Extended to support Power Macintosh for Linux/ppc by Paul Mackerras + * + * (c) 1995 by Michael Schlueter & Michael Marte + * + * Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS + * interface and the u-law to signed byte conversion. + * + * Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue, + * /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like + * to thank: + * - Michael Schlueter for initial ideas and documentation on the MFP and + * the DMA sound hardware. + * - Therapy? for their CD 'Troublegum' which really made me rock. + * + * /dev/sndstat is based on code by Hannu Savolainen, the author of the + * VoxWare family of drivers. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * History: + * + * 1995/8/25 First release + * + * 1995/9/02 Roman Hodek: + * - Fixed atari_stram_alloc() call, the timer + * programming and several race conditions + * 1995/9/14 Roman Hodek: + * - After some discussion with Michael Schlueter, + * revised the interrupt disabling + * - Slightly speeded up U8->S8 translation by using + * long operations where possible + * - Added 4:3 interpolation for /dev/audio + * + * 1995/9/20 Torsten Scherer: + * - Fixed a bug in sq_write and changed /dev/audio + * converting to play at 12517Hz instead of 6258Hz. + * + * 1995/9/23 Torsten Scherer: + * - Changed sq_interrupt() and sq_play() to pre-program + * the DMA for another frame while there's still one + * running. This allows the IRQ response to be + * arbitrarily delayed and playing will still continue. + * + * 1995/10/14 Guenther Kelleter, Torsten Scherer: + * - Better support for Falcon audio (the Falcon doesn't + * raise an IRQ at the end of a frame, but at the + * beginning instead!). uses 'if (codec_dma)' in lots + * of places to simply switch between Falcon and TT + * code. + * + * 1995/11/06 Torsten Scherer: + * - Started introducing a hardware abstraction scheme + * (may perhaps also serve for Amigas?) + * - Can now play samples at almost all frequencies by + * means of a more generalized expand routine + * - Takes a good deal of care to cut data only at + * sample sizes + * - Buffer size is now a kernel runtime option + * - Implemented fsync() & several minor improvements + * Guenther Kelleter: + * - Useful hints and bug fixes + * - Cross-checked it for Falcons + * + * 1996/3/9 Geert Uytterhoeven: + * - Support added for Amiga, A-law, 16-bit little + * endian. + * - Unification to drivers/sound/dmasound.c. + * + * 1996/4/6 Martin Mitchell: + * - Updated to 1.3 kernel. + * + * 1996/6/13 Topi Kanerva: + * - Fixed things that were broken (mainly the amiga + * 14-bit routines) + * - /dev/sndstat shows now the real hardware frequency + * - The lowpass filter is disabled by default now + * + * 1996/9/25 Geert Uytterhoeven: + * - Modularization + * + * 1998/6/10 Andreas Schwab: + * - Converted to use sound_core + * + * 1999/12/28 Richard Zidlicky: + * - Added support for Q40 + * + * 2000/2/27 Geert Uytterhoeven: + * - Clean up and split the code into 4 parts: + * o dmasound_core: machine-independent code + * o dmasound_atari: Atari TT and Falcon support + * o dmasound_awacs: Apple PowerMac support + * o dmasound_paula: Amiga support + * + * 2000/3/25 Geert Uytterhoeven: + * - Integration of dmasound_q40 + * - Small clean ups + */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include "dmasound.h" + + + /* + * Declarations + */ + +int dmasound_catchRadius = 0; +static unsigned int numWriteBufs = 4; +static unsigned int writeBufSize = 32; /* in KB! */ +#ifdef HAS_RECORD +static unsigned int numReadBufs = 4; +static unsigned int readBufSize = 32; /* in KB! */ +#endif + +MODULE_PARM(dmasound_catchRadius, "i"); +MODULE_PARM(numWriteBufs, "i"); +MODULE_PARM(writeBufSize, "i"); +MODULE_PARM(numReadBufs, "i"); +MODULE_PARM(readBufSize, "i"); +MODULE_LICENSE("GPL"); + +#ifdef MODULE +static int sq_unit = -1; +static int mixer_unit = -1; +static int state_unit = -1; +static int irq_installed = 0; +#endif /* MODULE */ + + + /* + * Conversion tables + */ + +#ifdef HAS_8BIT_TABLES +/* 8 bit mu-law */ + +char dmasound_ulaw2dma8[] = { + -126, -122, -118, -114, -110, -106, -102, -98, + -94, -90, -86, -82, -78, -74, -70, -66, + -63, -61, -59, -57, -55, -53, -51, -49, + -47, -45, -43, -41, -39, -37, -35, -33, + -31, -30, -29, -28, -27, -26, -25, -24, + -23, -22, -21, -20, -19, -18, -17, -16, + -16, -15, -15, -14, -14, -13, -13, -12, + -12, -11, -11, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -7, -6, -6, + -6, -6, -5, -5, -5, -5, -4, -4, + -4, -4, -4, -4, -3, -3, -3, -3, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0, + 125, 121, 117, 113, 109, 105, 101, 97, + 93, 89, 85, 81, 77, 73, 69, 65, + 62, 60, 58, 56, 54, 52, 50, 48, + 46, 44, 42, 40, 38, 36, 34, 32, + 30, 29, 28, 27, 26, 25, 24, 23, + 22, 21, 20, 19, 18, 17, 16, 15, + 15, 14, 14, 13, 13, 12, 12, 11, + 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 6, 6, 5, 5, + 5, 5, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* 8 bit A-law */ + +char dmasound_alaw2dma8[] = { + -22, -21, -24, -23, -18, -17, -20, -19, + -30, -29, -32, -31, -26, -25, -28, -27, + -11, -11, -12, -12, -9, -9, -10, -10, + -15, -15, -16, -16, -13, -13, -14, -14, + -86, -82, -94, -90, -70, -66, -78, -74, + -118, -114, -126, -122, -102, -98, -110, -106, + -43, -41, -47, -45, -35, -33, -39, -37, + -59, -57, -63, -61, -51, -49, -55, -53, + -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -6, -6, -6, -6, -5, -5, -5, -5, + -8, -8, -8, -8, -7, -7, -7, -7, + -3, -3, -3, -3, -3, -3, -3, -3, + -4, -4, -4, -4, -4, -4, -4, -4, + 21, 20, 23, 22, 17, 16, 19, 18, + 29, 28, 31, 30, 25, 24, 27, 26, + 10, 10, 11, 11, 8, 8, 9, 9, + 14, 14, 15, 15, 12, 12, 13, 13, + 86, 82, 94, 90, 70, 66, 78, 74, + 118, 114, 126, 122, 102, 98, 110, 106, + 43, 41, 47, 45, 35, 33, 39, 37, + 59, 57, 63, 61, 51, 49, 55, 53, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 4, 4, 4, 4, + 7, 7, 7, 7, 6, 6, 6, 6, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3 +}; +#endif /* HAS_8BIT_TABLES */ + +#ifdef HAS_16BIT_TABLES + +/* 16 bit mu-law */ + +short dmasound_ulaw2dma16[] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0, +}; + +/* 16 bit A-law */ + +short dmasound_alaw2dma16[] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, + -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, + -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848, +}; +#endif /* HAS_16BIT_TABLES */ + + +#ifdef HAS_14BIT_TABLES + + /* + * Unused for now. Where are the MSB parts anyway?? + */ + +/* 14 bit mu-law (LSB) */ + +char dmasound_ulaw2dma14l[] = { + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 49, 17, 49, 17, 49, 17, 49, 17, + 49, 17, 49, 17, 49, 17, 49, 17, + 41, 57, 9, 25, 41, 57, 9, 25, + 41, 57, 9, 25, 41, 57, 9, 25, + 37, 45, 53, 61, 5, 13, 21, 29, + 37, 45, 53, 61, 5, 13, 21, 29, + 35, 39, 43, 47, 51, 55, 59, 63, + 3, 7, 11, 15, 19, 23, 27, 31, + 34, 36, 38, 40, 42, 44, 46, 48, + 50, 52, 54, 56, 58, 60, 62, 0, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, + 15, 47, 15, 47, 15, 47, 15, 47, + 15, 47, 15, 47, 15, 47, 15, 47, + 23, 7, 55, 39, 23, 7, 55, 39, + 23, 7, 55, 39, 23, 7, 55, 39, + 27, 19, 11, 3, 59, 51, 43, 35, + 27, 19, 11, 3, 59, 51, 43, 35, + 29, 25, 21, 17, 13, 9, 5, 1, + 61, 57, 53, 49, 45, 41, 37, 33, + 30, 28, 26, 24, 22, 20, 18, 16, + 14, 12, 10, 8, 6, 4, 2, 0 +}; + +/* 14 bit A-law (LSB) */ + +char dmasound_alaw2dma14l[] = { + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 16, 48, 16, 48, 16, 48, 16, 48, + 16, 48, 16, 48, 16, 48, 16, 48, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 42, 46, 34, 38, 58, 62, 50, 54, + 10, 14, 2, 6, 26, 30, 18, 22, + 42, 46, 34, 38, 58, 62, 50, 54, + 10, 14, 2, 6, 26, 30, 18, 22, + 40, 56, 8, 24, 40, 56, 8, 24, + 40, 56, 8, 24, 40, 56, 8, 24, + 20, 28, 4, 12, 52, 60, 36, 44, + 20, 28, 4, 12, 52, 60, 36, 44, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 48, 16, 48, 16, 48, 16, 48, 16, + 48, 16, 48, 16, 48, 16, 48, 16, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 22, 18, 30, 26, 6, 2, 14, 10, + 54, 50, 62, 58, 38, 34, 46, 42, + 22, 18, 30, 26, 6, 2, 14, 10, + 54, 50, 62, 58, 38, 34, 46, 42, + 24, 8, 56, 40, 24, 8, 56, 40, + 24, 8, 56, 40, 24, 8, 56, 40, + 44, 36, 60, 52, 12, 4, 28, 20, + 44, 36, 60, 52, 12, 4, 28, 20 +}; +#endif /* HAS_14BIT_TABLES */ + + + /* + * Mid level stuff + */ + +struct sound_settings dmasound; + +static inline void sound_silence(void) +{ + /* update hardware settings one more */ + dmasound.mach.init(); + + dmasound.mach.silence(); +} + +static inline void sound_init(void) +{ + dmasound.mach.init(); +} + +static inline int sound_set_format(int format) +{ + return dmasound.mach.setFormat(format); +} + +static int sound_set_speed(int speed) +{ + if (speed < 0) + return dmasound.soft.speed; + + dmasound.soft.speed = speed; + dmasound.mach.init(); + if (dmasound.minDev == SND_DEV_DSP) + dmasound.dsp.speed = dmasound.soft.speed; + + return dmasound.soft.speed; +} + +static int sound_set_stereo(int stereo) +{ + if (stereo < 0) + return dmasound.soft.stereo; + + stereo = !!stereo; /* should be 0 or 1 now */ + + dmasound.soft.stereo = stereo; + if (dmasound.minDev == SND_DEV_DSP) + dmasound.dsp.stereo = stereo; + dmasound.mach.init(); + + return stereo; +} + +static ssize_t sound_copy_translate(TRANS *trans, const u_char *userPtr, + size_t userCount, u_char frame[], + ssize_t *frameUsed, ssize_t frameLeft) +{ + ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t); + + switch (dmasound.soft.format) { + case AFMT_MU_LAW: + ct_func = trans->ct_ulaw; + break; + case AFMT_A_LAW: + ct_func = trans->ct_alaw; + break; + case AFMT_S8: + ct_func = trans->ct_s8; + break; + case AFMT_U8: + ct_func = trans->ct_u8; + break; + case AFMT_S16_BE: + ct_func = trans->ct_s16be; + break; + case AFMT_U16_BE: + ct_func = trans->ct_u16be; + break; + case AFMT_S16_LE: + ct_func = trans->ct_s16le; + break; + case AFMT_U16_LE: + ct_func = trans->ct_u16le; + break; + default: + return 0; + } + return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); +} + + + /* + * /dev/mixer abstraction + */ + +static struct { + int busy; + int modify_counter; +} mixer; + +static int mixer_open(struct inode *inode, struct file *file) +{ + dmasound.mach.open(); + mixer.busy = 1; + return 0; +} + +static int mixer_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + mixer.busy = 0; + dmasound.mach.release(); + unlock_kernel(); + return 0; +} +static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + mixer.modify_counter++; + switch (cmd) { + case OSS_GETVERSION: + return IOCTL_OUT(arg, SOUND_VERSION); + case SOUND_MIXER_INFO: + { + mixer_info info; + strncpy(info.id, dmasound.mach.name2, sizeof(info.id)); + strncpy(info.name, dmasound.mach.name2, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer.modify_counter; + if (copy_to_user((int *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + } + if (dmasound.mach.mixer_ioctl) + return dmasound.mach.mixer_ioctl(cmd, arg); + return -EINVAL; +} + +static struct file_operations mixer_fops = +{ + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: mixer_ioctl, + open: mixer_open, + release: mixer_release, +}; + +static void __init mixer_init(void) +{ +#ifndef MODULE + int mixer_unit; +#endif + mixer_unit = register_sound_mixer(&mixer_fops, -1); + if (mixer_unit < 0) + return; + + mixer.busy = 0; + dmasound.treble = 0; + dmasound.bass = 0; + if (dmasound.mach.mixer_init) + dmasound.mach.mixer_init(); +} + + + /* + * Sound queue stuff, the heart of the driver + */ + +struct sound_queue dmasound_write_sq; +#ifdef HAS_RECORD +struct sound_queue dmasound_read_sq; +#endif + +static int sq_allocate_buffers(struct sound_queue *sq, int num, int size) +{ + int i; + + if (sq->buffers) + return 0; + sq->numBufs = num; + sq->bufSize = size; + sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL); + if (!sq->buffers) + return -ENOMEM; + for (i = 0; i < num; i++) { + sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL); + if (!sq->buffers[i]) { + while (i--) + dmasound.mach.dma_free(sq->buffers[i], size); + kfree(sq->buffers); + sq->buffers = 0; + return -ENOMEM; + } + } + return 0; +} + +static void sq_release_buffers(struct sound_queue *sq) +{ + int i; + + if (sq->buffers) { + if (sq != &write_sq && dmasound.mach.abort_read) + dmasound.mach.abort_read(); + for (i = 0; i < sq->numBufs; i++) + dmasound.mach.dma_free(sq->buffers[i], sq->bufSize); + kfree(sq->buffers); + sq->buffers = NULL; + } +} + +static void sq_setup(struct sound_queue *sq, int max_count, int max_active, + int block_size) +{ + void (*setup_func)(void); + + sq->max_count = max_count; + sq->max_active = max_active; + sq->block_size = block_size; + + sq->front = sq->count = sq->rear_size = 0; + sq->syncing = 0; + sq->active = 0; + + if (sq == &write_sq) { + sq->rear = -1; + setup_func = dmasound.mach.write_sq_setup; + } else { + sq->rear = 0; + setup_func = dmasound.mach.read_sq_setup; + } + if (setup_func) + setup_func(); +} + +static inline void sq_play(void) +{ + dmasound.mach.play(); +} + +static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, + loff_t *ppos) +{ + ssize_t uWritten = 0; + u_char *dest; + ssize_t uUsed, bUsed, bLeft; + + /* ++TeSche: Is something like this necessary? + * Hey, that's an honest question! Or does any other part of the + * filesystem already checks this situation? I really don't know. + */ + if (uLeft == 0) + return 0; + + /* The interrupt doesn't start to play the last, incomplete frame. + * Thus we can append to it without disabling the interrupts! (Note + * also that write_sq.rear isn't affected by the interrupt.) + */ + + if (write_sq.count > 0 && + (bLeft = write_sq.block_size-write_sq.rear_size) > 0) { + dest = write_sq.buffers[write_sq.rear]; + bUsed = write_sq.rear_size; + uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, + dest, &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + write_sq.rear_size = bUsed; + } + + do { + while (write_sq.count == write_sq.max_active) { + sq_play(); + if (write_sq.open_mode & O_NONBLOCK) + return uWritten > 0 ? uWritten : -EAGAIN; + SLEEP(write_sq.action_queue); + if (signal_pending(current)) + return uWritten > 0 ? uWritten : -EINTR; + } + + /* Here, we can avoid disabling the interrupt by first + * copying and translating the data, and then updating + * the write_sq variables. Until this is done, the interrupt + * won't see the new frame and we can work on it + * undisturbed. + */ + + dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count]; + bUsed = 0; + bLeft = write_sq.block_size; + uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, + dest, &bUsed, bLeft); + if (uUsed <= 0) + break; + src += uUsed; + uWritten += uUsed; + uLeft -= uUsed; + if (bUsed) { + write_sq.rear = (write_sq.rear+1) % write_sq.max_count; + write_sq.rear_size = bUsed; + write_sq.count++; + } + } while (bUsed); /* uUsed may have been 0 */ + + sq_play(); + + return uUsed < 0? uUsed: uWritten; +} + +#ifdef HAS_RECORD + /* + * Here is how the values are used for reading. + * The value 'active' simply indicates the DMA is running. This is done + * so the driver semantics are DMA starts when the first read is posted. + * The value 'front' indicates the buffer we should next send to the user. + * The value 'rear' indicates the buffer the DMA is currently filling. + * When 'front' == 'rear' the buffer "ring" is empty (we always have an + * empty available). The 'rear_size' is used to track partial offsets + * into the current buffer. Right now, I just keep the DMA running. If + * the reader can't keep up, the interrupt tosses the oldest buffer. We + * could also shut down the DMA in this case. + */ + +static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, + loff_t *ppos) +{ + + ssize_t uRead, bLeft, bUsed, uUsed; + + if (uLeft == 0) + return 0; + + if (!read_sq.active && dmasound.mach.record) + dmasound.mach.record(); /* Kick off the record process. */ + + uRead = 0; + + /* Move what the user requests, depending upon other options. + */ + while (uLeft > 0) { + + /* When front == rear, the DMA is not done yet. + */ + while (read_sq.front == read_sq.rear) { + if (read_sq.open_mode & O_NONBLOCK) { + return uRead > 0 ? uRead : -EAGAIN; + } + SLEEP(read_sq.action_queue); + if (signal_pending(current)) + return uRead > 0 ? uRead : -EINTR; + } + + /* The amount we move is either what is left in the + * current buffer or what the user wants. + */ + bLeft = read_sq.block_size - read_sq.rear_size; + bUsed = read_sq.rear_size; + uUsed = sound_copy_translate(dmasound.trans_read, dst, uLeft, + read_sq.buffers[read_sq.front], + &bUsed, bLeft); + if (uUsed <= 0) + return uUsed; + dst += uUsed; + uRead += uUsed; + uLeft -= uUsed; + read_sq.rear_size += bUsed; + if (read_sq.rear_size >= read_sq.block_size) { + read_sq.rear_size = 0; + read_sq.front++; + if (read_sq.front >= read_sq.max_active) + read_sq.front = 0; + } + } + return uRead; +} +#endif /* HAS_RECORD */ + +static inline void sq_init_waitqueue(struct sound_queue *sq) +{ + init_waitqueue_head(&sq->action_queue); + init_waitqueue_head(&sq->open_queue); + init_waitqueue_head(&sq->sync_queue); + sq->busy = 0; +} + +static inline void sq_wake_up(struct sound_queue *sq, struct file *file, + mode_t mode) +{ + if (file->f_mode & mode) { + sq->busy = 0; + WAKE_UP(sq->open_queue); + } +} + +static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode, + int numbufs, int bufsize) +{ + int rc = 0; + + if (file->f_mode & mode) { + if (sq->busy) { + rc = -EBUSY; + if (file->f_flags & O_NONBLOCK) + return rc; + rc = -EINTR; + while (sq->busy) { + SLEEP(sq->open_queue); + if (signal_pending(current)) + return rc; + } + rc = 0; + } + sq->busy = 1; /* Let's play spot-the-race-condition */ + + if (sq_allocate_buffers(sq, numbufs, bufsize)) { + sq_wake_up(sq, file, mode); + return rc; + } + + sq_setup(sq, numbufs, numbufs, bufsize); + sq->open_mode = file->f_mode; + } + return rc; +} + +#define write_sq_init_waitqueue() sq_init_waitqueue(&write_sq) +#define write_sq_wake_up(file) sq_wake_up(&write_sq, file, FMODE_WRITE) +#define write_sq_release_buffers() sq_release_buffers(&write_sq) +#define write_sq_open(file) \ + sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10) + +#ifdef HAS_RECORD +#define read_sq_init_waitqueue() sq_init_waitqueue(&read_sq) +#define read_sq_wake_up(file) sq_wake_up(&read_sq, file, FMODE_READ) +#define read_sq_release_buffers() sq_release_buffers(&read_sq) +#define read_sq_open(file) \ + sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10) +#else /* !HAS_RECORD */ +#define read_sq_init_waitqueue() do {} while (0) +#define read_sq_wake_up(file) do {} while (0) +#define read_sq_release_buffers() do {} while (0) +#define read_sq_open(file) (0) +#endif /* !HAS_RECORD */ + +static int sq_open(struct inode *inode, struct file *file) +{ + int rc; + + dmasound.mach.open(); + if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) { + dmasound.mach.release(); + return rc; + } + + if (dmasound.mach.sq_open) + dmasound.mach.sq_open(); + dmasound.minDev = MINOR(inode->i_rdev) & 0x0f; + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + sound_init(); + if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { + sound_set_speed(8000); + sound_set_stereo(0); + sound_set_format(AFMT_MU_LAW); + } + +#if 0 + if (file->f_mode == FMODE_READ && dmasound.mach.record) { + /* Start dma'ing straight away */ + dmasound.mach.record(); + } +#endif + + return 0; +} + +static void sq_reset(void) +{ + sound_silence(); + write_sq.active = 0; + write_sq.count = 0; + write_sq.front = (write_sq.rear+1) % write_sq.max_count; +} + +static int sq_fsync(struct file *filp, struct dentry *dentry) +{ + int rc = 0; + + write_sq.syncing = 1; + sq_play(); /* there may be an incomplete frame waiting */ + + while (write_sq.active) { + SLEEP(write_sq.sync_queue); + if (signal_pending(current)) { + /* While waiting for audio output to drain, an + * interrupt occurred. Stop audio output immediately + * and clear the queue. */ + sq_reset(); + rc = -EINTR; + break; + } + } + + write_sq.syncing = 0; + return rc; +} + +static int sq_release(struct inode *inode, struct file *file) +{ + int rc = 0; + + lock_kernel(); + if (write_sq.busy) + rc = sq_fsync(file, file->f_dentry); + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + sound_silence(); + + write_sq_release_buffers(); + read_sq_release_buffers(); + dmasound.mach.release(); + + /* There is probably a DOS atack here. They change the mode flag. */ + /* XXX add check here */ + read_sq_wake_up(file); + write_sq_wake_up(file); + + /* Wake up a process waiting for the queue being released. + * Note: There may be several processes waiting for a call + * to open() returning. */ + unlock_kernel(); + + return rc; +} + +static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg) +{ + int val; + u_long fmt; + int data; + int size, nbufs; + audio_buf_info info; + + switch (cmd) { + case SNDCTL_DSP_RESET: + sq_reset(); + return 0; + case SNDCTL_DSP_POST: + case SNDCTL_DSP_SYNC: + return sq_fsync(file, file->f_dentry); + + /* ++TeSche: before changing any of these it's + * probably wise to wait until sound playing has + * settled down. */ + case SNDCTL_DSP_SPEED: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_speed(data)); + case SNDCTL_DSP_STEREO: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data)); + case SOUND_PCM_WRITE_CHANNELS: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_stereo(data-1)+1); + case SNDCTL_DSP_SETFMT: + sq_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, sound_set_format(data)); + case SNDCTL_DSP_GETFMTS: + fmt = 0; + if (dmasound.trans_write) { + if (dmasound.trans_write->ct_ulaw) + fmt |= AFMT_MU_LAW; + if (dmasound.trans_write->ct_alaw) + fmt |= AFMT_A_LAW; + if (dmasound.trans_write->ct_s8) + fmt |= AFMT_S8; + if (dmasound.trans_write->ct_u8) + fmt |= AFMT_U8; + if (dmasound.trans_write->ct_s16be) + fmt |= AFMT_S16_BE; + if (dmasound.trans_write->ct_u16be) + fmt |= AFMT_U16_BE; + if (dmasound.trans_write->ct_s16le) + fmt |= AFMT_S16_LE; + if (dmasound.trans_write->ct_u16le) + fmt |= AFMT_U16_LE; + } + return IOCTL_OUT(arg, fmt); + case SNDCTL_DSP_GETBLKSIZE: + size = write_sq.block_size + * dmasound.soft.size * (dmasound.soft.stereo + 1) + / (dmasound.hard.size * (dmasound.hard.stereo + 1)); + return IOCTL_OUT(arg, size); + case SNDCTL_DSP_SUBDIVIDE: + break; + case SNDCTL_DSP_SETFRAGMENT: + if (write_sq.count || write_sq.active || write_sq.syncing) + return -EINVAL; + IOCTL_IN(arg, size); + nbufs = size >> 16; + if (nbufs < 2 || nbufs > write_sq.numBufs) + nbufs = write_sq.numBufs; + size &= 0xffff; + if (size >= 8 && size <= 29) { + size = 1 << size; + size *= dmasound.hard.size * (dmasound.hard.stereo + 1); + size /= dmasound.soft.size * (dmasound.soft.stereo + 1); + if (size > write_sq.bufSize) + size = write_sq.bufSize; + } else + size = write_sq.bufSize; + sq_setup(&write_sq, write_sq.numBufs, nbufs, size); + return IOCTL_OUT(arg,write_sq.bufSize | write_sq.numBufs << 16); + case SNDCTL_DSP_GETOSPACE: + info.fragments = write_sq.max_active - write_sq.count; + info.fragstotal = write_sq.max_active; + info.fragsize = write_sq.block_size; + info.bytes = info.fragments * info.fragsize; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETCAPS: + val = 1; /* Revision level of this ioctl() */ + return IOCTL_OUT(arg,val); + + default: + return mixer_ioctl(inode, file, cmd, arg); + } + return -EINVAL; +} + +static struct file_operations sq_fops = +{ + owner: THIS_MODULE, + llseek: no_llseek, + write: sq_write, + ioctl: sq_ioctl, + open: sq_open, + release: sq_release, +#ifdef HAS_RECORD + read: sq_read, +#endif +}; + +static void __init sq_init(void) +{ +#ifndef MODULE + int sq_unit; +#endif + sq_unit = register_sound_dsp(&sq_fops, -1); + if (sq_unit < 0) + return; + + write_sq_init_waitqueue(); + read_sq_init_waitqueue(); + + /* whatever you like as startup mode for /dev/dsp, + * (/dev/audio hasn't got a startup mode). note that + * once changed a new open() will *not* restore these! + */ + dmasound.dsp.format = AFMT_U8; + dmasound.dsp.stereo = 0; + dmasound.dsp.size = 8; + + /* set minimum rate possible without expanding */ + dmasound.dsp.speed = dmasound.mach.min_dsp_speed; + + /* before the first open to /dev/dsp this wouldn't be set */ + dmasound.soft = dmasound.dsp; + dmasound.hard = dmasound.dsp; + + sound_silence(); +} + + + /* + * /dev/sndstat + */ + +static struct { + int busy; + char buf[512]; /* state.buf should not overflow! */ + int len, ptr; +} state; + +static int state_open(struct inode *inode, struct file *file) +{ + char *buffer = state.buf; + int len = 0; + + if (state.busy) + return -EBUSY; + + dmasound.mach.open(); + state.ptr = 0; + state.busy = 1; + + len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name); + + len += sprintf(buffer+len, "\tsound.format = 0x%x", + dmasound.soft.format); + switch (dmasound.soft.format) { + case AFMT_MU_LAW: + len += sprintf(buffer+len, " (mu-law)"); + break; + case AFMT_A_LAW: + len += sprintf(buffer+len, " (A-law)"); + break; + case AFMT_U8: + len += sprintf(buffer+len, " (unsigned 8 bit)"); + break; + case AFMT_S8: + len += sprintf(buffer+len, " (signed 8 bit)"); + break; + case AFMT_S16_BE: + len += sprintf(buffer+len, " (signed 16 bit big)"); + break; + case AFMT_U16_BE: + len += sprintf(buffer+len, " (unsigned 16 bit big)"); + break; + case AFMT_S16_LE: + len += sprintf(buffer+len, " (signed 16 bit little)"); + break; + case AFMT_U16_LE: + len += sprintf(buffer+len, " (unsigned 16 bit little)"); + break; + } + len += sprintf(buffer+len, "\n"); + len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n", + dmasound.soft.speed, dmasound.hard.speed); + len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n", + dmasound.soft.stereo, + dmasound.soft.stereo ? "stereo" : "mono"); + if (dmasound.mach.state_info) + len += dmasound.mach.state_info(buffer); + len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d" + " sq.max_active = %d\n", + write_sq.block_size, write_sq.max_count, + write_sq.max_active); + len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", + write_sq.count, write_sq.rear_size); + len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n", + write_sq.active, write_sq.syncing); + state.len = len; + return 0; +} + +static int state_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + state.busy = 0; + dmasound.mach.release(); + unlock_kernel(); + return 0; +} + +static ssize_t state_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int n = state.len - state.ptr; + if (n > count) + n = count; + if (n <= 0) + return 0; + if (copy_to_user(buf, &state.buf[state.ptr], n)) + return -EFAULT; + state.ptr += n; + return n; +} + +static struct file_operations state_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: state_read, + open: state_open, + release: state_release, +}; + +static void __init state_init(void) +{ +#ifndef MODULE + int state_unit; +#endif + state_unit = register_sound_special(&state_fops, SND_DEV_STATUS); + if (state_unit < 0) + return; + state.busy = 0; +} + + + /* + * Config & Setup + * + * This function is called by _one_ chipset-specific driver + */ + +int __init dmasound_init(void) +{ +#ifdef MODULE + if (irq_installed) + return -EBUSY; +#endif + + /* Set up sound queue, /dev/audio and /dev/dsp. */ + + /* Set default settings. */ + sq_init(); + + /* Set up /dev/sndstat. */ + state_init(); + + /* Set up /dev/mixer. */ + mixer_init(); + + if (!dmasound.mach.irqinit()) { + printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n"); + return -ENODEV; + } +#ifdef MODULE + irq_installed = 1; +#endif + + printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", + numWriteBufs, writeBufSize); + + return 0; +} + +#ifdef MODULE + +void dmasound_deinit(void) +{ + if (irq_installed) { + sound_silence(); + dmasound.mach.irqcleanup(); + } + + write_sq_release_buffers(); + read_sq_release_buffers(); + + if (mixer_unit >= 0) + unregister_sound_mixer(mixer_unit); + if (state_unit >= 0) + unregister_sound_special(state_unit); + if (sq_unit >= 0) + unregister_sound_dsp(sq_unit); +} + +#else /* !MODULE */ + +static int __init dmasound_setup(char *str) +{ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + /* check the bootstrap parameter for "dmasound=" */ + + switch (ints[0]) { + case 3: + if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS)) + printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius); + else + catchRadius = ints[3]; + /* fall through */ + case 2: + if (ints[1] < MIN_BUFFERS) + printk("dmasound_setup: illegal number of buffers, using default = %d\n", numWriteBufs); + else + numWriteBufs = ints[1]; + if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE) + printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize); + else + writeBufSize = ints[2]; + break; + case 0: + break; + default: + printk("dmasound_setup: illegal number of arguments\n"); + return 0; + } + return 1; +} + +__setup("dmasound=", dmasound_setup); + +#endif /* !MODULE */ + + + /* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(dmasound); +EXPORT_SYMBOL(dmasound_init); +#ifdef MODULE +EXPORT_SYMBOL(dmasound_deinit); +#endif +EXPORT_SYMBOL(dmasound_write_sq); +#ifdef HAS_RECORD +EXPORT_SYMBOL(dmasound_read_sq); +#endif +EXPORT_SYMBOL(dmasound_catchRadius); +#ifdef HAS_8BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma8); +EXPORT_SYMBOL(dmasound_alaw2dma8); +#endif +#ifdef HAS_16BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma16); +EXPORT_SYMBOL(dmasound_alaw2dma16); +#endif +#ifdef HAS_14BIT_TABLES +EXPORT_SYMBOL(dmasound_ulaw2dma14l); +EXPORT_SYMBOL(dmasound_ulaw2dma14h); +EXPORT_SYMBOL(dmasound_alaw2dma14l); +EXPORT_SYMBOL(dmasound_alaw2dma14h); +#endif + diff -Nru linux/sound/oss/dmasound/dmasound_paula.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_paula.c --- linux/sound/oss/dmasound/dmasound_paula.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_paula.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,723 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_paula.c + * + * Amiga `Paula' DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "dmasound.h" + + + /* + * The minimum period for audio depends on htotal (for OCS/ECS/AGA) + * (Imported from arch/m68k/amiga/amisound.c) + */ + +extern volatile u_short amiga_audio_min_period; + + + /* + * amiga_mksound() should be able to restore the period after beeping + * (Imported from arch/m68k/amiga/amisound.c) + */ + +extern u_short amiga_audio_period; + + + /* + * Audio DMA masks + */ + +#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3) +#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1) +#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3) + + + /* + * Helper pointers for 16(14)-bit sound + */ + +static int write_sq_block_size_half, write_sq_block_size_quarter; + + +/*** Low level stuff *********************************************************/ + + +static void AmiOpen(void); +static void AmiRelease(void); +static void *AmiAlloc(unsigned int size, int flags); +static void AmiFree(void *obj, unsigned int size); +static int AmiIrqInit(void); +#ifdef MODULE +static void AmiIrqCleanUp(void); +#endif +static void AmiSilence(void); +static void AmiInit(void); +static int AmiSetFormat(int format); +static int AmiSetVolume(int volume); +static int AmiSetTreble(int treble); +static void AmiPlayNextFrame(int index); +static void AmiPlay(void); +static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp); + +#ifdef CONFIG_HEARTBEAT + + /* + * Heartbeat interferes with sound since the 7 kHz low-pass filter and the + * power LED are controlled by the same line. + */ + +#ifdef CONFIG_APUS +#define mach_heartbeat ppc_md.heartbeat +#endif + +static void (*saved_heartbeat)(int) = NULL; + +static inline void disable_heartbeat(void) +{ + if (mach_heartbeat) { + saved_heartbeat = mach_heartbeat; + mach_heartbeat = NULL; + } + AmiSetTreble(dmasound.treble); +} + +static inline void enable_heartbeat(void) +{ + if (saved_heartbeat) + mach_heartbeat = saved_heartbeat; +} +#else /* !CONFIG_HEARTBEAT */ +#define disable_heartbeat() do { } while (0) +#define enable_heartbeat() do { } while (0) +#endif /* !CONFIG_HEARTBEAT */ + + +/*** Mid level stuff *********************************************************/ + +static void AmiMixerInit(void); +static int AmiMixerIoctl(u_int cmd, u_long arg); +static void AmiWriteSqSetup(void); +static int AmiStateInfo(char *buffer); + + +/*** Translations ************************************************************/ + +/* ++TeSche: radically changed for new expanding purposes... + * + * These two routines now deal with copying/expanding/translating the samples + * from user space into our buffer at the right frequency. They take care about + * how much data there's actually to read, how much buffer space there is and + * to convert samples into the right frequency/encoding. They will only work on + * complete samples so it may happen they leave some bytes in the input stream + * if the user didn't write a multiple of the current sample size. They both + * return the number of bytes they've used from both streams so you may detect + * such a situation. Luckily all programs should be able to cope with that. + * + * I think I've optimized anything as far as one can do in plain C, all + * variables should fit in registers and the loops are really short. There's + * one loop for every possible situation. Writing a more generalized and thus + * parameterized loop would only produce slower code. Feel free to optimize + * this in assembler if you like. :) + * + * I think these routines belong here because they're not yet really hardware + * independent, especially the fact that the Falcon can play 16bit samples + * only in stereo is hardcoded in both of them! + * + * ++geert: split in even more functions (one per format) + */ + + + /* + * Native format + */ + +static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, ssize_t frameLeft) +{ + ssize_t count, used; + + if (!dmasound.soft.stereo) { + void *p = &frame[*frameUsed]; + count = min_t(unsigned long, userCount, frameLeft) & ~1; + used = count; + if (copy_from_user(p, userPtr, count)) + return -EFAULT; + } else { + u_char *left = &frame[*frameUsed>>1]; + u_char *right = left+write_sq_block_size_half; + count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1; + used = count*2; + while (count > 0) { + if (get_user(*left++, userPtr++) + || get_user(*right++, userPtr++)) + return -EFAULT; + count--; + } + } + *frameUsed += used; + return used; +} + + + /* + * Copy and convert 8 bit data + */ + +#define GENERATE_AMI_CT8(funcname, convsample) \ +static ssize_t funcname(const u_char *userPtr, size_t userCount, \ + u_char frame[], ssize_t *frameUsed, \ + ssize_t frameLeft) \ +{ \ + ssize_t count, used; \ + \ + if (!dmasound.soft.stereo) { \ + u_char *p = &frame[*frameUsed]; \ + count = min_t(size_t, userCount, frameLeft) & ~1; \ + used = count; \ + while (count > 0) { \ + u_char data; \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *p++ = convsample(data); \ + count--; \ + } \ + } else { \ + u_char *left = &frame[*frameUsed>>1]; \ + u_char *right = left+write_sq_block_size_half; \ + count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \ + used = count*2; \ + while (count > 0) { \ + u_char data; \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *left++ = convsample(data); \ + if (get_user(data, userPtr++)) \ + return -EFAULT; \ + *right++ = convsample(data); \ + count--; \ + } \ + } \ + *frameUsed += used; \ + return used; \ +} + +#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)]) +#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)]) +#define AMI_CT_U8(x) ((x) ^ 0x80) + +GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW) +GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW) +GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8) + + + /* + * Copy and convert 16 bit data + */ + +#define GENERATE_AMI_CT_16(funcname, convsample) \ +static ssize_t funcname(const u_char *userPtr, size_t userCount, \ + u_char frame[], ssize_t *frameUsed, \ + ssize_t frameLeft) \ +{ \ + ssize_t count, used; \ + u_short data; \ + \ + if (!dmasound.soft.stereo) { \ + u_char *high = &frame[*frameUsed>>1]; \ + u_char *low = high+write_sq_block_size_half; \ + count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \ + used = count*2; \ + while (count > 0) { \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *high++ = data>>8; \ + *low++ = (data>>2) & 0x3f; \ + count--; \ + } \ + } else { \ + u_char *lefth = &frame[*frameUsed>>2]; \ + u_char *leftl = lefth+write_sq_block_size_quarter; \ + u_char *righth = lefth+write_sq_block_size_half; \ + u_char *rightl = righth+write_sq_block_size_quarter; \ + count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \ + used = count*4; \ + while (count > 0) { \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *lefth++ = data>>8; \ + *leftl++ = (data>>2) & 0x3f; \ + if (get_user(data, ((u_short *)userPtr)++)) \ + return -EFAULT; \ + data = convsample(data); \ + *righth++ = data>>8; \ + *rightl++ = (data>>2) & 0x3f; \ + count--; \ + } \ + } \ + *frameUsed += used; \ + return used; \ +} + +#define AMI_CT_S16BE(x) (x) +#define AMI_CT_U16BE(x) ((x) ^ 0x8000) +#define AMI_CT_S16LE(x) (le2be16((x))) +#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000) + +GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE) +GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE) +GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE) +GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE) + + +static TRANS transAmiga = { + ct_ulaw: ami_ct_ulaw, + ct_alaw: ami_ct_alaw, + ct_s8: ami_ct_s8, + ct_u8: ami_ct_u8, + ct_s16be: ami_ct_s16be, + ct_u16be: ami_ct_u16be, + ct_s16le: ami_ct_s16le, + ct_u16le: ami_ct_u16le, +}; + +/*** Low level stuff *********************************************************/ + + +static void AmiOpen(void) +{ + MOD_INC_USE_COUNT; +} + +static void AmiRelease(void) +{ + MOD_DEC_USE_COUNT; +} + +static inline void StopDMA(void) +{ + custom.aud[0].audvol = custom.aud[1].audvol = 0; + custom.aud[2].audvol = custom.aud[3].audvol = 0; + custom.dmacon = AMI_AUDIO_OFF; + enable_heartbeat(); +} + +static void *AmiAlloc(unsigned int size, int flags) +{ + return amiga_chip_alloc((long)size, "dmasound [Paula]"); +} + +static void AmiFree(void *obj, unsigned int size) +{ + amiga_chip_free (obj); +} + +static int __init AmiIrqInit(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); + + /* Register interrupt handler. */ + if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound", + AmiInterrupt)) + return 0; + return 1; +} + +#ifdef MODULE +static void AmiIrqCleanUp(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); + /* release the interrupt */ + free_irq(IRQ_AMIGA_AUD0, AmiInterrupt); +} +#endif /* MODULE */ + +static void AmiSilence(void) +{ + /* turn off DMA for audio channels */ + StopDMA(); +} + + +static void AmiInit(void) +{ + int period, i; + + AmiSilence(); + + if (dmasound.soft.speed) + period = amiga_colorclock/dmasound.soft.speed-1; + else + period = amiga_audio_min_period; + dmasound.hard = dmasound.soft; + dmasound.trans_write = &transAmiga; + + if (period < amiga_audio_min_period) { + /* we would need to squeeze the sound, but we won't do that */ + period = amiga_audio_min_period; + } else if (period > 65535) { + period = 65535; + } + dmasound.hard.speed = amiga_colorclock/(period+1); + + for (i = 0; i < 4; i++) + custom.aud[i].audper = period; + amiga_audio_period = period; +} + + +static int AmiSetFormat(int format) +{ + int size; + + /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */ + + switch (format) { + case AFMT_QUERY: + return dmasound.soft.format; + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + size = 8; + break; + case AFMT_S16_BE: + case AFMT_U16_BE: + case AFMT_S16_LE: + case AFMT_U16_LE: + size = 16; + break; + default: /* :-) */ + size = 8; + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = size; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = dmasound.soft.size; + } + AmiInit(); + + return format; +} + + +#define VOLUME_VOXWARE_TO_AMI(v) \ + (((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100) +#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64) + +static int AmiSetVolume(int volume) +{ + dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff); + custom.aud[0].audvol = dmasound.volume_left; + dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8); + custom.aud[1].audvol = dmasound.volume_right; + if (dmasound.hard.size == 16) { + if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { + custom.aud[2].audvol = 1; + custom.aud[3].audvol = 1; + } else { + custom.aud[2].audvol = 0; + custom.aud[3].audvol = 0; + } + } + return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | + (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); +} + +static int AmiSetTreble(int treble) +{ + dmasound.treble = treble; + if (treble < 50) + ciaa.pra &= ~0x02; + else + ciaa.pra |= 0x02; + return treble; +} + + +#define AMI_PLAY_LOADED 1 +#define AMI_PLAY_PLAYING 2 +#define AMI_PLAY_MASK 3 + + +static void AmiPlayNextFrame(int index) +{ + u_char *start, *ch0, *ch1, *ch2, *ch3; + u_long size; + + /* used by AmiPlay() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + size = (write_sq.count == index ? write_sq.rear_size + : write_sq.block_size)>>1; + + if (dmasound.hard.stereo) { + ch0 = start; + ch1 = start+write_sq_block_size_half; + size >>= 1; + } else { + ch0 = start; + ch1 = start; + } + + disable_heartbeat(); + custom.aud[0].audvol = dmasound.volume_left; + custom.aud[1].audvol = dmasound.volume_right; + if (dmasound.hard.size == 8) { + custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); + custom.aud[0].audlen = size; + custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); + custom.aud[1].audlen = size; + custom.dmacon = AMI_AUDIO_8; + } else { + size >>= 1; + custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0); + custom.aud[0].audlen = size; + custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1); + custom.aud[1].audlen = size; + if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { + /* We can play pseudo 14-bit only with the maximum volume */ + ch3 = ch0+write_sq_block_size_quarter; + ch2 = ch1+write_sq_block_size_quarter; + custom.aud[2].audvol = 1; /* we are being affected by the beeps */ + custom.aud[3].audvol = 1; /* restoring volume here helps a bit */ + custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2); + custom.aud[2].audlen = size; + custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3); + custom.aud[3].audlen = size; + custom.dmacon = AMI_AUDIO_14; + } else { + custom.aud[2].audvol = 0; + custom.aud[3].audvol = 0; + custom.dmacon = AMI_AUDIO_8; + } + } + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active |= AMI_PLAY_LOADED; +} + + +static void AmiPlay(void) +{ + int minframes = 1; + + custom.intena = IF_AUD0; + + if (write_sq.active & AMI_PLAY_LOADED) { + /* There's already a frame loaded */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + if (write_sq.active & AMI_PLAY_PLAYING) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; + + if (write_sq.count < minframes) { + /* Nothing to do */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + if (write_sq.count <= minframes && + write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + custom.intena = IF_SETCLR | IF_AUD0; + return; + } + + AmiPlayNextFrame(minframes); + + custom.intena = IF_SETCLR | IF_AUD0; +} + + +static void AmiInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + int minframes = 1; + + custom.intena = IF_AUD0; + + if (!write_sq.active) { + /* Playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + return; + } + + if (write_sq.active & AMI_PLAY_PLAYING) { + /* We've just finished a frame */ + write_sq.count--; + WAKE_UP(write_sq.action_queue); + } + + if (write_sq.active & AMI_PLAY_LOADED) + /* Increase threshold: frame 1 is already being played */ + minframes = 2; + + /* Shift the flags */ + write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK; + + if (!write_sq.active) + /* No frame is playing, disable audio DMA */ + StopDMA(); + + custom.intena = IF_SETCLR | IF_AUD0; + + if (write_sq.count >= minframes) + /* Try to play the next frame */ + AmiPlay(); + + if (!write_sq.active) + /* Nothing to play anymore. + Wake up a process waiting for audio output to drain. */ + WAKE_UP(write_sq.sync_queue); +} + +/*** Mid level stuff *********************************************************/ + + +/* + * /dev/mixer abstraction + */ + +static void __init AmiMixerInit(void) +{ + dmasound.volume_left = 64; + dmasound.volume_right = 64; + custom.aud[0].audvol = dmasound.volume_left; + custom.aud[3].audvol = 1; /* For pseudo 14bit */ + custom.aud[1].audvol = dmasound.volume_right; + custom.aud[2].audvol = 1; /* For pseudo 14bit */ + dmasound.treble = 50; +} + +static int AmiMixerIoctl(u_int cmd, u_long arg) +{ + int data; + switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE); + case SOUND_MIXER_READ_RECMASK: + return IOCTL_OUT(arg, 0); + case SOUND_MIXER_READ_STEREODEVS: + return IOCTL_OUT(arg, SOUND_MASK_VOLUME); + case SOUND_MIXER_READ_VOLUME: + return IOCTL_OUT(arg, + VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) | + VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8); + case SOUND_MIXER_WRITE_VOLUME: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_volume(data)); + case SOUND_MIXER_READ_TREBLE: + return IOCTL_OUT(arg, dmasound.treble); + case SOUND_MIXER_WRITE_TREBLE: + IOCTL_IN(arg, data); + return IOCTL_OUT(arg, dmasound_set_treble(data)); + } + return -EINVAL; +} + + +static void AmiWriteSqSetup(void) +{ + write_sq_block_size_half = write_sq.block_size>>1; + write_sq_block_size_quarter = write_sq_block_size_half>>1; +} + + +static int AmiStateInfo(char *buffer) +{ + int len = 0; + len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n", + dmasound.volume_left); + len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n", + dmasound.volume_right); + return len; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machAmiga = { + name: "Amiga", + name2: "AMIGA", + open: AmiOpen, + release: AmiRelease, + dma_alloc: AmiAlloc, + dma_free: AmiFree, + irqinit: AmiIrqInit, +#ifdef MODULE + irqcleanup: AmiIrqCleanUp, +#endif /* MODULE */ + init: AmiInit, + silence: AmiSilence, + setFormat: AmiSetFormat, + setVolume: AmiSetVolume, + setTreble: AmiSetTreble, + play: AmiPlay, + mixer_init: AmiMixerInit, + mixer_ioctl: AmiMixerIoctl, + write_sq_setup: AmiWriteSqSetup, + state_info: AmiStateInfo, + min_dsp_speed: 8000 +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_paula_init(void) +{ + int err; + + if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_AUDIO)) { + if (!request_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40, + "dmasound [Paula]")) + return -EBUSY; + dmasound.mach = machAmiga; + err = dmasound_init(); + if (err) + release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); + return err; + } else + return -ENODEV; +} + +static void __exit dmasound_paula_cleanup(void) +{ + dmasound_deinit(); + release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40); +} + +module_init(dmasound_paula_init); +module_exit(dmasound_paula_cleanup); +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/dmasound/dmasound_q40.c linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_q40.c --- linux/sound/oss/dmasound/dmasound_q40.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/dmasound/dmasound_q40.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,588 @@ + +/* + * linux/drivers/sound/dmasound/dmasound_q40.c + * + * Q40 DMA Sound Driver + * + * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits + */ + + +#include +#include +#include +#include + +#include +#include + +#include "dmasound.h" + + +static int expand_bal; /* Balance factor for expanding (not volume!) */ +static int expand_data; /* Data for expanding */ + + +/*** Low level stuff *********************************************************/ + + +static void Q40Open(void); +static void Q40Release(void); +static void *Q40Alloc(unsigned int size, int flags); +static void Q40Free(void *, unsigned int); +static int Q40IrqInit(void); +#ifdef MODULE +static void Q40IrqCleanUp(void); +#endif +static void Q40Silence(void); +static void Q40Init(void); +static int Q40SetFormat(int format); +static int Q40SetVolume(int volume); +static void Q40PlayNextFrame(int index); +static void Q40Play(void); +static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp); +static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp); +static void Q40Interrupt(void); + + +/*** Mid level stuff *********************************************************/ + + +#if 1 +/* userCount, frameUsed, frameLeft == byte counts */ +static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min_t(size_t, userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + while (count > 0) { + *p = table[*p]+128; + p++; + count--; + } + *frameUsed += used ; + return used; +} +#else +static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = sound.soft.stereo; + + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min_t(size_t, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]+128; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = table[data]+128; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +#if 1 +static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min_t(size_t, userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + while (count > 0) { + *p = *p + 128; + p++; + count--; + } + *frameUsed += used; + return used; +} +#else +static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = dmasound.soft.stereo; + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min_t(size_t, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data + 128; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data + 128; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +#if 1 +static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + + used = count = min_t(size_t, userCount, frameLeft); + if (copy_from_user(p,userPtr,count)) + return -EFAULT; + *frameUsed += used; + return used; +} +#else +static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + ssize_t count, used; + u_char *p = (u_char *) &frame[*frameUsed]; + u_char val; + int stereo = dmasound.soft.stereo; + + + frameLeft >>= 1; + if (stereo) + userCount >>= 1; + used = count = min_t(size_t, userCount, frameLeft); + while (count > 0) { + u_char data; + if (get_user(data, userPtr++)) + return -EFAULT; + val = data; + *p++ = val; + if (stereo) { + if (get_user(data, userPtr++)) + return -EFAULT; + val = data; + } + *p++ = val; + count--; + } + *frameUsed += used * 2; + return stereo? used * 2: used; +} +#endif + +/* a bit too complicated to optimise right now ..*/ +static ssize_t q40_ctx_law(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + unsigned char *table = (unsigned char *) + (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8); + unsigned int data = expand_data; + u_char *p = (u_char *) &frame[*frameUsed]; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = table[c]; + data += 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft); + utotal -= userCount; + return utotal; +} + + +static ssize_t q40_ctx_s8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + u_char *p = (u_char *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c ; + data += 0x80; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft); + utotal -= userCount; + return utotal; +} + + +static ssize_t q40_ctx_u8(const u_char *userPtr, size_t userCount, + u_char frame[], ssize_t *frameUsed, + ssize_t frameLeft) +{ + u_char *p = (u_char *) &frame[*frameUsed]; + unsigned int data = expand_data; + int bal = expand_bal; + int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; + int utotal, ftotal; + + ftotal = frameLeft; + utotal = userCount; + while (frameLeft) { + u_char c; + if (bal < 0) { + if (userCount == 0) + break; + if (get_user(c, userPtr++)) + return -EFAULT; + data = c ; + userCount--; + bal += hSpeed; + } + *p++ = data; + frameLeft--; + bal -= sSpeed; + } + expand_bal = bal; + expand_data = data; + *frameUsed += (ftotal - frameLeft) ; + utotal -= userCount; + return utotal; +} + + +static TRANS transQ40Normal = { + q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL +}; + +static TRANS transQ40Expanding = { + q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL +}; + + +/*** Low level stuff *********************************************************/ + + +static void Q40Open(void) +{ + MOD_INC_USE_COUNT; +} + +static void Q40Release(void) +{ + MOD_DEC_USE_COUNT; +} + + +static void *Q40Alloc(unsigned int size, int flags) +{ + return kmalloc(size, flags); /* change to vmalloc */ +} + +static void Q40Free(void *ptr, unsigned int size) +{ + kfree(ptr); +} + +static int __init Q40IrqInit(void) +{ + /* Register interrupt handler. */ + request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, + "DMA sound", Q40Interrupt); + + return(1); +} + + +#ifdef MODULE +static void Q40IrqCleanUp(void) +{ + master_outb(0,SAMPLE_ENABLE_REG); + free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); +} +#endif /* MODULE */ + + +static void Q40Silence(void) +{ + master_outb(0,SAMPLE_ENABLE_REG); + *DAC_LEFT=*DAC_RIGHT=0; +} + +static char *q40_pp=NULL; +static unsigned int q40_sc=0; + +static void Q40PlayNextFrame(int index) +{ + u_char *start; + u_long size; + u_char speed; + + /* used by Q40Play() if all doubts whether there really is something + * to be played are already wiped out. + */ + start = write_sq.buffers[write_sq.front]; + size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size); + + q40_pp=start; + q40_sc=size; + + write_sq.front = (write_sq.front+1) % write_sq.max_count; + write_sq.active++; + + speed=(dmasound.hard.speed==10000 ? 0 : 1); + + master_outb( 0,SAMPLE_ENABLE_REG); + free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); + if (dmasound.soft.stereo) + request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, + "Q40 sound", Q40Interrupt); + else + request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0, + "Q40 sound", Q40Interrupt); + + master_outb( speed, SAMPLE_RATE_REG); + master_outb( 1,SAMPLE_CLEAR_REG); + master_outb( 1,SAMPLE_ENABLE_REG); +} + +static void Q40Play(void) +{ + unsigned long flags; + + if (write_sq.active || write_sq.count<=0 ) { + /* There's already a frame loaded */ + return; + } + + /* nothing in the queue */ + if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { + /* hmmm, the only existing frame is not + * yet filled and we're not syncing? + */ + return; + } + save_flags(flags); cli(); + Q40PlayNextFrame(1); + restore_flags(flags); +} + +static void Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + if (q40_sc>1){ + *DAC_LEFT=*q40_pp++; + *DAC_RIGHT=*q40_pp++; + q40_sc -=2; + master_outb(1,SAMPLE_CLEAR_REG); + }else Q40Interrupt(); +} +static void Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp) +{ + if (q40_sc>0){ + *DAC_LEFT=*q40_pp; + *DAC_RIGHT=*q40_pp++; + q40_sc --; + master_outb(1,SAMPLE_CLEAR_REG); + }else Q40Interrupt(); +} +static void Q40Interrupt(void) +{ + if (!write_sq.active) { + /* playing was interrupted and sq_reset() has already cleared + * the sq variables, so better don't do anything here. + */ + WAKE_UP(write_sq.sync_queue); + master_outb(0,SAMPLE_ENABLE_REG); /* better safe */ + goto exit; + } else write_sq.active=0; + write_sq.count--; + Q40Play(); + + if (q40_sc<2) + { /* there was nothing to play, disable irq */ + master_outb(0,SAMPLE_ENABLE_REG); + *DAC_LEFT=*DAC_RIGHT=0; + } + WAKE_UP(write_sq.action_queue); + + exit: + master_outb(1,SAMPLE_CLEAR_REG); +} + + +static void Q40Init(void) +{ + int i, idx; + const int freq[] = {10000, 20000}; + + /* search a frequency that fits into the allowed error range */ + + idx = -1; + for (i = 0; i < 2; i++) + if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius) + idx = i; + + dmasound.hard = dmasound.soft; + /*sound.hard.stereo=1;*/ /* no longer true */ + dmasound.hard.size=8; + + if (idx > -1) { + dmasound.soft.speed = freq[idx]; + dmasound.trans_write = &transQ40Normal; + } else + dmasound.trans_write = &transQ40Expanding; + + Q40Silence(); + + if (dmasound.hard.speed > 20000) { + /* we would need to squeeze the sound, but we won't do that */ + dmasound.hard.speed = 20000; + dmasound.trans_write = &transQ40Normal; + } else if (dmasound.hard.speed > 10000) { + dmasound.hard.speed = 20000; + } else { + dmasound.hard.speed = 10000; + } + expand_bal = -dmasound.soft.speed; +} + + +static int Q40SetFormat(int format) +{ + /* Q40 sound supports only 8bit modes */ + + switch (format) { + case AFMT_QUERY: + return(dmasound.soft.format); + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_S8: + case AFMT_U8: + break; + default: + format = AFMT_S8; + } + + dmasound.soft.format = format; + dmasound.soft.size = 8; + if (dmasound.minDev == SND_DEV_DSP) { + dmasound.dsp.format = format; + dmasound.dsp.size = 8; + } + Q40Init(); + + return(format); +} + +static int Q40SetVolume(int volume) +{ + return 0; +} + + +/*** Machine definitions *****************************************************/ + + +static MACHINE machQ40 = { + name: "Q40", + name2: "Q40", + open: Q40Open, + release: Q40Release, + dma_alloc: Q40Alloc, + dma_free: Q40Free, + irqinit: Q40IrqInit, +#ifdef MODULE + irqcleanup: Q40IrqCleanUp, +#endif /* MODULE */ + init: Q40Init, + silence: Q40Silence, + setFormat: Q40SetFormat, + setVolume: Q40SetVolume, + play: Q40Play +}; + + +/*** Config & Setup **********************************************************/ + + +int __init dmasound_q40_init(void) +{ + if (MACH_IS_Q40) { + dmasound.mach = machQ40; + return dmasound_init(); + } else + return -ENODEV; +} + +static void __exit dmasound_q40_cleanup(void) +{ + dmasound_deinit(); +} + +module_init(dmasound_q40_init); +module_exit(dmasound_q40_cleanup); +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/emu10k1/8010.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/8010.h --- linux/sound/oss/emu10k1/8010.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/8010.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,624 @@ +/* + ********************************************************************** + * 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 + +#include + +/************************************************************************************************/ +/* PCI function 0 registers, address = + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ + /* NOTE: The CHANNELNUM and ADDRESS words can */ + /* be modified independently of each other. */ +#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */ + /* channel number of the register to be */ + /* accessed. For non per-channel registers the */ + /* value should be set to zero. */ +#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ + +#define 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_GPOUT0 0x00001000 /* set to enable digital out on 5.1 cards */ + +#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_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ +#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ + /* will automatically mute their output when */ + /* they are not rate-locked to the external */ + /* async audio source */ +#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE 0x01020014 +#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ + /* NOTE: This is a 'cheap' way to implement a */ + /* master mute function on the mute button, and */ + /* in general should not be used unless a more */ + /* sophisticated master mute function has not */ + /* been written. */ +#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ + /* Should be set to 1 when the EMU10K1 is */ + /* completely initialized. */ + +#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 */ + +/********************************************************************************************************/ +/* 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 0x07190009 +#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */ +#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ +#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ +#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ +#define CCR_READADDRESS 0x06100009 +#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ +#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ + /* NOTE: This is valid only if CACHELOOPFLAG is set */ +#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ +#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ + +#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ + /* NOTE: This register is normally not used */ +#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */ + +#define FXRT 0x0b /* Effects send routing register */ + /* NOTE: It is illegal to assign the same routing to */ + /* two effects sends. */ +#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */ +#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */ +#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ +#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ + +#define MAPA 0x0c /* Cache map A */ + +#define MAPB 0x0d /* Cache map B */ + +#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ +#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ + +#define ENVVOL 0x10 /* Volume envelope register */ +#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDV 0x11 /* Volume envelope hold and attack register */ +#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ +#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ + +#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ +#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */ + /* this channel and from writing to pitch, filter and */ + /* volume targets. */ +#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL1 0x13 /* Modulation LFO value */ +#define LFOVAL_MASK 0x0000ffff /* Current value of modulation LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ENVVAL 0x14 /* Modulation envelope register */ +#define ENVVAL_MASK 0x0000ffff /* Current value of modulation envelope state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ +#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ +#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ + /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ + +#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ +#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ + /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL2 0x17 /* Vibrato LFO register */ +#define LFOVAL2_MASK 0x0000ffff /* Current value of vibrato LFO state variable */ + /* 0x8000-n == 666*n usec delay */ + +#define IP 0x18 /* Initial pitch register */ +#define IP_MASK 0x0000ffff /* Exponential initial pitch shift */ + /* 4 bits of octave, 12 bits of fractional octave */ +#define IP_UNITY 0x0000e000 /* Unity pitch shift */ + +#define IFATN 0x19 /* Initial filter cutoff and attenuation register */ +#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */ + /* 6 most significant bits are semitones */ + /* 2 least significant bits are fractions */ +#define IFATN_FILTERCUTOFF 0x08080019 +#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ +#define IFATN_ATTENUATION 0x08000019 + + +#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ +#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ + /* Signed 2's complement, +/- one octave peak extremes */ +#define PEFE_PITCHAMOUNT 0x0808001a +#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ + /* Signed 2's complement, +/- six octaves peak extremes */ +#define PEFE_FILTERAMOUNT 0x0800001a +#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ +#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ + /* Signed 2's complement, +/- one octave extremes */ +#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ + /* Signed 2's complement, +/- three octave extremes */ + + +#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ +#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ + /* Signed 2's complement, with +/- 12dB extremes */ + +#define 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 */ + +/* definitions for debug register - taken from the alsa drivers */ +#define DBG_ZC 0x80000000 /* zero tram counter */ +#define DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ +#define DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ +#define DBG_SINGLE_STEP 0x00008000 /* single step mode */ +#define DBG_STEP 0x00004000 /* start single step */ +#define DBG_CONDITION_CODE 0x00003e00 /* condition code */ +#define DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ + + +#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 AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_CNTR 0x10 /* Center enable */ +#define AC97SLOT_LFE 0x20 /* LFE enable */ + +#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ + +#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */ + +#define ZVSRCS 0x62 /* ZVideo sample rate converter status */ + /* NOTE: This one has no SPDIFLOCKED field */ + /* Assumes sample lock */ + +/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */ +#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */ +#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ +#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ + +#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 -Nru linux/sound/oss/emu10k1/Makefile linux-2.4.19-pre5-mjc/sound/oss/emu10k1/Makefile --- linux/sound/oss/emu10k1/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/Makefile Mon Apr 8 22:31:22 2002 @@ -0,0 +1,23 @@ +# Makefile for Creative Labs EMU10K1 +# +# 12 Apr 2000 Rui Sousa + +O_TARGET := emu10k1.o + +obj-y := audio.o cardmi.o cardmo.o cardwi.o cardwo.o ecard.o \ + efxmgr.o emuadxmg.o hwaccess.o irqmgr.o main.o midi.o \ + mixer.o passthrough.o recmgr.o timer.o voicemgr.o +obj-m := $(O_TARGET) + +ifdef DEBUG + EXTRA_CFLAGS += -DEMU10K1_DEBUG +endif + +ifdef CONFIG_MIDI_EMU10K1 + EXTRA_CFLAGS += -DEMU10K1_SEQUENCER +endif + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s diff -Nru linux/sound/oss/emu10k1/audio.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.c --- linux/sound/oss/emu10k1/audio.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1570 @@ +/* + ********************************************************************** + * 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. + * + ********************************************************************** + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "hwaccess.h" +#include "cardwo.h" +#include "cardwi.h" +#include "recmgr.h" +#include "irqmgr.h" +#include "audio.h" +#include "8010.h" + +static void calculate_ofrag(struct woinst *); +static void calculate_ifrag(struct wiinst *); + +/* Audio file operations */ +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; + ssize_t ret = 0; + unsigned long flags; + + DPD(3, "emu10k1_audio_read(), buffer=%p, count=%d\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->mmapped) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -ENXIO; + } + + if (wiinst->state == WAVE_STATE_CLOSED) { + calculate_ifrag(wiinst); + + while (emu10k1_wavein_open(wave_dev) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + interruptible_sleep_on(&wave_dev->card->open_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&wiinst->lock, flags); + } + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + while (count > 0) { + u32 bytestocopy; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (!(wiinst->state & WAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_INPUT)) + emu10k1_wavein_start(wave_dev); + + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(3, "bytestocopy --> %d\n", bytestocopy); + + if ((bytestocopy >= wiinst->buffer.fragment_size) + || (bytestocopy >= count)) { + bytestocopy = min_t(u32, 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); + + interruptible_sleep_on(&wiinst->wait_queue); + + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + + } + } + + DPD(3, "bytes copied -> %d\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; + ssize_t ret; + unsigned long flags; + + DPD(3, "emu10k1_audio_write(), buffer=%p, count=%d\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->mmapped) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -ENXIO; + } + + if (woinst->format.passthrough) { + int r; + + woinst->buffer.ossfragshift = PT_BLOCKSIZE_LOG2; + woinst->buffer.numfrags = PT_BLOCKCOUNT; + calculate_ofrag(woinst); + + r = emu10k1_pt_write(file, buffer, count); + spin_unlock_irqrestore(&woinst->lock, flags); + return r; + } + + if (woinst->state == WAVE_STATE_CLOSED) { + calculate_ofrag(woinst); + + while (emu10k1_waveout_open(wave_dev) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + interruptible_sleep_on(&wave_dev->card->open_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&woinst->lock, flags); + } + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + ret = 0; + if (count % woinst->format.bytespersample) + return -EINVAL; + + count /= woinst->num_voices; + + while (count > 0) { + u32 bytestocopy; + + spin_lock_irqsave(&woinst->lock, flags); + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(3, "bytestocopy --> %d\n", bytestocopy); + + if ((bytestocopy >= woinst->buffer.fragment_size) + || (bytestocopy >= count)) { + + bytestocopy = min_t(u32, bytestocopy, count); + + emu10k1_waveout_xferdata(woinst, (u8 *) buffer, &bytestocopy); + + count -= bytestocopy; + buffer += bytestocopy * woinst->num_voices; + ret += bytestocopy * woinst->num_voices; + + spin_lock_irqsave(&woinst->lock, flags); + woinst->total_copied += bytestocopy; + + if (!(woinst->state & WAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) + && (woinst->total_copied >= woinst->buffer.fragment_size)) + emu10k1_waveout_start(wave_dev); + + 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); + + interruptible_sleep_on(&woinst->wait_queue); + + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + } + } + + DPD(3, "bytes copied -> %d\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; + struct woinst *woinst = NULL; + struct wiinst *wiinst = NULL; + int val = 0; + u32 bytestocopy; + unsigned long flags; + + DPF(4, "emu10k1_audio_ioctl()\n"); + + if (file->f_mode & FMODE_WRITE) + woinst = wave_dev->woinst; + + if (file->f_mode & FMODE_READ) + wiinst = wave_dev->wiinst; + + 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 (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_close(wave_dev); + } + + woinst->mmapped = 0; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_close(wave_dev); + } + + wiinst->mmapped = 0; + wiinst->total_recorded = 0; + wiinst->blocks = 0; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SYNC: + DPF(2, "SNDCTL_DSP_SYNC:\n"); + + if (file->f_mode & FMODE_WRITE) { + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN) { + + if (woinst->state & WAVE_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->mmapped = 0; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_close(wave_dev); + } + + wiinst->mmapped = 0; + wiinst->total_recorded = 0; + wiinst->blocks = 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"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, "val is %d\n", val); + + if (val > 0) { + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.samplingrate = val; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + + val = wiinst->format.samplingrate; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(2, "set recording sampling rate -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.samplingrate = val; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.samplingrate; + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "set playback sampling rate -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->format.samplingrate; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.samplingrate; + + return put_user(val, (int *) arg); + } + break; + + case SNDCTL_DSP_STEREO: + DPF(2, "SNDCTL_DSP_STEREO:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, " val is %d\n", val); + + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.channels = val ? 2 : 1; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + + val = wiinst->format.channels - 1; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording stereo -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.channels = val ? 2 : 1; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.channels - 1; + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "set playback stereo -> %d\n", val); + } + + return put_user(val, (int *) arg); + + break; + + case SNDCTL_DSP_CHANNELS: + DPF(2, "SNDCTL_DSP_CHANNELS:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, " val is %d\n", val); + + if (val > 0) { + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.channels = val; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + val = wiinst->format.channels; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording number of channels -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.channels = val; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.channels; + + spin_unlock_irqrestore(&woinst->lock, flags); + DPD(2, "set playback number of channels -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->format.channels; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.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; + if (emu10k1_find_control_gpr(&wave_dev->card->mgr, + wave_dev->card->pt.patch_name, + wave_dev->card->pt.enable_gpr_name) >= 0) + val |= AFMT_AC3; + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* Same as SNDCTL_DSP_SAMPLESIZE */ + DPF(2, "SNDCTL_DSP_SETFMT:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, " val is %d\n", val); + + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + struct wave_format format; + + spin_lock_irqsave(&wiinst->lock, flags); + + format = wiinst->format; + format.id = val; + + if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + + val = wiinst->format.id; + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "set recording format -> %d\n", val); + } + + if (file->f_mode & FMODE_WRITE) { + struct wave_format format; + + spin_lock_irqsave(&woinst->lock, flags); + + format = woinst->format; + format.id = val; + + if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + val = woinst->format.id; + + spin_unlock_irqrestore(&woinst->lock, flags); + DPD(2, "set playback format -> %d\n", val); + } + + return put_user(val, (int *) arg); + } else { + if (file->f_mode & FMODE_READ) + val = wiinst->format.id; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.id; + + return put_user(val, (int *) arg); + } + break; + + case SOUND_PCM_READ_BITS: + + if (file->f_mode & FMODE_READ) + val = wiinst->format.bitsperchannel; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.bitsperchannel; + + return put_user(val, (int *) arg); + + case SOUND_PCM_READ_RATE: + + if (file->f_mode & FMODE_READ) + val = wiinst->format.samplingrate; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.samplingrate; + + return put_user(val, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: + + if (file->f_mode & FMODE_READ) + val = wiinst->format.channels; + else if (file->f_mode & FMODE_WRITE) + val = woinst->format.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"); + + if (get_user(val, (int *) arg)) + return -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 (woinst->state & WAVE_STATE_OPEN) + emu10k1_waveout_start(wave_dev); + } else { + wave_dev->enablebits &= ~PCM_ENABLE_OUTPUT; + if (woinst->state & WAVE_STATE_STARTED) + 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 (wiinst->state & WAVE_STATE_OPEN) + emu10k1_wavein_start(wave_dev); + } else { + wave_dev->enablebits &= ~PCM_ENABLE_INPUT; + if (wiinst->state & WAVE_STATE_STARTED) + 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; + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + info.bytes = bytestocopy; + } else { + calculate_ofrag(woinst); + info.bytes = woinst->buffer.size; + } + spin_unlock_irqrestore(&woinst->lock, flags); + + info.bytes *= woinst->num_voices; + info.fragsize = woinst->buffer.fragment_size * woinst->num_voices; + info.fragstotal = woinst->buffer.numfrags * woinst->num_voices; + info.fragments = info.bytes / info.fragsize; + + 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; + + spin_lock_irqsave(&wiinst->lock, flags); + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + info.bytes = bytestocopy; + } else { + calculate_ifrag(wiinst); + info.bytes = 0; + } + spin_unlock_irqrestore(&wiinst->lock, flags); + + info.fragstotal = wiinst->buffer.numfrags; + info.fragments = info.bytes / wiinst->buffer.fragment_size; + info.fragsize = wiinst->buffer.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; + + spin_lock_irqsave(&woinst->lock, flags); + if (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + val = woinst->buffer.size - bytestocopy; + } else + val = 0; + + val *= woinst->num_voices; + spin_unlock_irqrestore(&woinst->lock, flags); + + 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 (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_update(wave_dev->card, wiinst); + cinfo.ptr = wiinst->buffer.hw_pos; + cinfo.bytes = cinfo.ptr + wiinst->total_recorded - wiinst->total_recorded % wiinst->buffer.size; + cinfo.blocks = cinfo.bytes / wiinst->buffer.fragment_size - wiinst->blocks; + wiinst->blocks = cinfo.bytes / wiinst->buffer.fragment_size; + } else { + cinfo.ptr = 0; + cinfo.bytes = 0; + cinfo.blocks = 0; + } + + if(wiinst->mmapped) + wiinst->buffer.bytestocopy %= wiinst->buffer.fragment_size; + + 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 (woinst->state & WAVE_STATE_OPEN || + (woinst->format.passthrough && wave_dev->card->pt.state)) { + int num_fragments; + if (woinst->format.passthrough) { + emu10k1_pt_waveout_update(wave_dev); + cinfo.bytes = woinst->total_played; + } else { + emu10k1_waveout_update(woinst); + cinfo.bytes = woinst->total_played; + } + cinfo.ptr = woinst->buffer.hw_pos; + num_fragments = cinfo.bytes / woinst->buffer.fragment_size; + cinfo.blocks = num_fragments - woinst->blocks; + woinst->blocks = num_fragments; + + cinfo.bytes *= woinst->num_voices; + cinfo.ptr *= woinst->num_voices; + } else { + cinfo.ptr = 0; + cinfo.bytes = 0; + cinfo.blocks = 0; + } + + if (woinst->mmapped) + woinst->buffer.free_bytes %= woinst->buffer.fragment_size; + + 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->buffer.fragment_size * woinst->num_voices; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + calculate_ifrag(wiinst); + val = wiinst->buffer.fragment_size; + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return put_user(val, (int *) arg); + + break; + + case SNDCTL_DSP_POST: + if (file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&woinst->lock, flags); + + if (!(woinst->state & WAVE_STATE_STARTED) + && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) + && (woinst->total_copied > 0)) + emu10k1_waveout_start(wave_dev); + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SUBDIVIDE: + DPF(2, "SNDCTL_DSP_SUBDIVIDE: not implemented\n"); + break; + + case SNDCTL_DSP_SETFRAGMENT: + DPF(2, "SNDCTL_DSP_SETFRAGMENT:\n"); + + if (get_user(val, (int *) arg)) + return -EFAULT; + + DPD(2, "val is %#x\n", val); + + if (val == 0) + return -EIO; + + if (file->f_mode & FMODE_WRITE) { + /* digital pass-through fragment count and size are fixed values */ + if (woinst->state & WAVE_STATE_OPEN || woinst->format.passthrough) + return -EINVAL; /* too late to change */ + + woinst->buffer.ossfragshift = val & 0xffff; + woinst->buffer.numfrags = (val >> 16) & 0xffff; + } + + if (file->f_mode & FMODE_READ) { + if (wiinst->state & WAVE_STATE_OPEN) + return -EINVAL; /* too late to change */ + + wiinst->buffer.ossfragshift = val & 0xffff; + wiinst->buffer.numfrags = (val >> 16) & 0xffff; + } + + break; + + case SNDCTL_COPR_LOAD: + { + copr_buffer *buf; + u32 i; + + DPF(4, "SNDCTL_COPR_LOAD:\n"); + + buf = kmalloc(sizeof(copr_buffer), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, (copr_buffer *) arg, sizeof(copr_buffer))) { + kfree (buf); + return -EFAULT; + } + + if ((buf->command != CMD_READ) && (buf->command != CMD_WRITE)) { + kfree (buf); + return -EINVAL; + } +#ifdef DBGEMU + if ( (buf->offs < 0) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) { +#else + if ( ((buf->offs < 0x100 ) || (buf->offs + buf->len > 0x800) || (buf->len > 1000)) + && !( ( buf->offs == DBG) && (buf->len ==1) )){ +#endif + kfree(buf); + return -EINVAL; + } + + if (buf->command == CMD_READ) { + 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(copr_buffer))) { + kfree(buf); + return -EFAULT; + } + } else { + for (i = 0; i < buf->len; i++) + sblive_writeptr(wave_dev->card, buf->offs + i, 0, ((u32 *) buf->data)[i]); + } + + kfree (buf); + break; + } + + default: /* Default is unrecognized command */ + DPD(2, "default: %#x\n", cmd); + return -EINVAL; + } + return 0; +} + +static struct page *emu10k1_mm_nopage (struct vm_area_struct * vma, unsigned long address, int write_access) +{ + struct emu10k1_wavedevice *wave_dev = vma->vm_private_data; + struct woinst *woinst = wave_dev->woinst; + struct wiinst *wiinst = wave_dev->wiinst; + struct page *dmapage; + unsigned long pgoff; + int rd, wr; + + DPF(4, "emu10k1_mm_nopage()\n"); + DPD(4, "addr: %#lx\n", address); + + if (address > vma->vm_end) { + DPF(2, "EXIT, returning NOPAGE_SIGBUS\n"); + return NOPAGE_SIGBUS; /* Disallow mremap */ + } + + pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); + if (woinst != NULL) + wr = woinst->mmapped; + else + wr = 0; + + if (wiinst != NULL) + rd = wiinst->mmapped; + else + rd = 0; + + /* if full-duplex (read+write) and we have two sets of bufs, + * then the playback buffers come first, sez soundcard.c */ + if (wr) { + if (pgoff >= woinst->buffer.pages) { + pgoff -= woinst->buffer.pages; + dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE); + } else + dmapage = virt_to_page (woinst->buffer.mem[0].addr[pgoff]); + } else { + dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE); + } + + get_page (dmapage); + + DPD(4, "page: %#lx\n", dmapage); + return dmapage; +} + +struct vm_operations_struct emu10k1_mm_ops = { + nopage: emu10k1_mm_nopage, +}; + +static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + unsigned long max_pages, n_pages, pgoffset; + struct woinst *woinst = NULL; + struct wiinst *wiinst = NULL; + unsigned long flags; + + DPF(2, "emu10k1_audio_mmap()\n"); + + max_pages = 0; + if (vma->vm_flags & VM_WRITE) { + woinst = wave_dev->woinst; + + spin_lock_irqsave(&woinst->lock, flags); + + /* No m'mapping possible for multichannel */ + if (woinst->num_voices > 1) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + if (woinst->state == WAVE_STATE_CLOSED) { + calculate_ofrag(woinst); + + if (emu10k1_waveout_open(wave_dev) < 0) { + spin_unlock_irqrestore(&woinst->lock, flags); + ERROR(); + return -EINVAL; + } + } + + woinst->mmapped = 1; + max_pages += woinst->buffer.pages; + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (vma->vm_flags & VM_READ) { + wiinst = wave_dev->wiinst; + + spin_lock_irqsave(&wiinst->lock, flags); + if (wiinst->state == WAVE_STATE_CLOSED) { + calculate_ifrag(wiinst); + + if (emu10k1_wavein_open(wave_dev) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + ERROR(); + return -EINVAL; + } + } + + wiinst->mmapped = 1; + max_pages += wiinst->buffer.pages; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + n_pages = ((vma->vm_end - vma->vm_start) + PAGE_SIZE - 1) >> PAGE_SHIFT; + pgoffset = vma->vm_pgoff; + + DPD(3, "vma_start: %#lx, vma_end: %#lx, vma_offset: %d\n", vma->vm_start, vma->vm_end, pgoffset); + DPD(3, "n_pages: %d, max_pages: %d\n", n_pages, max_pages); + + if (pgoffset + n_pages > max_pages) + return -EINVAL; + + vma->vm_flags |= VM_RESERVED; + vma->vm_ops = &emu10k1_mm_ops; + vma->vm_private_data = wave_dev; + + return 0; +} + +static int emu10k1_audio_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct emu10k1_card *card = NULL; + 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->audio_dev ^ minor) & ~0xf) || !((card->audio_dev1 ^ minor) & ~0xf)) + goto match; + } + + return -ENODEV; + +match: + + wave_dev = (struct emu10k1_wavedevice *) kmalloc(sizeof(struct emu10k1_wavedevice), GFP_KERNEL); + + if (wave_dev == NULL) { + ERROR(); + return -ENOMEM; + } + + 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_READ) { + /* Recording */ + struct wiinst *wiinst; + + if ((wiinst = (struct wiinst *) kmalloc(sizeof(struct wiinst), GFP_KERNEL)) == NULL) { + ERROR(); + return -ENODEV; + } + + wiinst->recsrc = card->wavein.recsrc; + wiinst->fxwc = card->wavein.fxwc; + + switch (wiinst->recsrc) { + case WAVERECORD_AC97: + wiinst->format.id = AFMT_S16_LE; + wiinst->format.samplingrate = 8000; + wiinst->format.bitsperchannel = 16; + wiinst->format.channels = 1; + break; + case WAVERECORD_MIC: + wiinst->format.id = AFMT_S16_LE; + wiinst->format.samplingrate = 8000; + wiinst->format.bitsperchannel = 16; + wiinst->format.channels = 1; + break; + case WAVERECORD_FX: + wiinst->format.id = AFMT_S16_LE; + wiinst->format.samplingrate = 48000; + wiinst->format.bitsperchannel = 16; + wiinst->format.channels = hweight32(wiinst->fxwc); + break; + default: + BUG(); + break; + } + + wiinst->state = WAVE_STATE_CLOSED; + + wiinst->buffer.ossfragshift = 0; + wiinst->buffer.fragment_size = 0; + wiinst->buffer.numfrags = 0; + + init_waitqueue_head(&wiinst->wait_queue); + + wiinst->mmapped = 0; + wiinst->total_recorded = 0; + wiinst->blocks = 0; + wiinst->lock = SPIN_LOCK_UNLOCKED; + tasklet_init(&wiinst->timer.tasklet, emu10k1_wavein_bh, (unsigned long) wave_dev); + wave_dev->wiinst = wiinst; + emu10k1_wavein_setformat(wave_dev, &wiinst->format); + } + + if (file->f_mode & FMODE_WRITE) { + struct woinst *woinst; + int i; + + if ((woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL)) == NULL) { + ERROR(); + return -ENODEV; + } + + if (wave_dev->wiinst != NULL) { + woinst->format = wave_dev->wiinst->format; + } else { + woinst->format.id = AFMT_U8; + woinst->format.samplingrate = 8000; + woinst->format.bitsperchannel = 8; + woinst->format.channels = 1; + } + + woinst->state = WAVE_STATE_CLOSED; + + woinst->buffer.fragment_size = 0; + woinst->buffer.ossfragshift = 0; + woinst->buffer.numfrags = 0; + woinst->device = (card->audio_dev1 == minor); + woinst->timer.state = TIMER_STATE_UNINSTALLED; + woinst->num_voices = 1; + for (i = 0; i < WAVEOUT_MAXVOICES; i++) { + woinst->voice[i].usage = VOICE_USAGE_FREE; + woinst->buffer.mem[i].emupageindex = -1; + } + + init_waitqueue_head(&woinst->wait_queue); + + woinst->mmapped = 0; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->blocks = 0; + woinst->lock = SPIN_LOCK_UNLOCKED; + tasklet_init(&woinst->timer.tasklet, emu10k1_waveout_bh, (unsigned long) wave_dev); + wave_dev->woinst = woinst; + emu10k1_waveout_setformat(wave_dev, &woinst->format); + } + + file->private_data = (void *) wave_dev; + + return 0; +} + +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; + unsigned long flags; + + card = wave_dev->card; + + DPF(2, "emu10k1_audio_release()\n"); + + if (file->f_mode & FMODE_WRITE) { + struct woinst *woinst = wave_dev->woinst; + + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->format.passthrough && card->pt.state != PT_STATE_INACTIVE) { + spin_lock(&card->pt.lock); + emu10k1_pt_stop(card); + spin_unlock(&card->pt.lock); + } + if (woinst->state & WAVE_STATE_OPEN) { + if (woinst->state & WAVE_STATE_STARTED) { + if (!(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); + } + } + } + emu10k1_waveout_close(wave_dev); + } + + spin_unlock_irqrestore(&woinst->lock, flags); + /* remove the tasklet */ + tasklet_kill(&woinst->timer.tasklet); + kfree(wave_dev->woinst); + } + + if (file->f_mode & FMODE_READ) { + struct wiinst *wiinst = wave_dev->wiinst; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state & WAVE_STATE_OPEN) { + emu10k1_wavein_close(wave_dev); + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + tasklet_kill(&wiinst->timer.tasklet); + kfree(wave_dev->wiinst); + } + + kfree(wave_dev); + + if (waitqueue_active(&card->open_wait)) + wake_up_interruptible(&card->open_wait); + + return 0; +} + +/* FIXME sort out poll() + mmap() */ +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; + 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) { + spin_lock_irqsave(&woinst->lock, flags); + + if (woinst->state & WAVE_STATE_OPEN) { + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + + if (bytestocopy >= woinst->buffer.fragment_size) + mask |= POLLOUT | POLLWRNORM; + } else + mask |= POLLOUT | POLLWRNORM; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wiinst->state == WAVE_STATE_CLOSED) { + calculate_ifrag(wiinst); + if (emu10k1_wavein_open(wave_dev) < 0) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return (mask |= POLLERR); + } + } + + if (!(wiinst->state & WAVE_STATE_STARTED)) { + wave_dev->enablebits |= PCM_ENABLE_INPUT; + emu10k1_wavein_start(wave_dev); + } + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + + if (bytestocopy >= wiinst->buffer.fragment_size) + mask |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return mask; +} + +static void calculate_ofrag(struct woinst *woinst) +{ + struct waveout_buffer *buffer = &woinst->buffer; + u32 fragsize; + + if (buffer->fragment_size) + return; + + if (!buffer->ossfragshift) { + fragsize = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) { + fragsize >>= 1; + buffer->ossfragshift++; + } + } + + if (buffer->ossfragshift < WAVEOUT_MINFRAGSHIFT) + buffer->ossfragshift = WAVEOUT_MINFRAGSHIFT; + + buffer->fragment_size = 1 << buffer->ossfragshift; + + if (!buffer->numfrags) { + u32 numfrags; + + numfrags = (woinst->format.bytespervoicesample * woinst->format.samplingrate * WAVEOUT_DEFAULTBUFLEN) / + (buffer->fragment_size * 1000) - 1; + + buffer->numfrags = 1; + + while (numfrags) { + numfrags >>= 1; + buffer->numfrags <<= 1; + } + } + + if (buffer->numfrags < MINFRAGS) + buffer->numfrags = MINFRAGS; + + if (buffer->numfrags * buffer->fragment_size > WAVEOUT_MAXBUFSIZE) { + buffer->numfrags = WAVEOUT_MAXBUFSIZE / buffer->fragment_size; + + if (buffer->numfrags < MINFRAGS) { + buffer->numfrags = MINFRAGS; + buffer->fragment_size = WAVEOUT_MAXBUFSIZE / MINFRAGS; + } + + } else if (buffer->numfrags * buffer->fragment_size < WAVEOUT_MINBUFSIZE) + buffer->numfrags = WAVEOUT_MINBUFSIZE / buffer->fragment_size; + + buffer->size = buffer->fragment_size * buffer->numfrags; + buffer->pages = buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0); + + DPD(2, " calculated playback fragment_size -> %d\n", buffer->fragment_size); + DPD(2, " calculated playback numfrags -> %d\n", buffer->numfrags); + + return; +} + +static void calculate_ifrag(struct wiinst *wiinst) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + u32 fragsize, bufsize, size[4]; + int i, j; + + if (buffer->fragment_size) + return; + + if (!buffer->ossfragshift) { + fragsize = (wiinst->format.bytespersec * WAVEIN_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) { + fragsize >>= 1; + buffer->ossfragshift++; + } + } + + if (buffer->ossfragshift < WAVEIN_MINFRAGSHIFT) + buffer->ossfragshift = WAVEIN_MINFRAGSHIFT; + + buffer->fragment_size = 1 << buffer->ossfragshift; + + if (!buffer->numfrags) + buffer->numfrags = (wiinst->format.bytespersec * WAVEIN_DEFAULTBUFLEN) / (buffer->fragment_size * 1000) - 1; + + if (buffer->numfrags < MINFRAGS) + buffer->numfrags = MINFRAGS; + + if (buffer->numfrags * buffer->fragment_size > WAVEIN_MAXBUFSIZE) { + buffer->numfrags = WAVEIN_MAXBUFSIZE / buffer->fragment_size; + + if (buffer->numfrags < MINFRAGS) { + buffer->numfrags = MINFRAGS; + buffer->fragment_size = WAVEIN_MAXBUFSIZE / MINFRAGS; + } + } else if (buffer->numfrags * buffer->fragment_size < WAVEIN_MINBUFSIZE) + buffer->numfrags = WAVEIN_MINBUFSIZE / buffer->fragment_size; + + bufsize = buffer->fragment_size * buffer->numfrags; + + if (bufsize >= 0x10000) { + buffer->size = 0x10000; + buffer->sizeregval = 0x1f; + } else { + buffer->size = 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]) { + buffer->size = size[j]; + size[j] *= 2; + buffer->sizeregval = i * 4 + j + 1; + } else + goto exitloop; + exitloop: + if (buffer->size == 0) { + buffer->size = 384; + buffer->sizeregval = 0x01; + } + } + + buffer->numfrags = buffer->size / buffer->fragment_size; + buffer->pages = buffer->size / PAGE_SIZE + ((buffer->size % PAGE_SIZE) ? 1 : 0); + if (buffer->size % buffer->fragment_size) + BUG(); + + DPD(2, " calculated recording fragment_size -> %d\n", buffer->fragment_size); + DPD(2, " calculated recording numfrags -> %d\n", buffer->numfrags); + DPD(2, " buffer size register -> %#04x\n", buffer->sizeregval); + + return; +} + +void emu10k1_wavein_bh(unsigned long refdata) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) refdata; + struct wiinst *wiinst = wave_dev->wiinst; + u32 bytestocopy; + unsigned long flags; + + if (!wiinst) + return; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (!(wiinst->state & WAVE_STATE_STARTED)) { + spin_unlock_irqrestore(&wiinst->lock, flags); + return; + } + + emu10k1_wavein_update(wave_dev->card, wiinst); + emu10k1_wavein_getxfersize(wiinst, &bytestocopy); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (bytestocopy >= wiinst->buffer.fragment_size) { + if (waitqueue_active(&wiinst->wait_queue)) + wake_up_interruptible(&wiinst->wait_queue); + } else + DPD(3, "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; + u32 bytestocopy; + unsigned long flags; + + if (!woinst) + return; + + spin_lock_irqsave(&woinst->lock, flags); + + if (!(woinst->state & WAVE_STATE_STARTED)) { + spin_unlock_irqrestore(&woinst->lock, flags); + return; + } + + emu10k1_waveout_update(woinst); + emu10k1_waveout_getxfersize(woinst, &bytestocopy); + + if (woinst->buffer.fill_silence) { + spin_unlock_irqrestore(&woinst->lock, flags); + emu10k1_waveout_fillsilence(woinst); + } else + spin_unlock_irqrestore(&woinst->lock, flags); + + if (bytestocopy >= woinst->buffer.fragment_size) { + if (waitqueue_active(&woinst->wait_queue)) + wake_up_interruptible(&woinst->wait_queue); + } else + DPD(3, "Not enough transfer size -> %d\n", bytestocopy); + + return; +} + +struct file_operations emu10k1_audio_fops = { + owner: THIS_MODULE, + llseek: no_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 -Nru linux/sound/oss/emu10k1/audio.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.h --- linux/sound/oss/emu10k1/audio.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/audio.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,49 @@ +/* + ********************************************************************** + * 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 MINFRAGS 2 /* _don't_ got bellow 2 */ + +struct emu10k1_wavedevice +{ + struct emu10k1_card *card; + struct wiinst *wiinst; + struct woinst *woinst; + u16 enablebits; +}; + +void emu10k1_waveout_bh(unsigned long); +void emu10k1_wavein_bh(unsigned long); + +#endif /* _AUDIO_H */ diff -Nru linux/sound/oss/emu10k1/cardmi.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.c --- linux/sound/oss/emu10k1/cardmi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,817 @@ +/* + ********************************************************************** + * 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 +#include + +#include "hwaccess.h" +#include "8010.h" +#include "cardmi.h" +#include "irqmgr.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 -1; + + /* 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 0; +} + +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 -1; + } + + /* 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 0; +} + +/* 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 -1; + } + + 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 0; +} + +/* 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)); + + 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 0; +} + +/* 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 0; +} + +/* 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 0; +} + +/* 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 0; +} + +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) < 0) { + break; + } else { + ++count; + card_mpuin->midiq[idx].data = MPUIvalue; + card_mpuin->midiq[idx].timein = (jiffies * 1000) / HZ; + idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; + } + } + + if (count) { + card_mpuin->qtail = idx; + + tasklet_hi_schedule(&card_mpuin->tasklet); + } + + return 0; +} + +/*****************************************************************************/ + +/* 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 0; +} + +/* 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 -1; + + 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 -1; + } + + 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 -1; + } + + 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 -1; + } + + 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 0; +} + +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 -1; + } + + 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 0; +} + +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 -1; + } + + 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 0; +} + +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 -1; + } + + 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 -1; + } + + 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 0; +} + +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 -1; + } + + 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 0; + } + + 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 -Nru linux/sound/oss/emu10k1/cardmi.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.h --- linux/sound/oss/emu10k1/cardmi.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmi.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,114 @@ +/* + ********************************************************************** + * 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" +#include + +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 -Nru linux/sound/oss/emu10k1/cardmo.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.c --- linux/sound/oss/emu10k1/cardmo.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.c Mon Apr 8 22:31:22 2002 @@ -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 + +#include "hwaccess.h" +#include "8010.h" +#include "cardmo.h" +#include "irqmgr.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 -1; + + /* 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 0; +} + +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 0; +} + +/* 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 0; + + midihdr->flags |= MIDIBUF_INQUEUE; + midihdr->flags &= ~MIDIBUF_DONE; + + if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) { + /* Message lost */ + return -1; + } + + 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 0; +} + +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; + 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) { + if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) { + DPF(2, "emu10k1_mpuoutDpcCallback error!!\n"); + } else { + ++cByteSent; + --midiq->sizeLeft; + ++midiq->midibyte; + } + } + + 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 0; +} diff -Nru linux/sound/oss/emu10k1/cardmo.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.h --- linux/sound/oss/emu10k1/cardmo.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardmo.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,62 @@ +/* + ********************************************************************** + * 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" +#include + +#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 -Nru linux/sound/oss/emu10k1/cardwi.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.c --- linux/sound/oss/emu10k1/cardwi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,371 @@ +/* + ********************************************************************** + * 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 +#include "hwaccess.h" +#include "timer.h" +#include "recmgr.h" +#include "audio.h" +#include "cardwi.h" + +/** + * query_format - returns a valid sound format + * + * This function will return a valid sound format as close + * to the requested one as possible. + */ +void query_format(int recsrc, struct wave_format *wave_fmt) +{ + + switch (recsrc) { + case WAVERECORD_AC97: + + if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2)) + 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; + + switch (wave_fmt->id) { + case AFMT_S16_LE: + wave_fmt->bitsperchannel = 16; + break; + case AFMT_U8: + wave_fmt->bitsperchannel = 8; + break; + default: + wave_fmt->id = AFMT_S16_LE; + wave_fmt->bitsperchannel = 16; + break; + } + + break; + + /* these can't be changed from the original values */ + case WAVERECORD_MIC: + case WAVERECORD_FX: + break; + + default: + BUG(); + break; + } + + wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3; + wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel; + wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate; +} + +static int alloc_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + buffer->addr = pci_alloc_consistent(card->pci_dev, buffer->size * buffer->cov, + &buffer->dma_handle); + if (buffer->addr == NULL) + return -1; + + return 0; +} + +static void free_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + if (buffer->addr != NULL) + pci_free_consistent(card->pci_dev, buffer->size * buffer->cov, + buffer->addr, buffer->dma_handle); +} + +int emu10k1_wavein_open(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + struct wiinst **wiinst_tmp = NULL; + u32 delay; + unsigned long flags; + + DPF(2, "emu10k1_wavein_open()\n"); + + switch (wiinst->recsrc) { + case WAVERECORD_AC97: + wiinst_tmp = &card->wavein.ac97; + break; + case WAVERECORD_MIC: + wiinst_tmp = &card->wavein.mic; + break; + case WAVERECORD_FX: + wiinst_tmp = &card->wavein.fx; + break; + default: + BUG(); + break; + } + + spin_lock_irqsave(&card->lock, flags); + if (*wiinst_tmp != NULL) { + spin_unlock_irqrestore(&card->lock, flags); + return -1; + } + + *wiinst_tmp = wiinst; + spin_unlock_irqrestore(&card->lock, flags); + + /* handle 8 bit recording */ + if (wiinst->format.bytesperchannel == 1) { + if (wiinst->buffer.size > 0x8000) { + wiinst->buffer.size = 0x8000; + wiinst->buffer.sizeregval = 0x1f; + } else + wiinst->buffer.sizeregval += 4; + + wiinst->buffer.cov = 2; + } else + wiinst->buffer.cov = 1; + + if (alloc_buffer(card, &wiinst->buffer) < 0) { + ERROR(); + emu10k1_wavein_close(wave_dev); + return -1; + } + + emu10k1_set_record_src(card, wiinst); + + delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; + + emu10k1_timer_install(card, &wiinst->timer, delay / 2); + + wiinst->state = WAVE_STATE_OPEN; + + return 0; +} + +void emu10k1_wavein_close(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + unsigned long flags; + + DPF(2, "emu10k1_wavein_close()\n"); + + emu10k1_wavein_stop(wave_dev); + + emu10k1_timer_uninstall(card, &wiinst->timer); + + free_buffer(card, &wiinst->buffer); + + 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: + BUG(); + break; + } + spin_unlock_irqrestore(&card->lock, flags); + + wiinst->state = WAVE_STATE_CLOSED; +} + +void emu10k1_wavein_start(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + + DPF(2, "emu10k1_wavein_start()\n"); + + emu10k1_start_record(card, &wiinst->buffer); + emu10k1_timer_enable(wave_dev->card, &wiinst->timer); + + wiinst->buffer.hw_pos = 0; + wiinst->buffer.pos = 0; + wiinst->buffer.bytestocopy = 0; + + wiinst->state |= WAVE_STATE_STARTED; +} + +void emu10k1_wavein_stop(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + + DPF(2, "emu10k1_wavein_stop()\n"); + + if (!(wiinst->state & WAVE_STATE_STARTED)) + return; + + emu10k1_timer_disable(card, &wiinst->timer); + emu10k1_stop_record(card, &wiinst->buffer); + + wiinst->state &= ~WAVE_STATE_STARTED; +} + +int emu10k1_wavein_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format) +{ + struct emu10k1_card *card = wave_dev->card; + struct wiinst *wiinst = wave_dev->wiinst; + u32 delay; + + DPF(2, "emu10k1_wavein_setformat()\n"); + + if (wiinst->state & WAVE_STATE_STARTED) + return -1; + + query_format(wiinst->recsrc, format); + + if ((wiinst->format.samplingrate != format->samplingrate) + || (wiinst->format.bitsperchannel != format->bitsperchannel) + || (wiinst->format.channels != format->channels)) { + + wiinst->format = *format; + + if (wiinst->state == WAVE_STATE_CLOSED) + return 0; + + wiinst->buffer.size *= wiinst->buffer.cov; + + if (wiinst->format.bytesperchannel == 1) { + wiinst->buffer.cov = 2; + wiinst->buffer.size /= wiinst->buffer.cov; + } else + wiinst->buffer.cov = 1; + + emu10k1_timer_uninstall(card, &wiinst->timer); + + delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; + + emu10k1_timer_install(card, &wiinst->timer, delay / 2); + } + + return 0; +} + +void emu10k1_wavein_getxfersize(struct wiinst *wiinst, u32 * size) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + + *size = buffer->bytestocopy; + + if (wiinst->mmapped) + return; + + if (*size > buffer->size) { + *size = buffer->size; + buffer->pos = buffer->hw_pos; + buffer->bytestocopy = buffer->size; + DPF(1, "buffer overrun\n"); + } +} + +static void copy_block(u8 *dst, u8 * src, u32 str, u32 len, u8 cov) +{ + if (cov == 1) + __copy_to_user(dst, src + str, len); + else { + u8 byte; + u32 i; + + src += 1 + 2 * str; + + for (i = 0; i < len; i++) { + byte = src[2 * i] ^ 0x80; + __copy_to_user(dst + i, &byte, 1); + } + } +} + +void emu10k1_wavein_xferdata(struct wiinst *wiinst, u8 * data, u32 * size) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + u32 sizetocopy, sizetocopy_now, start; + unsigned long flags; + + sizetocopy = min_t(u32, buffer->size, *size); + *size = sizetocopy; + + if (!sizetocopy) + return; + + spin_lock_irqsave(&wiinst->lock, flags); + start = buffer->pos; + buffer->pos += sizetocopy; + buffer->pos %= buffer->size; + buffer->bytestocopy -= sizetocopy; + sizetocopy_now = buffer->size - start; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + + copy_block(data, buffer->addr, start, sizetocopy_now, buffer->cov); + copy_block(data + sizetocopy_now, buffer->addr, 0, sizetocopy, buffer->cov); + } else { + copy_block(data, buffer->addr, start, sizetocopy, buffer->cov); + } +} + +void emu10k1_wavein_update(struct emu10k1_card *card, struct wiinst *wiinst) +{ + u32 hw_pos; + u32 diff; + + /* There is no actual start yet */ + if (!(wiinst->state & WAVE_STATE_STARTED)) { + hw_pos = wiinst->buffer.hw_pos; + } else { + /* hw_pos in byte units */ + hw_pos = sblive_readptr(card, wiinst->buffer.idxreg, 0) / wiinst->buffer.cov; + } + + diff = (wiinst->buffer.size + hw_pos - wiinst->buffer.hw_pos) % wiinst->buffer.size; + wiinst->total_recorded += diff; + wiinst->buffer.bytestocopy += diff; + + wiinst->buffer.hw_pos = hw_pos; +} diff -Nru linux/sound/oss/emu10k1/cardwi.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.h --- linux/sound/oss/emu10k1/cardwi.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwi.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,90 @@ +/* + ********************************************************************** + * 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" +#include "audio.h" +#include "timer.h" + +struct wavein_buffer { + u16 ossfragshift; + u32 fragment_size; + u32 numfrags; + u32 hw_pos; /* hardware cursor position */ + u32 pos; /* software cursor position */ + u32 bytestocopy; /* bytes of recorded data available */ + u32 size; + u32 pages; + u32 sizereg; + u32 sizeregval; + u32 addrreg; + u32 idxreg; + u32 adcctl; + void *addr; + u8 cov; + dma_addr_t dma_handle; +}; + +struct wiinst +{ + u8 state; + struct emu_timer timer; + struct wave_format format; + struct wavein_buffer buffer; + wait_queue_head_t wait_queue; + u8 mmapped; + u32 total_recorded; /* total bytes read() from device */ + u32 blocks; + spinlock_t lock; + u8 recsrc; + u16 fxwc; +}; + +#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 wiinst *, u32 *); +void emu10k1_wavein_xferdata(struct wiinst *, u8 *, u32 *); +int emu10k1_wavein_setformat(struct emu10k1_wavedevice *, struct wave_format *); +void emu10k1_wavein_update(struct emu10k1_card *, struct wiinst *); + + +#endif /* _CARDWI_H */ diff -Nru linux/sound/oss/emu10k1/cardwo.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.c --- linux/sound/oss/emu10k1/cardwo.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,693 @@ +/* + ********************************************************************** + * 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 +#include "hwaccess.h" +#include "8010.h" +#include "voicemgr.h" +#include "cardwo.h" +#include "audio.h" + +static u32 samplerate_to_linearpitch(u32 samplingrate) +{ + samplingrate = (samplingrate << 8) / 375; + return (samplingrate >> 1) + (samplingrate & 1); +} + +static void query_format(struct emu10k1_wavedevice *wave_dev, struct wave_format *wave_fmt) +{ + int i, j, do_passthrough = 0, is_ac3 = 0; + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + + if ((wave_fmt->channels > 2) && (wave_fmt->id != AFMT_S16_LE) && (wave_fmt->id != AFMT_U8)) + wave_fmt->channels = 2; + + if ((wave_fmt->channels < 1) || (wave_fmt->channels > WAVEOUT_MAXVOICES)) + wave_fmt->channels = 2; + + if (wave_fmt->channels == 2) + woinst->num_voices = 1; + else + woinst->num_voices = wave_fmt->channels; + + if (wave_fmt->samplingrate >= 0x2ee00) + wave_fmt->samplingrate = 0x2ee00; + + wave_fmt->passthrough = 0; + do_passthrough = is_ac3 = 0; + + if (card->pt.selected) + do_passthrough = 1; + + switch (wave_fmt->id) { + case AFMT_S16_LE: + wave_fmt->bitsperchannel = 16; + break; + case AFMT_U8: + wave_fmt->bitsperchannel = 8; + break; + case AFMT_AC3: + do_passthrough = 1; + is_ac3 = 1; + break; + default: + wave_fmt->id = AFMT_S16_LE; + wave_fmt->bitsperchannel = 16; + break; + } + if (do_passthrough) { + i = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.intr_gpr_name); + j = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.enable_gpr_name); + /* currently only one waveout instance may use pass-through */ + if (i < 0 || j < 0 || woinst->state != WAVE_STATE_CLOSED || + card->pt.state != PT_STATE_INACTIVE || + (wave_fmt->samplingrate != 48000 && !is_ac3) || + (wave_fmt->samplingrate != 48000 && !is_ac3)) { + DPF(2, "unable to set pass-through mode\n"); + } else { + wave_fmt->samplingrate = 48000; + wave_fmt->channels = 2; + wave_fmt->passthrough = 1; + card->pt.intr_gpr = i; + card->pt.enable_gpr = j; + card->pt.state = PT_STATE_INACTIVE; + card->pt.pos_gpr = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.pos_gpr_name); + DPD(2, "is_ac3 is %d\n", is_ac3); + card->pt.ac3data = is_ac3; + wave_fmt->bitsperchannel = 16; + } + } + + wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3; + + if (wave_fmt->channels == 2) + wave_fmt->bytespervoicesample = wave_fmt->channels * wave_fmt->bytesperchannel; + else + wave_fmt->bytespervoicesample = wave_fmt->bytesperchannel; + + wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel; + wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate; +} + +/** + * alloc_buffer - + * + * allocates the memory buffer for a voice. Two page tables are kept for each buffer. + * One (dma_handle) keeps track of the host memory pages used and the other (virtualpagetable) + * is passed to the device so that it can do DMA to host memory. + * + */ +static int alloc_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum) +{ + u32 pageindex, pagecount; + unsigned long busaddx; + int i; + + DPD(2, "requested pages is: %d\n", buffer->pages); + + if ((buffer->mem[voicenum].emupageindex = + emu10k1_addxmgr_alloc(buffer->pages * PAGE_SIZE, card)) < 0) + return -1; + + /* Fill in virtual memory table */ + for (pagecount = 0; pagecount < buffer->pages; pagecount++) { + if ((buffer->mem[voicenum].addr[pagecount] = + pci_alloc_consistent(card->pci_dev, PAGE_SIZE, + &buffer->mem[voicenum].dma_handle[pagecount])) == NULL) { + buffer->pages = pagecount; + return -1; + } + + DPD(2, "Virtual Addx: %p\n", buffer->mem[voicenum].addr[pagecount]); + + for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { + busaddx = buffer->mem[voicenum].dma_handle[pagecount] + i * EMUPAGESIZE; + + DPD(3, "Bus Addx: %#lx\n", busaddx); + + pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; + + ((u32 *) card->virtualpagetable.addr)[pageindex] = cpu_to_le32((busaddx * 2) | pageindex); + } + } + + return 0; +} + +/** + * free_buffer - + * + * frees the memory buffer for a voice. + */ +static void free_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum) +{ + u32 pagecount, pageindex; + int i; + + if (buffer->mem[voicenum].emupageindex < 0) + return; + + for (pagecount = 0; pagecount < buffer->pages; pagecount++) { + pci_free_consistent(card->pci_dev, PAGE_SIZE, + buffer->mem[voicenum].addr[pagecount], + buffer->mem[voicenum].dma_handle[pagecount]); + + for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) { + pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i; + ((u32 *) card->virtualpagetable.addr)[pageindex] = + cpu_to_le32((card->silentpage.dma_handle * 2) | pageindex); + } + } + + emu10k1_addxmgr_free(card, buffer->mem[voicenum].emupageindex); + buffer->mem[voicenum].emupageindex = -1; +} + +static int get_voice(struct emu10k1_card *card, struct woinst *woinst, unsigned int voicenum) +{ + struct emu_voice *voice = &woinst->voice[voicenum]; + /* Allocate voices here, if no voices available, return error. + * Init voice_allocdesc first.*/ + + voice->usage = VOICE_USAGE_PLAYBACK; + + voice->flags = 0; + + if (woinst->format.channels == 2) + voice->flags |= VOICE_FLAGS_STEREO; + + if (woinst->format.bitsperchannel == 16) + voice->flags |= VOICE_FLAGS_16BIT; + + if (emu10k1_voice_alloc(card, voice) < 0) { + voice->usage = VOICE_USAGE_FREE; + return -1; + } + + /* Calculate pitch */ + voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8); + voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate); + + DPD(2, "Initial pitch --> %#x\n", voice->initial_pitch); + + voice->startloop = (woinst->buffer.mem[voicenum].emupageindex << 12) / + woinst->format.bytespervoicesample; + voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespervoicesample; + voice->start = voice->startloop; + + if (voice->flags & VOICE_FLAGS_STEREO) { + voice->params[0].send_a = card->waveout.send_a[1]; + voice->params[0].send_b = card->waveout.send_b[1]; + voice->params[0].send_c = card->waveout.send_c[1]; + voice->params[0].send_d = card->waveout.send_d[1]; + + if (woinst->device) + voice->params[0].send_routing = 0x7654; + else + voice->params[0].send_routing = card->waveout.send_routing[1]; + + voice->params[0].volume_target = 0xffff; + voice->params[0].initial_fc = 0xff; + voice->params[0].initial_attn = 0x00; + voice->params[0].byampl_env_sustain = 0x7f; + voice->params[0].byampl_env_decay = 0x7f; + + voice->params[1].send_a = card->waveout.send_a[2]; + voice->params[1].send_b = card->waveout.send_b[2]; + voice->params[1].send_c = card->waveout.send_c[2]; + voice->params[1].send_d = card->waveout.send_d[2]; + + if (woinst->device) + voice->params[1].send_routing = 0x7654; + else + voice->params[1].send_routing = card->waveout.send_routing[2]; + + voice->params[1].volume_target = 0xffff; + voice->params[1].initial_fc = 0xff; + voice->params[1].initial_attn = 0x00; + voice->params[1].byampl_env_sustain = 0x7f; + voice->params[1].byampl_env_decay = 0x7f; + } else { + if (woinst->num_voices > 1) { + voice->params[0].send_a = 0xff; + voice->params[0].send_b = 0; + voice->params[0].send_c = 0; + voice->params[0].send_d = 0; + + voice->params[0].send_routing = + 0xfff0 + card->mchannel_fx + voicenum; + } else { + voice->params[0].send_a = card->waveout.send_a[0]; + voice->params[0].send_b = card->waveout.send_b[0]; + voice->params[0].send_c = card->waveout.send_c[0]; + voice->params[0].send_d = card->waveout.send_d[0]; + + if (woinst->device) + voice->params[0].send_routing = 0x7654; + else + voice->params[0].send_routing = card->waveout.send_routing[0]; + } + + voice->params[0].volume_target = 0xffff; + voice->params[0].initial_fc = 0xff; + voice->params[0].initial_attn = 0x00; + voice->params[0].byampl_env_sustain = 0x7f; + voice->params[0].byampl_env_decay = 0x7f; + } + + DPD(2, "voice: startloop=%#x, endloop=%#x\n", voice->startloop, voice->endloop); + + emu10k1_voice_playback_setup(voice); + + return 0; +} + +int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + struct waveout_buffer *buffer = &woinst->buffer; + unsigned int voicenum; + u32 delay; + + DPF(2, "emu10k1_waveout_open()\n"); + + for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { + if (alloc_buffer(card, buffer, voicenum) < 0) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return -1; + } + + if (get_voice(card, woinst, voicenum) < 0) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return -1; + } + } + + buffer->fill_silence = 0; + buffer->silence_bytes = 0; + buffer->silence_pos = 0; + buffer->hw_pos = 0; + buffer->free_bytes = woinst->buffer.size; + + delay = (48000 * woinst->buffer.fragment_size) / + (woinst->format.samplingrate * woinst->format.bytespervoicesample); + + emu10k1_timer_install(card, &woinst->timer, delay / 2); + + woinst->state = WAVE_STATE_OPEN; + + return 0; +} + +void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + unsigned int voicenum; + + DPF(2, "emu10k1_waveout_close()\n"); + + emu10k1_waveout_stop(wave_dev); + + emu10k1_timer_uninstall(card, &woinst->timer); + + for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { + emu10k1_voice_free(&woinst->voice[voicenum]); + free_buffer(card, &woinst->buffer, voicenum); + } + + woinst->state = WAVE_STATE_CLOSED; +} + +void emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + + DPF(2, "emu10k1_waveout_start()\n"); + + /* Actual start */ + emu10k1_voices_start(woinst->voice, woinst->num_voices, woinst->total_played); + + emu10k1_timer_enable(card, &woinst->timer); + + woinst->state |= WAVE_STATE_STARTED; +} + +int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + unsigned int voicenum; + u32 delay; + + DPF(2, "emu10k1_waveout_setformat()\n"); + + if (woinst->state & WAVE_STATE_STARTED) + return -1; + + query_format(wave_dev, format); + + if (woinst->format.samplingrate != format->samplingrate || + woinst->format.channels != format->channels || + woinst->format.bitsperchannel != format->bitsperchannel) { + + woinst->format = *format; + + if (woinst->state == WAVE_STATE_CLOSED) + return 0; + + emu10k1_timer_uninstall(card, &woinst->timer); + + for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) { + emu10k1_voice_free(&woinst->voice[voicenum]); + + if (get_voice(card, woinst, voicenum) < 0) { + ERROR(); + emu10k1_waveout_close(wave_dev); + return -1; + } + } + + delay = (48000 * woinst->buffer.fragment_size) / + (woinst->format.samplingrate * woinst->format.bytespervoicesample); + + emu10k1_timer_install(card, &woinst->timer, delay / 2); + } + + return 0; +} + +void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev) +{ + struct emu10k1_card *card = wave_dev->card; + struct woinst *woinst = wave_dev->woinst; + + DPF(2, "emu10k1_waveout_stop()\n"); + + if (!(woinst->state & WAVE_STATE_STARTED)) + return; + + emu10k1_timer_disable(card, &woinst->timer); + + /* Stop actual voices */ + emu10k1_voices_stop(woinst->voice, woinst->num_voices); + + emu10k1_waveout_update(woinst); + + woinst->state &= ~WAVE_STATE_STARTED; +} + +/** + * emu10k1_waveout_getxfersize - + * + * gives the total free bytes on the voice buffer, including silence bytes + * (basically: total_free_bytes = free_bytes + silence_bytes). + * + */ +void emu10k1_waveout_getxfersize(struct woinst *woinst, u32 *total_free_bytes) +{ + struct waveout_buffer *buffer = &woinst->buffer; + int pending_bytes; + + if (woinst->mmapped) { + *total_free_bytes = buffer->free_bytes; + return; + } + + pending_bytes = buffer->size - buffer->free_bytes; + + buffer->fill_silence = (pending_bytes < (signed) buffer->fragment_size) ? 1 : 0; + + if (pending_bytes > (signed) buffer->silence_bytes) { + *total_free_bytes = (buffer->free_bytes + buffer->silence_bytes); + } else { + *total_free_bytes = buffer->size; + buffer->silence_bytes = pending_bytes; + if (pending_bytes < 0) { + buffer->silence_pos = buffer->hw_pos; + buffer->silence_bytes = 0; + buffer->free_bytes = buffer->size; + DPF(1, "buffer underrun\n"); + } + } +} + +/** + * copy_block - + * + * copies a block of pcm data to a voice buffer. + * Notice that the voice buffer is actually a set of disjointed memory pages. + * + */ +static void copy_block(void **dst, u32 str, u8 *src, u32 len) +{ + unsigned int pg; + unsigned int pgoff; + unsigned int k; + + pg = str / PAGE_SIZE; + pgoff = str % PAGE_SIZE; + + if (len > PAGE_SIZE - pgoff) { + k = PAGE_SIZE - pgoff; + __copy_from_user((u8 *)dst[pg] + pgoff, src, k); + len -= k; + while (len > PAGE_SIZE) { + __copy_from_user(dst[++pg], src + k, PAGE_SIZE); + k += PAGE_SIZE; + len -= PAGE_SIZE; + } + __copy_from_user(dst[++pg], src + k, len); + + } else + __copy_from_user((u8 *)dst[pg] + pgoff, src, len); +} + +/** + * copy_ilv_block - + * + * copies a block of pcm data containing n interleaved channels to n mono voice buffers. + * Notice that the voice buffer is actually a set of disjointed memory pages. + * + */ +static void copy_ilv_block(struct woinst *woinst, u32 str, u8 *src, u32 len) +{ + unsigned int pg; + unsigned int pgoff; + unsigned int voice_num; + struct waveout_mem *mem = woinst->buffer.mem; + + pg = str / PAGE_SIZE; + pgoff = str % PAGE_SIZE; + + while (len) { + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) { + __copy_from_user((u8 *)(mem[voice_num].addr[pg]) + pgoff, src, woinst->format.bytespervoicesample); + src += woinst->format.bytespervoicesample; + } + + len -= woinst->format.bytespervoicesample; + + pgoff += woinst->format.bytespervoicesample; + if (pgoff >= PAGE_SIZE) { + pgoff = 0; + pg++; + } + } +} + +/** + * fill_block - + * + * fills a set voice buffers with a block of a given sample. + * + */ +static void fill_block(struct woinst *woinst, u32 str, u8 data, u32 len) +{ + unsigned int pg; + unsigned int pgoff; + unsigned int voice_num; + struct waveout_mem *mem = woinst->buffer.mem; + unsigned int k; + + pg = str / PAGE_SIZE; + pgoff = str % PAGE_SIZE; + + if (len > PAGE_SIZE - pgoff) { + k = PAGE_SIZE - pgoff; + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, k); + len -= k; + while (len > PAGE_SIZE) { + pg++; + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset(mem[voice_num].addr[pg], data, PAGE_SIZE); + + len -= PAGE_SIZE; + } + pg++; + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset(mem[voice_num].addr[pg], data, len); + + } else { + for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) + memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, len); + } +} + +/** + * emu10k1_waveout_xferdata - + * + * copies pcm data to the voice buffer. Silence samples + * previously added to the buffer are overwritten. + * + */ +void emu10k1_waveout_xferdata(struct woinst *woinst, u8 *data, u32 *size) +{ + struct waveout_buffer *buffer = &woinst->buffer; + u32 sizetocopy, sizetocopy_now, start; + unsigned long flags; + + sizetocopy = min_t(u32, buffer->size, *size); + *size = sizetocopy; + + if (!sizetocopy) + return; + + spin_lock_irqsave(&woinst->lock, flags); + start = (buffer->size + buffer->silence_pos - buffer->silence_bytes) % buffer->size; + + if (sizetocopy > buffer->silence_bytes) { + buffer->silence_pos += sizetocopy - buffer->silence_bytes; + buffer->free_bytes -= sizetocopy - buffer->silence_bytes; + buffer->silence_bytes = 0; + } else + buffer->silence_bytes -= sizetocopy; + + spin_unlock_irqrestore(&woinst->lock, flags); + + sizetocopy_now = buffer->size - start; + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + if (woinst->num_voices > 1) { + copy_ilv_block(woinst, start, data, sizetocopy_now); + copy_ilv_block(woinst, 0, data + sizetocopy_now * woinst->num_voices, sizetocopy); + } else { + copy_block(buffer->mem[0].addr, start, data, sizetocopy_now); + copy_block(buffer->mem[0].addr, 0, data + sizetocopy_now, sizetocopy); + } + } else { + if (woinst->num_voices > 1) + copy_ilv_block(woinst, start, data, sizetocopy); + else + copy_block(buffer->mem[0].addr, start, data, sizetocopy); + } +} + +/** + * emu10k1_waveout_fillsilence - + * + * adds samples of silence to the voice buffer so that we + * don't loop over stale pcm data. + * + */ +void emu10k1_waveout_fillsilence(struct woinst *woinst) +{ + struct waveout_buffer *buffer = &woinst->buffer; + u32 sizetocopy, sizetocopy_now, start; + u8 filldata; + unsigned long flags; + + sizetocopy = buffer->fragment_size; + + if (woinst->format.bitsperchannel == 16) + filldata = 0x00; + else + filldata = 0x80; + + spin_lock_irqsave(&woinst->lock, flags); + buffer->silence_bytes += sizetocopy; + buffer->free_bytes -= sizetocopy; + buffer->silence_pos %= buffer->size; + start = buffer->silence_pos; + buffer->silence_pos += sizetocopy; + spin_unlock_irqrestore(&woinst->lock, flags); + + sizetocopy_now = buffer->size - start; + + if (sizetocopy > sizetocopy_now) { + sizetocopy -= sizetocopy_now; + fill_block(woinst, start, filldata, sizetocopy_now); + fill_block(woinst, 0, filldata, sizetocopy); + } else { + fill_block(woinst, start, filldata, sizetocopy); + } +} + +/** + * emu10k1_waveout_update - + * + * updates the position of the voice buffer hardware pointer (hw_pos) + * and the number of free bytes on the buffer (free_bytes). + * The free bytes _don't_ include silence bytes that may have been + * added to the buffer. + * + */ +void emu10k1_waveout_update(struct woinst *woinst) +{ + u32 hw_pos; + u32 diff; + + /* There is no actual start yet */ + if (!(woinst->state & WAVE_STATE_STARTED)) { + hw_pos = woinst->buffer.hw_pos; + } else { + /* hw_pos in sample units */ + hw_pos = sblive_readptr(woinst->voice[0].card, CCCA_CURRADDR, woinst->voice[0].num); + + if(hw_pos < woinst->voice[0].start) + hw_pos += woinst->buffer.size / woinst->format.bytespervoicesample - woinst->voice[0].start; + else + hw_pos -= woinst->voice[0].start; + + hw_pos *= woinst->format.bytespervoicesample; + } + + diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size; + woinst->total_played += diff; + woinst->buffer.free_bytes += diff; + woinst->buffer.hw_pos = hw_pos; +} diff -Nru linux/sound/oss/emu10k1/cardwo.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.h --- linux/sound/oss/emu10k1/cardwo.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/cardwo.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,98 @@ +/* + ********************************************************************** + * 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" +#include "audio.h" +#include "voicemgr.h" +#include "timer.h" + +/* setting this to other than a power of two may break some applications */ +#define WAVEOUT_MAXBUFSIZE MAXBUFSIZE +#define WAVEOUT_MINBUFSIZE 64 + +#define WAVEOUT_DEFAULTFRAGLEN 20 /* Time to play a fragment in ms (latency) */ +#define WAVEOUT_DEFAULTBUFLEN 500 /* Time to play the entire buffer in ms */ + +#define WAVEOUT_MINFRAGSHIFT 6 +#define WAVEOUT_MAXVOICES 6 + +/* waveout_mem is cardwo internal */ +struct waveout_mem { + int emupageindex; + void *addr[BUFMAXPAGES]; + dma_addr_t dma_handle[BUFMAXPAGES]; +}; + +struct waveout_buffer { + u16 ossfragshift; + u32 numfrags; + u32 fragment_size; /* in bytes units */ + u32 size; /* in bytes units */ + u32 pages; /* buffer size in page units*/ + struct waveout_mem mem[WAVEOUT_MAXVOICES]; + u32 silence_pos; /* software cursor position (including silence bytes) */ + u32 hw_pos; /* hardware cursor position */ + u32 free_bytes; /* free bytes available on the buffer (not including silence bytes) */ + u8 fill_silence; + u32 silence_bytes; /* silence bytes on the buffer */ +}; + +struct woinst +{ + u8 state; + u8 num_voices; + struct emu_voice voice[WAVEOUT_MAXVOICES]; + struct emu_timer timer; + struct wave_format format; + struct waveout_buffer buffer; + wait_queue_head_t wait_queue; + u8 mmapped; + u32 total_copied; /* total number of bytes written() to the buffer (excluding silence) */ + u32 total_played; /* total number of bytes played including silence */ + u32 blocks; + u8 device; + spinlock_t lock; +}; + +int emu10k1_waveout_open(struct emu10k1_wavedevice *); +void emu10k1_waveout_close(struct emu10k1_wavedevice *); +void emu10k1_waveout_start(struct emu10k1_wavedevice *); +void emu10k1_waveout_stop(struct emu10k1_wavedevice *); +void emu10k1_waveout_getxfersize(struct woinst*, u32 *); +void emu10k1_waveout_xferdata(struct woinst*, u8*, u32 *); +void emu10k1_waveout_fillsilence(struct woinst*); +int emu10k1_waveout_setformat(struct emu10k1_wavedevice*, struct wave_format*); +void emu10k1_waveout_update(struct woinst*); + +#endif /* _CARDWO_H */ diff -Nru linux/sound/oss/emu10k1/ecard.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.c --- linux/sound/oss/emu10k1/ecard.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,157 @@ +/* + ********************************************************************** + * ecard.c - E-card initialization code + * 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 "ecard.h" +#include "hwaccess.h" + +/* Private routines */ +static void ecard_setadcgain(struct emu10k1_card *, struct ecard_state *, u16); +static void ecard_write(struct emu10k1_card *, u32); + +/************************************************************************** + * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The + * trim value consists of a 16bit value which is composed of two + * 8 bit gain/trim values, one for the left channel and one for the + * right channel. The following table maps from the Gain/Attenuation + * value in decibels into the corresponding bit pattern for a single + * channel. + */ + +static void ecard_setadcgain(struct emu10k1_card *card, struct ecard_state *ecard, u16 gain) +{ + u32 currbit; + ecard->adc_gain = gain; + + /* Enable writing to the TRIM registers */ + ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); + + /* Do it again to insure that we meet hold time requirements */ + ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); + + for (currbit = (1L << 15); currbit; currbit >>= 1) { + + u32 value = ecard->control_bits & ~(EC_TRIM_CSN|EC_TRIM_SDATA); + + if (gain & currbit) + value |= EC_TRIM_SDATA; + + /* Clock the bit */ + ecard_write(card, value); + ecard_write(card, value | EC_TRIM_SCLK); + ecard_write(card, value); + } + + ecard_write(card, ecard->control_bits); +} + +/************************************************************************** + * @func Clock bits into the Ecard's control latch. The Ecard uses a + * control latch will is loaded bit-serially by toggling the Modem control + * lines from function 2 on the E8010. This function hides these details + * and presents the illusion that we are actually writing to a distinct + * register. + */ +static void ecard_write(struct emu10k1_card *card, u32 value) +{ + u16 count; + u32 data, hcvalue; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + hcvalue = inl(card->iobase + HCFG) & ~(HOOKN_BIT|HANDN_BIT|PULSEN_BIT); + + outl(card->iobase + HCFG, hcvalue); + + for (count = 0 ; count < EC_NUM_CONTROL_BITS; count++) { + + /* Set up the value */ + data = ((value & 0x1) ? PULSEN_BIT : 0); + value >>= 1; + + outl(card->iobase + HCFG, hcvalue | data); + + /* Clock the shift register */ + outl(card->iobase + HCFG, hcvalue | data | HANDN_BIT); + outl(card->iobase + HCFG, hcvalue | data); + } + + /* Latch the bits */ + outl(card->iobase + HCFG, hcvalue | HOOKN_BIT); + outl(card->iobase + HCFG, hcvalue); + + spin_unlock_irqrestore(&card->lock, flags); +} + +void __devinit emu10k1_ecard_init(struct emu10k1_card *card) +{ + u32 hcvalue; + struct ecard_state ecard; + + /* Set up the initial settings */ + ecard.mux0_setting = EC_DEFAULT_SPDIF0_SEL; + ecard.mux1_setting = EC_DEFAULT_SPDIF1_SEL; + ecard.mux2_setting = 0; + ecard.adc_gain = EC_DEFAULT_ADC_GAIN; + ecard.control_bits = EC_RAW_RUN_MODE | + EC_SPDIF0_SELECT(ecard.mux0_setting) | + EC_SPDIF1_SELECT(ecard.mux1_setting); + + + /* Step 0: Set the codec type in the hardware control register + * and enable audio output */ + hcvalue = emu10k1_readfn0(card, HCFG); + emu10k1_writefn0(card, HCFG, hcvalue | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S); + + /* Step 1: Turn off the led and deassert TRIM_CS */ + ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 2: Calibrate the ADC and DAC */ + ecard_write(card, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 3: Wait for awhile; FIXME: Is this correct? */ + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ); + + /* Step 4: Switch off the DAC and ADC calibration. Note + * That ADC_CAL is actually an inverted signal, so we assert + * it here to stop calibration. */ + ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 4: Switch into run mode */ + ecard_write(card, ecard.control_bits); + + /* Step 5: Set the analog input gain */ + ecard_setadcgain(card, &ecard, ecard.adc_gain); +} + + diff -Nru linux/sound/oss/emu10k1/ecard.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.h --- linux/sound/oss/emu10k1/ecard.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/ecard.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,113 @@ +/* + ********************************************************************** + * ecard.h + * Copyright 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 _ECARD_H +#define _ECARD_H + +#include "8010.h" +#include "hwaccess.h" +#include + +/* In A1 Silicon, these bits are in the HC register */ +#define HOOKN_BIT (1L << 12) +#define HANDN_BIT (1L << 11) +#define PULSEN_BIT (1L << 10) + +#define EC_GDI1 (1 << 13) +#define EC_GDI0 (1 << 14) + +#define EC_NUM_CONTROL_BITS 20 + +#define EC_AC3_DATA_SELN 0x0001L +#define EC_EE_DATA_SEL 0x0002L +#define EC_EE_CNTRL_SELN 0x0004L +#define EC_EECLK 0x0008L +#define EC_EECS 0x0010L +#define EC_EESDO 0x0020L +#define EC_TRIM_CSN 0x0040L +#define EC_TRIM_SCLK 0x0080L +#define EC_TRIM_SDATA 0x0100L +#define EC_TRIM_MUTEN 0x0200L +#define EC_ADCCAL 0x0400L +#define EC_ADCRSTN 0x0800L +#define EC_DACCAL 0x1000L +#define EC_DACMUTEN 0x2000L +#define EC_LEDN 0x4000L + +#define EC_SPDIF0_SEL_SHIFT 15 +#define EC_SPDIF1_SEL_SHIFT 17 +#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT) +#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT) +#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK) +#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK) +#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should + * be incremented any time the EEPROM's + * format is changed. */ + +#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */ + +/* Addresses for special values stored in to EEPROM */ +#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */ +#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */ +#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */ + +#define EC_LAST_PROMFILE_ADDR 0x2f + +#define EC_SERIALNUM_ADD 0x30 /* First word of serial number. The number + * can be up to 30 characters in length + * and is stored as a NULL-terminated + * ASCII string. Any unused bytes must be + * filled with zeros */ +#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */ + + + +/* Most of this stuff is pretty self-evident. According to the hardware + * dudes, we need to leave the ADCCAL bit low in order to avoid a DC + * offset problem. Weird. + */ +#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | EC_TRIM_CSN) + + +#define EC_DEFAULT_ADC_GAIN 0xC4C4 +#define EC_DEFAULT_SPDIF0_SEL 0x0 +#define EC_DEFAULT_SPDIF1_SEL 0x4 + +#define HC_EA 0x01L + +/* ECARD state structure. This structure maintains the state + * for various portions of the ECARD's onboard hardware. + */ +struct ecard_state { + u32 control_bits; + u16 adc_gain; + u16 mux0_setting; + u16 mux1_setting; + u16 mux2_setting; +}; + +void emu10k1_ecard_init(struct emu10k1_card *) __devinit; + +#endif /* _ECARD_H */ diff -Nru linux/sound/oss/emu10k1/efxmgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.c --- linux/sound/oss/emu10k1/efxmgr.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,218 @@ +/* + ********************************************************************** + * efxmgr.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 +#include "hwaccess.h" +#include "efxmgr.h" + +int emu10k1_find_control_gpr(struct patch_manager *mgr, const char *patch_name, const char *gpr_name) +{ + struct dsp_patch *patch; + struct dsp_rpatch *rpatch; + char s[PATCH_NAME_SIZE + 4]; + u32 *gpr_used; + int i; + + DPD(2, "emu10k1_find_control_gpr(): %s %s\n", patch_name, gpr_name); + + rpatch = &mgr->rpatch; + if (!strcmp(rpatch->name, patch_name)) { + gpr_used = rpatch->gpr_used; + goto match; + } + + for (i = 0; i < mgr->current_pages * PATCHES_PER_PAGE; i++) { + patch = PATCH(mgr, i); + sprintf(s,"%s", patch->name); + + if (!strcmp(s, patch_name)) { + gpr_used = patch->gpr_used; + goto match; + } + } + + return -1; + + match: + for (i = 0; i < NUM_GPRS; i++) + if (mgr->gpr[i].type == GPR_TYPE_CONTROL && + test_bit(i, gpr_used) && + !strcmp(mgr->gpr[i].name, gpr_name)) + return i; + + return -1; +} + +void emu10k1_set_control_gpr(struct emu10k1_card *card, int addr, s32 val, int flag) +{ + struct patch_manager *mgr = &card->mgr; + + DPD(2, "emu10k1_set_control_gpr(): %d %x\n", addr, val); + + if (addr < 0 || addr >= NUM_GPRS) + return; + + if (flag) + val += sblive_readptr(card, GPR_BASE + addr, 0); + + if (val > mgr->gpr[addr].max) + val = mgr->gpr[addr].max; + else if (val < mgr->gpr[addr].min) + val = mgr->gpr[addr].min; + + sblive_writeptr(card, GPR_BASE + addr, 0, val); +} + +//TODO: make this configurable: +#define VOLCTRL_CHANNEL SOUND_MIXER_VOLUME +#define VOLCTRL_STEP_SIZE 5 + +//An internal function for setting OSS mixer controls. +void emu10k1_set_oss_vol(struct emu10k1_card *card, int oss_mixer, + unsigned int left, unsigned int right) +{ + extern char volume_params[SOUND_MIXER_NRDEVICES]; + + card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + + if (!card->isaps) + card->ac97.write_mixer(&card->ac97, oss_mixer, left, right); + + emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, + volume_params[oss_mixer]); + + emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, + volume_params[oss_mixer]); +} + +//FIXME: mute should unmute when pressed a second time +void emu10k1_mute_irqhandler(struct emu10k1_card *card) +{ + int oss_channel = VOLCTRL_CHANNEL; + int left, right; + static int val = 0; + + if (val) { + left = val & 0xff; + right = (val >> 8) & 0xff; + val = 0; + } else { + val = card->ac97.mixer_state[oss_channel]; + left = 0; + right = 0; + } + + emu10k1_set_oss_vol(card, oss_channel, left, right); +} + +void emu10k1_volincr_irqhandler(struct emu10k1_card *card) +{ + int oss_channel = VOLCTRL_CHANNEL; + int left, right; + + left = card->ac97.mixer_state[oss_channel] & 0xff; + right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; + + if ((left += VOLCTRL_STEP_SIZE) > 100) + left = 100; + + if ((right += VOLCTRL_STEP_SIZE) > 100) + right = 100; + + emu10k1_set_oss_vol(card, oss_channel, left, right); +} + +void emu10k1_voldecr_irqhandler(struct emu10k1_card *card) +{ + int oss_channel = VOLCTRL_CHANNEL; + int left, right; + + left = card->ac97.mixer_state[oss_channel] & 0xff; + right = (card->ac97.mixer_state[oss_channel] >> 8) & 0xff; + + if ((left -= VOLCTRL_STEP_SIZE) < 0) + left = 0; + + if ((right -= VOLCTRL_STEP_SIZE) < 0) + right = 0; + + emu10k1_set_oss_vol(card, oss_channel, left, right); +} + +void emu10k1_set_volume_gpr(struct emu10k1_card *card, int addr, s32 vol, int scale) +{ + struct patch_manager *mgr = &card->mgr; + unsigned long flags; + int muting; + + const s32 log2lin[5] ={ // attenuation (dB) + 0x7fffffff, // 0.0 + 0x7fffffff * 0.840896415253715 , // 1.5 + 0x7fffffff * 0.707106781186548, // 3.0 + 0x7fffffff * 0.594603557501361 , // 4.5 + }; + + if (addr < 0) + return; + + muting = (scale == 0x10) ? 0x7f: scale; + + vol = (100 - vol ) * scale / 100; + + // Thanks to the comp.dsp newsgroup for this neat trick: + vol = (vol >= muting) ? 0 : (log2lin[vol & 3] >> (vol >> 2)); + + spin_lock_irqsave(&mgr->lock, flags); + emu10k1_set_control_gpr(card, addr, vol, 0); + spin_unlock_irqrestore(&mgr->lock, flags); +} + +void emu10k1_dsp_irqhandler(struct emu10k1_card *card) +{ + unsigned long flags; + + if (card->pt.state != PT_STATE_INACTIVE) { + u32 bc; + bc = sblive_readptr(card, GPR_BASE + card->pt.intr_gpr, 0); + if (bc != 0) { + DPD(3, "pt interrupt, bc = %d\n", bc); + spin_lock_irqsave(&card->pt.lock, flags); + card->pt.blocks_played = bc; + if (card->pt.blocks_played >= card->pt.blocks_copied) { + DPF(1, "buffer underrun in passthrough playback\n"); + emu10k1_pt_stop(card); + } + wake_up_interruptible(&card->pt.wait); + spin_unlock_irqrestore(&card->pt.lock, flags); + } + } +} + diff -Nru linux/sound/oss/emu10k1/efxmgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.h --- linux/sound/oss/emu10k1/efxmgr.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/efxmgr.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,254 @@ +/* + ********************************************************************** + * 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) + +#define NUM_INPUTS 0x20 +#define NUM_OUTPUTS 0x20 +#define NUM_GPRS 0x100 +#define GPR_NAME_SIZE 32 +#define PATCH_NAME_SIZE 32 + +struct dsp_rpatch { + char name[PATCH_NAME_SIZE]; + u16 code_start; + u16 code_size; + + u32 gpr_used[NUM_GPRS / 32]; + u32 gpr_input[NUM_GPRS / 32]; + u32 route[NUM_OUTPUTS]; + u32 route_v[NUM_OUTPUTS]; +}; + +struct dsp_patch { + char name[PATCH_NAME_SIZE]; + u8 id; + u32 input; /* bitmap of the lines used as inputs */ + u32 output; /* bitmap of the lines used as outputs */ + u16 code_start; + u16 code_size; + + u32 gpr_used[NUM_GPRS / 32]; /* bitmap of used gprs */ + u32 gpr_input[NUM_GPRS / 32]; + u8 traml_istart; /* starting address of the internal tram lines used */ + u8 traml_isize; /* number of internal tram lines used */ + + u8 traml_estart; + u8 traml_esize; + + u16 tramb_istart; /* starting address of the internal tram memory used */ + u16 tramb_isize; /* amount of internal memory used */ + u32 tramb_estart; + u32 tramb_esize; +}; + +struct dsp_gpr { + u8 type; /* gpr type, STATIC, DYNAMIC, INPUT, OUTPUT, CONTROL */ + char name[GPR_NAME_SIZE]; /* gpr value, only valid for control gprs */ + s32 min, max; /* value range for this gpr, only valid for control gprs */ + u8 line; /* which input/output line is the gpr attached, only valid for input/output gprs */ + u8 usage; +}; + +enum { + GPR_TYPE_NULL = 0, + GPR_TYPE_IO, + GPR_TYPE_STATIC, + GPR_TYPE_DYNAMIC, + GPR_TYPE_CONTROL, + GPR_TYPE_CONSTANT +}; + +#define GPR_BASE 0x100 +#define OUTPUT_BASE 0x20 + +#define MAX_PATCHES_PAGES 32 + +struct patch_manager { + void *patch[MAX_PATCHES_PAGES]; + int current_pages; + struct dsp_rpatch rpatch; + struct dsp_gpr gpr[NUM_GPRS]; /* gpr usage table */ + spinlock_t lock; + s16 ctrl_gpr[SOUND_MIXER_NRDEVICES][2]; +}; + + +#define PATCHES_PER_PAGE (PAGE_SIZE / sizeof(struct dsp_patch)) + +#define PATCH(mgr, i) ((struct dsp_patch *) (mgr)->patch[(i) / PATCHES_PER_PAGE] + (i) % PATCHES_PER_PAGE) + +/* PCM volume control */ +#define TMP_PCM_L 0x100 //temp PCM L (after the vol control) +#define TMP_PCM_R 0x101 +#define VOL_PCM_L 0x102 //vol PCM +#define VOL_PCM_R 0x103 + +/* Routing patch */ +#define TMP_AC_L 0x104 //tmp ac97 out +#define TMP_AC_R 0x105 +#define TMP_REAR_L 0x106 //output - Temp Rear +#define TMP_REAR_R 0x107 +#define TMP_DIGI_L 0x108 //output - Temp digital +#define TMP_DIGI_R 0x109 +#define DSP_VOL_L 0x10a // main dsp volume +#define DSP_VOL_R 0x10b + +/* hw inputs */ +#define PCM_IN_L 0x00 +#define PCM_IN_R 0x01 + +#define PCM1_IN_L 0x04 +#define PCM1_IN_R 0x05 +//mutilchannel playback stream appear here: + +#define MULTI_FRONT_L 0x08 +#define MULTI_FRONT_R 0x09 +#define MULTI_REAR_L 0x0a +#define MULTI_REAR_R 0x0b +#define MULTI_CENTER 0x0c +#define MULTI_LFE 0x0d + +#define AC97_IN_L 0x10 +#define AC97_IN_R 0x11 +#define SPDIF_CD_L 0x12 +#define SPDIF_CD_R 0x13 + +/* hw outputs */ +#define AC97_FRONT_L 0x20 +#define AC97_FRONT_R 0x21 +#define DIGITAL_OUT_L 0x22 +#define DIGITAL_OUT_R 0x23 +#define DIGITAL_CENTER 0x24 +#define DIGITAL_LFE 0x25 + +#define ANALOG_REAR_L 0x28 +#define ANALOG_REAR_R 0x29 +#define ADC_REC_L 0x2a +#define ADC_REC_R 0x2b + +#define ANALOG_CENTER 0x31 +#define ANALOG_LFE 0x32 + + +#define INPUT_PATCH_START(patch, nm, ln, i) \ +do { \ + patch = PATCH(mgr, patch_n); \ + strcpy(patch->name, nm); \ + patch->code_start = pc * 2; \ + patch->input = (1<<(0x1f&ln)); \ + patch->output= (1<<(0x1f&ln)); \ + patch->id = i; \ +} while(0) + +#define INPUT_PATCH_END(patch) \ +do { \ + patch->code_size = pc * 2 - patch->code_start; \ + patch_n++; \ +} while(0) + + +#define ROUTING_PATCH_START(patch, nm) \ +do { \ + patch = &mgr->rpatch; \ + strcpy(patch->name, nm); \ + patch->code_start = pc * 2; \ +} while(0) + +#define ROUTING_PATCH_END(patch) \ +do { \ + patch->code_size = pc * 2 - patch->code_start; \ +} while(0) + +#define CONNECT(input, output) set_bit(input, &rpatch->route[(output) - OUTPUT_BASE]); + +#define CONNECT_V(input, output) set_bit(input, &rpatch->route_v[(output) - OUTPUT_BASE]); + +#define OUTPUT_PATCH_START(patch, nm, ln, i) \ +do { \ + patch = PATCH(mgr, patch_n); \ + strcpy(patch->name, nm); \ + patch->code_start = pc * 2; \ + patch->input = (1<<(0x1f&ln)); \ + patch->output= (1<<(0x1f&ln)); \ + patch->id = i; \ +} while(0) + +#define OUTPUT_PATCH_END(patch) \ +do { \ + patch->code_size = pc * 2 - patch->code_start; \ + patch_n++; \ +} while(0) + +#define GET_OUTPUT_GPR(patch, g, ln) \ +do { \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + mgr->gpr[(g) - GPR_BASE].line = ln; \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ +} while(0) + +#define GET_INPUT_GPR(patch, g, ln) \ +do { \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + mgr->gpr[(g) - GPR_BASE].line = ln; \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ + set_bit((g) - GPR_BASE, patch->gpr_input); \ +} while(0) + +#define GET_DYNAMIC_GPR(patch, g) \ +do { \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_DYNAMIC; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ +} while(0) + +#define GET_CONTROL_GPR(patch, g, nm, a, b) \ +do { \ + strcpy(mgr->gpr[(g) - GPR_BASE].name, nm); \ + mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_CONTROL; \ + mgr->gpr[(g) - GPR_BASE].usage++; \ + mgr->gpr[(g) - GPR_BASE].min = a; \ + mgr->gpr[(g) - GPR_BASE].max = b; \ + sblive_writeptr(card, g, 0, b); \ + set_bit((g) - GPR_BASE, patch->gpr_used); \ +} while(0) + +#endif /* _EFXMGR_H */ diff -Nru linux/sound/oss/emu10k1/emuadxmg.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/emuadxmg.c --- linux/sound/oss/emu10k1/emuadxmg.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/emuadxmg.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,104 @@ + +/* + ********************************************************************** + * 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); + + spin_lock_irqsave(&card->lock, flags); + + while (index < (MAXPAGES - 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) { + + /* 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]; + } + } + } + + spin_unlock_irqrestore(&card->lock, flags); + + 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 -Nru linux/sound/oss/emu10k1/hwaccess.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.c --- linux/sound/oss/emu10k1/hwaccess.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,480 @@ +/* + ********************************************************************** + * 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 + +#include "hwaccess.h" +#include "8010.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 emu10k1_writefn0(struct emu10k1_card *card, u32 reg, u32 data) +{ + unsigned long flags; + + 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; + reg &= 0x7f; + + spin_lock_irqsave(&card->lock, flags); + data |= inl(card->iobase + reg) & ~mask; + outl(data, card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + } else { + spin_lock_irqsave(&card->lock, flags); + outl(data, card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + } + + return; +} + +u32 emu10k1_readfn0(struct emu10k1_card * card, u32 reg) +{ + u32 val; + unsigned long flags; + + if (reg & 0xff000000) { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + reg &= 0x7f; + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + + return (val & mask) >> offset; + } else { + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + reg); + spin_unlock_irqrestore(&card->lock, flags); + return val; + } +} + +/************************************************************************ +* 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); + } +} + +/* ... : data, reg, ... , TAGLIST_END */ +void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...) +{ + va_list args; + + unsigned long flags; + u32 reg; + + va_start(args, channel); + + spin_lock_irqsave(&card->lock, flags); + while ((reg = va_arg(args, u32)) != TAGLIST_END) { + u32 data = va_arg(args, u32); + u32 regptr = (((reg << 16) & PTR_ADDRESS_MASK) + | (channel & PTR_CHANNELNUM_MASK)); + outl(regptr, card->iobase + PTR); + if (reg & 0xff000000) { + int size = (reg >> 24) & 0x3f; + int offset = (reg >> 16) & 0x1f; + u32 mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + + data |= inl(card->iobase + DATA) & ~mask; + } + outl(data, card->iobase + DATA); + } + spin_unlock_irqrestore(&card->lock, flags); + + va_end(args); + + 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_irq_enable(struct emu10k1_card *card, u32 irq_mask) +{ + u32 val; + unsigned long flags; + + DPF(2,"emu10k1_irq_enable()\n"); + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + INTE) | irq_mask; + outl(val, card->iobase + INTE); + spin_unlock_irqrestore(&card->lock, flags); + return; +} + +void emu10k1_irq_disable(struct emu10k1_card *card, u32 irq_mask) +{ + u32 val; + unsigned long flags; + + DPF(2,"emu10k1_irq_disable()\n"); + + spin_lock_irqsave(&card->lock, flags); + val = inl(card->iobase + INTE) & ~irq_mask; + outl(val, card->iobase + INTE); + spin_unlock_irqrestore(&card->lock, flags); + return; +} + +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 = emu10k1_readfn0(card, WC_SAMPLECOUNTER); + while (wait--) { + uCount = 0; + while (uCount++ < TIMEOUT) { + newtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER); + if (newtime != curtime) + break; + } + + if (uCount >= TIMEOUT) + break; + + curtime = newtime; + } +} + +u16 emu10k1_ac97_read(struct ac97_codec *codec, u8 reg) +{ + struct emu10k1_card *card = codec->private_data; + u16 data; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + outb(reg, card->iobase + AC97ADDRESS); + data = inw(card->iobase + AC97DATA); + + spin_unlock_irqrestore(&card->lock, flags); + + return data; +} + +void emu10k1_ac97_write(struct ac97_codec *codec, u8 reg, u16 value) +{ + struct emu10k1_card *card = codec->private_data; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + outb(reg, card->iobase + AC97ADDRESS); + outw(value, card->iobase + AC97DATA); + + spin_unlock_irqrestore(&card->lock, flags); +} + +/********************************************************* +* 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 = 0; + } else + ret = -1; + + 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 = 0; + } else + ret = -1; + + 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 0; + else + return -1; + } + + return 0; +} + +int emu10k1_mpu_acquire(struct emu10k1_card *card) +{ + /* FIXME: This should be a macro */ + ++card->mpuacqcount; + + return 0; +} + +int emu10k1_mpu_release(struct emu10k1_card *card) +{ + /* FIXME: this should be a macro */ + --card->mpuacqcount; + + return 0; +} diff -Nru linux/sound/oss/emu10k1/hwaccess.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.h --- linux/sound/oss/emu10k1/hwaccess.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/hwaccess.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,239 @@ +/* + ********************************************************************** + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "efxmgr.h" +#include "passthrough.h" +#include "midi.h" + +#define EMUPAGESIZE 4096 /* don't change */ +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 /* don't change */ +/* setting this to other than a power of two may break some applications */ +#define MAXBUFSIZE 65536 +#define MAXPAGES 8192 +#define BUFMAXPAGES (MAXBUFSIZE / PAGE_SIZE) + +#define FLAGS_AVAILABLE 0x0001 +#define FLAGS_READY 0x0002 + +struct memhandle +{ + dma_addr_t dma_handle; + void *addr; + u32 size; +}; + +#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) +#else +# define DPD(level,x,y...) do { } while (0) /* not debugging: nothing */ +# define DPF(level,x) do { } while (0) +#endif /* EMU10K1_DEBUG */ + +#define ERROR() DPF(1,"error\n") + +/* DATA STRUCTURES */ + +struct emu10k1_waveout +{ + u16 send_routing[3]; + + u8 send_a[3]; + u8 send_b[3]; + u8 send_c[3]; + u8 send_d[3]; +}; + +struct emu10k1_wavein +{ + struct wiinst *ac97; + struct wiinst *mic; + struct wiinst *fx; + + u8 recsrc; + u32 fxwc; +}; + +#define CMD_READ 1 +#define CMD_WRITE 2 + +struct mixer_private_ioctl { + u32 cmd; + u32 val[90]; +}; + +/* bogus ioctls numbers to escape from OSS mixer limitations */ +#define CMD_WRITEFN0 _IOW('D', 0, struct mixer_private_ioctl) +#define CMD_READFN0 _IOR('D', 1, struct mixer_private_ioctl) +#define CMD_WRITEPTR _IOW('D', 2, struct mixer_private_ioctl) +#define CMD_READPTR _IOR('D', 3, struct mixer_private_ioctl) +#define CMD_SETRECSRC _IOW('D', 4, struct mixer_private_ioctl) +#define CMD_GETRECSRC _IOR('D', 5, struct mixer_private_ioctl) +#define CMD_GETVOICEPARAM _IOR('D', 6, struct mixer_private_ioctl) +#define CMD_SETVOICEPARAM _IOW('D', 7, struct mixer_private_ioctl) +#define CMD_GETPATCH _IOR('D', 8, struct mixer_private_ioctl) +#define CMD_GETGPR _IOR('D', 9, struct mixer_private_ioctl) +#define CMD_GETCTLGPR _IOR('D', 10, struct mixer_private_ioctl) +#define CMD_SETPATCH _IOW('D', 11, struct mixer_private_ioctl) +#define CMD_SETGPR _IOW('D', 12, struct mixer_private_ioctl) +#define CMD_SETCTLGPR _IOW('D', 13, struct mixer_private_ioctl) +#define CMD_SETGPOUT _IOW('D', 14, struct mixer_private_ioctl) +#define CMD_GETGPR2OSS _IOR('D', 15, struct mixer_private_ioctl) +#define CMD_SETGPR2OSS _IOW('D', 16, struct mixer_private_ioctl) +#define CMD_SETMCH_FX _IOW('D', 17, struct mixer_private_ioctl) +#define CMD_SETPASSTHROUGH _IOW('D', 18, struct mixer_private_ioctl) +#define CMD_PRIVATE3_VERSION _IOW('D', 19, struct mixer_private_ioctl) + +//up this number when breaking compatibility +#define PRIVATE3_VERSION 1 + +struct emu10k1_card +{ + struct list_head list; + + struct memhandle virtualpagetable; + struct memhandle tankmem; + struct memhandle silentpage; + + spinlock_t lock; + + u8 voicetable[NUM_G]; + u16 emupagetable[MAXPAGES]; + + struct list_head timers; + unsigned timer_delay; + spinlock_t timer_lock; + + struct pci_dev *pci_dev; + unsigned long iobase; + unsigned long length; + unsigned short model; + unsigned int irq; + + int audio_dev; + int audio_dev1; + int midi_dev; +#ifdef EMU10K1_SEQUENCER + int seq_dev; + struct emu10k1_mididevice *seq_mididev; +#endif + + struct ac97_codec ac97; + int ac97_supported_mixers; + int ac97_stereo_mixers; + + /* Number of first fx voice for multichannel output */ + u8 mchannel_fx; + struct emu10k1_waveout waveout; + struct emu10k1_wavein wavein; + struct emu10k1_mpuout *mpuout; + struct emu10k1_mpuin *mpuin; + + 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 */ + + int isaps; + + struct patch_manager mgr; + struct pt_data pt; +}; + +int emu10k1_addxmgr_alloc(u32, struct emu10k1_card *); +void emu10k1_addxmgr_free(struct emu10k1_card *, int); + + + +int emu10k1_find_control_gpr(struct patch_manager *, const char *, const char *); +void emu10k1_set_control_gpr(struct emu10k1_card *, int , s32, int ); + +void emu10k1_set_volume_gpr(struct emu10k1_card *, int, s32, int); + + +#define VOL_6BIT 0x40 +#define VOL_5BIT 0x20 +#define VOL_4BIT 0x10 + +#define TIMEOUT 16384 + +u32 srToPitch(u32); +u8 sumVolumeToAttenuation(u32); + +extern struct list_head emu10k1_devs; + +/* Hardware Abstraction Layer access functions */ + +void emu10k1_writefn0(struct emu10k1_card *, u32 , u32 ); +u32 emu10k1_readfn0(struct emu10k1_card *, u32 ); + +void sblive_writeptr(struct emu10k1_card *, u32 , u32 , u32 ); +void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...); +#define TAGLIST_END 0 + +u32 sblive_readptr(struct emu10k1_card *, u32 , u32 ); + +void emu10k1_irq_enable(struct emu10k1_card *, u32); +void emu10k1_irq_disable(struct emu10k1_card *, u32); +void emu10k1_set_stop_on_loop(struct emu10k1_card *, u32); +void emu10k1_clear_stop_on_loop(struct emu10k1_card *, u32); + +/* AC97 Codec register access function */ +u16 emu10k1_ac97_read(struct ac97_codec *, u8); +void emu10k1_ac97_write(struct ac97_codec *, u8, 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 -Nru linux/sound/oss/emu10k1/icardmid.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardmid.h --- linux/sound/oss/emu10k1/icardmid.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardmid.h Mon Apr 8 22:31:22 2002 @@ -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 -Nru linux/sound/oss/emu10k1/icardwav.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardwav.h --- linux/sound/oss/emu10k1/icardwav.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/icardwav.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,53 @@ +/* + ********************************************************************** + * 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 + +struct wave_format +{ + int id; + int samplingrate; + u8 bitsperchannel; + u8 channels; /* 1 = Mono, 2 = Stereo, 3, ... = Multichannel */ + u8 bytesperchannel; + u8 bytespervoicesample; + u8 bytespersample; + int bytespersec; + u8 passthrough; +}; + +/* emu10k1_wave states */ +#define WAVE_STATE_OPEN 0x01 +#define WAVE_STATE_STARTED 0x02 +#define WAVE_STATE_CLOSED 0x04 + +#endif /* _ICARDWAV_H */ diff -Nru linux/sound/oss/emu10k1/irqmgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.c --- linux/sound/oss/emu10k1/irqmgr.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,104 @@ + +/* + ********************************************************************** + * 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 "8010.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; + + DPD(4, "emu10k1_interrupt called, irq = %u\n", irq); + + /* + ** 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 + */ + + while ((irqstatus = inl(card->iobase + IPR))) { + DPD(4, "irq status %#x\n", irqstatus); + + /* acknowledge interrupt */ + outl(irqstatus, card->iobase + IPR); + + if (irqstatus & IRQTYPE_TIMER) { + emu10k1_timer_irqhandler(card); + irqstatus &= ~IRQTYPE_TIMER; + } + + if (irqstatus & IRQTYPE_DSP) { + emu10k1_dsp_irqhandler(card); + irqstatus &= ~IRQTYPE_DSP; + } + + if (irqstatus & IRQTYPE_MPUIN) { + emu10k1_mpuin_irqhandler(card); + irqstatus &= ~IRQTYPE_MPUIN; + } + + if (irqstatus & IRQTYPE_MPUOUT) { + emu10k1_mpuout_irqhandler(card); + irqstatus &= ~IRQTYPE_MPUOUT; + } + + if (irqstatus & IPR_MUTE) { + emu10k1_mute_irqhandler(card); + irqstatus &=~IPR_MUTE; + } + + if (irqstatus & IPR_VOLINCR) { + emu10k1_volincr_irqhandler(card); + irqstatus &=~IPR_VOLINCR; + } + + if (irqstatus & IPR_VOLDECR) { + emu10k1_voldecr_irqhandler(card); + irqstatus &=~IPR_VOLDECR; + } + + if (irqstatus) + emu10k1_irq_disable(card, irqstatus); + } +} diff -Nru linux/sound/oss/emu10k1/irqmgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.h --- linux/sound/oss/emu10k1/irqmgr.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/irqmgr.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,52 @@ +/* + ********************************************************************** + * 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 + +void emu10k1_timer_irqhandler(struct emu10k1_card *); +void emu10k1_dsp_irqhandler(struct emu10k1_card *); +void emu10k1_mute_irqhandler(struct emu10k1_card *); +void emu10k1_volincr_irqhandler(struct emu10k1_card *); +void emu10k1_voldecr_irqhandler(struct emu10k1_card *); + +#endif /* _IRQ_H */ diff -Nru linux/sound/oss/emu10k1/main.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/main.c --- linux/sound/oss/emu10k1/main.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/main.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1181 @@ +/* + ********************************************************************** + * 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/dsp1: Routes to rear speakers only + * /dev/mixer: Standard /dev/mixer device, OSS-compatible + * /dev/midi: Raw MIDI UART device, mostly OSS-compatible + * /dev/sequencer: Sequencer Interface (requires sound.o) + * + * 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. + * 0.6 Make use of pci_alloc_consistent, improve compatibility layer for 2.2 kernels, + * code reorganization and cleanup. + * 0.7 Support for the Emu-APS. Bug fixes for voice cache setup, mmaped sound + poll(). + * Support for setting external TRAM size. + * 0.8 Make use of the kernel ac97 interface. Support for a dsp patch manager. + * 0.9 Re-enables rear speakers volume controls + * 0.10 Initializes rear speaker volume. + * Dynamic patch storage allocation. + * New private ioctls to change control gpr values. + * Enable volume control interrupts. + * By default enable dsp routes to digital out. + * 0.11 Fixed fx / 4 problem. + * 0.12 Implemented mmaped for recording. + * Fixed bug: not unreserving mmaped buffer pages. + * IRQ handler cleanup. + * 0.13 Fixed problem with dsp1 + * Simplified dsp patch writing (inside the driver) + * Fixed several bugs found by the Stanford tools + * 0.14 New control gpr to oss mixer mapping feature (Chris Purnell) + * Added AC3 Passthrough Support (Juha Yrjola) + * Added Support for 5.1 cards (digital out and the third analog out) + * 0.15 Added Sequencer Support (Daniel Mack) + * Support for multichannel pcm playback (Eduard Hasenleithner) + * 0.16 Mixer improvements, added old treble/bass support (Daniel Bertrand) + * Small code format cleanup. + * Deadlock bug fix for emu10k1_volxxx_irqhandler(). + * + *********************************************************************/ + +/* These are only included once per module */ +#include +#include +#include +#include +#include +#include + +#include "hwaccess.h" +#include "8010.h" +#include "efxmgr.h" +#include "cardwo.h" +#include "cardwi.h" +#include "cardmo.h" +#include "cardmi.h" +#include "recmgr.h" +#include "ecard.h" + + +#ifdef EMU10K1_SEQUENCER +#define MIDI_SYNTH_NAME "EMU10K1 MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT + +#include "../sound_config.h" +#include "../midi_synth.h" + +/* this should be in dev_table.h */ +#define SNDCARD_EMU10K1 46 +#endif + +#define DRIVER_VERSION "0.16" + +/* FIXME: is this right? */ +/* does the card support 32 bit bus master?*/ +#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 EMU_APS_SUBID 0x40011102 + +enum { + EMU10K1 = 0, +}; + +static char *card_names[] __devinitdata = { + "EMU10K1", +}; + +static struct pci_device_id emu10k1_pci_tbl[] = { + {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; + +#ifdef EMU10K1_SEQUENCER +static struct midi_operations emu10k1_midi_operations; +#endif + +extern void emu10k1_interrupt(int, void *, struct pt_regs *s); + +static int __devinit emu10k1_audio_init(struct emu10k1_card *card) +{ + card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1); + if (card->audio_dev < 0) { + printk(KERN_ERR "emu10k1: cannot register first audio device!\n"); + goto err_dev; + } + + card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1); + if (card->audio_dev1 < 0) { + printk(KERN_ERR "emu10k1: cannot register second audio device!\n"); + goto err_dev1; + } + + /* Assign default playback voice parameters */ + card->mchannel_fx = 8; + /* mono voice */ + card->waveout.send_a[0] = 0xff; + card->waveout.send_b[0] = 0xff; + card->waveout.send_c[0] = 0x00; + card->waveout.send_d[0] = 0x00; + card->waveout.send_routing[0] = 0x3210; + + /* stereo voice */ + /* left */ + card->waveout.send_a[1] = 0xff; + card->waveout.send_b[1] = 0x00; + card->waveout.send_c[1] = 0x00; + card->waveout.send_d[1] = 0x00; + card->waveout.send_routing[1] = 0x3210; + + /* right */ + card->waveout.send_a[2] = 0x00; + card->waveout.send_b[2] = 0xff; + card->waveout.send_c[2] = 0x00; + card->waveout.send_d[2] = 0x00; + card->waveout.send_routing[2] = 0x3210; + + /* Assign default recording parameters */ + /* FIXME */ + if(card->isaps) + card->wavein.recsrc = WAVERECORD_FX; + else + card->wavein.recsrc = WAVERECORD_AC97; + + card->wavein.fxwc = 0x0003; + return 0; + +err_dev1: + unregister_sound_dsp(card->audio_dev); +err_dev: + return -ENODEV; +} + +static void __devinit emu10k1_audio_cleanup(struct emu10k1_card *card) +{ + unregister_sound_dsp(card->audio_dev1); + unregister_sound_dsp(card->audio_dev); +} + +static int __devinit emu10k1_mixer_init(struct emu10k1_card *card) +{ + char s[32]; + card->ac97.dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1); + if (card->ac97.dev_mixer < 0) { + printk(KERN_ERR "emu10k1: cannot register mixer device\n"); + return -EIO; + } + + card->ac97.private_data = card; + + if (!card->isaps) { + card->ac97.id = 0; + card->ac97.codec_read = emu10k1_ac97_read; + card->ac97.codec_write = emu10k1_ac97_write; + + if (ac97_probe_codec (&card->ac97) == 0) { + printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n"); + goto err_out; + } + /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version + does not support this, it shouldn't do any harm */ + sblive_writeptr(card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE); + + // Force 5bit + //card->ac97.bit_resolution=5; + + if (!proc_mkdir ("driver/emu10k1", 0)) { + printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n"); + goto err_out; + } + + sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); + if (!proc_mkdir (s, 0)) { + printk(KERN_ERR "emu10k1: unable to create proc directory %s\n", s); + goto err_emu10k1_proc; + } + + sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s); + goto err_ac97_proc; + } + + /* these will store the original values and never be modified */ + card->ac97_supported_mixers = card->ac97.supported_mixers; + card->ac97_stereo_mixers = card->ac97.stereo_mixers; + } + + return 0; + + err_ac97_proc: + sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); + remove_proc_entry(s, NULL); + + err_emu10k1_proc: + remove_proc_entry("driver/emu10k1", NULL); + err_out: + unregister_sound_mixer (card->ac97.dev_mixer); + return -EIO; +} + +static void __devinit emu10k1_mixer_cleanup(struct emu10k1_card *card) +{ + char s[32]; + + if (!card->isaps) { + sprintf(s, "driver/emu10k1/%s/ac97", card->pci_dev->slot_name); + remove_proc_entry(s, NULL); + + sprintf(s, "driver/emu10k1/%s", card->pci_dev->slot_name); + remove_proc_entry(s, NULL); + + remove_proc_entry("driver/emu10k1", NULL); + } + + unregister_sound_mixer (card->ac97.dev_mixer); +} + +static int __devinit emu10k1_midi_init(struct emu10k1_card *card) +{ + int ret; + + card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1); + if (card->midi_dev < 0) { + printk(KERN_ERR "emu10k1: cannot register midi device!\n"); + return -ENODEV; + } + + + card->mpuout = kmalloc(sizeof(struct emu10k1_mpuout), GFP_KERNEL); + if (card->mpuout == NULL) { + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuout: out of memory\n"); + ret = -ENOMEM; + goto err_out1; + } + + 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); + + card->mpuin = kmalloc(sizeof(struct emu10k1_mpuin), GFP_KERNEL); + if (card->mpuin == NULL) { + printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuin: out of memory\n"); + ret = -ENOMEM; + goto err_out2; + } + + 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) < 0) { + ERROR(); + ret = -EIO; + goto err_out3; + } + +#ifdef EMU10K1_SEQUENCER + card->seq_dev = sound_alloc_mididev(); + if (card->seq_dev == -1) + printk(KERN_WARNING "emu10k1: unable to register sequencer device!"); + else { + std_midi_synth.midi_dev = card->seq_dev; + midi_devs[card->seq_dev] = + (struct midi_operations *) + kmalloc(sizeof(struct midi_operations), GFP_KERNEL); + + if (midi_devs[card->seq_dev] == NULL) { + printk(KERN_ERR "emu10k1: unable to allocate memory!"); + sound_unload_mididev(card->seq_dev); + card->seq_dev = -1; + return 0; + } else { + memcpy((char *)midi_devs[card->seq_dev], + (char *)&emu10k1_midi_operations, + sizeof(struct midi_operations)); + midi_devs[card->seq_dev]->devc = card; + sequencer_init(); + } + } + card->seq_mididev = 0; +#endif + return 0; + +err_out3: + kfree(card->mpuin); +err_out2: + kfree(card->mpuout); +err_out1: + unregister_sound_midi(card->midi_dev); + return ret; +} + +static void __devinit emu10k1_midi_cleanup(struct emu10k1_card *card) +{ + tasklet_kill(&card->mpuout->tasklet); + kfree(card->mpuout); + + tasklet_kill(&card->mpuin->tasklet); + kfree(card->mpuin); + +#ifdef EMU10K1_SEQUENCER + if (card->seq_dev > -1) { + kfree(midi_devs[card->seq_dev]); + midi_devs[card->seq_dev] = NULL; + sound_unload_mididev(card->seq_dev); + card->seq_dev = -1; + } +#endif + + unregister_sound_midi(card->midi_dev); +} + +static void __devinit voice_init(struct emu10k1_card *card) +{ + int i; + + for (i = 0; i < NUM_G; i++) + card->voicetable[i] = VOICE_USAGE_FREE; +} + +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; +} + +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 - 1; +} + +static void __devinit fx_cleanup(struct patch_manager *mgr) +{ + int i; + for(i = 0; i < mgr->current_pages; i++) + free_page((unsigned long) mgr->patch[i]); +} + +static int __devinit fx_init(struct emu10k1_card *card) +{ + struct patch_manager *mgr = &card->mgr; + struct dsp_patch *patch; + struct dsp_rpatch *rpatch; + s32 left, right; + int i; + u32 pc = 0; + u32 patch_n; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + mgr->ctrl_gpr[i][0] = -1; + mgr->ctrl_gpr[i][1] = -1; + } + + for (i = 0; i < 512; i++) + OP(6, 0x40, 0x40, 0x40, 0x40); + + for (i = 0; i < 256; i++) + sblive_writeptr_tag(card, 0, + FXGPREGBASE + i, 0, + TANKMEMADDRREGBASE + i, 0, + TAGLIST_END); + + /* !! The number bellow must equal the number of patches, currently 11 !! */ + mgr->current_pages = (11 + PATCHES_PER_PAGE - 1) / PATCHES_PER_PAGE; + for (i = 0; i < mgr->current_pages; i++) { + mgr->patch[i] = (void *)__get_free_page(GFP_KERNEL); + if (mgr->patch[i] == NULL) { + mgr->current_pages = i; + fx_cleanup(mgr); + return -ENOMEM; + } + memset(mgr->patch[i], 0, PAGE_SIZE); + } + + pc = 0; + patch_n = 0; + //first free GPR = 0x11b + + /* FX volume correction and Volume control*/ + INPUT_PATCH_START(patch, "Pcm L vol", 0x0, 0); + GET_OUTPUT_GPR(patch, 0x100, 0x0); + GET_CONTROL_GPR(patch, 0x106, "Vol", 0, 0x7fffffff); + GET_DYNAMIC_GPR(patch, 0x112); + + OP(4, 0x112, 0x40, PCM_IN_L, 0x44); //*4 + OP(0, 0x100, 0x040, 0x112, 0x106); //*vol + INPUT_PATCH_END(patch); + + + INPUT_PATCH_START(patch, "Pcm R vol", 0x1, 0); + GET_OUTPUT_GPR(patch, 0x101, 0x1); + GET_CONTROL_GPR(patch, 0x107, "Vol", 0, 0x7fffffff); + GET_DYNAMIC_GPR(patch, 0x112); + + OP(4, 0x112, 0x40, PCM_IN_R, 0x44); + OP(0, 0x101, 0x040, 0x112, 0x107); + + INPUT_PATCH_END(patch); + + + // CD-Digital In Volume control + INPUT_PATCH_START(patch, "CD-Digital Vol L", 0x12, 0); + GET_OUTPUT_GPR(patch, 0x10c, 0x12); + GET_CONTROL_GPR(patch, 0x10d, "Vol", 0, 0x7fffffff); + + OP(0, 0x10c, 0x040, SPDIF_CD_L, 0x10d); + INPUT_PATCH_END(patch); + + INPUT_PATCH_START(patch, "CD-Digital Vol R", 0x13, 0); + GET_OUTPUT_GPR(patch, 0x10e, 0x13); + GET_CONTROL_GPR(patch, 0x10f, "Vol", 0, 0x7fffffff); + + OP(0, 0x10e, 0x040, SPDIF_CD_R, 0x10f); + INPUT_PATCH_END(patch); + + //Volume Correction for Multi-channel Inputs + INPUT_PATCH_START(patch, "Multi-Channel Gain", 0x08, 0); + patch->input=patch->output=0x3F00; + + GET_OUTPUT_GPR(patch, 0x113, MULTI_FRONT_L); + GET_OUTPUT_GPR(patch, 0x114, MULTI_FRONT_R); + GET_OUTPUT_GPR(patch, 0x115, MULTI_REAR_L); + GET_OUTPUT_GPR(patch, 0x116, MULTI_REAR_R); + GET_OUTPUT_GPR(patch, 0x117, MULTI_CENTER); + GET_OUTPUT_GPR(patch, 0x118, MULTI_LFE); + + OP(4, 0x113, 0x40, MULTI_FRONT_L, 0x44); + OP(4, 0x114, 0x40, MULTI_FRONT_R, 0x44); + OP(4, 0x115, 0x40, MULTI_REAR_L, 0x44); + OP(4, 0x116, 0x40, MULTI_REAR_R, 0x44); + OP(4, 0x117, 0x40, MULTI_CENTER, 0x44); + OP(4, 0x118, 0x40, MULTI_LFE, 0x44); + + INPUT_PATCH_END(patch); + + + //Routing patch start + ROUTING_PATCH_START(rpatch, "Routing"); + GET_INPUT_GPR(rpatch, 0x100, 0x0); + GET_INPUT_GPR(rpatch, 0x101, 0x1); + GET_INPUT_GPR(rpatch, 0x10c, 0x12); + GET_INPUT_GPR(rpatch, 0x10e, 0x13); + GET_INPUT_GPR(rpatch, 0x113, MULTI_FRONT_L); + GET_INPUT_GPR(rpatch, 0x114, MULTI_FRONT_R); + GET_INPUT_GPR(rpatch, 0x115, MULTI_REAR_L); + GET_INPUT_GPR(rpatch, 0x116, MULTI_REAR_R); + GET_INPUT_GPR(rpatch, 0x117, MULTI_CENTER); + GET_INPUT_GPR(rpatch, 0x118, MULTI_LFE); + + GET_DYNAMIC_GPR(rpatch, 0x102); + GET_DYNAMIC_GPR(rpatch, 0x103); + + GET_OUTPUT_GPR(rpatch, 0x104, 0x8); + GET_OUTPUT_GPR(rpatch, 0x105, 0x9); + GET_OUTPUT_GPR(rpatch, 0x10a, 0x2); + GET_OUTPUT_GPR(rpatch, 0x10b, 0x3); + + + /* input buffer */ + OP(6, 0x102, AC97_IN_L, 0x40, 0x40); + OP(6, 0x103, AC97_IN_R, 0x40, 0x40); + + + /* Digital In + PCM + MULTI_FRONT-> AC97 out (front speakers)*/ + OP(6, AC97_FRONT_L, 0x100, 0x10c, 0x113); + + CONNECT(MULTI_FRONT_L, AC97_FRONT_L); + CONNECT(PCM_IN_L, AC97_FRONT_L); + CONNECT(SPDIF_CD_L, AC97_FRONT_L); + + OP(6, AC97_FRONT_R, 0x101, 0x10e, 0x114); + + CONNECT(MULTI_FRONT_R, AC97_FRONT_R); + CONNECT(PCM_IN_R, AC97_FRONT_R); + CONNECT(SPDIF_CD_R, AC97_FRONT_R); + + /* Digital In + PCM + AC97 In + PCM1 + MULTI_REAR --> Rear Channel */ + OP(6, 0x104, PCM1_IN_L, 0x100, 0x115); + OP(6, 0x104, 0x104, 0x10c, 0x102); + + CONNECT(MULTI_REAR_L, ANALOG_REAR_L); + CONNECT(AC97_IN_L, ANALOG_REAR_L); + CONNECT(PCM_IN_L, ANALOG_REAR_L); + CONNECT(SPDIF_CD_L, ANALOG_REAR_L); + CONNECT(PCM1_IN_L, ANALOG_REAR_L); + + OP(6, 0x105, PCM1_IN_R, 0x101, 0x116); + OP(6, 0x105, 0x105, 0x10e, 0x103); + + CONNECT(MULTI_REAR_R, ANALOG_REAR_R); + CONNECT(AC97_IN_R, ANALOG_REAR_R); + CONNECT(PCM_IN_R, ANALOG_REAR_R); + CONNECT(SPDIF_CD_R, ANALOG_REAR_R); + CONNECT(PCM1_IN_R, ANALOG_REAR_R); + + /* Digital In + PCM + AC97 In + MULTI_FRONT --> Digital out */ + OP(6, 0x10b, 0x100, 0x102, 0x10c); + OP(6, 0x10b, 0x10b, 0x113, 0x40); + + CONNECT(MULTI_FRONT_L, DIGITAL_OUT_L); + CONNECT(PCM_IN_L, DIGITAL_OUT_L); + CONNECT(AC97_IN_L, DIGITAL_OUT_L); + CONNECT(SPDIF_CD_L, DIGITAL_OUT_L); + + OP(6, 0x10a, 0x101, 0x103, 0x10e); + OP(6, 0x10b, 0x10b, 0x114, 0x40); + + CONNECT(MULTI_FRONT_R, DIGITAL_OUT_R); + CONNECT(PCM_IN_R, DIGITAL_OUT_R); + CONNECT(AC97_IN_R, DIGITAL_OUT_R); + CONNECT(SPDIF_CD_R, DIGITAL_OUT_R); + + /* AC97 In --> ADC Recording Buffer */ + OP(6, ADC_REC_L, 0x102, 0x40, 0x40); + + CONNECT(AC97_IN_L, ADC_REC_L); + + OP(6, ADC_REC_R, 0x103, 0x40, 0x40); + + CONNECT(AC97_IN_R, ADC_REC_R); + + + /* fx12:Analog-Center */ + OP(6, ANALOG_CENTER, 0x117, 0x40, 0x40); + CONNECT(MULTI_CENTER, ANALOG_CENTER); + + /* fx11:Analog-LFE */ + OP(6, ANALOG_LFE, 0x118, 0x40, 0x40); + CONNECT(MULTI_LFE, ANALOG_LFE); + + /* fx12:Digital-Center */ + OP(6, DIGITAL_CENTER, 0x117, 0x40, 0x40); + CONNECT(MULTI_CENTER, DIGITAL_CENTER); + + /* fx11:Analog-LFE */ + OP(6, DIGITAL_LFE, 0x118, 0x40, 0x40); + CONNECT(MULTI_LFE, DIGITAL_LFE); + + ROUTING_PATCH_END(rpatch); + + + // Rear volume control + OUTPUT_PATCH_START(patch, "Vol Rear L", 0x8, 0); + GET_INPUT_GPR(patch, 0x104, 0x8); + GET_CONTROL_GPR(patch, 0x119, "Vol", 0, 0x7fffffff); + + OP(0, ANALOG_REAR_L, 0x040, 0x104, 0x119); + OUTPUT_PATCH_END(patch); + + + OUTPUT_PATCH_START(patch, "Vol Rear R", 0x9, 0); + GET_INPUT_GPR(patch, 0x105, 0x9); + GET_CONTROL_GPR(patch, 0x11a, "Vol", 0, 0x7fffffff); + + OP(0, ANALOG_REAR_R, 0x040, 0x105, 0x11a); + OUTPUT_PATCH_END(patch); + + + //Master volume control on front-digital + OUTPUT_PATCH_START(patch, "Vol Master L", 0x2, 1); + GET_INPUT_GPR(patch, 0x10a, 0x2); + GET_CONTROL_GPR(patch, 0x108, "Vol", 0, 0x7fffffff); + + OP(0, DIGITAL_OUT_L, 0x040, 0x10a, 0x108); + OUTPUT_PATCH_END(patch); + + + OUTPUT_PATCH_START(patch, "Vol Master R", 0x3, 1); + GET_INPUT_GPR(patch, 0x10b, 0x3); + GET_CONTROL_GPR(patch, 0x109, "Vol", 0, 0x7fffffff); + + OP(0, DIGITAL_OUT_R, 0x040, 0x10b, 0x109); + OUTPUT_PATCH_END(patch); + + + /* delimiter patch */ + patch = PATCH(mgr, patch_n); + patch->code_size = 0; + + sblive_writeptr(card, DBG, 0, 0); + + mgr->lock = SPIN_LOCK_UNLOCKED; + + + //Master volume + mgr->ctrl_gpr[SOUND_MIXER_VOLUME][0] = 8; + mgr->ctrl_gpr[SOUND_MIXER_VOLUME][1] = 9; + + left = card->ac97.mixer_state[SOUND_MIXER_VOLUME] & 0xff; + right = (card->ac97.mixer_state[SOUND_MIXER_VOLUME] >> 8) & 0xff; + + emu10k1_set_volume_gpr(card, 8, left, 1 << card->ac97.bit_resolution); + emu10k1_set_volume_gpr(card, 9, right, 1 << card->ac97.bit_resolution); + + //Rear volume + mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][0] = 0x19; + mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][1] = 0x1a; + + left = right = 67; + card->ac97.mixer_state[SOUND_MIXER_OGAIN] = (right << 8) | left; + + card->ac97.supported_mixers |= SOUND_MASK_OGAIN; + card->ac97.stereo_mixers |= SOUND_MASK_OGAIN; + + emu10k1_set_volume_gpr(card, 0x19, left, VOL_5BIT); + emu10k1_set_volume_gpr(card, 0x1a, right, VOL_5BIT); + + //PCM Volume + mgr->ctrl_gpr[SOUND_MIXER_PCM][0] = 6; + mgr->ctrl_gpr[SOUND_MIXER_PCM][1] = 7; + + left = card->ac97.mixer_state[SOUND_MIXER_PCM] & 0xff; + right = (card->ac97.mixer_state[SOUND_MIXER_PCM] >> 8) & 0xff; + + emu10k1_set_volume_gpr(card, 6, left, VOL_5BIT); + emu10k1_set_volume_gpr(card, 7, right, VOL_5BIT); + + //CD-Digital Volume + mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][0] = 0xd; + mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][1] = 0xf; + + left = right = 67; + card->ac97.mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left; + + card->ac97.supported_mixers |= SOUND_MASK_DIGITAL1; + card->ac97.stereo_mixers |= SOUND_MASK_DIGITAL1; + + emu10k1_set_volume_gpr(card, 0xd, left, VOL_5BIT); + emu10k1_set_volume_gpr(card, 0xf, right, VOL_5BIT); + + //hard wire the ac97's pcm, we'll do that in dsp code instead. + emu10k1_ac97_write(&card->ac97, 0x18, 0x0); + card->ac97_supported_mixers &= ~SOUND_MASK_PCM; + card->ac97_stereo_mixers &= ~SOUND_MASK_PCM; + + //set Igain to 0dB by default, maybe consider hardwiring it here. + emu10k1_ac97_write(&card->ac97, AC97_RECORD_GAIN, 0x0000); + card->ac97.mixer_state[SOUND_MIXER_IGAIN] = 0x101; + + return 0; +} + +static int __devinit hw_init(struct emu10k1_card *card) +{ + int nCh; + u32 pagecount; /* tmp */ + int ret; + + /* Disable audio and lock cache */ + emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE); + + /* Reset recording buffers */ + sblive_writeptr_tag(card, 0, + MICBS, ADCBS_BUFSIZE_NONE, + MICBA, 0, + FXBS, ADCBS_BUFSIZE_NONE, + FXBA, 0, + ADCBS, ADCBS_BUFSIZE_NONE, + ADCBA, 0, + TAGLIST_END); + + /* Disable channel interrupt */ + emu10k1_writefn0(card, INTE, 0); + sblive_writeptr_tag(card, 0, + CLIEL, 0, + CLIEH, 0, + SOLEL, 0, + SOLEH, 0, + TAGLIST_END); + + /* Init envelope engine */ + for (nCh = 0; nCh < NUM_G; nCh++) { + sblive_writeptr_tag(card, nCh, + DCYSUSV, 0, + IP, 0, + VTFT, 0xffff, + CVCF, 0xffff, + PTRX, 0, + CPF, 0, + CCR, 0, + + PSST, 0, + DSL, 0x10, + CCCA, 0, + Z1, 0, + Z2, 0, + FXRT, 0xd01c0000, + + ATKHLDM, 0, + DCYSUSM, 0, + IFATN, 0xffff, + PEFE, 0, + FMMOD, 0, + TREMFRQ, 24, /* 1 Hz */ + FM2FRQ2, 24, /* 1 Hz */ + TEMPENV, 0, + + /*** These are last so OFF prevents writing ***/ + LFOVAL2, 0, + LFOVAL1, 0, + ATKHLDV, 0, + ENVVOL, 0, + ENVVAL, 0, + TAGLIST_END); + } + + /* + ** 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) + */ + + sblive_writeptr_tag(card, 0, + + /* SPDIF0 */ + SPCS0, (SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), + + /* SPDIF1 */ + SPCS1, (SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), + + /* SPDIF2 & SPDIF3 */ + SPCS2, (SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT), + + TAGLIST_END); + + ret = fx_init(card); /* initialize effects engine */ + if (ret < 0) + return ret; + + card->tankmem.size = 0; + + card->virtualpagetable.size = MAXPAGES * sizeof(u32); + + card->virtualpagetable.addr = pci_alloc_consistent(card->pci_dev, card->virtualpagetable.size, &card->virtualpagetable.dma_handle); + if (card->virtualpagetable.addr == NULL) { + ERROR(); + ret = -ENOMEM; + goto err0; + } + + card->silentpage.size = EMUPAGESIZE; + + card->silentpage.addr = pci_alloc_consistent(card->pci_dev, card->silentpage.size, &card->silentpage.dma_handle); + if (card->silentpage.addr == NULL) { + ERROR(); + ret = -ENOMEM; + goto err1; + } + + for (pagecount = 0; pagecount < MAXPAGES; pagecount++) + ((u32 *) card->virtualpagetable.addr)[pagecount] = cpu_to_le32((card->silentpage.dma_handle * 2) | pagecount); + + /* Init page table & tank memory base register */ + sblive_writeptr_tag(card, 0, + PTB, card->virtualpagetable.dma_handle, + TCB, 0, + TCBS, 0, + TAGLIST_END); + + for (nCh = 0; nCh < NUM_G; nCh++) { + sblive_writeptr_tag(card, nCh, + MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + TAGLIST_END); + } + + /* Hokay, now enable the AUD bit */ + /* Enable Audio = 1 */ + /* Mute Disable Audio = 0 */ + /* Lock Tank Memory = 1 */ + /* Lock Sound Memory = 0 */ + /* Auto Mute = 1 */ + + if (card->model == 0x20 || card->model == 0xc400 || + (card->model == 0x21 && card->chiprev < 6)) + emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE); + else + emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE); + + /* Enable Vol_Ctrl irqs */ + emu10k1_irq_enable(card, INTE_VOLINCRENABLE | INTE_VOLDECRENABLE | INTE_MUTEENABLE | INTE_FXDSPENABLE); + + /* FIXME: TOSLink detection */ + card->has_toslink = 0; + + /* Initialize digital passthrough variables */ + card->pt.pos_gpr = card->pt.intr_gpr = card->pt.enable_gpr = -1; + card->pt.selected = 0; + card->pt.state = PT_STATE_INACTIVE; + card->pt.spcs_to_use = 0x01; + card->pt.patch_name = "AC3pass"; + card->pt.intr_gpr_name = "count"; + card->pt.enable_gpr_name = "enable"; + card->pt.pos_gpr_name = "ptr"; + spin_lock_init(&card->pt.lock); + init_waitqueue_head(&card->pt.wait); + +/* 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 0; + + err1: + pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle); + err0: + fx_cleanup(&card->mgr); + + return ret; +} + +static int __devinit emu10k1_init(struct emu10k1_card *card) +{ + /* Init Card */ + if (hw_init(card) < 0) + return -1; + + voice_init(card); + timer_init(card); + addxmgr_init(card); + + DPD(2, " hw control register -> %#x\n", emu10k1_readfn0(card, HCFG)); + + return 0; +} + +static void __devinit emu10k1_cleanup(struct emu10k1_card *card) +{ + int ch; + + emu10k1_writefn0(card, INTE, 0); + + /** Shutdown the chip **/ + for (ch = 0; ch < NUM_G; ch++) + sblive_writeptr(card, DCYSUSV, ch, 0); + + for (ch = 0; ch < NUM_G; ch++) { + sblive_writeptr_tag(card, ch, + VTFT, 0, + CVCF, 0, + PTRX, 0, + CPF, 0, + TAGLIST_END); + } + + /* Disable audio and lock cache */ + emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE); + + sblive_writeptr_tag(card, 0, + PTB, 0, + + /* Reset recording buffers */ + MICBS, ADCBS_BUFSIZE_NONE, + MICBA, 0, + FXBS, ADCBS_BUFSIZE_NONE, + FXBA, 0, + FXWC, 0, + ADCBS, ADCBS_BUFSIZE_NONE, + ADCBA, 0, + TCBS, 0, + TCB, 0, + DBG, 0x8000, + + /* Disable channel interrupt */ + CLIEL, 0, + CLIEH, 0, + SOLEL, 0, + SOLEH, 0, + TAGLIST_END); + + + pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle); + pci_free_consistent(card->pci_dev, card->silentpage.size, card->silentpage.addr, card->silentpage.dma_handle); + + if(card->tankmem.size != 0) + pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle); + + /* release patch storage memory */ + fx_cleanup(&card->mgr); +} + +/* Driver initialization routine */ +static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct emu10k1_card *card; + u32 subsysvid; + int ret; + + if (pci_set_dma_mask(pci_dev, EMU10K1_DMA_MASK)) { + printk(KERN_ERR "emu10k1: architecture does not support 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if (pci_enable_device(pci_dev)) + return -EIO; + + pci_set_master(pci_dev); + + 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)); + + card->iobase = pci_resource_start(pci_dev, 0); + card->length = pci_resource_len(pci_dev, 0); + + if (request_region(card->iobase, card->length, card_names[pci_id->driver_data]) == NULL) { + printk(KERN_ERR "emu10k1: IO space in use\n"); + ret = -EBUSY; + goto err_region; + } + + pci_set_drvdata(pci_dev, card); + + 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"); + ret = -EBUSY; + goto err_irq; + } + + pci_read_config_byte(pci_dev, PCI_REVISION_ID, &card->chiprev); + pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &card->model); + + printk(KERN_INFO "emu10k1: %s rev %d model 0x%x found, IO at 0x%04lx-0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->chiprev, card->model, card->iobase, + card->iobase + card->length - 1, card->irq); + + pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid); + card->isaps = (subsysvid == EMU_APS_SUBID); + + spin_lock_init(&card->lock); + init_MUTEX(&card->open_sem); + card->open_mode = 0; + init_waitqueue_head(&card->open_wait); + + ret = emu10k1_audio_init(card); + if(ret < 0) { + printk(KERN_ERR "emu10k1: cannot initialize audio devices\n"); + goto err_audio; + } + + ret = emu10k1_mixer_init(card); + if(ret < 0) { + printk(KERN_ERR "emu10k1: cannot initialize AC97 codec\n"); + goto err_mixer; + } + + ret = emu10k1_midi_init(card); + if (ret < 0) { + printk(KERN_ERR "emu10k1: cannot register midi device\n"); + goto err_midi; + } + + ret = emu10k1_init(card); + if (ret < 0) { + printk(KERN_ERR "emu10k1: cannot initialize device\n"); + goto err_emu10k1_init; + } + + if (card->isaps) + emu10k1_ecard_init(card); + + list_add(&card->list, &emu10k1_devs); + + return 0; + +err_emu10k1_init: + emu10k1_midi_cleanup(card); + +err_midi: + emu10k1_mixer_cleanup(card); + +err_mixer: + emu10k1_audio_cleanup(card); + +err_audio: + free_irq(card->irq, card); + +err_irq: + release_region(card->iobase, card->length); + pci_set_drvdata(pci_dev, NULL); + +err_region: + kfree(card); + + return ret; +} + +static void __devexit emu10k1_remove(struct pci_dev *pci_dev) +{ + struct emu10k1_card *card = pci_get_drvdata(pci_dev); + + list_del(&card->list); + + emu10k1_cleanup(card); + emu10k1_midi_cleanup(card); + emu10k1_mixer_cleanup(card); + emu10k1_audio_cleanup(card); + free_irq(card->irq, card); + release_region(card->iobase, card->length); + kfree(card); + pci_set_drvdata(pci_dev, NULL); +} + +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."); +MODULE_LICENSE("GPL"); + +static struct pci_driver emu10k1_pci_driver = { + name: "emu10k1", + id_table: emu10k1_pci_tbl, + probe: emu10k1_probe, + remove: __devexit_p(emu10k1_remove), +}; + +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; +} + +module_init(emu10k1_init_module); +module_exit(emu10k1_cleanup_module); + +#ifdef EMU10K1_SEQUENCER + +/* in midi.c */ +extern int emu10k1_seq_midi_open(int dev, int mode, + void (*input)(int dev, unsigned char midi_byte), + void (*output)(int dev)); +extern void emu10k1_seq_midi_close(int dev); +extern int emu10k1_seq_midi_out(int dev, unsigned char midi_byte); +extern int emu10k1_seq_midi_start_read(int dev); +extern int emu10k1_seq_midi_end_read(int dev); +extern void emu10k1_seq_midi_kick(int dev); +extern int emu10k1_seq_midi_buffer_status(int dev); + +static struct midi_operations emu10k1_midi_operations = +{ + THIS_MODULE, + {"EMU10K1 MIDI", 0, 0, SNDCARD_EMU10K1}, + &std_midi_synth, + {0}, + emu10k1_seq_midi_open, + emu10k1_seq_midi_close, + NULL, + emu10k1_seq_midi_out, + emu10k1_seq_midi_start_read, + emu10k1_seq_midi_end_read, + emu10k1_seq_midi_kick, + NULL, + emu10k1_seq_midi_buffer_status, + NULL +}; + +#endif diff -Nru linux/sound/oss/emu10k1/midi.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.c --- linux/sound/oss/emu10k1/midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,597 @@ +/* + ********************************************************************** + * 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 +#include +#include +#include +#include +#include + +#include "hwaccess.h" +#include "cardmo.h" +#include "cardmi.h" +#include "midi.h" + +#ifdef EMU10K1_SEQUENCER +#include "../sound_config.h" +#endif + +static spinlock_t midi_spinlock __attribute((unused)) = 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 -1; + } + + if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) < 0) { + ERROR(); + kfree(midihdr->data); + kfree(midihdr); + return -1; + } + + *midihdrptr = midihdr; + list_add_tail(&midihdr->list, &midi_dev->mid_hdrs); + + return 0; +} + +static int emu10k1_midi_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct emu10k1_card *card = NULL; + 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_dev == minor) + goto match; + } + + return -ENODEV; + +match: +#ifdef EMU10K1_SEQUENCER + if (card->seq_mididev) /* card is opened by sequencer */ + return -EBUSY; +#endif + + /* 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); + return -EBUSY; + } + + up(&card->open_sem); + interruptible_sleep_on(&card->open_wait); + + if (signal_pending(current)) { + return -ERESTARTSYS; + } + + down(&card->open_sem); + } + + if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL) + 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) < 0) { + ERROR(); + kfree(midi_dev); + return -ENODEV; + } + + /* Add two buffers to receive sysex buffer */ + if (midiin_add_buffer(midi_dev, &midihdr1) < 0) { + kfree(midi_dev); + return -ENODEV; + } + + if (midiin_add_buffer(midi_dev, &midihdr2) < 0) { + list_del(&midihdr1->list); + kfree(midihdr1->data); + kfree(midihdr1); + kfree(midi_dev); + return -ENODEV; + } + } + + if (file->f_mode & FMODE_WRITE) { + struct midi_openinfo dsCardMidiOpenInfo; + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) { + ERROR(); + kfree(midi_dev); + 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; + + lock_kernel(); + + 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); + + unlock_kernel(); + + 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) < 0) { + 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) < 0) { + 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 */ + spin_unlock_irqrestore(&midi_spinlock, flags); + return -1; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return 0; +} + +/* MIDI file operations */ +struct file_operations emu10k1_midi_fops = { + owner: THIS_MODULE, + read: emu10k1_midi_read, + write: emu10k1_midi_write, + poll: emu10k1_midi_poll, + open: emu10k1_midi_open, + release: emu10k1_midi_release, +}; + + +#ifdef EMU10K1_SEQUENCER + +/* functions used for sequencer access */ + +int emu10k1_seq_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev)) +{ + struct emu10k1_card *card; + struct midi_openinfo dsCardMidiOpenInfo; + struct emu10k1_mididevice *midi_dev; + + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return -EINVAL; + + card = midi_devs[dev]->devc; + + if (card->open_mode) /* card is opened native */ + return -EBUSY; + + DPF(2, "emu10k1_seq_midi_open()\n"); + + if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL) + 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); + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) { + ERROR(); + return -ENODEV; + } + + card->seq_mididev = midi_dev; + + return 0; +} + +void emu10k1_seq_midi_close(int dev) +{ + struct emu10k1_card *card; + + DPF(2, "emu10k1_seq_midi_close()\n"); + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return; + + card = midi_devs[dev]->devc; + emu10k1_mpuout_close(card); + + if (card->seq_mididev) { + kfree(card->seq_mididev); + card->seq_mididev = 0; + } +} + +int emu10k1_seq_midi_out(int dev, unsigned char midi_byte) +{ + struct emu10k1_card *card; + struct midi_hdr *midihdr; + unsigned long flags; + + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return -EINVAL; + + card = midi_devs[dev]->devc; + + if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) + return -EINVAL; + + midihdr->bufferlength = 1; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; + + if ((midihdr->data = (u8 *) kmalloc(1, GFP_KERNEL)) == NULL) { + ERROR(); + kfree(midihdr); + return -EINVAL; + } + + *(midihdr->data) = midi_byte; + + spin_lock_irqsave(&midi_spinlock, flags); + + if (emu10k1_mpuout_add_buffer(card, midihdr) < 0) { + ERROR(); + kfree(midihdr->data); + kfree(midihdr); + spin_unlock_irqrestore(&midi_spinlock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return 1; +} + +int emu10k1_seq_midi_start_read(int dev) +{ + return 0; +} + +int emu10k1_seq_midi_end_read(int dev) +{ + return 0; +} + +void emu10k1_seq_midi_kick(int dev) +{ +} + +int emu10k1_seq_midi_buffer_status(int dev) +{ + int count; + struct midi_queue *queue; + struct emu10k1_card *card; + + if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL) + return -EINVAL; + + count = 0; + + card = midi_devs[dev]->devc; + queue = card->mpuout->firstmidiq; + + while (queue != NULL) { + count++; + if (queue == card->mpuout->lastmidiq) + break; + + queue = queue->next; + } + + return count; +} + +#endif + diff -Nru linux/sound/oss/emu10k1/midi.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.h --- linux/sound/oss/emu10k1/midi.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/midi.h Mon Apr 8 22:31:22 2002 @@ -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 -Nru linux/sound/oss/emu10k1/mixer.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/mixer.c --- linux/sound/oss/emu10k1/mixer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/mixer.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,675 @@ +/* + ********************************************************************** + * mixer.c - /dev/mixer 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 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 +#include +#include +#include + +#include "hwaccess.h" +#include "8010.h" +#include "recmgr.h" + + +static const u32 bass_table[41][5] = { + { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 }, + { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d }, + { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee }, + { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c }, + { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b }, + { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 }, + { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f }, + { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 }, + { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 }, + { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 }, + { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 }, + { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be }, + { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b }, + { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c }, + { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b }, + { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 }, + { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a }, + { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 }, + { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 }, + { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e }, + { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee }, + { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 }, + { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 }, + { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 }, + { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 }, + { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e }, + { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 }, + { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 }, + { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 }, + { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 }, + { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 }, + { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca }, + { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 }, + { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 }, + { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 }, + { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 }, + { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a }, + { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f }, + { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 }, + { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 }, + { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 } +}; + +static const u32 treble_table[41][5] = { + { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 }, + { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 }, + { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 }, + { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca }, + { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 }, + { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 }, + { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 }, + { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 }, + { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 }, + { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df }, + { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff }, + { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 }, + { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c }, + { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 }, + { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 }, + { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 }, + { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 }, + { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d }, + { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 }, + { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 }, + { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f }, + { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb }, + { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 }, + { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 }, + { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd }, + { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 }, + { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad }, + { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 }, + { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 }, + { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c }, + { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 }, + { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 }, + { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 }, + { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 }, + { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 }, + { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 }, + { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d }, + { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b }, + { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 }, + { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd }, + { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } +}; + + +static 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, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_BASS][0] + i, 0, bass_table[l][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, GPR_BASE + card->mgr.ctrl_gpr[SOUND_MIXER_TREBLE][0] + i , 0, treble_table[l][i]); +} + +const char volume_params[SOUND_MIXER_NRDEVICES]= { +/* Used by the ac97 driver */ + [SOUND_MIXER_VOLUME] = VOL_6BIT, + [SOUND_MIXER_BASS] = VOL_4BIT, + [SOUND_MIXER_TREBLE] = VOL_4BIT, + [SOUND_MIXER_PCM] = VOL_5BIT, + [SOUND_MIXER_SPEAKER] = VOL_4BIT, + [SOUND_MIXER_LINE] = VOL_5BIT, + [SOUND_MIXER_MIC] = VOL_5BIT, + [SOUND_MIXER_CD] = VOL_5BIT, + [SOUND_MIXER_ALTPCM] = VOL_6BIT, + [SOUND_MIXER_IGAIN] = VOL_4BIT, + [SOUND_MIXER_LINE1] = VOL_5BIT, + [SOUND_MIXER_PHONEIN] = VOL_5BIT, + [SOUND_MIXER_PHONEOUT] = VOL_6BIT, + [SOUND_MIXER_VIDEO] = VOL_5BIT, +/* Not used by the ac97 driver */ + [SOUND_MIXER_SYNTH] = VOL_5BIT, + [SOUND_MIXER_IMIX] = VOL_5BIT, + [SOUND_MIXER_RECLEV] = VOL_5BIT, + [SOUND_MIXER_OGAIN] = VOL_5BIT, + [SOUND_MIXER_LINE2] = VOL_5BIT, + [SOUND_MIXER_LINE3] = VOL_5BIT, + [SOUND_MIXER_DIGITAL1] = VOL_5BIT, + [SOUND_MIXER_DIGITAL2] = VOL_5BIT, + [SOUND_MIXER_DIGITAL3] = VOL_5BIT, + [SOUND_MIXER_RADIO] = VOL_5BIT, + [SOUND_MIXER_MONITOR] = VOL_5BIT +}; + +/* Mixer file operations */ +static int emu10k1_private_mixer(struct emu10k1_card *card, unsigned int cmd, unsigned long arg) +{ + struct mixer_private_ioctl *ctl; + struct dsp_patch *patch; + u32 size, page; + int addr, size_reg, i, ret; + unsigned int id, ch; + + switch (cmd) { + + case SOUND_MIXER_PRIVATE3: + + ctl = (struct mixer_private_ioctl *) kmalloc(sizeof(struct mixer_private_ioctl), GFP_KERNEL); + if (ctl == NULL) + return -ENOMEM; + + if (copy_from_user(ctl, (void *) arg, sizeof(struct mixer_private_ioctl))) { + kfree(ctl); + return -EFAULT; + } + + ret = 0; + switch (ctl->cmd) { +#ifdef DBGEMU + case CMD_WRITEFN0: + emu10k1_writefn0(card, ctl->val[0], ctl->val[1]); + break; + + case CMD_WRITEPTR: + if (ctl->val[1] >= 0x40 || ctl->val[0] > 0xff) { + ret = -EINVAL; + break; + } + + if ((ctl->val[0] & 0x7ff) > 0x3f) + ctl->val[1] = 0x00; + + sblive_writeptr(card, ctl->val[0], ctl->val[1], ctl->val[2]); + + break; +#endif + case CMD_READFN0: + ctl->val[2] = emu10k1_readfn0(card, ctl->val[0]); + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_READPTR: + if (ctl->val[1] >= 0x40 || (ctl->val[0] & 0x7ff) > 0xff) { + ret = -EINVAL; + break; + } + + if ((ctl->val[0] & 0x7ff) > 0x3f) + ctl->val[1] = 0x00; + + ctl->val[2] = sblive_readptr(card, ctl->val[0], ctl->val[1]); + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETRECSRC: + switch (ctl->val[0]) { + case WAVERECORD_AC97: + if (card->isaps) { + ret = -EINVAL; + break; + } + + card->wavein.recsrc = WAVERECORD_AC97; + break; + + case WAVERECORD_MIC: + card->wavein.recsrc = WAVERECORD_MIC; + break; + + case WAVERECORD_FX: + card->wavein.recsrc = WAVERECORD_FX; + card->wavein.fxwc = ctl->val[1] & 0xffff; + + if (!card->wavein.fxwc) + ret = -EINVAL; + + break; + + default: + ret = -EINVAL; + break; + } + break; + + case CMD_GETRECSRC: + ctl->val[0] = card->wavein.recsrc; + ctl->val[1] = card->wavein.fxwc; + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_GETVOICEPARAM: + ctl->val[0] = card->waveout.send_routing[0]; + ctl->val[1] = card->waveout.send_a[0] | card->waveout.send_b[0] << 8 | + card->waveout.send_c[0] << 16 | card->waveout.send_d[0] << 24; + + ctl->val[2] = card->waveout.send_routing[1]; + ctl->val[3] = card->waveout.send_a[1] | card->waveout.send_b[1] << 8 | + card->waveout.send_c[1] << 16 | card->waveout.send_d[1] << 24; + + ctl->val[4] = card->waveout.send_routing[2]; + ctl->val[5] = card->waveout.send_a[2] | card->waveout.send_b[2] << 8 | + card->waveout.send_c[2] << 16 | card->waveout.send_d[2] << 24; + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETVOICEPARAM: + card->waveout.send_routing[0] = ctl->val[0] & 0xffff; + card->waveout.send_a[0] = ctl->val[1] & 0xff; + card->waveout.send_b[0] = (ctl->val[1] >> 8) & 0xff; + card->waveout.send_c[0] = (ctl->val[1] >> 16) & 0xff; + card->waveout.send_d[0] = (ctl->val[1] >> 24) & 0xff; + + card->waveout.send_routing[1] = ctl->val[2] & 0xffff; + card->waveout.send_a[1] = ctl->val[3] & 0xff; + card->waveout.send_b[1] = (ctl->val[3] >> 8) & 0xff; + card->waveout.send_c[1] = (ctl->val[3] >> 16) & 0xff; + card->waveout.send_d[1] = (ctl->val[3] >> 24) & 0xff; + + card->waveout.send_routing[2] = ctl->val[4] & 0xffff; + card->waveout.send_a[2] = ctl->val[5] & 0xff; + card->waveout.send_b[2] = (ctl->val[5] >> 8) & 0xff; + card->waveout.send_c[2] = (ctl->val[5] >> 16) & 0xff; + card->waveout.send_d[2] = (ctl->val[5] >> 24) & 0xff; + + break; + + case CMD_SETMCH_FX: + card->mchannel_fx = ctl->val[0] & 0x000f; + break; + + case CMD_GETPATCH: + if (ctl->val[0] == 0) { + if (copy_to_user((void *) arg, &card->mgr.rpatch, sizeof(struct dsp_rpatch))) + ret = -EFAULT; + } else { + if ((ctl->val[0] - 1) / PATCHES_PER_PAGE >= card->mgr.current_pages) { + ret = -EINVAL; + break; + } + + if (copy_to_user((void *) arg, PATCH(&card->mgr, ctl->val[0] - 1), sizeof(struct dsp_patch))) + ret = -EFAULT; + } + + break; + + case CMD_GETGPR: + id = ctl->val[0]; + + if (id > NUM_GPRS) { + ret = -EINVAL; + break; + } + + if (copy_to_user((void *) arg, &card->mgr.gpr[id], sizeof(struct dsp_gpr))) + ret = -EFAULT; + + break; + + case CMD_GETCTLGPR: + addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, &((char *) ctl->val)[PATCH_NAME_SIZE]); + ctl->val[0] = sblive_readptr(card, addr, 0); + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETPATCH: + if (ctl->val[0] == 0) + memcpy(&card->mgr.rpatch, &ctl->val[1], sizeof(struct dsp_rpatch)); + else { + page = (ctl->val[0] - 1) / PATCHES_PER_PAGE; + if (page > MAX_PATCHES_PAGES) { + ret = -EINVAL; + break; + } + + if (page >= card->mgr.current_pages) { + for (i = card->mgr.current_pages; i < page + 1; i++) { + card->mgr.patch[i] = (void *)__get_free_page(GFP_KERNEL); + if(card->mgr.patch[i] == NULL) { + card->mgr.current_pages = i; + ret = -ENOMEM; + break; + } + memset(card->mgr.patch[i], 0, PAGE_SIZE); + } + card->mgr.current_pages = page + 1; + } + + patch = PATCH(&card->mgr, ctl->val[0] - 1); + + memcpy(patch, &ctl->val[1], sizeof(struct dsp_patch)); + + if (patch->code_size == 0) { + for(i = page + 1; i < card->mgr.current_pages; i++) + free_page((unsigned long) card->mgr.patch[i]); + + card->mgr.current_pages = page + 1; + } + } + break; + + case CMD_SETGPR: + if (ctl->val[0] > NUM_GPRS) { + ret = -EINVAL; + break; + } + + memcpy(&card->mgr.gpr[ctl->val[0]], &ctl->val[1], sizeof(struct dsp_gpr)); + break; + + case CMD_SETCTLGPR: + addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, (char *) ctl->val + PATCH_NAME_SIZE); + emu10k1_set_control_gpr(card, addr, *((s32 *)((char *) ctl->val + 2 * PATCH_NAME_SIZE)), 0); + break; + + case CMD_SETGPOUT: + if (ctl->val[0] > 2 || ctl->val[1] > 1) { + ret= -EINVAL; + break; + } + + emu10k1_writefn0(card, (1 << 24) | (((ctl->val[0]) + 10) << 16) | HCFG, ctl->val[1]); + break; + + case CMD_GETGPR2OSS: + id = ctl->val[0]; + ch = ctl->val[1]; + + if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { + ret = -EINVAL; + break; + } + + ctl->val[2] = card->mgr.ctrl_gpr[id][ch]; + + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + + break; + + case CMD_SETGPR2OSS: + id = ctl->val[0]; + ch = ctl->val[1]; + addr = ctl->val[2]; + + if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) { + ret = -EINVAL; + break; + } + + card->mgr.ctrl_gpr[id][ch] = addr; + + if (card->isaps) + break; + + if (addr >= 0) { + unsigned int state = card->ac97.mixer_state[id]; + + if (ch) { + state >>= 8; + card->ac97.stereo_mixers |= (1 << id); + } else { + card->ac97.supported_mixers |= (1 << id); + } + + if (id == SOUND_MIXER_TREBLE) { + set_treble(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff); + } else if (id == SOUND_MIXER_BASS) { + set_bass(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff); + } else + emu10k1_set_volume_gpr(card, addr, state & 0xff, + volume_params[id]); + } else { + if (ch) { + card->ac97.stereo_mixers &= ~(1 << id); + card->ac97.stereo_mixers |= card->ac97_stereo_mixers; + } else { + card->ac97.supported_mixers &= ~(1 << id); + card->ac97.supported_mixers |= card->ac97_supported_mixers; + } + } + break; + + case CMD_SETPASSTHROUGH: + card->pt.selected = ctl->val[0] ? 1 : 0; + if (card->pt.state != PT_STATE_INACTIVE) + break; + + card->pt.spcs_to_use = ctl->val[0] & 0x07; + break; + + case CMD_PRIVATE3_VERSION: + ctl->val[0]=PRIVATE3_VERSION; + if (copy_to_user((void *) arg, ctl, sizeof(struct mixer_private_ioctl))) + ret = -EFAULT; + break; + + default: + ret = -EINVAL; + break; + } + + kfree(ctl); + return ret; + break; + + case SOUND_MIXER_PRIVATE4: + + if (copy_from_user(&size, (void *) arg, sizeof(size))) + return -EFAULT; + + DPD(2, "External tram size %#x\n", size); + + if (size > 0x1fffff) + return -EINVAL; + + size_reg = 0; + + if (size != 0) { + size = (size - 1) >> 14; + + while (size) { + size >>= 1; + size_reg++; + } + + size = 0x4000 << size_reg; + } + + DPD(2, "External tram size %#x %#x\n", size, size_reg); + + if (size != card->tankmem.size) { + if (card->tankmem.size > 0) { + emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 1); + + sblive_writeptr_tag(card, 0, TCB, 0, TCBS, 0, TAGLIST_END); + + pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle); + + card->tankmem.size = 0; + } + + if (size != 0) { + card->tankmem.addr = pci_alloc_consistent(card->pci_dev, size, &card->tankmem.dma_handle); + if (card->tankmem.addr == NULL) + return -ENOMEM; + + card->tankmem.size = size; + + sblive_writeptr_tag(card, 0, TCB, card->tankmem.dma_handle, TCBS, size_reg, TAGLIST_END); + + emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 0); + } + } + return 0; + break; + + default: + break; + } + + return -EINVAL; +} + +static int emu10k1_dsp_mixer(struct emu10k1_card *card, unsigned int oss_mixer, unsigned long arg) +{ + unsigned int left, right; + int val; + int scale; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff); + left = (val & 0xff); + + if (right > 100) right = 100; + if (left > 100) left = 100; + + card->ac97.mixer_state[oss_mixer] = (right << 8) | left; + if (oss_mixer == SOUND_MIXER_TREBLE) { + set_treble(card, left, right); + return 0; + } if (oss_mixer == SOUND_MIXER_BASS) { + set_bass(card, left, right); + return 0; + } + + if (oss_mixer == SOUND_MIXER_VOLUME) + scale = 1 << card->ac97.bit_resolution; + else + scale = volume_params[oss_mixer]; + + emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, scale); + emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, scale); + + if (card->ac97_supported_mixers & (1 << oss_mixer)) + card->ac97.write_mixer(&card->ac97, oss_mixer, left, right); + + return 0; +} + +static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + struct emu10k1_card *card = file->private_data; + unsigned int oss_mixer = _IOC_NR(cmd); + + ret = -EINVAL; + if (!card->isaps) { + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + + strncpy(info.id, card->ac97.name, sizeof(info.id)); + strncpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name)); + info.modify_counter = card->ac97.modcnt; + + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + + if ((_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES) + ret = emu10k1_dsp_mixer(card, oss_mixer, arg); + else + ret = card->ac97.mixer_ioctl(&card->ac97, cmd, arg); + } + + if (ret < 0) + ret = emu10k1_private_mixer(card, cmd, arg); + + return ret; +} + +static int emu10k1_mixer_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct emu10k1_card *card = NULL; + 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->ac97.dev_mixer == minor) + goto match; + } + + return -ENODEV; + + match: + file->private_data = card; + return 0; +} + +static int emu10k1_mixer_release(struct inode *inode, struct file *file) +{ + DPF(4, "emu10k1_mixer_release()\n"); + return 0; +} + +struct file_operations emu10k1_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: emu10k1_mixer_ioctl, + open: emu10k1_mixer_open, + release: emu10k1_mixer_release, +}; diff -Nru linux/sound/oss/emu10k1/passthrough.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.c --- linux/sound/oss/emu10k1/passthrough.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,239 @@ +/* + ********************************************************************** + * passthrough.c -- Emu10k1 digital passthrough + * Copyright (C) 2001 Juha Yrjölä + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * May 15, 2001 Juha Yrjölä 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwaccess.h" +#include "cardwo.h" +#include "cardwi.h" +#include "recmgr.h" +#include "irqmgr.h" +#include "audio.h" +#include "8010.h" + +static void pt_putsamples(struct pt_data *pt, u16 *ptr, u16 left, u16 right) +{ + unsigned int idx; + + ptr[pt->copyptr] = left; + idx = pt->copyptr + PT_SAMPLES/2; + idx %= PT_SAMPLES; + ptr[idx] = right; +} + +static inline int pt_can_write(struct pt_data *pt) +{ + return pt->blocks_copied < pt->blocks_played + 8; +} + +static int pt_wait_for_write(struct emu10k1_wavedevice *wavedev, int nonblock) +{ + struct emu10k1_card *card = wavedev->card; + struct pt_data *pt = &card->pt; + + if (nonblock && !pt_can_write(pt)) + return -EAGAIN; + while (!pt_can_write(pt) && pt->state != PT_STATE_INACTIVE) { + interruptible_sleep_on(&pt->wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (pt->state == PT_STATE_INACTIVE) + return -EAGAIN; + + return 0; +} + +static int pt_putblock(struct emu10k1_wavedevice *wave_dev, u16 *block, int nonblock) +{ + struct woinst *woinst = wave_dev->woinst; + struct emu10k1_card *card = wave_dev->card; + struct pt_data *pt = &card->pt; + u16 *ptr = (u16 *) card->tankmem.addr; + int i = 0, r; + unsigned long flags; + + r = pt_wait_for_write(wave_dev, nonblock); + if (r < 0) + return r; + spin_lock_irqsave(&card->pt.lock, flags); + while (i < PT_BLOCKSAMPLES) { + pt_putsamples(pt, ptr, block[2*i], block[2*i+1]); + if (pt->copyptr == 0) + pt->copyptr = PT_SAMPLES; + pt->copyptr--; + i++; + } + woinst->total_copied += PT_BLOCKSIZE; + pt->blocks_copied++; + if (pt->blocks_copied >= 4 && pt->state != PT_STATE_PLAYING) { + DPF(2, "activating digital pass-through playback\n"); + sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 1); + pt->state = PT_STATE_PLAYING; + } + spin_unlock_irqrestore(&card->pt.lock, flags); + return 0; +} + +static int pt_setup(struct emu10k1_wavedevice *wave_dev) +{ + u32 bits; + struct emu10k1_card *card = wave_dev->card; + struct pt_data *pt = &card->pt; + int i; + + for (i = 0; i < 3; i++) { + pt->old_spcs[i] = sblive_readptr(card, SPCS0 + i, 0); + if (pt->spcs_to_use & (1 << i)) { + DPD(2, "using S/PDIF port %d\n", i); + bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | + 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; + if (pt->ac3data) + bits |= SPCS_NOTAUDIODATA; + sblive_writeptr(card, SPCS0 + i, 0, bits); + } + } + return 0; +} + +ssize_t emu10k1_pt_write(struct file *file, const char *buffer, size_t count) +{ + struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; + struct emu10k1_card *card = wave_dev->card; + struct pt_data *pt = &card->pt; + int nonblock, i, r, blocks, blocks_copied, bytes_copied = 0; + + DPD(3, "emu10k1_pt_write(): %d bytes\n", count); + + nonblock = file->f_flags & O_NONBLOCK; + + if (card->tankmem.size < PT_SAMPLES*2) + return -EFAULT; + if (pt->state == PT_STATE_INACTIVE) { + DPF(2, "bufptr init\n"); + pt->playptr = PT_SAMPLES-1; + pt->copyptr = PT_INITPTR; + pt->blocks_played = pt->blocks_copied = 0; + memset(card->tankmem.addr, 0, card->tankmem.size); + pt->state = PT_STATE_ACTIVATED; + pt->buf = kmalloc(PT_BLOCKSIZE, GFP_KERNEL); + pt->prepend_size = 0; + if (pt->buf == NULL) + return -ENOMEM; + pt_setup(wave_dev); + } + if (pt->prepend_size) { + int needed = PT_BLOCKSIZE - pt->prepend_size; + + DPD(3, "prepend size %d, prepending %d bytes\n", pt->prepend_size, needed); + if (count < needed) { + copy_from_user(pt->buf + pt->prepend_size, buffer, count); + pt->prepend_size += count; + DPD(3, "prepend size now %d\n", pt->prepend_size); + return count; + } + copy_from_user(pt->buf + pt->prepend_size, buffer, needed); + r = pt_putblock(wave_dev, (u16 *) pt->buf, nonblock); + if (r) + return r; + bytes_copied += needed; + pt->prepend_size = 0; + } + blocks = (count-bytes_copied)/PT_BLOCKSIZE; + blocks_copied = 0; + while (blocks > 0) { + u16 *bufptr = (u16 *) buffer + (bytes_copied/2); + copy_from_user(pt->buf, bufptr, PT_BLOCKSIZE); + bufptr = (u16 *) pt->buf; + r = pt_putblock(wave_dev, bufptr, nonblock); + if (r) { + if (bytes_copied) + return bytes_copied; + else + return r; + } + bytes_copied += PT_BLOCKSIZE; + blocks--; + blocks_copied++; + } + i = count - bytes_copied; + if (i) { + pt->prepend_size = i; + copy_from_user(pt->buf, buffer + bytes_copied, i); + bytes_copied += i; + DPD(3, "filling prepend buffer with %d bytes", i); + } + return bytes_copied; +} + +void emu10k1_pt_stop(struct emu10k1_card *card) +{ + struct pt_data *pt = &card->pt; + int i; + + if (pt->state != PT_STATE_INACTIVE) { + DPF(2, "digital pass-through stopped\n"); + sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 0); + for (i = 0; i < 3; i++) { + if (pt->spcs_to_use & (1 << i)) + sblive_writeptr(card, SPCS0 + i, 0, pt->old_spcs[i]); + } + pt->state = PT_STATE_INACTIVE; + kfree(pt->buf); + } +} + +void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev) +{ + struct woinst *woinst = wave_dev->woinst; + struct pt_data *pt = &wave_dev->card->pt; + u32 pos; + + if (pt->state == PT_STATE_PLAYING && pt->pos_gpr >= 0) { + pos = sblive_readptr(wave_dev->card, GPR_BASE + pt->pos_gpr, 0); + if (pos > PT_BLOCKSAMPLES) + pos = PT_BLOCKSAMPLES; + pos = 4 * (PT_BLOCKSAMPLES - pos); + } else + pos = 0; + woinst->total_played = pt->blocks_played * woinst->buffer.fragment_size + pos; + woinst->buffer.hw_pos = pos; +} diff -Nru linux/sound/oss/emu10k1/passthrough.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.h --- linux/sound/oss/emu10k1/passthrough.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/passthrough.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,70 @@ +/* + ********************************************************************** + * passthrough.h -- Emu10k1 digital passthrough header file + * Copyright (C) 2001 Juha Yrjölä + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * May 15, 2001 Juha Yrjölä 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 _PASSTHROUGH_H +#define _PASSTHROUGH_H + +#include "audio.h" + +/* number of 16-bit stereo samples in XTRAM buffer */ +#define PT_SAMPLES 0x8000 +#define PT_BLOCKSAMPLES 0x400 +#define PT_BLOCKSIZE (PT_BLOCKSAMPLES*4) +#define PT_BLOCKSIZE_LOG2 12 +#define PT_BLOCKCOUNT (PT_SAMPLES/PT_BLOCKSAMPLES) +#define PT_INITPTR (PT_SAMPLES/2-1) + +#define PT_STATE_INACTIVE 0 +#define PT_STATE_ACTIVATED 1 +#define PT_STATE_PLAYING 2 + +/* passthrough struct */ +struct pt_data +{ + u8 selected, state, spcs_to_use; + int intr_gpr, enable_gpr, pos_gpr; + u32 blocks_played, blocks_copied, old_spcs[3]; + u32 playptr, copyptr; + u32 prepend_size; + u8 *buf; + u8 ac3data; + + char *patch_name, *intr_gpr_name, *enable_gpr_name, *pos_gpr_name; + + wait_queue_head_t wait; + spinlock_t lock; +}; + +ssize_t emu10k1_pt_write(struct file *file, const char *buf, size_t count); +void emu10k1_pt_stop(struct emu10k1_card *card); +void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev); + +#endif /* _PASSTHROUGH_H */ diff -Nru linux/sound/oss/emu10k1/recmgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.c --- linux/sound/oss/emu10k1/recmgr.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,138 @@ +/* + ********************************************************************** + * 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 "8010.h" +#include "recmgr.h" + +void emu10k1_start_record(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + DPF(2, "emu10k1_start_record()\n"); + + sblive_writeptr(card, buffer->sizereg, 0, buffer->sizeregval); + + if (buffer->adcctl) + sblive_writeptr(card, ADCCR, 0, buffer->adcctl); + + return; +} + +void emu10k1_stop_record(struct emu10k1_card *card, struct wavein_buffer *buffer) +{ + DPF(2, "emu10k1_stop_record()\n"); + + /* Disable record transfer */ + if (buffer->adcctl) + sblive_writeptr(card, ADCCR, 0, 0); + + sblive_writeptr(card, buffer->sizereg, 0, ADCBS_BUFSIZE_NONE); + + return; +} + +void emu10k1_set_record_src(struct emu10k1_card *card, struct wiinst *wiinst) +{ + struct wavein_buffer *buffer = &wiinst->buffer; + + DPF(2, "emu10k1_set_record_src()\n"); + + switch (wiinst->recsrc) { + + case WAVERECORD_AC97: + DPF(2, "recording source: AC97\n"); + buffer->sizereg = ADCBS; + buffer->addrreg = ADCBA; + buffer->idxreg = ADCIDX_IDX; + + switch (wiinst->format.samplingrate) { + case 0xBB80: + buffer->adcctl = ADCCR_SAMPLERATE_48; + break; + case 0xAC44: + buffer->adcctl = ADCCR_SAMPLERATE_44; + break; + case 0x7D00: + buffer->adcctl = ADCCR_SAMPLERATE_32; + break; + case 0x5DC0: + buffer->adcctl = ADCCR_SAMPLERATE_24; + break; + case 0x5622: + buffer->adcctl = ADCCR_SAMPLERATE_22; + break; + case 0x3E80: + buffer->adcctl = ADCCR_SAMPLERATE_16; + break; + case 0x2B11: + buffer->adcctl = ADCCR_SAMPLERATE_11; + break; + case 0x1F40: + buffer->adcctl = ADCCR_SAMPLERATE_8; + break; + default: + BUG(); + break; + } + + buffer->adcctl |= ADCCR_LCHANENABLE; + + if (wiinst->format.channels == 2) + buffer->adcctl |= ADCCR_RCHANENABLE; + + break; + + case WAVERECORD_MIC: + DPF(2, "recording source: MIC\n"); + buffer->sizereg = MICBS; + buffer->addrreg = MICBA; + buffer->idxreg = MICIDX_IDX; + buffer->adcctl = 0; + break; + + case WAVERECORD_FX: + DPF(2, "recording source: FX\n"); + buffer->sizereg = FXBS; + buffer->addrreg = FXBA; + buffer->idxreg = FXIDX_IDX; + buffer->adcctl = 0; + + sblive_writeptr(card, FXWC, 0, wiinst->fxwc); + break; + default: + BUG(); + break; + } + + DPD(2, "bus addx: %#x\n", buffer->dma_handle); + + sblive_writeptr(card, buffer->addrreg, 0, buffer->dma_handle); + + return; +} diff -Nru linux/sound/oss/emu10k1/recmgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.h --- linux/sound/oss/emu10k1/recmgr.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/recmgr.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,48 @@ +/* + ********************************************************************** + * 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 + +#include "hwaccess.h" +#include "cardwi.h" + +/* Recording resources */ +#define WAVERECORD_AC97 0x01 +#define WAVERECORD_MIC 0x02 +#define WAVERECORD_FX 0x03 + +void emu10k1_start_record(struct emu10k1_card *, struct wavein_buffer *); +void emu10k1_stop_record(struct emu10k1_card *, struct wavein_buffer *); +void emu10k1_set_record_src(struct emu10k1_card *, struct wiinst *wiinst); + + +#endif /* _RECORDMGR_H */ diff -Nru linux/sound/oss/emu10k1/timer.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.c --- linux/sound/oss/emu10k1/timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.c Mon Apr 8 22:31:22 2002 @@ -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" +#include "8010.h" +#include "irqmgr.h" +#include "timer.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->state & TIMER_STATE_ACTIVE) { + t->count++; + if (t->count == t->count_max) { + t->count = 0; + tasklet_hi_schedule(&t->tasklet); + } + } + } + + spin_unlock(&card->timer_lock); + + return; +} + +void emu10k1_timer_install(struct emu10k1_card *card, struct emu_timer *timer, u32 delay) +{ + struct emu_timer *t; + struct list_head *entry; + unsigned long flags; + + if (delay < 5) + delay = 5; + + timer->delay = delay; + timer->state = TIMER_STATE_INSTALLED; + + 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); + + emu10k1_writefn0(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; +} + +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; + + if (timer->state == TIMER_STATE_UNINSTALLED) + return; + + 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); + + emu10k1_writefn0(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); + + timer->state = TIMER_STATE_UNINSTALLED; + + return; +} + +void emu10k1_timer_enable(struct emu10k1_card *card, struct emu_timer *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&card->timer_lock, flags); + timer->state |= TIMER_STATE_ACTIVE; + 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->state &= ~TIMER_STATE_ACTIVE; + spin_unlock_irqrestore(&card->timer_lock, flags); + + return; +} diff -Nru linux/sound/oss/emu10k1/timer.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.h --- linux/sound/oss/emu10k1/timer.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/timer.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,54 @@ +/* + ********************************************************************** + * 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 + +#include +#include +#include "hwaccess.h" + +struct emu_timer +{ + struct list_head list; + struct tasklet_struct tasklet; + u8 state; + u32 count; /* current number of interrupts */ + u32 count_max; /* number of interrupts needed to schedule the bh */ + u32 delay; /* timer delay */ +}; + +void emu10k1_timer_install(struct emu10k1_card *, struct emu_timer *, 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 +#define TIMER_STATE_INSTALLED 0x01 +#define TIMER_STATE_ACTIVE 0x02 +#define TIMER_STATE_UNINSTALLED 0x04 + +#endif /* _TIMER_H */ diff -Nru linux/sound/oss/emu10k1/voicemgr.c linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.c --- linux/sound/oss/emu10k1/voicemgr.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,281 @@ +/* + ********************************************************************** + * 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 "voicemgr.h" +#include "8010.h" + +int emu10k1_voice_alloc(struct emu10k1_card *card, struct emu_voice *voice) +{ + u8 *voicetable = card->voicetable; + int i; + unsigned long flags; + + DPF(2, "emu10k1_voice_alloc()\n"); + + spin_lock_irqsave(&card->lock, flags); + + if (voice->flags & VOICE_FLAGS_STEREO) { + for (i = 0; i < NUM_G; i += 2) + if ((voicetable[i] == VOICE_USAGE_FREE) && (voicetable[i + 1] == VOICE_USAGE_FREE)) { + voicetable[i] = voice->usage; + voicetable[i + 1] = voice->usage; + break; + } + } else { + for (i = 0; i < NUM_G; i++) + if (voicetable[i] == VOICE_USAGE_FREE) { + voicetable[i] = voice->usage; + break; + } + } + + spin_unlock_irqrestore(&card->lock, flags); + + if (i >= NUM_G) + return -1; + + voice->card = card; + voice->num = i; + + for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { + DPD(2, " voice allocated -> %d\n", voice->num + i); + + sblive_writeptr_tag(card, voice->num + i, IFATN, 0xffff, + DCYSUSV, 0, + VTFT, 0x0000ffff, + PTRX, 0, + TAGLIST_END); + } + + return 0; +} + +void emu10k1_voice_free(struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + int i; + unsigned long flags; + + DPF(2, "emu10k1_voice_free()\n"); + + if (voice->usage == VOICE_USAGE_FREE) + return; + + for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { + DPD(2, " voice released -> %d\n", voice->num + i); + + sblive_writeptr_tag(card, voice->num + i, DCYSUSV, 0, + VTFT, 0x0000ffff, + PTRX_PITCHTARGET, 0, + CVCF, 0x0000ffff, + CPF, 0, + TAGLIST_END); + } + + voice->usage = VOICE_USAGE_FREE; + + spin_lock_irqsave(&card->lock, flags); + + card->voicetable[voice->num] = VOICE_USAGE_FREE; + + if (voice->flags & VOICE_FLAGS_STEREO) + card->voicetable[voice->num + 1] = VOICE_USAGE_FREE; + + spin_unlock_irqrestore(&card->lock, flags); +} + +void emu10k1_voice_playback_setup(struct emu_voice *voice) +{ + struct emu10k1_card *card = voice->card; + u32 start; + int i; + + DPF(2, "emu10k1_voice_playback_setup()\n"); + + if (voice->flags & VOICE_FLAGS_STEREO) { + /* Set stereo bit */ + start = 28; + sblive_writeptr(card, CPF, voice->num, CPF_STEREO_MASK); + sblive_writeptr(card, CPF, voice->num + 1, CPF_STEREO_MASK); + } else { + start = 30; + sblive_writeptr(card, CPF, voice->num, 0); + } + + if(!(voice->flags & VOICE_FLAGS_16BIT)) + start *= 2; + + voice->start += start; + + for (i = 0; i < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); i++) { + sblive_writeptr(card, FXRT, voice->num + i, voice->params[i].send_routing << 16); + + /* Stop CA */ + /* Assumption that PT is already 0 so no harm overwriting */ + sblive_writeptr(card, PTRX, voice->num + i, (voice->params[i].send_a << 8) | voice->params[i].send_b); + + sblive_writeptr_tag(card, voice->num + i, + /* CSL, ST, CA */ + DSL, voice->endloop | (voice->params[i].send_d << 24), + PSST, voice->startloop | (voice->params[i].send_c << 24), + CCCA, (voice->start) | CCCA_INTERPROM_0 | ((voice->flags & VOICE_FLAGS_16BIT) ? 0 : CCCA_8BITSELECT), + /* Clear filter delay memory */ + Z1, 0, + Z2, 0, + /* Invalidate maps */ + MAPA, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + MAPB, MAP_PTI_MASK | (card->silentpage.dma_handle * 2), + /* modulation envelope */ + CVCF, 0x0000ffff, + VTFT, 0x0000ffff, + ATKHLDM, 0, + DCYSUSM, 0x007f, + LFOVAL1, 0x8000, + LFOVAL2, 0x8000, + FMMOD, 0, + TREMFRQ, 0, + FM2FRQ2, 0, + ENVVAL, 0x8000, + /* volume envelope */ + ATKHLDV, 0x7f7f, + ENVVOL, 0x8000, + /* filter envelope */ + PEFE_FILTERAMOUNT, 0x7f, + /* pitch envelope */ + PEFE_PITCHAMOUNT, 0, TAGLIST_END); + + voice->params[i].fc_target = 0xffff; + } +} + +void emu10k1_voices_start(struct emu_voice *first_voice, unsigned int num_voices, int set) +{ + struct emu10k1_card *card = first_voice->card; + struct emu_voice *voice; + unsigned int voicenum; + int j; + + DPF(2, "emu10k1_voices_start()\n"); + + for (voicenum = 0; voicenum < num_voices; voicenum++) + { + voice = first_voice + voicenum; + + if (!set) { + u32 cra, ccis, cs, sample; + if (voice->flags & VOICE_FLAGS_STEREO) { + cra = 64; + ccis = 28; + cs = 4; + } else { + cra = 64; + ccis = 30; + cs = 2; + } + + if(voice->flags & VOICE_FLAGS_16BIT) { + sample = 0x00000000; + } else { + sample = 0x80808080; + ccis *= 2; + } + + for(j = 0; j < cs; j++) + sblive_writeptr(card, CD0 + j, voice->num, sample); + + /* Reset cache */ + sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, 0); + if (voice->flags & VOICE_FLAGS_STEREO) + sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num + 1, 0); + + sblive_writeptr(card, CCR_READADDRESS, voice->num, cra); + + if (voice->flags & VOICE_FLAGS_STEREO) + sblive_writeptr(card, CCR_READADDRESS, voice->num + 1, cra); + + /* Fill cache */ + sblive_writeptr(card, CCR_CACHEINVALIDSIZE, voice->num, ccis); + } + + for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { + sblive_writeptr_tag(card, voice->num + j, + IFATN, (voice->params[j].initial_fc << 8) | voice->params[j].initial_attn, + VTFT, (voice->params[j].volume_target << 16) | voice->params[j].fc_target, + CVCF, (voice->params[j].volume_target << 16) | voice->params[j].fc_target, + DCYSUSV, (voice->params[j].byampl_env_sustain << 8) | voice->params[j].byampl_env_decay, + TAGLIST_END); + + emu10k1_clear_stop_on_loop(card, voice->num + j); + } + } + + + for (voicenum = 0; voicenum < num_voices; voicenum++) + { + voice = first_voice + voicenum; + + for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { + sblive_writeptr(card, PTRX_PITCHTARGET, voice->num + j, voice->pitch_target); + + if (j == 0) + sblive_writeptr(card, CPF_CURRENTPITCH, voice->num, voice->pitch_target); + + sblive_writeptr(card, IP, voice->num + j, voice->initial_pitch); + } + } +} + +void emu10k1_voices_stop(struct emu_voice *first_voice, int num_voices) +{ + struct emu10k1_card *card = first_voice->card; + struct emu_voice *voice; + unsigned int voice_num; + int j; + + DPF(2, "emu10k1_voice_stop()\n"); + + for (voice_num = 0; voice_num < num_voices; voice_num++) + { + voice = first_voice + voice_num; + + for (j = 0; j < (voice->flags & VOICE_FLAGS_STEREO ? 2 : 1); j++) { + sblive_writeptr_tag(card, voice->num + j, + PTRX_PITCHTARGET, 0, + CPF_CURRENTPITCH, 0, + IFATN, 0xffff, + VTFT, 0x0000ffff, + CVCF, 0x0000ffff, + IP, 0, + TAGLIST_END); + } + } +} + diff -Nru linux/sound/oss/emu10k1/voicemgr.h linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.h --- linux/sound/oss/emu10k1/voicemgr.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/emu10k1/voicemgr.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,91 @@ +/* + ********************************************************************** + * 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 + +#include "hwaccess.h" + +/* struct emu_voice.usage flags */ +#define VOICE_USAGE_FREE 0x01 +#define VOICE_USAGE_MIDI 0x02 +#define VOICE_USAGE_PLAYBACK 0x04 + +/* struct emu_voice.flags flags */ +#define VOICE_FLAGS_STEREO 0x02 +#define VOICE_FLAGS_16BIT 0x04 + +struct voice_param +{ + /* FX bus amount send */ + + u32 send_routing; + + u32 send_a; + u32 send_b; + u32 send_c; + u32 send_d; + + u32 initial_fc; + u32 fc_target; + + u32 initial_attn; + u32 volume_target; + + u32 byampl_env_sustain; + u32 byampl_env_decay; +}; + + +struct emu_voice +{ + struct emu10k1_card *card; + u8 usage; /* Free, MIDI, playback */ + u8 num; /* Voice ID */ + u8 flags; /* Stereo/mono, 8/16 bit */ + + u32 startloop; + u32 endloop; + u32 start; + + u32 initial_pitch; + u32 pitch_target; + + struct voice_param params[2]; +}; + +int emu10k1_voice_alloc(struct emu10k1_card *, struct emu_voice *); +void emu10k1_voice_free(struct emu_voice *); +void emu10k1_voice_playback_setup(struct emu_voice *); +void emu10k1_voices_start(struct emu_voice *, unsigned int, int); +void emu10k1_voices_stop(struct emu_voice *, int); + +#endif /* _VOICEMGR_H */ diff -Nru linux/sound/oss/es1370.c linux-2.4.19-pre5-mjc/sound/oss/es1370.c --- linux/sound/oss/es1370.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/es1370.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2784 @@ +/*****************************************************************************/ + +/* + * es1370.c -- Ensoniq ES1370/Asahi Kasei AK4531 audio driver. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * joystick if 1 enables the joystick interface on the card; but it still + * needs a driver for joysticks connected to a standard IBM-PC + * joyport. It is tested with the joy-analog driver. This + * module must be loaded before the joystick driver. Kmod will + * not ensure that. + * lineout if 1 the LINE jack is used as an output instead of an input. + * LINE then contains the unmixed dsp output. This can be used + * to make the card a four channel one: use dsp to output two + * channels to LINE and dac to output the other two channels to + * SPKR. Set the mixer to only output synth to SPKR. + * micbias sets the +5V bias to the mic if using an electretmic. + * + * + * Note: sync mode is not yet supported (i.e. running dsp and dac from the same + * clock source) + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but output only, + * only 5512, 11025, 22050 and 44100 samples/s, + * outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 26.03.1998 0.1 Initial release + * 31.03.1998 0.2 Fix bug in GETOSPACE + * 04.04.1998 0.3 Make it work (again) under 2.0.33 + * Fix mixer write operation not returning the actual + * settings + * 05.04.1998 0.4 First attempt at using the new PCI stuff + * 29.04.1998 0.5 Fix hang when ^C is pressed on amp + * 07.05.1998 0.6 Don't double lock around stop_*() in *_release() + * 10.05.1998 0.7 First stab at a simple midi interface (no bells&whistles) + * 14.05.1998 0.8 Don't allow excessive interrupt rates + * 08.06.1998 0.9 First release using Alan Cox' soundcore instead of + * miscdevice + * 05.07.1998 0.10 Fixed the driver to correctly maintin OSS style volume + * settings (not sure if this should be standard) + * Fixed many references: f_flags should be f_mode + * -- Gerald Britton + * 03.08.1998 0.11 Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se + * On module startup, set DAC2 to 11kSPS instead of 5.5kSPS, + * as it produces an annoying ssssh in the lower sampling rate + * Do not include modversions.h + * 22.08.1998 0.12 Mixer registers actually have 5 instead of 4 bits + * pointed out by Itai Nahshon + * 31.08.1998 0.13 Fix realplayer problems - dac.count issues + * 08.10.1998 0.14 Joystick support fixed + * -- Oliver Neukum + * 10.12.1998 0.15 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.16 Don't wake up app until there are fragsize bytes to read/write + * 06.01.1999 0.17 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.18 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.20 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: joystick address handling might still be wrong on archs + * other than i386 + * 10.05.1999 0.21 Added support for an electret mic for SB PCI64 + * to the Linux kernel sound driver. This mod also straighten + * out the question marks around the mic impedance setting + * (micz). From Kim.Berts@fisub.mail.abb.com + * 11.05.1999 0.22 Implemented the IMIX call to mute recording monitor. + * Guenter Geiger + * 15.06.1999 0.23 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.24 Add pci_set_master + * 02.08.1999 0.25 Added workaround for the "phantom write" bug first + * documented by Dave Sharpless from Anchor Games + * 03.08.1999 0.26 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1370=joystick[,lineout[,micbias]]" + * removed CONFIG_SOUND_ALSA_ES1370_JOYPORT_BOOT kludge + * 12.08.1999 0.27 module_init/__setup fixes + * 19.08.1999 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca + * 31.08.1999 0.29 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.30 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.31 More waitqueue races fixed + * 08.01.2000 0.32 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * 07.02.2000 0.33 Use pci_alloc_consistent and pci_register_driver + * 21.11.2000 0.34 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.35 More dma buffer initializations, patch from + * Tjeerd Mulder + * 07.01.2001 0.36 Timeout change in wrcodec as requested by Frank Klemm + * 31.01.2001 0.37 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * + * some important things missing in Ensoniq documentation: + * + * Experimental PCLKDIV results: play the same waveforms on both DAC1 and DAC2 + * and vary PCLKDIV to obtain zero beat. + * 5512sps: 254 + * 44100sps: 30 + * seems to be fs = 1411200/(PCLKDIV+2) + * + * should find out when curr_sample_ct is cleared and + * where exactly the CCB fetches data + * + * The card uses a 22.5792 MHz crystal. + * The LINEIN jack may be converted to an AOUT jack by + * setting pin 47 (XCTL0) of the ES1370 to high. + * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic + * + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define DBG(x) {} +/*#define DBG(x) {x}*/ + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif + +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#endif + +#define ES1370_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370) + +#define ES1370_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_STATUS 0x04 +#define ES1370_REG_UART_DATA 0x08 +#define ES1370_REG_UART_STATUS 0x09 +#define ES1370_REG_UART_CONTROL 0x09 +#define ES1370_REG_UART_TEST 0x0a +#define ES1370_REG_MEMPAGE 0x0c +#define ES1370_REG_CODEC 0x10 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1370_REG_DAC1_SCOUNT 0x24 +#define ES1370_REG_DAC2_SCOUNT 0x28 +#define ES1370_REG_ADC_SCOUNT 0x2c + +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 +#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c + +#define ES1370_FMT_U8_MONO 0 +#define ES1370_FMT_U8_STEREO 1 +#define ES1370_FMT_S16_MONO 2 +#define ES1370_FMT_S16_STEREO 3 +#define ES1370_FMT_STEREO 1 +#define ES1370_FMT_S16 2 +#define ES1370_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; + +#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) +#define DAC2_DIVTOSR(x) (1411200/((x)+2)) + +#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ +#define CTRL_XCTL1 0x40000000 /* electret mic bias */ +#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ +#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ +#define CTRL_SH_PCLKDIV 16 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ +#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ +#define CTRL_SH_WTSRSEL 12 +#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ +#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ +#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ +#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ +#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ +#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 5 +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* misc stuff */ + +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +/* --------------------------------------------------------------------- */ + +struct es1370_state { + /* magic */ + unsigned int magic; + + /* list of es1370 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned long io; /* long for SPARC */ + unsigned int irq; + + /* mixer registers; there is no HW readback */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + unsigned int imix; + } mix; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; + struct semaphore sem; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* + * The following buffer is used to point the phantom write channel to, + * so that it cannot wreak havoc. The attribute makes sure it doesn't + * cross a page boundary and ensures dword alignment for the DMA engine + */ +static unsigned char bugbuf[16] __attribute__ ((aligned (16))); + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data) +{ + unsigned long tmo = jiffies + HZ/10, j; + + do { + j = jiffies; + if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) { + outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC); + return; + } + schedule(); + } while ((signed)(tmo-j) > 0); + printk(KERN_ERR "es1370: write to codec register timeout\n"); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac1(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac2(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static inline void dealloc_dmabuf(struct es1370_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + fmt &= ES1370_FMT_MASK; + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(db->dmaaddr, s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct es1370_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR); +} + +static inline int prog_dmabuf_dac2(struct es1370_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR); +} + +static inline int prog_dmabuf_dac1(struct es1370_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], + (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR); +} + +static inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1370_update_ptr(struct es1370_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_adc.error++; + } + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1370_handle_midi(struct es1370_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1370_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL); +} + +static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1370_state *s = (struct es1370_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1370_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + es1370_update_ptr(s); + es1370_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1370_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static const struct { + unsigned volidx:4; + unsigned left:4; + unsigned right:4; + unsigned stereo:1; + unsigned recmask:13; + unsigned avail:1; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, /* master */ + [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, /* voice */ + [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, /* FM */ + [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, /* CD */ + [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, /* Line */ + [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, /* AUX */ + [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 }, /* Mono1 */ + [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 }, /* Mono2 */ + [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 }, /* Mic */ + [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */ +}; + +static void set_recsrc(struct es1370_state *s, unsigned int val) +{ + unsigned int i, j; + + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].recmask) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].recmask; + } + s->mix.recsrc = val; + wrcodec(s, 0x12, j & 0xd5); + wrcodec(s, 0x13, j & 0xaa); + wrcodec(s, 0x14, (j >> 8) & 0x17); + wrcodec(s, 0x15, (j >> 8) & 0x0f); + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; + if (!s->mix.imix) { + i &= 0xff60; /* mute record and line monitor */ + } + wrcodec(s, 0x10, i); + wrcodec(s, 0x11, i >> 8); +} + +static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + /* enable/disable/query mixer preamp */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + s->mix.micpreamp = !!val; + wrcodec(s, 0x19, s->mix.micpreamp); + } + return put_user(s->mix.micpreamp, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + /* enable/disable/query use of linein as second lineout */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + spin_lock_irqsave(&s->lock, flags); + if (val) + s->ctrl |= CTRL_XCTL0; + else + s->ctrl &= ~CTRL_XCTL0; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->ctrl & CTRL_XCTL0) ? 1 : 0, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE3) { + /* enable/disable/query microphone impedance setting */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + spin_lock_irqsave(&s->lock, flags); + if (val) + s->ctrl |= CTRL_XCTL1; + else + s->ctrl &= ~CTRL_XCTL1; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->ctrl & CTRL_XCTL1) ? 1 : 0, (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(s->mix.recsrc, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + val = SOUND_MASK_IMIX; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].avail) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].recmask) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].stereo) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + case SOUND_MIXER_IMIX: + return put_user(s->mix.imix, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_IMIX: + if (get_user(s->mix.imix, (int *)arg)) + return -EFAULT; + set_recsrc(s, s->mix.recsrc); + return 0; + + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, (int *)arg)) + return -EFAULT; + set_recsrc(s, val); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (mixtable[i].stereo) { + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (l < 7) { + rl = 0x80; + l = 0; + } else { + rl = 31 - ((l - 7) / 3); + l = (31 - rl) * 3 + 7; + } + if (r < 7) { + rr = 0x80; + r = 0; + } else { + rr = 31 - ((r - 7) / 3); + r = (31 - rr) * 3 + 7; + } + wrcodec(s, mixtable[i].right, rr); + } else { + if (mixtable[i].left == 15) { + if (l < 2) { + rr = rl = 0x80; + r = l = 0; + } else { + rl = 7 - ((l - 2) / 14); + r = l = (7 - rl) * 14 + 2; + } + } else { + if (l < 7) { + rl = 0x80; + r = l = 0; + } else { + rl = 31 - ((l - 7) / 3); + r = l = (31 - rl) * 3 + 7; + } + } + } + wrcodec(s, mixtable[i].left, rl); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[mixtable[i].volidx] = val; +#endif + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static int es1370_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int es1370_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations es1370_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: es1370_ioctl_mixdev, + open: es1370_open_mixdev, + release: es1370_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1370_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped || !s->dma_dac1.ready) + return 0; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 + / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1370_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped || !s->dma_dac2.ready) + return 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + goto out; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + down(&s->sem); + if (s->dma_adc.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } +out: + up(&s->sem); + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac2.mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + goto out; + ret = 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac2.enabled) + start_dac2(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + down(&s->sem); + if (s->dma_dac2.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac2.enabled) + start_dac2(s); + } +out: + up(&s->sem); + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) + return 0; + poll_wait(file, &s->dma_dac2.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + struct dmabuf *db; + int ret = 0; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->sem); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) { + goto out; + } + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) { + goto out; + } + db = &s->dma_adc; + } else { + ret = -EINVAL; + goto out; + } + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + ret = -EINVAL; + goto out; + } + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + db->mapped = 1; +out: + up(&s->sem); + unlock_kernel(); + return ret; +} + +static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; + if (val < 4000) + val = 4000; + if (val > 50000) + val = 50000; + stop_adc(s); + stop_dac2(s); + s->dma_adc.ready = s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + s->dma_dac2.enabled = 1; + start_dac2(s); + } else { + s->dma_dac2.enabled = 0; + stop_dac2(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac2.dmasize - count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? + 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_READ|FMODE_WRITE))) + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + s->dma_dac2.enabled = 1; + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + init_MUTEX(&s->sem); + return 0; +} + +static int es1370_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + dealloc_dmabuf(s, &s->dma_dac2); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1370_read, + write: es1370_write, + poll: es1370_poll, + ioctl: es1370_ioctl, + mmap: es1370_mmap, + open: es1370_open, + release: es1370_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + add_wait_queue(&s->dma_dac1.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac1.enabled) + start_dac1(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac1.enabled) + start_dac1(s); + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) + return 0; + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + lock_kernel(); + if ((ret = prog_dmabuf_dac1(s)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + goto out; + s->dma_dac1.mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + unsigned ctrl; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + for (ctrl = 0; ctrl <= 2; ctrl++) + if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2) + break; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (s->dma_dac1.mapped) + return -EINVAL; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + s->dma_dac1.enabled = 1; + start_dac1(s); + } else { + s->dma_dac1.enabled = 0; + stop_dac1(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac1.dmasize - count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open_dac(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (!((s->dev_dac ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + s->dma_dac1.enabled = 1; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + return 0; +} + +static int es1370_release_dac(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(s, &s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_dac_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + write: es1370_write_dac, + poll: es1370_poll_dac, + ioctl: es1370_ioctl_dac, + mmap: es1370_mmap_dac, + open: es1370_open_dac, + release: es1370_release_dac, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + es1370_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int es1370_midi_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + unlock_kernel(); + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1370_midi_read, + write: es1370_midi_write, + poll: es1370_midi_poll, + open: es1370_midi_open, + release: es1370_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; +static int lineout[NR_DEVICE] = { 0, }; +static int micbias[NR_DEVICE] = { 0, }; + +static unsigned int devindex = 0; + +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_LINE3, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 } +}; + +static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct es1370_state *s; + mm_segment_t fs; + int i, val, ret; + + if ((ret=pci_enable_device(pcidev))) + return ret; + + if ( !(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || + !pci_resource_start(pcidev, 0) + ) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + i = pci_set_dma_mask(pcidev, 0xffffffff); + if (i) { + printk(KERN_WARNING "es1370: architecture does not support 32bit PCI busmaster DMA\n"); + return i; + } + if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1370: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct es1370_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac1.wait); + init_waitqueue_head(&s->dma_dac2.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = ES1370_MAGIC; + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + if (!request_region(s->io, ES1370_EXTENT, "es1370")) { + printk(KERN_ERR "es1370: io ports %#lx-%#lx in use\n", s->io, s->io+ES1370_EXTENT-1); + ret = -EBUSY; + goto err_region; + } + if ((ret=request_irq(s->irq, es1370_interrupt, SA_SHIRQ, "es1370",s))) { + printk(KERN_ERR "es1370: irq %u in use\n", s->irq); + goto err_irq; + } + + /* initialize codec registers */ + /* note: setting CTRL_SERR_DIS is reported to break + * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */ + s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); + s->gameport.io = 0; + if (joystick[devindex]) { + if (!request_region(0x200, JOY_EXTENT, "es1370")) + printk(KERN_ERR "es1370: joystick io port 0x200 in use\n"); + else { + s->ctrl |= CTRL_JYSTK_EN; + s->gameport.io = 0x200; + } + } + if (lineout[devindex]) + s->ctrl |= CTRL_XCTL0; + if (micbias[devindex]) + s->ctrl |= CTRL_XCTL1; + s->sctrl = 0; + printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n" + KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n", + s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off", + (s->ctrl & CTRL_XCTL0) ? "out" : "in", + (s->ctrl & CTRL_XCTL1) ? "1" : "0"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) { + ret = s->dev_dac; + goto err_dev3; + } + if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev4; + } + /* initialize the chips */ + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + /* point phantom write channel to "bugbuf" */ + outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff)); + outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff)); + pci_set_master(pcidev); /* enable bus mastering */ + wrcodec(s, 0x16, 3); /* no RST, PD */ + wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ + wrcodec(s, 0x18, 0); /* recording source is mixer */ + wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */ + s->mix.imix = 1; + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* register gameport */ + gameport_register_port(&s->gameport); + + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1370: cannot register misc device\n"); + free_irq(s->irq, s); + if (s->gameport.io) + release_region(s->gameport.io, JOY_EXTENT); + err_irq: + release_region(s->io, ES1370_EXTENT); + err_region: + kfree(s); + return ret; +} + +static void __devinit es1370_remove(struct pci_dev *dev) +{ + struct es1370_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); + outl(CTRL_SERR_DIS | (1 << CTRL_SH_WTSRSEL), s->io+ES1370_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, JOY_EXTENT); + } + release_region(s->io, ES1370_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver es1370_driver = { + name: "es1370", + id_table: id_table, + probe: es1370_probe, + remove: es1370_remove +}; + +static int __init init_es1370(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1370: version v0.37 time " __TIME__ " " __DATE__ "\n"); + return pci_module_init(&es1370_driver); +} + +static void __exit cleanup_es1370(void) +{ + printk(KERN_INFO "es1370: unloading\n"); + pci_unregister_driver(&es1370_driver); +} + +module_init(init_es1370); +module_exit(cleanup_es1370); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: es1370=[joystick[,lineout[,micbias]]] */ + +static int __init es1370_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + + (void) + ( (get_option(&str,&joystick[nr_dev]) == 2) + && (get_option(&str,&lineout [nr_dev]) == 2) + && get_option(&str,&micbias [nr_dev]) + ); + + nr_dev++; + return 1; +} + +__setup("es1370=", es1370_setup); + +#endif /* MODULE */ diff -Nru linux/sound/oss/es1371.c linux-2.4.19-pre5-mjc/sound/oss/es1371.c --- linux/sound/oss/es1371.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/es1371.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,3084 @@ +/*****************************************************************************/ + +/* + * es1371.c -- Creative Ensoniq ES1371. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to Ensoniq + * + * + * Module command line parameters: + * joystick must be set to the base I/O-Port to be used for + * the gameport. Legal values are 0x200, 0x208, 0x210 and 0x218. + * The gameport is mirrored eight times. + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 04.06.1998 0.1 Initial release + * Mixer stuff should be overhauled; especially optional AC97 mixer bits + * should be detected. This results in strange behaviour of some mixer + * settings, like master volume and mic. + * 08.06.1998 0.2 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.1998 0.3 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.1998 0.4 Fix realplayer problems - dac.count issues + * 27.10.1998 0.5 Fix joystick support + * -- Oliver Neukum (c188@org.chemie.uni-muenchen.de) + * 10.12.1998 0.6 Fix drain_dac trying to wait on not yet initialized DMA + * 23.12.1998 0.7 Fix a few f_file & FMODE_ bugs + * Don't wake up app until there are fragsize bytes to read/write + * 06.01.1999 0.8 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.9 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.10 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.11 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Another Alpha fix (wait_src_ready in init routine) + * reported by "Ivan N. Kokshaysky" + * Note: joystick address handling might still be wrong on archs + * other than i386 + * 15.06.1999 0.12 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.13 Add pci_set_master + * 03.08.1999 0.14 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1371=joystickaddr" + * removed CONFIG_SOUND_ALSA_ES1371_JOYPORT_BOOT kludge + * 10.08.1999 0.15 (Re)added S/PDIF module option for cards revision >= 4. + * Initial version by Dave Platt . + * module_init/__setup fixes + * 08.16.1999 0.16 Joe Cotellese + * Added detection for ES1371 revision ID so that we can + * detect the ES1373 and later parts. + * added AC97 #defines for readability + * added a /proc file system for dumping hardware state + * updated SRC and CODEC w/r functions to accomodate bugs + * in some versions of the ES137x chips. + * 31.08.1999 0.17 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.18 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 21.10.1999 0.19 Round sampling rates, requested by + * Kasamatsu Kenichi + * 27.10.1999 0.20 Added SigmaTel 3D enhancement string + * Codec ID printing changes + * 28.10.1999 0.21 More waitqueue races fixed + * Joe Cotellese + * Changed PCI detection routine so we can more easily + * detect ES137x chip and derivatives. + * 05.01.2000 0.22 Should now work with rev7 boards; patch by + * Eric Lemar, elemar@cs.washington.edu + * 08.01.2000 0.23 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * 07.02.2000 0.24 Use pci_alloc_consistent and pci_register_driver + * 07.02.2000 0.25 Use ac97_codec + * 01.03.2000 0.26 SPDIF patch by Mikael Bouillot + * Use pci_module_init + * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.28 More dma buffer initializations, patch from + * Tjeerd Mulder + * 05.01.2001 0.29 Hopefully updates will not be required anymore when Creative bumps + * the CT5880 revision. + * suggested by Stephan Müller + * 31.01.2001 0.30 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * 14.07.2001 0.31 Add list of laptops needing amplifier control + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define ES1371_DEBUG +#define DBG(x) {} +/*#define DBG(x) {x}*/ + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif + +#ifndef PCI_VENDOR_ID_ECTIVA +#define PCI_VENDOR_ID_ECTIVA 0x1102 +#endif + +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 +#endif + +#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#endif + +#ifndef PCI_DEVICE_ID_ECTIVA_EV1938 +#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 +#endif + +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define CT5880REV_CT5880_C 0x02 +#define CT5880REV_CT5880_D 0x03 +#define ES1371REV_ES1371_B 0x09 +#define EV1938REV_EV1938_A 0x00 +#define ES1371REV_ES1373_8 0x08 + +#define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) + +#define ES1371_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1371_REG_CONTROL 0x00 +#define ES1371_REG_STATUS 0x04 /* on the 5880 it is control/status */ +#define ES1371_REG_UART_DATA 0x08 +#define ES1371_REG_UART_STATUS 0x09 +#define ES1371_REG_UART_CONTROL 0x09 +#define ES1371_REG_UART_TEST 0x0a +#define ES1371_REG_MEMPAGE 0x0c +#define ES1371_REG_SRCONV 0x10 +#define ES1371_REG_CODEC 0x14 +#define ES1371_REG_LEGACY 0x18 +#define ES1371_REG_SERIAL_CONTROL 0x20 +#define ES1371_REG_DAC1_SCOUNT 0x24 +#define ES1371_REG_DAC2_SCOUNT 0x28 +#define ES1371_REG_ADC_SCOUNT 0x2c + +#define ES1371_REG_DAC1_FRAMEADR 0xc30 +#define ES1371_REG_DAC1_FRAMECNT 0xc34 +#define ES1371_REG_DAC2_FRAMEADR 0xc38 +#define ES1371_REG_DAC2_FRAMECNT 0xc3c +#define ES1371_REG_ADC_FRAMEADR 0xd30 +#define ES1371_REG_ADC_FRAMECNT 0xd34 + +#define ES1371_FMT_U8_MONO 0 +#define ES1371_FMT_U8_STEREO 1 +#define ES1371_FMT_S16_MONO 2 +#define ES1371_FMT_S16_STEREO 3 +#define ES1371_FMT_STEREO 1 +#define ES1371_FMT_S16 2 +#define ES1371_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define CTRL_RECEN_B 0x08000000 /* 1 = don't mix analog in to digital out */ +#define CTRL_SPDIFEN_B 0x04000000 +#define CTRL_JOY_SHIFT 24 +#define CTRL_JOY_MASK 3 +#define CTRL_JOY_200 0x00000000 /* joystick base address */ +#define CTRL_JOY_208 0x01000000 +#define CTRL_JOY_210 0x02000000 +#define CTRL_JOY_218 0x03000000 +#define CTRL_GPIO_IN0 0x00100000 /* general purpose inputs/outputs */ +#define CTRL_GPIO_IN1 0x00200000 +#define CTRL_GPIO_IN2 0x00400000 +#define CTRL_GPIO_IN3 0x00800000 +#define CTRL_GPIO_OUT0 0x00010000 +#define CTRL_GPIO_OUT1 0x00020000 +#define CTRL_GPIO_OUT2 0x00040000 +#define CTRL_GPIO_OUT3 0x00080000 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_SYNCRES 0x00004000 /* AC97 warm reset */ +#define CTRL_ADCSTOP 0x00002000 /* stop ADC transfers */ +#define CTRL_PWR_INTRM 0x00001000 /* 1 = power level ints enabled */ +#define CTRL_M_CB 0x00000800 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_PDLEV0 0x00000000 /* power down level */ +#define CTRL_PDLEV1 0x00000100 +#define CTRL_PDLEV2 0x00000200 +#define CTRL_PDLEV3 0x00000300 +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port */ +#define CTRL_XTALCLKDIS 0x00000002 /* 1 = disable crystal clock input */ +#define CTRL_PCICLKDIS 0x00000001 /* 1 = disable PCI clock distribution */ + + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define CSTAT_5880_AC97_RST 0x20000000 /* CT5880 Reset bit */ +#define STAT_EN_SPDIF 0x00040000 /* enable S/PDIF circuitry */ +#define STAT_TS_SPDIF 0x00020000 /* test S/PDIF circuitry */ +#define STAT_TESTMODE 0x00010000 /* test ASIC */ +#define STAT_SYNC_ERR 0x00000100 /* 1 = codec sync error */ +#define STAT_VC 0x000000c0 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 6 +#define STAT_MPWR 0x00000020 /* power level interrupt */ +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +/* sample rate converter */ +#define SRC_OKSTATE 1 + +#define SRC_RAMADDR_MASK 0xfe000000 +#define SRC_RAMADDR_SHIFT 25 +#define SRC_DAC1FREEZE (1UL << 21) +#define SRC_DAC2FREEZE (1UL << 20) +#define SRC_ADCFREEZE (1UL << 19) + + +#define SRC_WE 0x01000000 /* read/write control for SRC RAM */ +#define SRC_BUSY 0x00800000 /* SRC busy */ +#define SRC_DIS 0x00400000 /* 1 = disable SRC */ +#define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */ +#define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */ +#define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */ +#define SRC_CTLMASK 0x00780000 +#define SRC_RAMDATA_MASK 0x0000ffff +#define SRC_RAMDATA_SHIFT 0 + +#define SRCREG_ADC 0x78 +#define SRCREG_DAC1 0x70 +#define SRCREG_DAC2 0x74 +#define SRCREG_VOL_ADC 0x6c +#define SRCREG_VOL_DAC1 0x7c +#define SRCREG_VOL_DAC2 0x7e + +#define SRCREG_TRUNC_N 0x00 +#define SRCREG_INT_REGS 0x01 +#define SRCREG_ACCUM_FRAC 0x02 +#define SRCREG_VFREQ_FRAC 0x03 + +#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_PIADD_MASK 0x007f0000 +#define CODEC_PIADD_SHIFT 16 +#define CODEC_PIDAT_MASK 0x0000ffff +#define CODEC_PIDAT_SHIFT 0 + +#define CODEC_RDY 0x80000000 /* AC97 read data valid */ +#define CODEC_WIP 0x40000000 /* AC97 write in progress */ +#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_POADD_MASK 0x007f0000 +#define CODEC_POADD_SHIFT 16 +#define CODEC_PODAT_MASK 0x0000ffff +#define CODEC_PODAT_SHIFT 0 + + +#define LEGACY_JFAST 0x80000000 /* fast joystick timing */ +#define LEGACY_FIRQ 0x01000000 /* force IRQ */ + +#define SCTRL_DACTEST 0x00400000 /* 1 = DAC test, test vector generation purposes */ +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + + +/* misc stuff */ +#define POLL_COUNT 0x1000 +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define ES1371_MODULE_NAME "es1371" +#define PFX ES1371_MODULE_NAME ": " + +/* --------------------------------------------------------------------- */ + +struct es1371_state { + /* magic */ + unsigned int magic; + + /* list of es1371 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned long io; /* long for SPARC */ + unsigned int irq; + + /* PCI ID's */ + u16 vendor; + u16 device; + u8 rev; /* the chip revision */ + + /* options */ + int spdif_volume; /* S/PDIF output is enabled if != -1 */ + +#ifdef ES1371_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; +#endif /* ES1371_DEBUG */ + + struct ac97_codec codec; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + unsigned dac1rate, dac2rate, adcrate; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; + struct semaphore sem; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static unsigned wait_src_ready(struct es1371_state *s) +{ + unsigned int t, r; + + for (t = 0; t < POLL_COUNT; t++) { + if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY)) + return r; + udelay(1); + } + printk(KERN_DEBUG PFX "sample rate converter timeout r = 0x%08x\n", r); + return r; +} + +static unsigned src_read(struct es1371_state *s, unsigned reg) +{ + unsigned int temp,i,orig; + + /* wait for ready */ + temp = wait_src_ready (s); + + /* we can only access the SRC at certain times, make sure + we're allowed to before we read */ + + orig = temp; + /* expose the SRC state bits */ + outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL, + s->io + ES1371_REG_SRCONV); + + /* now, wait for busy and the correct time to read */ + temp = wait_src_ready (s); + + if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){ + /* wait for the right state */ + for (i=0; iio + ES1371_REG_SRCONV); + if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 )) + break; + } + } + + /* hide the state bits */ + outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV); + return temp; + + +} + +static void src_write(struct es1371_state *s, unsigned reg, unsigned data) +{ + + unsigned int r; + + r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); + r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; + r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK; + outl(r | SRC_WE, s->io + ES1371_REG_SRCONV); + +} + +/* --------------------------------------------------------------------- */ + +/* most of the following here is black magic */ +static void set_adc_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int n, truncm, freq; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + s->adcrate = (48000UL << 15) / (freq / n); + spin_lock_irqsave(&s->lock, flags); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + src_write(s, SRCREG_ADC+SRCREG_INT_REGS, + (src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff); + src_write(s, SRCREG_VOL_ADC, n << 8); + src_write(s, SRCREG_VOL_ADC+1, n << 8); + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void set_dac1_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = ((rate << 15) + 1500) / 3000; + s->dac1rate = (freq * 3000 + 16384) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac2_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = ((rate << 15) + 1500) / 3000; + s->dac2rate = (freq * 3000 + 16384) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static void __init src_init(struct es1371_state *s) +{ + unsigned int i; + + /* before we enable or disable the SRC we need + to wait for it to become ready */ + wait_src_ready(s); + + outl(SRC_DIS, s->io + ES1371_REG_SRCONV); + + for (i = 0; i < 0x80; i++) + src_write(s, i, 0); + + src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_VOL_ADC, 1 << 12); + src_write(s, SRCREG_VOL_ADC+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC2, 1 << 12); + src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); + set_adc_rate(s, 22050); + set_dac1_rate(s, 22050); + set_dac2_rate(s, 22050); + + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) + */ + wait_src_ready(s); + outl(0, s->io+ES1371_REG_SRCONV); +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct es1371_state *s = (struct es1371_state *)codec->private_data; + unsigned long flags; + unsigned t, x; + + for (t = 0; t < POLL_COUNT; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + spin_lock_irqsave(&s->lock, flags); + + /* save the current state for later */ + x = wait_src_ready(s); + + /* enable SRC state data in SRC mux */ + outl((x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + s->io+ES1371_REG_SRCONV); + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | + ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC); + + /* restore SRC reg */ + wait_src_ready(s); + outl(x, s->io+ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct es1371_state *s = (struct es1371_state *)codec->private_data; + unsigned long flags; + unsigned t, x; + + /* wait for WIP to go away */ + for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + spin_lock_irqsave(&s->lock, flags); + + /* save the current state for later */ + x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)); + + /* enable SRC state data in SRC mux */ + outl( x | 0x00010000, + s->io+ES1371_REG_SRCONV); + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC); + /* restore SRC reg */ + wait_src_ready(s); + outl(x, s->io+ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); + + /* wait for WIP again */ + for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) + if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) + break; + + return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac1(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac2(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + + +static inline void dealloc_dmabuf(struct es1371_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + fmt &= ES1371_FMT_MASK; + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + outl(db->dmaaddr, s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct es1371_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK, + ES1371_REG_ADC_FRAMEADR); +} + +static inline int prog_dmabuf_dac2(struct es1371_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC2_FRAMEADR); +} + +static inline int prog_dmabuf_dac1(struct es1371_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC1_FRAMEADR); +} + +static inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1371_update_ptr(struct es1371_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_adc.error++; + } + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1371_handle_midi(struct es1371_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1371_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL); +} + +static void es1371_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1371_state *s = (struct es1371_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1371_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + es1371_update_ptr(s); + es1371_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1371_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +/* Conversion table for S/PDIF PCM volume emulation through the SRC */ +/* dB-linear table of DAC vol values; -0dB to -46.5dB with mute */ +static const unsigned short DACVolTable[101] = +{ + 0x1000, 0x0f2a, 0x0e60, 0x0da0, 0x0cea, 0x0c3e, 0x0b9a, 0x0aff, + 0x0a6d, 0x09e1, 0x095e, 0x08e1, 0x086a, 0x07fa, 0x078f, 0x072a, + 0x06cb, 0x0670, 0x061a, 0x05c9, 0x057b, 0x0532, 0x04ed, 0x04ab, + 0x046d, 0x0432, 0x03fa, 0x03c5, 0x0392, 0x0363, 0x0335, 0x030b, + 0x02e2, 0x02bc, 0x0297, 0x0275, 0x0254, 0x0235, 0x0217, 0x01fb, + 0x01e1, 0x01c8, 0x01b0, 0x0199, 0x0184, 0x0170, 0x015d, 0x014b, + 0x0139, 0x0129, 0x0119, 0x010b, 0x00fd, 0x00f0, 0x00e3, 0x00d7, + 0x00cc, 0x00c1, 0x00b7, 0x00ae, 0x00a5, 0x009c, 0x0094, 0x008c, + 0x0085, 0x007e, 0x0077, 0x0071, 0x006b, 0x0066, 0x0060, 0x005b, + 0x0057, 0x0052, 0x004e, 0x004a, 0x0046, 0x0042, 0x003f, 0x003c, + 0x0038, 0x0036, 0x0033, 0x0030, 0x002e, 0x002b, 0x0029, 0x0027, + 0x0025, 0x0023, 0x0021, 0x001f, 0x001e, 0x001c, 0x001b, 0x0019, + 0x0018, 0x0017, 0x0016, 0x0014, 0x0000 +}; + +/* + * when we are in S/PDIF mode, we want to disable any analog output so + * we filter the mixer ioctls + */ +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)codec->private_data; + int val; + unsigned long flags; + unsigned int left, right; + + VALIDATE_STATE(s); + /* filter mixer ioctls to catch PCM and MASTER volume when in S/PDIF mode */ + if (s->spdif_volume == -1) + return codec->mixer_ioctl(codec, cmd, arg); + switch (cmd) { + case SOUND_MIXER_WRITE_VOLUME: + return 0; + + case SOUND_MIXER_WRITE_PCM: /* use SRC for PCM volume */ + if (get_user(val, (int *)arg)) + return -EFAULT; + right = ((val >> 8) & 0xff); + left = (val & 0xff); + if (right > 100) + right = 100; + if (left > 100) + left = 100; + s->spdif_volume = (right << 8) | left; + spin_lock_irqsave(&s->lock, flags); + src_write(s, SRCREG_VOL_DAC2, DACVolTable[100 - left]); + src_write(s, SRCREG_VOL_DAC2+1, DACVolTable[100 - right]); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SOUND_MIXER_READ_PCM: + return put_user(s->spdif_volume, (int *)arg); + } + return codec->mixer_ioctl(codec, cmd, arg); +} + +/* --------------------------------------------------------------------- */ + +/* + * AC97 Mixer Register to Connections mapping of the Concert 97 board + * + * AC97_MASTER_VOL_STEREO Line Out + * AC97_MASTER_VOL_MONO TAD Output + * AC97_PCBEEP_VOL none + * AC97_PHONE_VOL TAD Input (mono) + * AC97_MIC_VOL MIC Input (mono) + * AC97_LINEIN_VOL Line Input (stereo) + * AC97_CD_VOL CD Input (stereo) + * AC97_VIDEO_VOL none + * AC97_AUX_VOL Aux Input (stereo) + * AC97_PCMOUT_VOL Wave Output (stereo) + */ + +static int es1371_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct list_head *list; + struct es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (s->codec.dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int es1371_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + struct ac97_codec *codec = &s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations es1371_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: es1371_ioctl_mixdev, + open: es1371_open_mixdev, + release: es1371_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1371_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped || !s->dma_dac1.ready) + return 0; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG PFX "dac1 dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1371_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped || !s->dma_dac2.ready) + return 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + __set_current_state(TASK_UNINTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG PFX "dac2 dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + goto out2; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + down(&s->sem); + if (s->dma_adc.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } +out: + up(&s->sem); +out2: + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac2.mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + down(&s->sem); + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + goto out3; + ret = 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac2.enabled) + start_dac2(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + down(&s->sem); + if (s->dma_dac2.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac2.enabled) + start_dac2(s); + } +out: + up(&s->sem); +out2: + remove_wait_queue(&s->dma_dac2.wait, &wait); +out3: + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) + return 0; + poll_wait(file, &s->dma_dac2.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + struct dmabuf *db; + int ret = 0; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->sem); + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) { + goto out; + } + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) { + goto out; + } + db = &s->dma_adc; + } else { + ret = -EINVAL; + goto out; + } + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + ret = -EINVAL; + goto out; + } + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + db->mapped = 1; +out: + up(&s->sem); + unlock_kernel(); + return ret; +} + +static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + set_dac2_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + s->dma_dac2.enabled = 1; + start_dac2(s); + } else { + s->dma_dac2.enabled = 0; + stop_dac2(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac2.dmasize - count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixdev_ioctl(&s->codec, cmd, arg); +} + +static int es1371_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + s->dma_dac2.enabled = 1; + set_dac2_rate(s, 8000); + } + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + init_MUTEX(&s->sem); + return 0; +} + +static int es1371_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + dealloc_dmabuf(s, &s->dma_dac2); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1371_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1371_read, + write: es1371_write, + poll: es1371_poll, + ioctl: es1371_ioctl, + mmap: es1371_mmap, + open: es1371_open, + release: es1371_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + add_wait_queue(&s->dma_dac1.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac1.enabled) + start_dac1(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac1.enabled) + start_dac1(s); + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) + return 0; + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + lock_kernel(); + if ((ret = prog_dmabuf_dac1(s)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + goto out; + s->dma_dac1.mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + set_dac1_rate(s, val); + } + return put_user(s->dac1rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + s->dma_dac1.enabled = 1; + start_dac1(s); + } else { + s->dma_dac1.enabled = 0; + stop_dac1(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac1.dmasize - count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->dac1rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixdev_ioctl(&s->codec, cmd, arg); +} + +static int es1371_open_dac(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (!((s->dev_dac ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + s->dma_dac1.enabled = 1; + set_dac1_rate(s, 8000); + spin_lock_irqsave(&s->lock, flags); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + return 0; +} + +static int es1371_release_dac(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(s, &s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1371_dac_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + write: es1371_write_dac, + poll: es1371_poll_dac, + ioctl: es1371_ioctl_dac, + mmap: es1371_mmap_dac, + open: es1371_open_dac, + release: es1371_release_dac, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + es1371_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1371_midi_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1371_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1371_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int es1371_midi_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + unlock_kernel(); + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG PFX "midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1371_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: es1371_midi_read, + write: es1371_midi_write, + poll: es1371_midi_poll, + open: es1371_midi_open, + release: es1371_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef ES1371_DEBUG +static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) +{ + struct es1371_state *s; + int cnt, len = 0; + + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct es1371_state, devs); + /* print out header */ + len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n"); + + /* print out CODEC state */ + len += sprintf (buf + len, "AC97 CODEC state\n"); + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(&s->codec, cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* ES1371_DEBUG */ + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; +static int spdif[NR_DEVICE] = { 0, }; +static int nomix[NR_DEVICE] = { 0, }; + +static unsigned int devindex = 0; +static int amplifier = 0; + +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "sets address and enables joystick interface (still need separate driver)"); +MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(spdif, "if 1 the output is in S/PDIF digital mode"); +MODULE_PARM(nomix, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(nomix, "if 1 no analog audio is mixed to the digital output"); +MODULE_PARM(amplifier, "i"); +MODULE_PARM_DESC(amplifier, "Set to 1 if the machine needs the amp control enabling (many laptops)"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_IGAIN, 0x4040 } +}; + +static struct +{ + short svid, sdid; +} amplifier_needed[] = +{ + { 0x107B, 0x2150 }, /* Gateway Solo 2150 */ + { 0x13BD, 0x100C }, /* Mebius PC-MJ100V */ + { 0x1102, 0x5938 }, /* Targa Xtender 300 */ + { 0x1102, 0x8938 }, /* IPC notebook */ + { PCI_ANY_ID, PCI_ANY_ID } +}; + + +static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct es1371_state *s; + mm_segment_t fs; + int i, val, res = -1; + int idx; + unsigned long tmo; + signed long tmo2; + unsigned int cssr; + + if ((res=pci_enable_device(pcidev))) + return res; + + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + i = pci_set_dma_mask(pcidev, 0xffffffff); + if (i) { + printk(KERN_WARNING "es1371: architecture does not support 32bit PCI busmaster DMA\n"); + return i; + } + if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { + printk(KERN_WARNING PFX "out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct es1371_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac1.wait); + init_waitqueue_head(&s->dma_dac2.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = ES1371_MAGIC; + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + s->vendor = pcidev->vendor; + s->device = pcidev->device; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); + s->codec.private_data = s; + s->codec.id = 0; + s->codec.codec_read = rdcodec; + s->codec.codec_write = wrcodec; + printk(KERN_INFO PFX "found chip, vendor id 0x%04x device id 0x%04x revision 0x%02x\n", + s->vendor, s->device, s->rev); + if (!request_region(s->io, ES1371_EXTENT, "es1371")) { + printk(KERN_ERR PFX "io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1); + res = -EBUSY; + goto err_region; + } + if ((res=request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371",s))) { + printk(KERN_ERR PFX "irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO PFX "found es1371 rev %d at io %#lx irq %u\n" + KERN_INFO PFX "features: joystick 0x%x\n", s->rev, s->io, s->irq, joystick[devindex]); + /* register devices */ + if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1))<0)) + goto err_dev1; + if ((res=(s->codec.dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1)) < 0)) + goto err_dev2; + if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0)) + goto err_dev3; + if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1))<0 )) + goto err_dev4; +#ifdef ES1371_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry("es1371",0,NULL,proc_es1371_dump,NULL); +#endif /* ES1371_DEBUG */ + + /* initialize codec registers */ + s->ctrl = 0; + + /* Check amplifier requirements */ + + if(amplifier) + s->ctrl |= CTRL_GPIO_OUT0; + else for(idx = 0; amplifier_needed[idx].svid != PCI_ANY_ID; idx++) + { + if(pcidev->subsystem_vendor == amplifier_needed[idx].svid && + pcidev->subsystem_device == amplifier_needed[idx].sdid) + { + s->ctrl |= CTRL_GPIO_OUT0; /* turn internal amplifier on */ + printk(KERN_INFO PFX "Enabling internal amplifier.\n"); + } + } + s->gameport.io = 0; + if ((joystick[devindex] & ~0x18) == 0x200) { + if (!request_region(joystick[devindex], JOY_EXTENT, "es1371")) + printk(KERN_ERR PFX "joystick address 0x%x already in use\n", joystick[devindex]); + else { + s->ctrl |= CTRL_JYSTK_EN | (((joystick[devindex] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + s->gameport.io = joystick[devindex]; + } + } else if (joystick[devindex] == 1) { + for (i = 0x218; i >= 0x200; i -= 0x08) { + if (request_region(i, JOY_EXTENT, "es1371")) { + s->ctrl |= CTRL_JYSTK_EN | (((i >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + s->gameport.io = i; + break; + } + } + if (!s->gameport.io) + printk(KERN_ERR PFX "no free joystick address found\n"); + } + s->sctrl = 0; + cssr = 0; + s->spdif_volume = -1; + /* check to see if s/pdif mode is being requested */ + if (spdif[devindex]) { + if (s->rev >= 4) { + printk(KERN_INFO PFX "enabling S/PDIF output\n"); + s->spdif_volume = 0; + cssr |= STAT_EN_SPDIF; + s->ctrl |= CTRL_SPDIFEN_B; + if (nomix[devindex]) /* don't mix analog inputs to s/pdif output */ + s->ctrl |= CTRL_RECEN_B; + } else { + printk(KERN_ERR PFX "revision %d does not support S/PDIF\n", s->rev); + } + } + /* initialize the chips */ + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(0, s->io+ES1371_REG_LEGACY); + pci_set_master(pcidev); /* enable bus mastering */ + /* if we are a 5880 turn on the AC97 */ + if (s->vendor == PCI_VENDOR_ID_ENSONIQ && + ((s->device == PCI_DEVICE_ID_ENSONIQ_CT5880 && s->rev >= CT5880REV_CT5880_C) || + (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_CT5880_A) || + (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_ES1373_8))) { + cssr |= CSTAT_5880_AC97_RST; + outl(cssr, s->io+ES1371_REG_STATUS); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + tmo = jiffies + (HZ / 50) + 1; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } + } + /* AC97 warm reset to start the bitclk */ + outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); + udelay(2); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + /* init the sample rate converter */ + src_init(s); + /* codec init */ + if (!ac97_probe_codec(&s->codec)) { + res = -ENODEV; + goto err_gp; + } + /* set default values */ + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixdev_ioctl(&s->codec, initvol[i].mixch, (unsigned long)&val); + } + /* mute master and PCM when in S/PDIF mode */ + if (s->spdif_volume != -1) { + val = 0x0000; + s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, (unsigned long)&val); + s->codec.mixer_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, (unsigned long)&val); + } + set_fs(fs); + /* turn on S/PDIF output driver if requested */ + outl(cssr, s->io+ES1371_REG_STATUS); + /* register gameport */ + gameport_register_port(&s->gameport); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_gp: + if (s->gameport.io) + release_region(s->gameport.io, JOY_EXTENT); + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->codec.dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR PFX "cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1371_EXTENT); + err_region: + kfree(s); + return res; +} + +static void __devinit es1371_remove(struct pci_dev *dev) +{ + struct es1371_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); +#ifdef ES1371_DEBUG + if (s->ps) + remove_proc_entry("es1371", NULL); +#endif /* ES1371_DEBUG */ + outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, JOY_EXTENT); + } + release_region(s->io, ES1371_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec.dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver es1371_driver = { + name: "es1371", + id_table: id_table, + probe: es1371_probe, + remove: es1371_remove +}; + +static int __init init_es1371(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO PFX "version v0.30 time " __TIME__ " " __DATE__ "\n"); + return pci_module_init(&es1371_driver); +} + +static void __exit cleanup_es1371(void) +{ + printk(KERN_INFO PFX "unloading\n"); + pci_unregister_driver(&es1371_driver); +} + +module_init(init_es1371); +module_exit(cleanup_es1371); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: es1371=[joystick] */ + +static int __init es1371_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + if (get_option(&str, &joystick[nr_dev]) == 2) + (void)get_option(&str, &spdif[nr_dev]); + nr_dev++; + return 1; +} + +__setup("es1371=", es1371_setup); + +#endif /* MODULE */ diff -Nru linux/sound/oss/esssolo1.c linux-2.4.19-pre5-mjc/sound/oss/esssolo1.c --- linux/sound/oss/esssolo1.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/esssolo1.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2484 @@ +/****************************************************************************/ + +/* + * esssolo1.c -- ESS Technology Solo1 (ES1946) audio driver. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Module command line parameters: + * none so far + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * Revision history + * 10.11.1998 0.1 Initial release (without any hardware) + * 22.03.1999 0.2 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * 15.06.1999 0.4 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.5 Add pci_set_master + * 12.08.1999 0.6 Fix MIDI UART crashing the driver + * Changed mixer semantics from OSS documented + * behaviour to OSS "code behaviour". + * Recording might actually work now. + * The real DDMA controller address register is at PCI config + * 0x60, while the register at 0x18 is used as a placeholder + * register for BIOS address allocation. This register + * is supposed to be copied into 0x60, according + * to the Solo1 datasheet. When I do that, I can access + * the DDMA registers except the mask bit, which + * is stuck at 1. When I copy the contents of 0x18 +0x10 + * to the DDMA base register, everything seems to work. + * The fun part is that the Windows Solo1 driver doesn't + * seem to do these tricks. + * Bugs remaining: plops and clicks when starting/stopping playback + * 31.08.1999 0.7 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.8 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 07.10.1999 0.9 Fix initialization; complain if sequencer writes time out + * Revised resource grabbing for the FM synthesizer + * 28.10.1999 0.10 More waitqueue races fixed + * 09.12.1999 0.11 Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M) + * Disabling recording on Alpha + * 12.01.2000 0.12 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * Integrated (aka redid 8-)) APM support patch by Zach Brown + * 07.02.2000 0.13 Use pci_alloc_consistent and pci_register_driver + * 19.02.2000 0.14 Use pci_dma_supported to determine if recording should be disabled + * 13.03.2000 0.15 Reintroduce initialization of a couple of PCI config space registers + * 21.11.2000 0.16 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.17 More dma buffer initializations, patch from + * Tjeerd Mulder + * 31.01.2001 0.18 Register/Unregister gameport, original patch from + * Nathaniel Daw + * Fix SETTRIGGER non OSS API conformity + * 10.03.2001 provide abs function, prevent picking up a bogus kernel macro + * for abs. Bug report by Andrew Morton + * 15.05.2001 pci_enable_device moved, return values in probe cleaned + * up. Marcus Meissner + * 22.05.2001 0.19 more cleanups, changed PM to PCI 2.4 style, got rid + * of global list of devices, using pci device data. + * Marcus Meissner + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125d +#endif +#ifndef PCI_DEVICE_ID_ESS_SOLO1 +#define PCI_DEVICE_ID_ESS_SOLO1 0x1969 +#endif + +#define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) + +#define DDMABASE_OFFSET 0 /* chip bug workaround kludge */ +#define DDMABASE_EXTENT 16 + +#define IOBASE_EXTENT 16 +#define SBBASE_EXTENT 16 +#define VCBASE_EXTENT (DDMABASE_EXTENT+DDMABASE_OFFSET) +#define MPUBASE_EXTENT 4 +#define GPBASE_EXTENT 4 +#define GAMEPORT_EXTENT 4 + +#define FMSYNTH_EXTENT 4 + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +static struct pci_driver solo1_driver; + +/* --------------------------------------------------------------------- */ + +struct solo1_state { + /* magic */ + unsigned int magic; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned long iobase, sbbase, vcbase, ddmabase, mpubase; /* long for SPARC */ + unsigned int irq; + + /* mixer registers */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + /* wave stuff */ + unsigned fmt; + unsigned channels; + unsigned rate; + unsigned char clkdiv; + unsigned ena; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; +}; + +/* --------------------------------------------------------------------- */ + +static inline void write_seq(struct solo1_state *s, unsigned char data) +{ + int i; + unsigned long flags; + + /* the __cli stunt is to send the data within the command window */ + for (i = 0; i < 0xffff; i++) { + __save_flags(flags); + __cli(); + if (!(inb(s->sbbase+0xc) & 0x80)) { + outb(data, s->sbbase+0xc); + __restore_flags(flags); + return; + } + __restore_flags(flags); + } + printk(KERN_ERR "esssolo1: write_seq timeout\n"); + outb(data, s->sbbase+0xc); +} + +static inline int read_seq(struct solo1_state *s, unsigned char *data) +{ + int i; + + if (!data) + return 0; + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) { + *data = inb(s->sbbase+0xa); + return 1; + } + printk(KERN_ERR "esssolo1: read_seq timeout\n"); + return 0; +} + +static int inline reset_ctrl(struct solo1_state *s) +{ + int i; + + outb(3, s->sbbase+6); /* clear sequencer and FIFO */ + udelay(10); + outb(0, s->sbbase+6); + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) + if (inb(s->sbbase+0xa) == 0xaa) { + write_seq(s, 0xc6); /* enter enhanced mode */ + return 1; + } + return 0; +} + +static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + write_seq(s, reg); + write_seq(s, data); +} + +#if 0 /* unused */ +static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) +{ + unsigned char r; + + write_seq(s, 0xc0); + write_seq(s, reg); + read_seq(s, &r); + return r; +} +#endif /* unused */ + +static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + outb(reg, s->sbbase+4); + outb(data, s->sbbase+5); +} + +static unsigned char read_mixer(struct solo1_state *s, unsigned char reg) +{ + outb(reg, s->sbbase+4); + return inb(s->sbbase+5); +} + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_dac(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x10); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->ena |= FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + udelay(10); + write_mixer(s, 0x78, 0x13); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ena |= FMODE_READ; + write_ctrl(s, 0xb8, 0xf); +#if 0 + printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf); + printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", + inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8)); +#endif + outb(0, s->ddmabase+0xd); /* master reset */ + outb(1, s->ddmabase+0xf); /* mask */ + outb(0x54/*0x14*/, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + outb(0, s->ddmabase+0xf); + } + spin_unlock_irqrestore(&s->lock, flags); +#if 0 + printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x SBstat: 0x%02x\n" + KERN_DEBUG "solo1: DMA: stat: 0x%02x cnt: 0x%04x mask: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->sbbase+0xc), + inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf)); + printk(KERN_DEBUG "solo1: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1: B1: 0x%02x B2: 0x%02x B4: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), + read_ctrl(s, 0xb9)); +#endif +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static inline void dealloc_dmabuf(struct solo1_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db) +{ + int order; + unsigned bytespersec; + unsigned bufs, sample_shift = 0; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->channels > 1) + sample_shift++; + bytespersec = s->rate << sample_shift; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytespersec) + db->fragshift = ld2(bytespersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytespersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift; + db->dmasize = db->numfrag << db->fragshift; + db->enabled = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_adc(s); + /* check if PCI implementation supports 24bit busmaster DMA */ + if (s->dev->dma_mask > 0xffffff) + return -EIO; + if ((c = prog_dmabuf(s, &s->dma_adc))) + return c; + va = s->dma_adc.dmaaddr; + if ((va & ~((1<<24)-1))) + panic("solo1: buffer above 16M boundary"); + outb(0, s->ddmabase+0xd); /* clear */ + outb(1, s->ddmabase+0xf); /* mask */ + /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ + outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(va, s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + c = - s->dma_adc.fragsamples; + write_ctrl(s, 0xa4, c); + write_ctrl(s, 0xa5, c >> 8); + outb(0, s->ddmabase+0xf); + s->dma_adc.ready = 1; + return 0; +} + +static inline int prog_dmabuf_dac(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_dac(s); + if ((c = prog_dmabuf(s, &s->dma_dac))) + return c; + memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */ + va = s->dma_dac.dmaaddr; + if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1)) + panic("solo1: buffer crosses 1M boundary"); + outl(va, s->iobase); + /* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */ + outw(s->dma_dac.dmasize, s->iobase+4); + c = - s->dma_dac.fragsamples; + write_mixer(s, 0x74, c); + write_mixer(s, 0x76, c >> 8); + outb(0xa, s->iobase+6); + s->dma_dac.ready = 1; + return 0; +} + +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ + +static void solo1_update_ptr(struct solo1_state *s) +{ + int diff; + unsigned hwptr; + + /* update ADC pointer */ + if (s->ena & FMODE_READ) { + hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; +#if 0 + printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count); +#endif + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC pointer */ + if (s->ena & FMODE_WRITE) { + hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; +#if 0 + printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count); +#endif + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, + s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count < (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* --------------------------------------------------------------------- */ + +static void prog_codec(struct solo1_state *s) +{ + unsigned long flags; + int fdiv, filter; + unsigned char c; + + reset_ctrl(s); + write_seq(s, 0xd3); + /* program sampling rates */ + filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */ + fdiv = 256 - 7160000 / (filter * 82); + spin_lock_irqsave(&s->lock, flags); + write_ctrl(s, 0xa1, s->clkdiv); + write_ctrl(s, 0xa2, fdiv); + write_mixer(s, 0x70, s->clkdiv); + write_mixer(s, 0x72, fdiv); + /* program ADC parameters */ + write_ctrl(s, 0xb8, 0xe); + write_ctrl(s, 0xb9, /*0x1*/0); + write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12); + c = 0xd0; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 0x04; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 0x20; + if (s->channels > 1) + c ^= 0x48; + write_ctrl(s, 0xb7, (c & 0x70) | 1); + write_ctrl(s, 0xb7, c); + write_ctrl(s, 0xb1, 0x50); + write_ctrl(s, 0xb2, 0x50); + /* program DAC parameters */ + c = 0x40; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 1; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 4; + if (s->channels > 1) + c |= 2; + write_mixer(s, 0x7a, c); + write_mixer(s, 0x78, 0x10); + s->ena = 0; + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SOLO1_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg) +{ + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, + SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 + }; + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, /* voice */ + [SOUND_MIXER_SYNTH] = 2, /* FM */ + [SOUND_MIXER_CD] = 3, /* CD */ + [SOUND_MIXER_LINE] = 4, /* Line */ + [SOUND_MIXER_LINE1] = 5, /* AUX */ + [SOUND_MIXER_MIC] = 6, /* Mic */ + [SOUND_MIXER_LINE2] = 7, /* Mono in */ + [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ + [SOUND_MIXER_RECLEV] = 9, /* Recording level */ + [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ + }; + static const unsigned char mixreg[] = { + 0x7c, /* voice */ + 0x36, /* FM */ + 0x38, /* CD */ + 0x3e, /* Line */ + 0x3a, /* AUX */ + 0x1a, /* Mic */ + 0x6d /* Mono in */ + }; + unsigned char l, r, rl, rr, vidx; + int i, val; + + VALIDATE_STATE(s); + + if (cmd == SOUND_MIXER_PRIVATE1) { + /* enable/disable/query mixer preamp */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + val = val ? 0xff : 0xf7; + write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); + } + val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; + return put_user(val, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + /* enable/disable/query spatializer */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != -1) { + val &= 0x3f; + write_mixer(s, 0x52, val); + write_mixer(s, 0x50, val ? 0x08 : 0); + } + return put_user(read_mixer(s, 0x52), (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_src[read_mixer(s, 0x1c) & 7], (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx-1], (int *)arg); + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ +#if 0 + { + static const unsigned char regs[] = { + 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c + }; + int i; + + for (i = 0; i < sizeof(regs); i++) + printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", + regs[i], read_mixer(s, regs[i])); + printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", + 0xb4, read_ctrl(s, 0xb4)); + } +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + i = hweight32(val); + if (i == 0) + return 0; + else if (i > 1) + val &= ~mixer_src[read_mixer(s, 0x1c) & 7]; + for (i = 0; i < 8; i++) { + if (mixer_src[i] & val) + break; + } + if (i > 7) + return 0; + write_mixer(s, 0x1c, i); + return 0; + + case SOUND_MIXER_VOLUME: + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (l < 6) { + rl = 0x40; + l = 0; + } else { + rl = (l * 2 - 11) / 3; + l = (rl * 3 + 11) / 2; + } + if (r < 6) { + rr = 0x40; + r = 0; + } else { + rr = (r * 2 - 11) / 3; + r = (rr * 3 + 11) / 2; + } + write_mixer(s, 0x60, rl); + write_mixer(s, 0x62, rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[9] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[9] = val; +#endif + return put_user(s->mix.vol[9], (int *)arg); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + else if (l < 2) + l = 2; + rl = (l - 2) / 14; + l = rl * 14 + 2; + write_mixer(s, 0x3c, rl); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = l * 0x101; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int *)arg); + + case SOUND_MIXER_RECLEV: + if (get_user(val, (int *)arg)) + return -EFAULT; + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_ctrl(s, 0xb4, (rl << 4) | rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_mixer(s, mixreg[vidx-1], (rl << 4) | rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx-1] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[vidx-1] = val; +#endif + return put_user(s->mix.vol[vidx-1], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static int solo1_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + drvr = pci_dev_driver (pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_mixer == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int solo1_release_mixdev(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int solo1_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct solo1_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations solo1_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: solo1_ioctl_mixdev, + open: solo1_open_mixdev, + release: solo1_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct solo1_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "solo1: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt); +#endif + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); +#endif + } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; +#if 0 + printk(KERN_DEBUG "solo1_write: reg 70: 0x%02x 71: 0x%02x 72: 0x%02x 74: 0x%02x 76: 0x%02x 78: 0x%02x 7A: 0x%02x\n" + KERN_DEBUG "solo1_write: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x70), read_mixer(s, 0x71), read_mixer(s, 0x72), read_mixer(s, 0x74), read_mixer(s, 0x76), + read_mixer(s, 0x78), read_mixer(s, 0x7a), inl(s->iobase), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); + printk(KERN_DEBUG "solo1_write: reg 78: 0x%02x reg 7A: 0x%02x DMAcnt: 0x%04x DMAstat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); +#endif + ret = 0; + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac.enabled) + start_dac(s); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int solo1_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize > s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int solo1_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret, count; + int div1, div2; + unsigned rate1, rate2; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + prog_codec(s); + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program sampling rates */ + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + div1 = (768000 + val / 2) / val; + rate1 = (768000 + div1 / 2) / div1; + div1 = -div1; + div2 = (793800 + val / 2) / val; + rate2 = (793800 + div2 / 2) / div2; + div2 = (-div2) & 0x7f; + if (abs(val - rate2) < abs(val - rate1)) { + rate1 = rate2; + div1 = div2; + } + s->rate = rate1; + s->clkdiv = div1; + prog_codec(s); + } + return put_user(s->rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = val ? 2 : 1; + prog_codec(s); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = (val >= 2) ? 2 : 1; + prog_codec(s); + } + return put_user(s->channels, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program format */ + if (val != AFMT_S16_LE && val != AFMT_U16_LE && + val != AFMT_S8 && val != AFMT_U8) + val = AFMT_U8; + s->fmt = val; + prog_codec(s); + } + return put_user(s->fmt, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + s->dma_dac.enabled = 1; + start_adc(s); + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + } else { + s->dma_dac.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + stop_dac(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); +#if 0 + printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n" + KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n", + cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift, + s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples); +#endif + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user(s->channels, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & (AFMT_S8|AFMT_U8)) ? 8 : 16, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int solo1_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + outb(0, s->iobase+6); /* disable DMA */ + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + outb(1, s->ddmabase+0xf); /* mask DMA channel */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ~(FMODE_READ | FMODE_WRITE); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static int solo1_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (FMODE_READ | FMODE_WRITE)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->fmt = AFMT_U8; + s->channels = 1; + s->rate = 8000; + s->clkdiv = 96 | 0x80; + s->ena = 0; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + prog_codec(s); + return 0; +} + +static /*const*/ struct file_operations solo1_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: solo1_read, + write: solo1_write, + poll: solo1_poll, + ioctl: solo1_ioctl, + mmap: solo1_mmap, + open: solo1_open, + release: solo1_release, +}; + +/* --------------------------------------------------------------------- */ + +/* hold spinlock for the following! */ +static void solo1_handle_midi(struct solo1_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->mpubase)) + return; + wake = 0; + while (!(inb(s->mpubase+1) & 0x80)) { + ch = inb(s->mpubase); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->mpubase); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static void solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct solo1_state *s = (struct solo1_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->iobase+7); /* get interrupt source(s) */ + if (!intsrc) + return; + (void)inb(s->sbbase+0xe); /* clear interrupt */ + spin_lock(&s->lock); + /* clear audio interrupts first */ + if (intsrc & 0x20) + write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f); + solo1_update_ptr(s); + solo1_handle_midi(s); + spin_unlock(&s->lock); +} + +static void solo1_midi_timer(unsigned long data) +{ + struct solo1_state *s = (struct solo1_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + solo1_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int solo1_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_midi == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(0xff, s->mpubase+1); /* reset command */ + outb(0x3f, s->mpubase+1); /* uart command */ + if (!(inb(s->mpubase+1) & 0x80)) + inb(s->mpubase); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */ + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = solo1_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int solo1_midi_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + unlock_kernel(); + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "solo1: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */ + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations solo1_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: solo1_midi_read, + write: solo1_midi_write, + poll: solo1_midi_poll, + open: solo1_midi_open, + release: solo1_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->sbbase + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->sbbase); + outb((p.kbd_split & 1) << 6, s->sbbase+1); + outb(0xbd, s->sbbase); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->sbbase+2); + outb(arg, s->sbbase+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->sbbase+2); + outb(arg & 1, s->sbbase+3); + return 0; + + default: + return -EINVAL; + } +} + +static int solo1_dmfm_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_dmfm == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) { + up(&s->open_sem); + printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); + return -EBUSY; + } + /* init the stuff */ + outb(1, s->sbbase); + outb(0x20, s->sbbase+1); /* enable waveforms */ + outb(4, s->sbbase+2); + outb(0, s->sbbase+3); /* no 4op enabled */ + outb(5, s->sbbase+2); + outb(1, s->sbbase+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + return 0; +} + +static int solo1_dmfm_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + release_region(s->sbbase, FMSYNTH_EXTENT); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations solo1_dmfm_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: solo1_dmfm_ioctl, + open: solo1_dmfm_open, + release: solo1_dmfm_release, +}; + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 } +}; + +static int setup_solo1(struct solo1_state *s) +{ + struct pci_dev *pcidev = s->dev; + mm_segment_t fs; + int i, val; + + /* initialize DDMA base address */ + printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); + pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); + /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ + pci_write_config_dword(pcidev, 0x50, 0); + /* disable legacy audio address decode */ + pci_write_config_word(pcidev, 0x40, 0x907f); + + /* initialize the chips */ + if (!reset_ctrl(s)) { + printk(KERN_ERR "esssolo1: cannot reset controller\n"); + return -1; + } + outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ + + /* initialize mixer regs */ + write_mixer(s, 0x7f, 0); /* disable music digital recording */ + write_mixer(s, 0x7d, 0x0c); /* enable mic preamp, MONO_OUT is 2nd DAC right channel */ + write_mixer(s, 0x64, 0x45); /* volume control */ + write_mixer(s, 0x48, 0x10); /* enable music DAC/ES6xx interface */ + write_mixer(s, 0x50, 0); /* disable spatializer */ + write_mixer(s, 0x52, 0); + write_mixer(s, 0x14, 0); /* DAC1 minimum volume */ + write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(1, s->ddmabase+0xf); /* mask channel */ + /*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */ + + pci_set_master(pcidev); /* enable bus mastering */ + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + val = 1; /* enable mic preamp */ + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val); + set_fs(fs); + return 0; +} + +static int +solo1_suspend(struct pci_dev *pci_dev, u32 state) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return 1; + outb(0, s->iobase+6); + /* DMA master clear */ + outb(0, s->ddmabase+0xd); + /* reset sequencer and FIFO */ + outb(3, s->sbbase+6); + /* turn off DDMA controller address space */ + pci_write_config_word(s->dev, 0x60, 0); + return 0; +} + +static int +solo1_resume(struct pci_dev *pci_dev) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return 1; + setup_solo1(s); + return 0; +} + +static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct solo1_state *s; + int ret; + + if ((ret=pci_enable_device(pcidev))) + return ret; + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 1) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 2) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 3) & IORESOURCE_IO)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + + /* Recording requires 24-bit DMA, so attempt to set dma mask + * to 24 bits first, then 32 bits (playback only) if that fails. + */ + if (pci_set_dma_mask(pcidev, 0x00ffffff) && + pci_set_dma_mask(pcidev, 0xffffffff)) { + printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) { + printk(KERN_WARNING "solo1: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct solo1_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = SOLO1_MAGIC; + s->dev = pcidev; + s->iobase = pci_resource_start(pcidev, 0); + s->sbbase = pci_resource_start(pcidev, 1); + s->vcbase = pci_resource_start(pcidev, 2); + s->ddmabase = s->vcbase + DDMABASE_OFFSET; + s->mpubase = pci_resource_start(pcidev, 3); + s->gameport.io = pci_resource_start(pcidev, 4); + s->irq = pcidev->irq; + ret = -EBUSY; + if (!request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region1; + } + if (!request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region2; + } + if (!request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region3; + } + if (!request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region4; + } + if (s->gameport.io && !request_region(s->gameport.io, GAMEPORT_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: gameport io ports in use\n"); + s->gameport.io = 0; + } + if ((ret=request_irq(s->irq,solo1_interrupt,SA_SHIRQ,"ESS Solo1",s))) { + printk(KERN_ERR "solo1: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "solo1: joystick port at %#x\n", s->gameport.io+1); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev3; + } + if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) { + ret = s->dev_dmfm; + goto err_dev4; + } + if (setup_solo1(s)) { + ret = -EIO; + goto err; + } + /* register gameport */ + gameport_register_port(&s->gameport); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + return 0; + + err: + unregister_sound_dsp(s->dev_dmfm); + err_dev4: + unregister_sound_dsp(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "solo1: initialisation error\n"); + free_irq(s->irq, s); + err_irq: + if (s->gameport.io) + release_region(s->gameport.io, GAMEPORT_EXTENT); + release_region(s->iobase, IOBASE_EXTENT); + err_region4: + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); + err_region3: + release_region(s->ddmabase, DDMABASE_EXTENT); + err_region2: + release_region(s->mpubase, MPUBASE_EXTENT); + err_region1: + kfree(s); + return ret; +} + +static void __devinit solo1_remove(struct pci_dev *dev) +{ + struct solo1_state *s = pci_get_drvdata(dev); + + if (!s) + return; + /* stop DMA controller */ + outb(0, s->iobase+6); + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(3, s->sbbase+6); /* reset sequencer and FIFO */ + synchronize_irq(); + pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */ + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, GAMEPORT_EXTENT); + } + release_region(s->iobase, IOBASE_EXTENT); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); + release_region(s->ddmabase, DDMABASE_EXTENT); + release_region(s->mpubase, MPUBASE_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver solo1_driver = { + name: "ESS Solo1", + id_table: id_table, + probe: solo1_probe, + remove: solo1_remove, + suspend: solo1_suspend, + resume: solo1_resume +}; + + +static int __init init_solo1(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "solo1: version v0.19 time " __TIME__ " " __DATE__ "\n"); + if (!pci_register_driver(&solo1_driver)) { + pci_unregister_driver(&solo1_driver); + return -ENODEV; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ESS Solo1 Driver"); +MODULE_LICENSE("GPL"); + + +static void __exit cleanup_solo1(void) +{ + printk(KERN_INFO "solo1: unloading\n"); + pci_unregister_driver(&solo1_driver); +} + +/* --------------------------------------------------------------------- */ + +module_init(init_solo1); +module_exit(cleanup_solo1); + diff -Nru linux/sound/oss/gus.h linux-2.4.19-pre5-mjc/sound/oss/gus.h --- linux/sound/oss/gus.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/gus.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,30 @@ +/* + * gus.h + * + * Copyright: Christoph Hellwig + * + */ + +#include "ad1848.h" + +/* From gus_card.c */ +int gus_set_midi_irq(int num); +void gusintr(int irq, void *dev_id, struct pt_regs * dummy); + +/* From gus_wave.c */ +int gus_wave_detect(int baseaddr); +void gus_wave_init(struct address_info *hw_config); +void gus_wave_unload (struct address_info *hw_config); +void gus_voice_irq(void); +void gus_write8(int reg, unsigned int data); +void guswave_dma_irq(void); +void gus_delay(void); +int gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg); +void gus_timer_command (unsigned int addr, unsigned int val); + +/* From gus_midi.c */ +void gus_midi_init(struct address_info *hw_config); +void gus_midi_interrupt(int dummy); + +/* From ics2101.c */ +int ics2101_mixer_init(void); diff -Nru linux/sound/oss/gus_card.c linux-2.4.19-pre5-mjc/sound/oss/gus_card.c --- linux/sound/oss/gus_card.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/gus_card.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,298 @@ +/* + * sound/gus_card.c + * + * Detection routine for the Gravis Ultrasound. + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * + * Frank van de Pol : Fixed GUS MAX interrupt handling, enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + * Christoph Hellwig: Adapted to module_init/module_exit, simple cleanups. + * + * Status: + * Tested... + */ + + +#include +#include +#include + +#include "sound_config.h" + +#include "gus.h" +#include "gus_hw.h" + +void gusintr(int irq, void *dev_id, struct pt_regs *dummy); + +int gus_base = 0, gus_irq = 0, gus_dma = 0; +int gus_no_wave_dma = 0; +extern int gus_wave_volume; +extern int gus_pcm_volume; +extern int have_gus_max; +int gus_pnp_flag = 0; +#ifdef CONFIG_SOUND_ALSA_GUS16 +static int db16 = 0; /* Has a Gus16 AD1848 on it */ +#endif + +static void __init attach_gus(struct address_info *hw_config) +{ + gus_wave_init(hw_config); + + request_region(hw_config->io_base, 16, "GUS"); + request_region(hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */ + + if (sound_alloc_dma(hw_config->dma, "GUS")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma); + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (sound_alloc_dma(hw_config->dma2, "GUS(2)")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2); + gus_midi_init(hw_config); + if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) + printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); + + return; +} + +static int __init probe_gus(struct address_info *hw_config) +{ + int irq; + int io_addr; + + if (hw_config->card_subtype == 1) + gus_pnp_flag = 1; + + irq = hw_config->irq; + + if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */ + if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && + irq != 11 && irq != 12 && irq != 15) + { + printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq); + return 0; + } + if (check_region(hw_config->io_base, 16)) + printk(KERN_ERR "GUS: I/O range conflict (1)\n"); + else if (check_region(hw_config->io_base + 0x100, 16)) + printk(KERN_ERR "GUS: I/O range conflict (2)\n"); + else if (gus_wave_detect(hw_config->io_base)) + return 1; + +#ifndef EXCLUDE_GUS_IODETECT + + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) + if (io_addr != hw_config->io_base) /* + * Already tested + */ + if (!check_region(io_addr, 16)) + if (!check_region(io_addr + 0x100, 16)) + if (gus_wave_detect(io_addr)) + { + hw_config->io_base = io_addr; + return 1; + } +#endif + + printk("NO GUS card found !\n"); + return 0; +} + +static void __exit unload_gus(struct address_info *hw_config) +{ + DDB(printk("unload_gus(%x)\n", hw_config->io_base)); + + gus_wave_unload(hw_config); + + release_region(hw_config->io_base, 16); + release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ + free_irq(hw_config->irq, hw_config); + + sound_free_dma(hw_config->dma); + + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + sound_free_dma(hw_config->dma2); +} + +void gusintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + unsigned char src; + extern int gus_timer_enabled; + + sti(); + +#ifdef CONFIG_SOUND_ALSA_GUSMAX + if (have_gus_max) { + struct address_info *hw_config = dev_id; + adintr(irq, (void *)hw_config->slots[1], NULL); + } +#endif +#ifdef CONFIG_SOUND_ALSA_GUS16 + if (db16) { + struct address_info *hw_config = dev_id; + adintr(irq, (void *)hw_config->slots[3], NULL); + } +#endif + + while (1) + { + if (!(src = inb(u_IrqStatus))) + return; + + if (src & DMA_TC_IRQ) + { + guswave_dma_irq(); + } + if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + { + gus_midi_interrupt(0); + } + if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) + { + if (gus_timer_enabled) + sound_timer_interrupt(); + gus_write8(0x45, 0); /* Ack IRQ */ + gus_timer_command(4, 0x80); /* Reset IRQ flags */ + } + if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) + gus_voice_irq(); + } +} + +/* + * Some extra code for the 16 bit sampling option + */ + +#ifdef CONFIG_SOUND_ALSA_GUS16 + +static int __init probe_gus_db16(struct address_info *hw_config) +{ + return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); +} + +static void __init attach_gus_db16(struct address_info *hw_config) +{ + gus_pcm_volume = 100; + gus_wave_volume = 90; + + hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0, + hw_config->osp, + THIS_MODULE); +} + +static void __exit unload_gus_db16(struct address_info *hw_config) +{ + + ad1848_unload(hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0); + sound_unload_audiodev(hw_config->slots[3]); +} +#endif + +#ifdef CONFIG_SOUND_ALSA_GUS16 +static int gus16 = 0; +#endif +#ifdef CONFIG_SOUND_ALSA_GUSMAX +static int no_wave_dma = 0;/* Set if no dma is to be used for the + wave table (GF1 chip) */ +#endif + + +/* + * Note DMA2 of -1 has the right meaning in the GUS driver as well + * as here. + */ + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata type = 0; /* 1 for PnP */ + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(dma, "i"); +MODULE_PARM(dma16, "i"); +MODULE_PARM(type, "i"); +#ifdef CONFIG_SOUND_ALSA_GUSMAX +MODULE_PARM(no_wave_dma, "i"); +#endif +#ifdef CONFIG_SOUND_ALSA_GUS16 +MODULE_PARM(db16, "i"); +MODULE_PARM(gus16, "i"); +#endif +MODULE_LICENSE("GPL"); + +static int __init init_gus(void) +{ + printk(KERN_INFO "Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + cfg.card_subtype = type; +#ifdef CONFIG_SOUND_ALSA_GUSMAX + gus_no_wave_dma = no_wave_dma; +#endif + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n"); + return -EINVAL; + } + +#ifdef CONFIG_SOUND_ALSA_GUS16 + if (probe_gus_db16(&cfg) && gus16) { + /* FIXME: This can't work, can it ? -- Christoph */ + attach_gus_db16(&cfg); + db16 = 1; + } +#endif + if (!probe_gus(&cfg)) + return -ENODEV; + attach_gus(&cfg); + + return 0; +} + +static void __exit cleanup_gus(void) +{ +#ifdef CONFIG_SOUND_ALSA_GUS16 + if (db16) + unload_gus_db16(&cfg); +#endif + unload_gus(&cfg); +} + +module_init(init_gus); +module_exit(cleanup_gus); + +#ifndef MODULE +static int __init setup_gus(char *str) +{ + /* io, irq, dma, dma2 */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + + return 1; +} + +__setup("gus=", setup_gus); +#endif diff -Nru linux/sound/oss/gus_hw.h linux-2.4.19-pre5-mjc/sound/oss/gus_hw.h --- linux/sound/oss/gus_hw.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/gus_hw.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,50 @@ + +/* + * I/O addresses + */ + +#define u_Base (gus_base + 0x000) +#define u_Mixer u_Base +#define u_Status (gus_base + 0x006) +#define u_TimerControl (gus_base + 0x008) +#define u_TimerData (gus_base + 0x009) +#define u_IRQDMAControl (gus_base + 0x00b) +#define u_MidiControl (gus_base + 0x100) +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 +#define u_MidiStatus u_MidiControl +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 +#define u_MidiData (gus_base + 0x101) +#define u_Voice (gus_base + 0x102) +#define u_Command (gus_base + 0x103) +#define u_DataLo (gus_base + 0x104) +#define u_DataHi (gus_base + 0x105) +#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ +#define u_MixSelect (gus_base + 0x506) /* registers. */ +#define u_IrqStatus u_Status +# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + +#define ICS2101 1 +# define ICS_MIXDEVS 6 +# define DEV_MIC 0 +# define DEV_LINE 1 +# define DEV_CD 2 +# define DEV_GF1 3 +# define DEV_UNUSED 4 +# define DEV_VOL 5 + +# define CHN_LEFT 0 +# define CHN_RIGHT 1 +#define CS4231 2 +#define u_DRAMIO (gus_base + 0x107) diff -Nru linux/sound/oss/gus_linearvol.h linux-2.4.19-pre5-mjc/sound/oss/gus_linearvol.h --- linux/sound/oss/gus_linearvol.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/gus_linearvol.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,18 @@ +static unsigned short gus_linearvol[128] = { + 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, + 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, + 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, + 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, + 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, + 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, + 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, + 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, + 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, + 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, + 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, + 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc +}; diff -Nru linux/sound/oss/gus_midi.c linux-2.4.19-pre5-mjc/sound/oss/gus_midi.c --- linux/sound/oss/gus_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/gus_midi.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,262 @@ +/* + * sound/gus2_midi.c + * + * The low level driver for the GUS Midi Interface. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to gus_midi_init() + */ + +#include +#include "sound_config.h" + +#include "gus.h" +#include "gus_hw.h" + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static int output_used = 0; +static volatile unsigned char gus_midi_control; + +static void (*midi_input_intr) (int dev, unsigned char data); + +static unsigned char tmp_queue[256]; +extern int gus_pnp_flag; +static volatile int qlen; +static volatile unsigned char qhead, qtail; +extern int gus_base, gus_irq, gus_dma; +extern int *gus_osp; + +static int GUS_MIDI_STATUS(void) +{ + return inb(u_MidiStatus); +} + +static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev)) +{ + if (midi_busy) + { +/* printk("GUS: Midi busy\n");*/ + return -EBUSY; + } + outb((MIDI_RESET), u_MidiControl); + gus_delay(); + + gus_midi_control = 0; + input_opened = 0; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + if (!gus_pnp_flag) + { + gus_midi_control |= MIDI_ENABLE_RCV; + input_opened = 1; + } + outb((gus_midi_control), u_MidiControl); /* Enable */ + + midi_busy = 1; + qlen = qhead = qtail = output_used = 0; + midi_input_intr = input; + + return 0; +} + +static int dump_to_midi(unsigned char midi_byte) +{ + unsigned long flags; + int ok = 0; + + output_used = 1; + + save_flags(flags); + cli(); + + if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY) + { + ok = 1; + outb((midi_byte), u_MidiData); + } + else + { + /* + * Enable Midi xmit interrupts (again) + */ + gus_midi_control |= MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + } + + restore_flags(flags); + return ok; +} + +static void gus_midi_close(int dev) +{ + /* + * Reset FIFO pointers, disable intrs + */ + + outb((MIDI_RESET), u_MidiControl); + midi_busy = 0; +} + +static int gus_midi_out(int dev, unsigned char midi_byte) +{ + unsigned long flags; + + /* + * Drain the local queue first + */ + + save_flags(flags); + cli(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + restore_flags(flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; /* + * OK + */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* + * Local queue full + */ + save_flags(flags); + cli(); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + restore_flags(flags); + return 1; +} + +static int gus_midi_start_read(int dev) +{ + return 0; +} + +static int gus_midi_end_read(int dev) +{ + return 0; +} + +static void gus_midi_kick(int dev) +{ +} + +static int gus_midi_buffer_status(int dev) +{ + unsigned long flags; + + if (!output_used) + return 0; + + save_flags(flags); + cli(); + + if (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + restore_flags(flags); + return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY); +} + +#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations gus_midi_operations = +{ + owner: THIS_MODULE, + info: {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, + converter: &std_midi_synth, + in_info: {0}, + open: gus_midi_open, + close: gus_midi_close, + outputc: gus_midi_out, + start_read: gus_midi_start_read, + end_read: gus_midi_end_read, + kick: gus_midi_kick, + buffer_status: gus_midi_buffer_status, +}; + +void __init gus_midi_init(struct address_info *hw_config) +{ + int dev = sound_alloc_mididev(); + + if (dev == -1) + { + printk(KERN_INFO "gus_midi: Too many midi devices detected\n"); + return; + } + outb((MIDI_RESET), u_MidiControl); + + std_midi_synth.midi_dev = my_dev = dev; + hw_config->slots[2] = dev; + midi_devs[dev] = &gus_midi_operations; + sequencer_init(); + return; +} + +void gus_midi_interrupt(int dummy) +{ + volatile unsigned char stat, data; + unsigned long flags; + int timeout = 10; + + save_flags(flags); + cli(); + + while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY)) + { + if (stat & MIDI_RCV_FULL) + { + data = inb(u_MidiData); + if (input_opened) + midi_input_intr(my_dev, data); + } + if (stat & MIDI_XMIT_EMPTY) + { + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + if (!qlen) + { + /* + * Disable Midi output interrupts, since no data in the buffer + */ + gus_midi_control &= ~MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + outb((gus_midi_control), u_MidiControl); + } + } + } + restore_flags(flags); +} diff -Nru linux/sound/oss/gus_vol.c linux-2.4.19-pre5-mjc/sound/oss/gus_vol.c --- linux/sound/oss/gus_vol.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/gus_vol.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,153 @@ + +/* + * gus_vol.c - Compute volume for GUS. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include "sound_config.h" + +#include "gus.h" +#include "gus_linearvol.h" + +#define GUS_VOLUME gus_wave_volume + + +extern int gus_wave_volume; + +/* + * Calculate gus volume from note velocity, main volume, expression, and + * intrinsic patch volume given in patch library. Expression is multiplied + * in, so it emphasizes differences in note velocity, while main volume is + * added in -- I don't know whether this is right, but it seems reasonable to + * me. (In the previous stage, main volume controller messages were changed + * to expression controller messages, if they were found to be used for + * dynamic volume adjustments, so here, main volume can be assumed to be + * constant throughout a song.) + * + * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so + * we can give a big boost to very weak voices like nylon guitar and the + * basses. The normal value is 64. Strings are assigned lower values. + */ + +unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev) +{ + int i, m, n, x; + + + /* + * A voice volume of 64 is considered neutral, so adjust the main volume if + * something other than this neutral value was assigned in the patch + * library. + */ + x = 256 + 6 * (voicev - 64); + + /* + * Boost expression by voice volume above neutral. + */ + + if (voicev > 65) + xpn += voicev - 64; + xpn += (voicev - 64) / 2; + + /* + * Combine multiplicative and level components. + */ + x = vel * xpn * 6 + (voicev / 4) * x; + +#ifdef GUS_VOLUME + /* + * Further adjustment by installation-specific master volume control + * (default 60). + */ + x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + /* + * Experimental support for the channel main volume + */ + + mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ + x = (x * mainv * mainv) / 16384; +#endif + + if (x < 2) + return (0); + else if (x >= 65535) + return ((15 << 8) | 255); + + /* + * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit + * mantissa m. + */ + + n = x; + i = 7; + if (n < 128) + { + while (i > 0 && n < (1 << i)) + i--; + } + else + { + while (n > 255) + { + n >>= 1; + i++; + } + } + /* + * Mantissa is part of linear volume not expressed in exponent. (This is + * not quite like real logs -- I wonder if it's right.) + */ + m = x - (1 << i); + + /* + * Adjust mantissa to 8 bits. + */ + if (m > 0) + { + if (i > 8) + m >>= i - 8; + else if (i < 8) + m <<= 8 - i; + } + return ((i << 8) + m); +} + +/* + * Volume-values are interpreted as linear values. Volume is based on the + * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) + * and the volume set by the mixer-device (default 60%). + */ + +unsigned short gus_linear_vol(int vol, int mainvol) +{ + int mixer_mainvol; + + if (vol <= 0) + vol = 0; + else if (vol >= 127) + vol = 127; + +#ifdef GUS_VOLUME + mixer_mainvol = GUS_VOLUME; +#else + mixer_mainvol = 100; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + if (mainvol <= 0) + mainvol = 0; + else if (mainvol >= 127) + mainvol = 127; +#else + mainvol = 127; +#endif + return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; +} diff -Nru linux/sound/oss/gus_wave.c linux-2.4.19-pre5-mjc/sound/oss/gus_wave.c --- linux/sound/oss/gus_wave.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/gus_wave.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,3540 @@ +/* + * sound/gus_wave.c + * + * Driver for the Gravis UltraSound wave table synth. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + * Bartlomiej Zolnierkiewicz : added some __init/__exit + */ + +#include +#include + +#define GUSPNP_AUTODETECT + +#include "sound_config.h" +#include + +#include "gus.h" +#include "gus_hw.h" + +#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) + +#define MAX_SAMPLE 150 +#define MAX_PATCH 256 + +#define NOT_SAMPLE 0xffff + +struct voice_info +{ + unsigned long orig_freq; + unsigned long current_freq; + unsigned long mode; + int fixed_pitch; + int bender; + int bender_range; + int panning; + int midi_volume; + unsigned int initial_volume; + unsigned int current_volume; + int loop_irq_mode, loop_irq_parm; +#define LMODE_FINISH 1 +#define LMODE_PCM 2 +#define LMODE_PCM_STOP 3 + int volume_irq_mode, volume_irq_parm; +#define VMODE_HALT 1 +#define VMODE_ENVELOPE 2 +#define VMODE_START_NOTE 3 + + int env_phase; + unsigned char env_rate[6]; + unsigned char env_offset[6]; + + /* + * Volume computation parameters for gus_adagio_vol() + */ + int main_vol, expression_vol, patch_vol; + + /* Variables for "Ultraclick" removal */ + int dev_pending, note_pending, volume_pending, + sample_pending; + char kill_pending; + long offset_pending; + +}; + +static struct voice_alloc_info *voice_alloc; +static struct address_info *gus_hw_config; +extern int gus_base; +extern int gus_irq, gus_dma; +extern int gus_pnp_flag; +extern int gus_no_wave_dma; +static int gus_dma2 = -1; +static int dual_dma_mode = 0; +static long gus_mem_size = 0; +static long free_mem_ptr = 0; +static int gus_busy = 0; +static int gus_no_dma = 0; +static int nr_voices = 0; +static int gus_devnum = 0; +static int volume_base, volume_scale, volume_method; +static int gus_recmask = SOUND_MASK_MIC; +static int recording_active = 0; +static int only_read_access = 0; +static int only_8_bits = 0; + +int iw_mode = 0; +int gus_wave_volume = 60; +int gus_pcm_volume = 80; +int have_gus_max = 0; +static int gus_line_vol = 100, gus_mic_vol = 0; +static unsigned char mix_image = 0x00; + +int gus_timer_enabled = 0; + +/* + * Current version of this driver doesn't allow synth and PCM functions + * at the same time. The active_device specifies the active driver + */ + +static int active_device = 0; + +#define GUS_DEV_WAVE 1 /* Wave table synth */ +#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ +#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ + +static int gus_audio_speed; +static int gus_audio_channels; +static int gus_audio_bits; +static int gus_audio_bsize; +static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */ + +static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper); + +/* + * Variables and buffers for PCM output + */ + +#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */ + +static int pcm_bsize, pcm_nblk, pcm_banksize; +static int pcm_datasize[MAX_PCM_BUFFERS]; +static volatile int pcm_head, pcm_tail, pcm_qlen; +static volatile int pcm_active; +static volatile int dma_active; +static int pcm_opened = 0; +static int pcm_current_dev; +static int pcm_current_block; +static unsigned long pcm_current_buf; +static int pcm_current_count; +static int pcm_current_intrflag; + +extern int *gus_osp; + +static struct voice_info voices[32]; + +static int freq_div_table[] = +{ + 44100, /* 14 */ + 41160, /* 15 */ + 38587, /* 16 */ + 36317, /* 17 */ + 34300, /* 18 */ + 32494, /* 19 */ + 30870, /* 20 */ + 29400, /* 21 */ + 28063, /* 22 */ + 26843, /* 23 */ + 25725, /* 24 */ + 24696, /* 25 */ + 23746, /* 26 */ + 22866, /* 27 */ + 22050, /* 28 */ + 21289, /* 29 */ + 20580, /* 30 */ + 19916, /* 31 */ + 19293 /* 32 */ +}; + +static struct patch_info *samples = NULL; +static long sample_ptrs[MAX_SAMPLE + 1]; +static int sample_map[32]; +static int free_sample; +static int mixer_type = 0; + + +static int patch_table[MAX_PATCH]; +static int patch_map[32]; + +static struct synth_info gus_info = { + "Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, + 0, 16, 0, MAX_PATCH +}; + +static void gus_poke(long addr, unsigned char data); +static void compute_and_set_volume(int voice, int volume, int ramp_time); +extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); +extern unsigned short gus_linear_vol(int vol, int mainvol); +static void compute_volume(int voice, int volume); +static void do_volume_irq(int voice); +static void set_input_volumes(void); +static void gus_tmr_install(int io_base); + +#define INSTANT_RAMP -1 /* Instant change. No ramping */ +#define FAST_RAMP 0 /* Fastest possible ramp */ + +static void reset_sample_memory(void) +{ + int i; + + for (i = 0; i <= MAX_SAMPLE; i++) + sample_ptrs[i] = -1; + for (i = 0; i < 32; i++) + sample_map[i] = -1; + for (i = 0; i < 32; i++) + patch_map[i] = -1; + + gus_poke(0, 0); /* Put a silent sample to the beginning */ + gus_poke(1, 0); + free_mem_ptr = 2; + + free_sample = 0; + + for (i = 0; i < MAX_PATCH; i++) + patch_table[i] = NOT_SAMPLE; +} + +void gus_delay(void) +{ + int i; + + for (i = 0; i < 7; i++) + inb(u_DRAMIO); +} + +static void gus_poke(long addr, unsigned char data) +{ /* Writes a byte to the DRAM */ + unsigned long flags; + + save_flags(flags); + cli(); + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); + + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + outb((data), u_DRAMIO); + restore_flags(flags); +} + +static unsigned char gus_peek(long addr) +{ /* Reads a byte from the DRAM */ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); + + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + tmp = inb(u_DRAMIO); + restore_flags(flags); + + return tmp; +} + +void gus_write8(int reg, unsigned int data) +{ /* Writes to an indirect register (8 bit) */ + unsigned long flags; + + save_flags(flags); + cli(); + + outb((reg), u_Command); + outb(((unsigned char) (data & 0xff)), u_DataHi); + + restore_flags(flags); +} + +static unsigned char gus_read8(int reg) +{ + /* Reads from an indirect register (8 bit). Offset 0x80. */ + unsigned long flags; + unsigned char val; + + save_flags(flags); + cli(); + outb((reg | 0x80), u_Command); + val = inb(u_DataHi); + restore_flags(flags); + + return val; +} + +static unsigned char gus_look8(int reg) +{ + /* Reads from an indirect register (8 bit). No additional offset. */ + unsigned long flags; + unsigned char val; + + save_flags(flags); + cli(); + outb((reg), u_Command); + val = inb(u_DataHi); + restore_flags(flags); + + return val; +} + +static void gus_write16(int reg, unsigned int data) +{ + /* Writes to an indirect register (16 bit) */ + unsigned long flags; + + save_flags(flags); + cli(); + + outb((reg), u_Command); + + outb(((unsigned char) (data & 0xff)), u_DataLo); + outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi); + + restore_flags(flags); +} + +static unsigned short gus_read16(int reg) +{ + /* Reads from an indirect register (16 bit). Offset 0x80. */ + unsigned long flags; + unsigned char hi, lo; + + save_flags(flags); + cli(); + + outb((reg | 0x80), u_Command); + + lo = inb(u_DataLo); + hi = inb(u_DataHi); + + restore_flags(flags); + + return ((hi << 8) & 0xff00) | lo; +} + +static unsigned short gus_look16(int reg) +{ + /* Reads from an indirect register (16 bit). No additional offset. */ + unsigned long flags; + unsigned char hi, lo; + + save_flags(flags); + cli(); + + outb((reg), u_Command); + + lo = inb(u_DataLo); + hi = inb(u_DataHi); + + restore_flags(flags); + + return ((hi << 8) & 0xff00) | lo; +} + +static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit) +{ + /* Writes an 24 bit memory address */ + unsigned long hold_address; + unsigned long flags; + + save_flags(flags); + cli(); + if (is16bit) + { + if (iw_mode) + { + /* Interwave spesific address translations */ + address >>= 1; + } + else + { + /* + * Special processing required for 16 bit patches + */ + + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + } + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); + /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ + gus_delay(); + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); + restore_flags(flags); +} + +static void gus_select_voice(int voice) +{ + if (voice < 0 || voice > 31) + return; + outb((voice), u_Voice); +} + +static void gus_select_max_voices(int nvoices) +{ + if (iw_mode) + nvoices = 32; + if (nvoices < 14) + nvoices = 14; + if (nvoices > 32) + nvoices = 32; + + voice_alloc->max_voice = nr_voices = nvoices; + gus_write8(0x0e, (nvoices - 1) | 0xc0); +} + +static void gus_voice_on(unsigned int mode) +{ + gus_write8(0x00, (unsigned char) (mode & 0xfc)); + gus_delay(); + gus_write8(0x00, (unsigned char) (mode & 0xfc)); +} + +static void gus_voice_off(void) +{ + gus_write8(0x00, gus_read8(0x00) | 0x03); +} + +static void gus_voice_mode(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x00, (gus_read8(0x00) & 0x03) | + (mode & 0xfc)); /* Don't touch last two bits */ + gus_delay(); + gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc)); +} + +static void gus_voice_freq(unsigned long freq) +{ + unsigned long divisor = freq_div_table[nr_voices - 14]; + unsigned short fc; + + /* Interwave plays at 44100 Hz with any number of voices */ + if (iw_mode) + fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100); + else + fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); + fc = fc << 1; + + gus_write16(0x01, fc); +} + +static void gus_voice_volume(unsigned int vol) +{ + gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */ + gus_write16(0x09, (unsigned short) (vol << 4)); +} + +static void gus_voice_balance(unsigned int balance) +{ + gus_write8(0x0c, (unsigned char) (balance & 0xff)); +} + +static void gus_ramp_range(unsigned int low, unsigned int high) +{ + gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff)); + gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff)); +} + +static void gus_ramp_rate(unsigned int scale, unsigned int rate) +{ + gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); +} + +static void gus_rampon(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x0d, mode & 0xfc); + gus_delay(); + gus_write8(0x0d, mode & 0xfc); +} + +static void gus_ramp_mode(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | + (mode & 0xfc)); /* Leave the last 2 bits alone */ + gus_delay(); + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc)); +} + +static void gus_rampoff(void) +{ + gus_write8(0x0d, 0x03); +} + +static void gus_set_voice_pos(int voice, long position) +{ + int sample_no; + + if ((sample_no = sample_map[voice]) != -1) { + if (position < samples[sample_no].len) { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].offset_pending = position; + else + gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0, + samples[sample_no].mode & WAVE_16_BITS); + } + } +} + +static void gus_voice_init(int voice) +{ + unsigned long flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_volume(0); + gus_voice_off(); + gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */ + gus_write8(0x00, 0x03); /* Voice off */ + gus_write8(0x0d, 0x03); /* Ramping off */ + voice_alloc->map[voice] = 0; + voice_alloc->alloc_times[voice] = 0; + restore_flags(flags); + +} + +static void gus_voice_init2(int voice) +{ + voices[voice].panning = 0; + voices[voice].mode = 0; + voices[voice].orig_freq = 20000; + voices[voice].current_freq = 20000; + voices[voice].bender = 0; + voices[voice].bender_range = 200; + voices[voice].initial_volume = 0; + voices[voice].current_volume = 0; + voices[voice].loop_irq_mode = 0; + voices[voice].loop_irq_parm = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].volume_irq_parm = 0; + voices[voice].env_phase = 0; + voices[voice].main_vol = 127; + voices[voice].patch_vol = 127; + voices[voice].expression_vol = 127; + voices[voice].sample_pending = -1; + voices[voice].fixed_pitch = 0; +} + +static void step_envelope(int voice) +{ + unsigned vol, prev_vol, phase; + unsigned char rate; + long int flags; + + if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) + { + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + restore_flags(flags); + return; + /* + * Sustain phase begins. Continue envelope after receiving note off. + */ + } + if (voices[voice].env_phase >= 5) + { + /* Envelope finished. Shoot the voice down */ + gus_voice_init(voice); + return; + } + prev_vol = voices[voice].current_volume; + phase = ++voices[voice].env_phase; + compute_volume(voice, voices[voice].midi_volume); + vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; + rate = voices[voice].env_rate[phase]; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + gus_voice_volume(prev_vol); + + + gus_write8(0x06, rate); /* Ramping rate */ + + voices[voice].volume_irq_mode = VMODE_ENVELOPE; + + if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ + { + restore_flags(flags); + step_envelope(voice); /* Continue the envelope on the next step */ + return; + } + if (vol > prev_vol) + { + if (vol >= (4096 - 64)) + vol = 4096 - 65; + gus_ramp_range(0, vol); + gus_rampon(0x20); /* Increasing volume, with IRQ */ + } + else + { + if (vol <= 64) + vol = 65; + gus_ramp_range(vol, 4030); + gus_rampon(0x60); /* Decreasing volume, with IRQ */ + } + voices[voice].current_volume = vol; + restore_flags(flags); +} + +static void init_envelope(int voice) +{ + voices[voice].env_phase = -1; + voices[voice].current_volume = 64; + + step_envelope(voice); +} + +static void start_release(int voice, long int flags) +{ + if (gus_read8(0x00) & 0x03) + return; /* Voice already stopped */ + + voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ + + voices[voice].current_volume = voices[voice].initial_volume = + gus_read16(0x09) >> 4; /* Get current volume */ + + voices[voice].mode &= ~WAVE_SUSTAIN_ON; + gus_rampoff(); + restore_flags(flags); + step_envelope(voice); +} + +static void gus_voice_fade(int voice) +{ + int instr_no = sample_map[voice], is16bits; + long int flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + if (instr_no < 0 || instr_no > MAX_SAMPLE) + { + gus_write8(0x00, 0x03); /* Hard stop */ + voice_alloc->map[voice] = 0; + restore_flags(flags); + return; + } + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + start_release(voice, flags); + restore_flags(flags); + return; + } + /* + * Ramp the volume down but not too quickly. + */ + if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + restore_flags(flags); + return; + } + gus_ramp_range(65, 4030); + gus_ramp_rate(2, 4); + gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */ + voices[voice].volume_irq_mode = VMODE_HALT; + restore_flags(flags); +} + +static void gus_reset(void) +{ + int i; + + gus_select_max_voices(24); + volume_base = 3071; + volume_scale = 4; + volume_method = VOL_METHOD_ADAGIO; + + for (i = 0; i < 32; i++) + { + gus_voice_init(i); /* Turn voice off */ + gus_voice_init2(i); + } +} + +static void gus_initialize(void) +{ + unsigned long flags; + unsigned char dma_image, irq_image, tmp; + + static unsigned char gus_irq_map[16] = { + 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 + }; + + static unsigned char gus_dma_map[8] = { + 0, 1, 0, 2, 0, 3, 4, 5 + }; + + save_flags(flags); + cli(); + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + + /* + * Clear all interrupts + */ + + gus_write8(0x41, 0); /* DMA control */ + gus_write8(0x45, 0); /* Timer control */ + gus_write8(0x49, 0); /* Sample control */ + + gus_select_max_voices(24); + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_reset(); /* Resets all voices */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + + /* + * Set up for Digital ASIC + */ + + outb((0x05), gus_base + 0x0f); + + mix_image |= 0x02; /* Disable line out (for a moment) */ + outb((mix_image), u_Mixer); + + outb((0x00), u_IRQDMAControl); + + outb((0x00), gus_base + 0x0f); + + /* + * Now set up the DMA and IRQ interface + * + * The GUS supports two IRQs and two DMAs. + * + * Just one DMA channel is used. This prevents simultaneous ADC and DAC. + * Adding this support requires significant changes to the dmabuf.c, dsp.c + * and audio.c also. + */ + + irq_image = 0; + tmp = gus_irq_map[gus_irq]; + if (!gus_pnp_flag && !tmp) + printk(KERN_WARNING "Warning! GUS IRQ not selected\n"); + irq_image |= tmp; + irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ + + dual_dma_mode = 1; + if (gus_dma2 == gus_dma || gus_dma2 == -1) + { + dual_dma_mode = 0; + dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ + + tmp = gus_dma_map[gus_dma]; + if (!tmp) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + dma_image |= tmp; + } + else + { + /* Setup dual DMA channel mode for GUS MAX */ + + dma_image = gus_dma_map[gus_dma]; + if (!dma_image) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + tmp = gus_dma_map[gus_dma2] << 3; + if (!tmp) + { + printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n"); + tmp = 0x40; /* Combine DMA channels */ + dual_dma_mode = 0; + } + dma_image |= tmp; + } + + /* + * For some reason the IRQ and DMA addresses must be written twice + */ + + /* + * Doing it first time + */ + + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */ + + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ + + /* + * Doing it second time + */ + + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image), u_IRQDMAControl); /* Set DMA address */ + + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + mix_image &= ~0x02; /* Enable line out */ + mix_image |= 0x08; /* Enable IRQ */ + outb((mix_image), u_Mixer); /* + * Turn mixer channels on + * Note! Mic in is left off. + */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + + gus_read8(0x0f); /* Clear pending IRQs */ + + if (iw_mode) + gus_write8(0x19, gus_read8(0x19) | 0x01); + restore_flags(flags); +} + + +static void __init pnp_mem_init(void) +{ +#include "iwmem.h" +#define CHUNK_SIZE (256*1024) +#define BANK_SIZE (4*1024*1024) +#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE) + + int bank, chunk, addr, total = 0; + int bank_sizes[4]; + int i, j, bits = -1, testbits = -1, nbanks = 0; + + /* + * This routine determines what kind of RAM is installed in each of the four + * SIMM banks and configures the DRAM address decode logic accordingly. + */ + + /* + * Place the chip into enhanced mode + */ + gus_write8(0x19, gus_read8(0x19) | 0x01); + gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */ + + /* + * Set memory configuration to 4 DRAM banks of 4M in each (16M total). + */ + + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c); + + /* + * Perform the DRAM size detection for each bank individually. + */ + for (bank = 0; bank < 4; bank++) + { + int size = 0; + + addr = bank * BANK_SIZE; + + /* Clean check points of each chunk */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + + /* Write a value to each chunk point and verify the result */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA); + + if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 && + gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA) + { + /* OK. There is RAM. Now check for possible shadows */ + int ok = 1, chunk2; + + for (chunk2 = 0; ok && chunk2 < chunk; chunk2++) + if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) || + gus_peek(addr + chunk2 * CHUNK_SIZE + 1L)) + ok = 0; /* Addressing wraps */ + + if (ok) + size = (chunk + 1) * CHUNK_SIZE; + } + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + bank_sizes[bank] = size; + if (size) + nbanks = bank + 1; + DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024)); + } + + if (nbanks == 0) /* No RAM - Give up */ + { + printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n"); + printk(KERN_ERR "Sound: Unable to work with this card.\n"); + gus_write8(0x19, gus_read8(0x19) & ~0x01); + gus_mem_size = 0; + return; + } + + /* + * Now we know how much DRAM there is in each bank. The next step is + * to find a DRAM size encoding (0 to 12) which is best for the combination + * we have. + * + * First try if any of the possible alternatives matches exactly the amount + * of memory we have. + */ + + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < 4; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + } + + /* + * If necessary, try to find a combination where other than the last + * bank matches our configuration and the last bank is left oversized. + * In this way we don't leave holes in the middle of memory. + */ + + if (bits == -1) /* No luck yet */ + { + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1]) + bits = -1; /* The last bank is too small */ + } + } + /* + * The last resort is to search for a combination where the banks are + * smaller than the actual SIMMs. This leaves some memory in the banks + * unused but doesn't leave holes in the DRAM address space. + */ + if (bits == -1) /* No luck yet */ + { + for (i = 0; i < 13; i++) + { + testbits = i; + for (j = 0; testbits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] > bank_sizes[j]) { + testbits = -1; + } + if(testbits > bits) bits = testbits; + } + if (bits != -1) + { + printk(KERN_INFO "Interwave: Can't use all installed RAM.\n"); + printk(KERN_INFO "Interwave: Try reordering SIMMS.\n"); + } + printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n"); + printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n"); + bits = 0; + } + DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits)); + + for (bank = 0; bank < 4; bank++) + { + DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024)); + + if (bank_sizes[bank] > mem_decode[bits][bank]) + total += mem_decode[bits][bank]; + else + total += bank_sizes[bank]; + } + + DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024)); + + /* + * Set the memory addressing mode. + */ + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits); + +/* Leave the chip into enhanced mode. Disable LFO */ + gus_mem_size = total; + iw_mode = 1; + gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02); +} + +int __init gus_wave_detect(int baseaddr) +{ + unsigned long i, max_mem = 1024L; + unsigned long loc; + unsigned char val; + + gus_base = baseaddr; + + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + +#ifdef GUSPNP_AUTODETECT + val = gus_look8(0x5b); /* Version number register */ + gus_write8(0x5b, ~val); /* Invert all bits */ + + if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */ + { + if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */ + { + DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4)); + gus_pnp_flag = 1; + } + else + { + DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b))); + gus_pnp_flag = 0; + } + } + gus_write8(0x5b, val); /* Restore all bits */ +#endif + + if (gus_pnp_flag) + pnp_mem_init(); + if (iw_mode) + return 1; + + /* See if there is first block there.... */ + gus_poke(0L, 0xaa); + if (gus_peek(0L) != 0xaa) + return (0); + + /* Now zero it out so that I can check for mirroring .. */ + gus_poke(0L, 0x00); + for (i = 1L; i < max_mem; i++) + { + int n, failed; + + /* check for mirroring ... */ + if (gus_peek(0L) != 0) + break; + loc = i << 10; + + for (n = loc - 1, failed = 0; n <= loc; n++) + { + gus_poke(loc, 0xaa); + if (gus_peek(loc) != 0xaa) + failed = 1; + gus_poke(loc, 0x55); + if (gus_peek(loc) != 0x55) + failed = 1; + } + if (failed) + break; + } + gus_mem_size = i << 10; + return 1; +} + +static int guswave_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + + switch (cmd) + { + case SNDCTL_SYNTH_INFO: + gus_info.nr_voices = nr_voices; + if (copy_to_user(arg, &gus_info, sizeof(gus_info))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_RESETSAMPLES: + reset_sample_memory(); + return 0; + + case SNDCTL_SEQ_PERCMODE: + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32; + + default: + return -EINVAL; + } +} + +static int guswave_set_instr(int dev, int voice, int instr_no) +{ + int sample_no; + + if (instr_no < 0 || instr_no > MAX_PATCH) + instr_no = 0; /* Default to acoustic piano */ + + if (voice < 0 || voice > 31) + return -EINVAL; + + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].sample_pending = instr_no; + return 0; + } + sample_no = patch_table[instr_no]; + patch_map[voice] = -1; + + if (sample_no == NOT_SAMPLE) + { +/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/ + return -EINVAL; /* Patch not defined */ + } + if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ + { +/* printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/ + return -EINVAL; + } + sample_map[voice] = sample_no; + patch_map[voice] = instr_no; + return 0; +} + +static int guswave_kill_note(int dev, int voice, int note, int velocity) +{ + unsigned long flags; + + save_flags(flags); + cli(); + /* voice_alloc->map[voice] = 0xffff; */ + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].kill_pending = 1; + restore_flags(flags); + } + else + { + restore_flags(flags); + gus_voice_fade(voice); + } + + return 0; +} + +static void guswave_aftertouch(int dev, int voice, int pressure) +{ +} + +static void guswave_panning(int dev, int voice, int value) +{ + if (voice >= 0 || voice < 32) + voices[voice].panning = value; +} + +static void guswave_volume_method(int dev, int mode) +{ + if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) + volume_method = mode; +} + +static void compute_volume(int voice, int volume) +{ + if (volume < 128) + voices[voice].midi_volume = volume; + + switch (volume_method) + { + case VOL_METHOD_ADAGIO: + voices[voice].initial_volume = + gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol, + voices[voice].expression_vol, + voices[voice].patch_vol); + break; + + case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ + voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol); + break; + + default: + voices[voice].initial_volume = volume_base + + (voices[voice].midi_volume * volume_scale); + } + + if (voices[voice].initial_volume > 4030) + voices[voice].initial_volume = 4030; +} + +static void compute_and_set_volume(int voice, int volume, int ramp_time) +{ + int curr, target, rate; + unsigned long flags; + + compute_volume(voice, volume); + voices[voice].current_volume = voices[voice].initial_volume; + + save_flags(flags); + cli(); + /* + * CAUTION! Interrupts disabled. Enable them before returning + */ + + gus_select_voice(voice); + + curr = gus_read16(0x09) >> 4; + target = voices[voice].initial_volume; + + if (ramp_time == INSTANT_RAMP) + { + gus_rampoff(); + gus_voice_volume(target); + restore_flags(flags); + return; + } + if (ramp_time == FAST_RAMP) + rate = 63; + else + rate = 16; + gus_ramp_rate(0, rate); + + if ((target - curr) / 64 == 0) /* Close enough to target. */ + { + gus_rampoff(); + gus_voice_volume(target); + restore_flags(flags); + return; + } + if (target > curr) + { + if (target > (4095 - 65)) + target = 4095 - 65; + gus_ramp_range(curr, target); + gus_rampon(0x00); /* Ramp up, once, no IRQ */ + } + else + { + if (target < 65) + target = 65; + + gus_ramp_range(target, curr); + gus_rampon(0x40); /* Ramp down, once, no irq */ + } + restore_flags(flags); +} + +static void dynamic_volume_change(int voice) +{ + unsigned char status; + unsigned long flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + status = gus_read8(0x00); /* Get voice status */ + restore_flags(flags); + + if (status & 0x03) + return; /* Voice was not running */ + + if (!(voices[voice].mode & WAVE_ENVELOPES)) + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + + /* + * Voice is running and has envelopes. + */ + + save_flags(flags); + cli(); + gus_select_voice(voice); + status = gus_read8(0x0d); /* Ramping status */ + restore_flags(flags); + + if (status & 0x03) /* Sustain phase? */ + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + if (voices[voice].env_phase < 0) + return; + + compute_volume(voice, voices[voice].midi_volume); + +} + +static void guswave_controller(int dev, int voice, int ctrl_num, int value) +{ + unsigned long flags; + unsigned long freq; + + if (voice < 0 || voice > 31) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + voices[voice].bender = value; + + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + { + freq = compute_finetune(voices[voice].orig_freq, value, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(freq); + restore_flags(flags); + } + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + case CTL_EXPRESSION: + value /= 128; + case CTRL_EXPRESSION: + if (volume_method == VOL_METHOD_ADAGIO) + { + voices[voice].expression_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + } + break; + + case CTL_PAN: + voices[voice].panning = (value * 2) - 128; + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; + + case CTRL_MAIN_VOLUME: + voices[voice].main_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + break; + + default: + break; + } +} + +static int guswave_start_note2(int dev, int voice, int note_num, int volume) +{ + int sample, best_sample, best_delta, delta_freq; + int is16bits, samplep, patch, pan; + unsigned long note_freq, base_note, freq, flags; + unsigned char mode = 0; + + if (voice < 0 || voice > 31) + { +/* printk("GUS: Invalid voice\n");*/ + return -EINVAL; + } + if (note_num == 255) + { + if (voices[voice].mode & WAVE_ENVELOPES) + { + voices[voice].midi_volume = volume; + dynamic_volume_change(voice); + return 0; + } + compute_and_set_volume(voice, volume, 1); + return 0; + } + if ((patch = patch_map[voice]) == -1) + return -EINVAL; + if ((samplep = patch_table[patch]) == NOT_SAMPLE) + { + return -EINVAL; + } + note_freq = note_to_freq(note_num); + + /* + * Find a sample within a patch so that the note_freq is between low_note + * and high_note. + */ + sample = -1; + + best_sample = samplep; + best_delta = 1000000; + while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) + { + delta_freq = note_freq - samples[samplep].base_note; + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = samplep; + best_delta = delta_freq; + } + if (samples[samplep].low_note <= note_freq && + note_freq <= samples[samplep].high_note) + { + sample = samplep; + } + else + samplep = samples[samplep].key; /* Link to next sample */ + } + if (sample == -1) + sample = best_sample; + + if (sample == -1) + { +/* printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/ + return 0; /* Should play default patch ??? */ + } + is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; + voices[voice].mode = samples[sample].mode; + voices[voice].patch_vol = samples[sample].volume; + + if (iw_mode) + gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + int i; + + for (i = 0; i < 6; i++) + { + voices[voice].env_rate[i] = samples[sample].env_rate[i]; + voices[voice].env_offset[i] = samples[sample].env_offset[i]; + } + } + sample_map[voice] = sample; + + if (voices[voice].fixed_pitch) /* Fixed pitch */ + { + freq = samples[sample].base_freq; + } + else + { + base_note = samples[sample].base_note / 100; + note_freq /= 100; + + freq = samples[sample].base_freq * note_freq / base_note; + } + + voices[voice].orig_freq = freq; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender, + voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + pan = (samples[sample].panning + voices[voice].panning) / 32; + pan += 7; + if (pan < 0) + pan = 0; + if (pan > 15) + pan = 15; + + if (samples[sample].mode & WAVE_16_BITS) + { + mode |= 0x04; /* 16 bits */ + if ((sample_ptrs[sample] / GUS_BANK_SIZE) != + ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE)) + printk(KERN_ERR "GUS: Sample address error\n"); + } + /************************************************************************* + * CAUTION! Interrupts disabled. Don't return before enabling + *************************************************************************/ + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_off(); + gus_rampoff(); + + restore_flags(flags); + + if (voices[voice].mode & WAVE_ENVELOPES) + { + compute_volume(voice, volume); + init_envelope(voice); + } + else + { + compute_and_set_volume(voice, volume, 0); + } + + save_flags(flags); + cli(); + gus_select_voice(voice); + + if (samples[sample].mode & WAVE_LOOP_BACK) + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len - + voices[voice].offset_pending, 0, is16bits); /* start=end */ + else + gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */ + + if (samples[sample].mode & WAVE_LOOPING) + { + mode |= 0x08; + + if (samples[sample].mode & WAVE_BIDIR_LOOP) + mode |= 0x10; + + if (samples[sample].mode & WAVE_LOOP_BACK) + { + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end - + voices[voice].offset_pending, + (samples[sample].fractions >> 4) & 0x0f, is16bits); + mode |= 0x40; + } + gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, + samples[sample].fractions & 0x0f, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + else + { + mode |= 0x20; /* Loop IRQ at the end */ + voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ + voices[voice].loop_irq_parm = 1; + gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + gus_voice_freq(freq); + gus_voice_balance(pan); + gus_voice_on(mode); + restore_flags(flags); + + return 0; +} + +/* + * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking + * when the note playing on the voice is changed. It uses volume + * ramping. + */ + +static int guswave_start_note(int dev, int voice, int note_num, int volume) +{ + long int flags; + int mode; + int ret_val = 0; + + save_flags(flags); + cli(); + if (note_num == 255) + { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].volume_pending = volume; + } + else + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + } + else + { + gus_select_voice(voice); + mode = gus_read8(0x00); + if (mode & 0x20) + gus_write8(0x00, mode & 0xdf); /* No interrupt! */ + + voices[voice].offset_pending = 0; + voices[voice].kill_pending = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].loop_irq_mode = 0; + + if (voices[voice].sample_pending >= 0) + { + restore_flags(flags); /* Run temporarily with interrupts enabled */ + guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending); + voices[voice].sample_pending = -1; + save_flags(flags); + cli(); + gus_select_voice(voice); /* Reselect the voice (just to be sure) */ + } + if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065)) + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + else + { + voices[voice].dev_pending = dev; + voices[voice].note_pending = note_num; + voices[voice].volume_pending = volume; + voices[voice].volume_irq_mode = VMODE_START_NOTE; + + gus_rampoff(); + gus_ramp_range(2000, 4065); + gus_ramp_rate(0, 63); /* Fastest possible rate */ + gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */ + } + } + restore_flags(flags); + return ret_val; +} + +static void guswave_reset(int dev) +{ + int i; + + for (i = 0; i < 32; i++) + { + gus_voice_init(i); + gus_voice_init2(i); + } +} + +static int guswave_open(int dev, int mode) +{ + int err; + + if (gus_busy) + return -EBUSY; + + voice_alloc->timestamp = 0; + + if (gus_no_wave_dma) { + gus_no_dma = 1; + } else { + if ((err = DMAbuf_open_dma(gus_devnum)) < 0) + { + /* printk( "GUS: Loading samples without DMA\n"); */ + gus_no_dma = 1; /* Upload samples using PIO */ + } + else + gus_no_dma = 0; + } + + init_waitqueue_head(&dram_sleeper); + gus_busy = 1; + active_device = GUS_DEV_WAVE; + + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + gus_initialize(); + gus_reset(); + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + + return 0; +} + +static void guswave_close(int dev) +{ + gus_busy = 0; + active_device = 0; + gus_reset(); + + if (!gus_no_dma) + DMAbuf_close_dma(gus_devnum); +} + +static int guswave_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info patch; + int instr; + long sizeof_patch; + + unsigned long blk_sz, blk_end, left, src_offs, target; + + sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ + + if (format != GUS_PATCH) + { +/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < sizeof_patch) + { +/* printk("GUS Error: Patch header too short\n");*/ + return -EINVAL; + } + count -= sizeof_patch; + + if (free_sample >= MAX_SAMPLE) + { +/* printk("GUS: Sample table full\n");*/ + return -ENOSPC; + } + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + copy_from_user(&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs); + + if (patch.mode & WAVE_ROM) + return -EINVAL; + if (gus_mem_size == 0) + return -ENOSPC; + + instr = patch.instr_no; + + if (instr < 0 || instr > MAX_PATCH) + { +/* printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/ + return -EINVAL; + } + if (count < patch.len) + { +/* printk(KERN_ERR "GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);*/ + patch.len = count; + } + if (patch.len <= 0 || patch.len > gus_mem_size) + { +/* printk(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/ + return -EINVAL; + } + if (patch.mode & WAVE_LOOPING) + { + if (patch.loop_start < 0 || patch.loop_start >= patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop start\n");*/ + return -EINVAL; + } + if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop end\n");*/ + return -EINVAL; + } + } + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ + + if (patch.mode & WAVE_16_BITS) + { + /* + * 16 bit samples must fit one 256k bank. + */ + if (patch.len >= GUS_BANK_SIZE) + { +/* printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);*/ + return -ENOSPC; + } + if ((free_mem_ptr / GUS_BANK_SIZE) != + ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) + { + unsigned long tmp_mem = + /* Align to 256K */ + ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; + + if ((tmp_mem + patch.len) > gus_mem_size) + return -ENOSPC; + + free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + } + } + if ((free_mem_ptr + patch.len) > gus_mem_size) + return -ENOSPC; + + sample_ptrs[free_sample] = free_mem_ptr; + + /* + * Tremolo is not possible with envelopes + */ + + if (patch.mode & WAVE_ENVELOPES) + patch.mode &= ~WAVE_TREMOLO; + + if (!(patch.mode & WAVE_FRACTIONS)) + { + patch.fractions = 0; + } + memcpy((char *) &samples[free_sample], &patch, sizeof_patch); + + /* + * Link this_one sample to the list of samples for patch 'instr'. + */ + + samples[free_sample].key = patch_table[instr]; + patch_table[instr] = free_sample; + + /* + * Use DMA to transfer the wave data to the DRAM + */ + + left = patch.len; + src_offs = 0; + target = free_mem_ptr; + + while (left) /* Not completely transferred yet */ + { + blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use; + if (blk_sz > left) + blk_sz = left; + + /* + * DMA cannot cross bank (256k) boundaries. Check for that. + */ + + blk_end = target + blk_sz; + + if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE)) + { + /* Split the block */ + blk_end &= ~(GUS_BANK_SIZE - 1); + blk_sz = blk_end - target; + } + if (gus_no_dma) + { + /* + * For some reason the DMA is not possible. We have to use PIO. + */ + long i; + unsigned char data; + + for (i = 0; i < blk_sz; i++) + { + get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i])); + if (patch.mode & WAVE_UNSIGNED) + if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) + data ^= 0x80; /* Convert to signed */ + gus_poke(target + i, data); + } + } + else + { + unsigned long address, hold_address; + unsigned char dma_command; + unsigned long flags; + + if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL) + { + printk(KERN_ERR "GUS: DMA buffer == NULL\n"); + return -ENOSPC; + } + /* + * OK, move now. First in and then out. + */ + + copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz); + + save_flags(flags); + cli(); + /******** INTERRUPTS DISABLED NOW ********/ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys, + blk_sz, DMA_MODE_WRITE); + + /* + * Set the DRAM address for the wave data + */ + + if (iw_mode) + { + /* Different address translation in enhanced mode */ + + unsigned char hi; + + if (gus_dma > 4) + address = target >> 1; /* Convert to 16 bit word address */ + else + address = target; + + hi = (unsigned char) ((address >> 16) & 0xf0); + hi += (unsigned char) (address & 0x0f); + + gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */ + gus_write8(0x50, hi); + } + else + { + address = target; + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + } + + /* + * Start the DMA transfer + */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + if (patch.mode & WAVE_UNSIGNED) + dma_command |= 0x80; /* Invert MSB */ + if (patch.mode & WAVE_16_BITS) + dma_command |= 0x40; /* 16 bit _DATA_ */ + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA _channel_ */ + + gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */ + + /* + * Sleep here until the DRAM DMA done interrupt is served + */ + active_device = GUS_DEV_WAVE; + + if (!interruptible_sleep_on_timeout(&dram_sleeper, HZ)) + printk("GUS: DMA Transfer timed out\n"); + restore_flags(flags); + } + + /* + * Now the next part + */ + + left -= blk_sz; + src_offs += blk_sz; + target += blk_sz; + + gus_write8(0x41, 0); /* Stop DMA */ + } + + free_mem_ptr += patch.len; + free_sample++; + return 0; +} + +static void guswave_hw_control(int dev, unsigned char *event_rec) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned int plong; + unsigned long flags; + + cmd = event_rec[2]; + voice = event_rec[3]; + p1 = *(unsigned short *) &event_rec[4]; + p2 = *(unsigned short *) &event_rec[6]; + plong = *(unsigned int *) &event_rec[4]; + + if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && + (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) + do_volume_irq(voice); + + switch (cmd) + { + case _GUS_NUMVOICES: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_select_max_voices(p1); + restore_flags(flags); + break; + + case _GUS_VOICESAMPLE: + guswave_set_instr(dev, voice, p1); + break; + + case _GUS_VOICEON: + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_on(p1); + restore_flags(flags); + break; + + case _GUS_VOICEOFF: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_off(); + restore_flags(flags); + break; + + case _GUS_VOICEFADE: + gus_voice_fade(voice); + break; + + case _GUS_VOICEMODE: + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_mode(p1); + restore_flags(flags); + break; + + case _GUS_VOICEBALA: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_balance(p1); + restore_flags(flags); + break; + + case _GUS_VOICEFREQ: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(plong); + restore_flags(flags); + break; + + case _GUS_VOICEVOL: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_volume(p1); + restore_flags(flags); + break; + + case _GUS_VOICEVOL2: /* Just update the software voice level */ + voices[voice].initial_volume = voices[voice].current_volume = p1; + break; + + case _GUS_RAMPRANGE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_ramp_range(p1, p2); + restore_flags(flags); + break; + + case _GUS_RAMPRATE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NJET-NJET */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_ramp_rate(p1, p2); + restore_flags(flags); + break; + + case _GUS_RAMPMODE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_ramp_mode(p1); + restore_flags(flags); + break; + + case _GUS_RAMPON: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* EI-EI */ + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_rampon(p1); + restore_flags(flags); + break; + + case _GUS_RAMPOFF: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NEJ-NEJ */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + restore_flags(flags); + break; + + case _GUS_VOLUME_SCALE: + volume_base = p1; + volume_scale = p2; + break; + + case _GUS_VOICE_POS: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_set_voice_pos(voice, plong); + restore_flags(flags); + break; + + default: + break; + } +} + +static int gus_audio_set_speed(int speed) +{ + if (speed <= 0) + speed = gus_audio_speed; + + if (speed < 4000) + speed = 4000; + + if (speed > 44100) + speed = 44100; + + gus_audio_speed = speed; + + if (only_read_access) + { + /* Compute nearest valid recording speed and return it */ + + /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */ + speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + speed = (9878400 / (speed * 16)) - 2; + } + return speed; +} + +static int gus_audio_set_channels(int channels) +{ + if (!channels) + return gus_audio_channels; + if (channels > 2) + channels = 2; + if (channels < 1) + channels = 1; + gus_audio_channels = channels; + return channels; +} + +static int gus_audio_set_bits(int bits) +{ + if (!bits) + return gus_audio_bits; + + if (bits != 8 && bits != 16) + bits = 8; + + if (only_8_bits) + bits = 8; + + gus_audio_bits = bits; + return bits; +} + +static int gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_speed(val); + break; + + case SOUND_PCM_READ_RATE: + val = gus_audio_speed; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val); + break; + + case SOUND_PCM_READ_CHANNELS: + val = gus_audio_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_bits(val); + break; + + case SOUND_PCM_READ_BITS: + val = gus_audio_bits; + break; + + case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ + case SOUND_PCM_READ_FILTER: + val = -EINVAL; + break; + default: + return -EINVAL; + } + return put_user(val, (int *)arg); +} + +static void gus_audio_reset(int dev) +{ + if (recording_active) + { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } +} + +static int saved_iw_mode; /* A hack hack hack */ + +static int gus_audio_open(int dev, int mode) +{ + if (gus_busy) + return -EBUSY; + + if (gus_pnp_flag && mode & OPEN_READ) + { +/* printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/ + return -EIO; + } + gus_initialize(); + + gus_busy = 1; + active_device = 0; + + saved_iw_mode = iw_mode; + if (iw_mode) + { + /* There are some problems with audio in enhanced mode so disable it */ + gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */ + iw_mode = 0; + } + + gus_reset(); + reset_sample_memory(); + gus_select_max_voices(14); + + pcm_active = 0; + dma_active = 0; + pcm_opened = 1; + if (mode & OPEN_READ) + { + recording_active = 1; + set_input_volumes(); + } + only_read_access = !(mode & OPEN_WRITE); + only_8_bits = mode & OPEN_READ; + if (only_8_bits) + audio_devs[dev]->format_mask = AFMT_U8; + else + audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE; + + return 0; +} + +static void gus_audio_close(int dev) +{ + iw_mode = saved_iw_mode; + gus_reset(); + gus_busy = 0; + pcm_opened = 0; + active_device = 0; + + if (recording_active) + { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } + recording_active = 0; +} + +static void gus_audio_update_volume(void) +{ + unsigned long flags; + int voice; + + if (pcm_active && pcm_opened) + for (voice = 0; voice < gus_audio_channels; voice++) + { + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + restore_flags(flags); + } +} + +static void play_next_pcm_block(void) +{ + unsigned long flags; + int speed = gus_audio_speed; + int this_one, is16bits, chn; + unsigned long dram_loc; + unsigned char mode[2], ramp_mode[2]; + + if (!pcm_qlen) + return; + + this_one = pcm_head; + + for (chn = 0; chn < gus_audio_channels; chn++) + { + mode[chn] = 0x00; + ramp_mode[chn] = 0x03; /* Ramping and rollover off */ + + if (chn == 0) + { + mode[chn] |= 0x20; /* Loop IRQ */ + voices[chn].loop_irq_mode = LMODE_PCM; + } + if (gus_audio_bits != 8) + { + is16bits = 1; + mode[chn] |= 0x04; /* 16 bit data */ + } + else + is16bits = 0; + + dram_loc = this_one * pcm_bsize; + dram_loc += chn * pcm_banksize; + + if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ + { + mode[chn] |= 0x08; /* Enable loop */ + ramp_mode[chn] = 0x03; /* Disable rollover bit */ + } + else + { + if (chn == 0) + ramp_mode[chn] = 0x04; /* Enable rollover bit */ + } + save_flags(flags); + cli(); + gus_select_voice(chn); + gus_voice_freq(speed); + + if (gus_audio_channels == 1) + gus_voice_balance(7); /* mono */ + else if (chn == 0) + gus_voice_balance(0); /* left */ + else + gus_voice_balance(15); /* right */ + + if (!pcm_active) /* Playback not already active */ + { + /* + * The playback was not started yet (or there has been a pause). + * Start the voice (again) and ask for a rollover irq at the end of + * this_one block. If this_one one is last of the buffers, use just + * the normal loop with irq. + */ + + gus_voice_off(); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + + gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */ + gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ + + if (chn != 0) + gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, + 0, is16bits); /* Loop end location */ + } + if (chn == 0) + gus_write_addr(0x04, dram_loc + pcm_bsize - 1, + 0, is16bits); /* Loop end location */ + else + mode[chn] |= 0x08; /* Enable looping */ + restore_flags(flags); + } + for (chn = 0; chn < gus_audio_channels; chn++) + { + save_flags(flags); + cli(); + gus_select_voice(chn); + gus_write8(0x0d, ramp_mode[chn]); + if (iw_mode) + gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */ + gus_voice_on(mode[chn]); + restore_flags(flags); + } + pcm_active = 1; +} + +static void gus_transfer_output_block(int dev, unsigned long buf, + int total_count, int intrflag, int chn) +{ + /* + * This routine transfers one block of audio data to the DRAM. In mono mode + * it's called just once. When in stereo mode, this_one routine is called + * once for both channels. + * + * The left/mono channel data is transferred to the beginning of dram and the + * right data to the area pointed by gus_page_size. + */ + + int this_one, count; + unsigned long flags; + unsigned char dma_command; + unsigned long address, hold_address; + + save_flags(flags); + cli(); + + count = total_count / gus_audio_channels; + + if (chn == 0) + { + if (pcm_qlen >= pcm_nblk) + printk(KERN_WARNING "GUS Warning: PCM buffers out of sync\n"); + + this_one = pcm_current_block = pcm_tail; + pcm_qlen++; + pcm_tail = (pcm_tail + 1) % pcm_nblk; + pcm_datasize[this_one] = count; + } + else + this_one = pcm_current_block; + + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE); + + address = this_one * pcm_bsize; + address += chn * pcm_banksize; + + if (audio_devs[dev]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + + if (gus_audio_bits != 8) + dma_command |= 0x40; /* 16 bit _DATA_ */ + else + dma_command |= 0x80; /* Invert MSB */ + + if (audio_devs[dev]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA channel */ + + gus_write8(0x41, dma_command); /* Kick start */ + + if (chn == (gus_audio_channels - 1)) /* Last channel */ + { + /* + * Last (right or mono) channel data + */ + dma_active = 1; /* DMA started. There is a unacknowledged buffer */ + active_device = GUS_DEV_PCM_DONE; + if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize)) + { + play_next_pcm_block(); + } + } + else + { + /* + * Left channel data. The right channel + * is transferred after DMA interrupt + */ + active_device = GUS_DEV_PCM_CONTINUE; + } + + restore_flags(flags); +} + +static void gus_uninterleave8(char *buf, int l) +{ +/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + char *buf2 = buf + halfsize, *src = bounce_buf; + + memcpy(bounce_buf, buf, l); + + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } +} + +static void gus_uninterleave16(short *buf, int l) +{ +/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + short *buf2 = buf + halfsize, *src = (short *) bounce_buf; + + memcpy(bounce_buf, (char *) buf, l * 2); + + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } +} + +static void gus_audio_output_block(int dev, unsigned long buf, int total_count, + int intrflag) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT; + + pcm_current_buf = buf; + pcm_current_count = total_count; + pcm_current_intrflag = intrflag; + pcm_current_dev = dev; + if (gus_audio_channels == 2) + { + char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys); + + if (gus_audio_bits == 8) + gus_uninterleave8(b, total_count); + else + gus_uninterleave16((short *) b, total_count / 2); + } + gus_transfer_output_block(dev, buf, total_count, intrflag, 0); +} + +static void gus_audio_start_input(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + unsigned char mode; + + save_flags(flags); + cli(); + + DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ); + mode = 0xa0; /* DMA IRQ enabled, invert MSB */ + + if (audio_devs[dev]->dmap_in->dma > 3) + mode |= 0x04; /* 16 bit DMA channel */ + if (gus_audio_channels > 1) + mode |= 0x02; /* Stereo */ + mode |= 0x01; /* DMA enable */ + + gus_write8(0x49, mode); + restore_flags(flags); +} + +static int gus_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + unsigned int rate; + + gus_audio_bsize = bsize; + audio_devs[dev]->dmap_in->flags |= DMA_NODMA; + rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + + gus_write8(0x48, rate & 0xff); /* Set sampling rate */ + + if (gus_audio_bits != 8) + { +/* printk("GUS Error: 16 bit recording not supported\n");*/ + return -EINVAL; + } + return 0; +} + +static int gus_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + int i; + + long mem_ptr, mem_size; + + audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT; + mem_ptr = 0; + mem_size = gus_mem_size / gus_audio_channels; + + if (mem_size > (256 * 1024)) + mem_size = 256 * 1024; + + pcm_bsize = bsize / gus_audio_channels; + pcm_head = pcm_tail = pcm_qlen = 0; + + pcm_nblk = 2; /* MAX_PCM_BUFFERS; */ + if ((pcm_bsize * pcm_nblk) > mem_size) + pcm_nblk = mem_size / pcm_bsize; + + for (i = 0; i < pcm_nblk; i++) + pcm_datasize[i] = 0; + + pcm_banksize = pcm_nblk * pcm_bsize; + + if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024)) + pcm_nblk--; + gus_write8(0x41, 0); /* Disable GF1 DMA */ + return 0; +} + +static int gus_local_qlen(int dev) +{ + return pcm_qlen; +} + + +static struct audio_driver gus_audio_driver = +{ + owner: THIS_MODULE, + open: gus_audio_open, + close: gus_audio_close, + output_block: gus_audio_output_block, + start_input: gus_audio_start_input, + ioctl: gus_audio_ioctl, + prepare_for_input: gus_audio_prepare_for_input, + prepare_for_output: gus_audio_prepare_for_output, + halt_io: gus_audio_reset, + local_qlen: gus_local_qlen, +}; + +static void guswave_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info = &synth_devs[dev]->chn_info[chn]; + + guswave_set_instr(dev, voice, info->pgm_num); + voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ + voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; + voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; + voices[voice].bender = 0; + voices[voice].bender_range = info->bender_range; + + if (chn == 9) + voices[voice].fixed_pitch = 1; +} + +static void guswave_bender(int dev, int voice, int value) +{ + int freq; + unsigned long flags; + + voices[voice].bender = value - 8192; + freq = compute_finetune(voices[voice].orig_freq, value - 8192, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(freq); + restore_flags(flags); +} + +static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, best = -1, best_time = 0x7fffffff; + + p = alloc->ptr; + /* + * First look for a completely stopped voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0) + { + alloc->ptr = p; + return p; + } + if (alloc->alloc_times[p] < best_time) + { + best = p; + best_time = alloc->alloc_times[p]; + } + p = (p + 1) % alloc->max_voice; + } + + /* + * Then look for a releasing voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0xffff) + { + alloc->ptr = p; + return p; + } + p = (p + 1) % alloc->max_voice; + } + if (best >= 0) + p = best; + + alloc->ptr = p; + return p; +} + +static struct synth_operations guswave_operations = +{ + owner: THIS_MODULE, + id: "GUS", + info: &gus_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_SAMPLE, + synth_subtype: SAMPLE_TYPE_GUS, + open: guswave_open, + close: guswave_close, + ioctl: guswave_ioctl, + kill_note: guswave_kill_note, + start_note: guswave_start_note, + set_instr: guswave_set_instr, + reset: guswave_reset, + hw_control: guswave_hw_control, + load_patch: guswave_load_patch, + aftertouch: guswave_aftertouch, + controller: guswave_controller, + panning: guswave_panning, + volume_method: guswave_volume_method, + bender: guswave_bender, + alloc_voice: guswave_alloc, + setup_voice: guswave_setup_voice +}; + +static void set_input_volumes(void) +{ + unsigned long flags; + unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ + + if (have_gus_max) /* Don't disturb GUS MAX */ + return; + + save_flags(flags); + cli(); + + /* + * Enable channels having vol > 10% + * Note! bit 0x01 means the line in DISABLED while 0x04 means + * the mic in ENABLED. + */ + if (gus_line_vol > 10) + mask &= ~0x01; + if (gus_mic_vol > 10) + mask |= 0x04; + + if (recording_active) + { + /* + * Disable channel, if not selected for recording + */ + if (!(gus_recmask & SOUND_MASK_LINE)) + mask |= 0x01; + if (!(gus_recmask & SOUND_MASK_MIC)) + mask &= ~0x04; + } + mix_image &= ~0x07; + mix_image |= mask & 0x07; + outb((mix_image), u_Mixer); + + restore_flags(flags); +} + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH|SOUND_MASK_PCM) + +int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int vol, val; + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if (!access_ok(VERIFY_WRITE, (int *)arg, sizeof(int))) + return -EFAULT; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + if (__get_user(val, (int *) arg)) + return -EFAULT; + + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + gus_recmask = val & MIX_DEVS; + if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) + gus_recmask = SOUND_MASK_MIC; + /* Note! Input volumes are updated during next open for recording */ + val = gus_recmask; + break; + + case SOUND_MIXER_MIC: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_mic_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_LINE: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_line_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_PCM: + gus_pcm_volume = val & 0xff; + if (gus_pcm_volume < 0) + gus_pcm_volume = 0; + if (gus_pcm_volume > 100) + gus_pcm_volume = 100; + gus_audio_update_volume(); + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + gus_wave_volume = val & 0xff; + if (gus_wave_volume < 0) + gus_wave_volume = 0; + if (gus_wave_volume > 100) + gus_wave_volume = 100; + if (active_device == GUS_DEV_WAVE) + { + int voice; + for (voice = 0; voice < nr_voices; voice++) + dynamic_volume_change(voice); /* Apply the new vol */ + } + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + else + { + switch (cmd & 0xff) + { + /* + * Return parameters + */ + case SOUND_MIXER_RECSRC: + val = gus_recmask; + break; + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = gus_mic_vol | (gus_mic_vol << 8); + break; + + case SOUND_MIXER_LINE: + val = gus_line_vol | (gus_line_vol << 8); + break; + + case SOUND_MIXER_PCM: + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + return __put_user(val, (int *)arg); +} + +static struct mixer_operations gus_mixer_operations = +{ + owner: THIS_MODULE, + id: "GUS", + name: "Gravis Ultrasound", + ioctl: gus_default_mixer_ioctl +}; + +static int __init gus_default_mixer_init(void) +{ + int n; + + if ((n = sound_alloc_mixerdev()) != -1) + { + /* + * Don't install if there is another + * mixer + */ + mixer_devs[n] = &gus_mixer_operations; + } + if (have_gus_max) + { + /* + * Enable all mixer channels on the GF1 side. Otherwise recording will + * not be possible using GUS MAX. + */ + mix_image &= ~0x07; + mix_image |= 0x04; /* All channels enabled */ + outb((mix_image), u_Mixer); + } + return n; +} + +void __init gus_wave_init(struct address_info *hw_config) +{ + unsigned long flags; + unsigned char val; + char *model_num = "2.4"; + char tmp[64], tmp2[64]; + int gus_type = 0x24; /* 2.4 */ + + int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; + int sdev; + + hw_config->slots[0] = -1; /* No wave */ + hw_config->slots[1] = -1; /* No ad1848 */ + hw_config->slots[4] = -1; /* No audio */ + hw_config->slots[5] = -1; /* No mixer */ + + if (!gus_pnp_flag) + { + if (irq < 0 || irq > 15) + { + printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return; + } + } + + if (dma < 0 || dma > 7 || dma == 4) + { + printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma); + return; + } + gus_irq = irq; + gus_dma = dma; + gus_dma2 = dma2; + gus_hw_config = hw_config; + + if (gus_dma2 == -1) + gus_dma2 = dma; + + /* + * Try to identify the GUS model. + * + * Versions < 3.6 don't have the digital ASIC. Try to probe it first. + */ + + save_flags(flags); + cli(); + outb((0x20), gus_base + 0x0f); + val = inb(gus_base + 0x0f); + restore_flags(flags); + + if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ + { + int ad_flags = 0; + + if (gus_pnp_flag) + ad_flags = 0x12345678; /* Interwave "magic" */ + /* + * It has the digital ASIC so the card is at least v3.4. + * Next try to detect the true model. + */ + + if (gus_pnp_flag) /* Hack hack hack */ + val = 10; + else + val = inb(u_MixSelect); + + /* + * Value 255 means pre-3.7 which don't have mixer. + * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. + * 10 and above is GUS MAX which has the CS4231 codec/mixer. + * + */ + + if (val == 255 || val < 5) + { + model_num = "3.4"; + gus_type = 0x34; + } + else if (val < 10) + { + model_num = "3.7"; + gus_type = 0x37; + mixer_type = ICS2101; + request_region(u_MixSelect, 1, "GUS mixer"); + } + else + { + model_num = "MAX"; + gus_type = 0x40; + mixer_type = CS4231; +#ifdef CONFIG_SOUND_ALSA_GUSMAX + { + unsigned char max_config = 0x40; /* Codec enable */ + + if (gus_dma2 == -1) + gus_dma2 = gus_dma; + + if (gus_dma > 3) + max_config |= 0x10; /* 16 bit capture DMA */ + + if (gus_dma2 > 3) + max_config |= 0x20; /* 16 bit playback DMA */ + + max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ + + outb((max_config), gus_base + 0x106); /* UltraMax control */ + } + + if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp)) + { + char *name = "GUS MAX"; + int old_num_mixers = num_mixers; + + if (gus_pnp_flag) + name = "GUS PnP"; + + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + have_gus_max = 1; + if (hw_config->name) + name = hw_config->name; + + hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c, + -irq, gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1, /* Share DMA channels with GF1 */ + hw_config->osp, + THIS_MODULE); + + if (num_mixers > old_num_mixers) + { + /* GUS has it's own mixer map */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } + } + else + printk(KERN_WARNING "GUS: No CS4231 ??"); +#else + printk(KERN_ERR "GUS MAX found, but not compiled in\n"); +#endif + } + } + else + { + /* + * ASIC not detected so the card must be 2.2 or 2.4. + * There could still be the 16-bit/mixer daughter card. + */ + } + + if (hw_config->name) + { + strncpy(tmp, hw_config->name, 45); + tmp[45] = 0; + sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024); + tmp2[sizeof(tmp2) - 1] = 0; + } + else if (gus_pnp_flag) + { + sprintf(tmp2, "Gravis UltraSound PnP (%dk)", + (int) gus_mem_size / 1024); + } + else + sprintf(tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); + + + samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)); + if (samples == NULL) + { + printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); + return; + } + conf_printf(tmp2, hw_config); + tmp2[sizeof(gus_info.name) - 1] = 0; + strcpy(gus_info.name, tmp2); + + if ((sdev = sound_alloc_synthdev()) == -1) + printk(KERN_WARNING "gus_init: Too many synthesizers\n"); + else + { + voice_alloc = &guswave_operations.alloc; + if (iw_mode) + guswave_operations.id = "IWAVE"; + hw_config->slots[0] = sdev; + synth_devs[sdev] = &guswave_operations; + sequencer_init(); + gus_tmr_install(gus_base + 8); + } + + reset_sample_memory(); + + gus_initialize(); + + if ((gus_mem_size > 0) & !gus_no_wave_dma) + { + hw_config->slots[4] = -1; + if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "Ultrasound", + &gus_audio_driver, + sizeof(struct audio_driver), + NEEDS_RESTART | + ((!iw_mode && dma2 != dma && dma2 != -1) ? + DMA_DUPLEX : 0), + AFMT_U8 | AFMT_S16_LE, + NULL, dma, dma2)) < 0) + { + return; + } + + hw_config->slots[4] = gus_devnum; + audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ + audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ + audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ + audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; + } + + /* + * Mixer dependent initialization. + */ + + switch (mixer_type) + { + case ICS2101: + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + request_region(u_MixSelect, 1, "GUS mixer"); + hw_config->slots[5] = ics2101_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + + case CS4231: + /* Initialized elsewhere (ad1848.c) */ + default: + hw_config->slots[5] = gus_default_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + } +} + +void __exit gus_wave_unload(struct address_info *hw_config) +{ +#ifdef CONFIG_SOUND_ALSA_GUSMAX + if (have_gus_max) + { + ad1848_unload(gus_base + 0x10c, + -gus_irq, + gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1); /* Share DMA channels with GF1 */ + } +#endif + + if (mixer_type == ICS2101) + { + release_region(u_MixSelect, 1); + } + if (hw_config->slots[0] != -1) + sound_unload_synthdev(hw_config->slots[0]); + if (hw_config->slots[1] != -1) + sound_unload_audiodev(hw_config->slots[1]); + if (hw_config->slots[2] != -1) + sound_unload_mididev(hw_config->slots[2]); + if (hw_config->slots[4] != -1) + sound_unload_audiodev(hw_config->slots[4]); + if (hw_config->slots[5] != -1) + sound_unload_mixerdev(hw_config->slots[5]); + + if(samples) + vfree(samples); + samples=NULL; +} + +static void do_loop_irq(int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + tmp = gus_read8(0x00); + tmp &= ~0x20; /* + * Disable wave IRQ for this_one voice + */ + gus_write8(0x00, tmp); + + if (tmp & 0x03) /* Voice stopped */ + voice_alloc->map[voice] = 0; + + mode = voices[voice].loop_irq_mode; + voices[voice].loop_irq_mode = 0; + parm = voices[voice].loop_irq_parm; + + switch (mode) + { + case LMODE_FINISH: /* + * Final loop finished, shoot volume down + */ + + if ((int) (gus_read16(0x09) >> 4) < 100) /* + * Get current volume + */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + break; + } + gus_ramp_range(65, 4065); + gus_ramp_rate(0, 63); /* + * Fastest possible rate + */ + gus_rampon(0x20 | 0x40); /* + * Ramp down, once, irq + */ + voices[voice].volume_irq_mode = VMODE_HALT; + break; + + case LMODE_PCM_STOP: + pcm_active = 0; /* Signal to the play_next_pcm_block routine */ + case LMODE_PCM: + { + pcm_qlen--; + pcm_head = (pcm_head + 1) % pcm_nblk; + if (pcm_qlen && pcm_active) + { + play_next_pcm_block(); + } + else + { + /* Underrun. Just stop the voice */ + gus_select_voice(0); /* Left channel */ + gus_voice_off(); + gus_rampoff(); + gus_select_voice(1); /* Right channel */ + gus_voice_off(); + gus_rampoff(); + pcm_active = 0; + } + + /* + * If the queue was full before this interrupt, the DMA transfer was + * suspended. Let it continue now. + */ + + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + break; + + default: + break; + } + restore_flags(flags); +} + +static void do_volume_irq(int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + save_flags(flags); + cli(); + + gus_select_voice(voice); + tmp = gus_read8(0x0d); + tmp &= ~0x20; /* + * Disable volume ramp IRQ + */ + gus_write8(0x0d, tmp); + + mode = voices[voice].volume_irq_mode; + voices[voice].volume_irq_mode = 0; + parm = voices[voice].volume_irq_parm; + + switch (mode) + { + case VMODE_HALT: /* Decay phase finished */ + if (iw_mode) + gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */ + restore_flags(flags); + gus_voice_init(voice); + break; + + case VMODE_ENVELOPE: + gus_rampoff(); + restore_flags(flags); + step_envelope(voice); + break; + + case VMODE_START_NOTE: + restore_flags(flags); + guswave_start_note2(voices[voice].dev_pending, voice, + voices[voice].note_pending, voices[voice].volume_pending); + if (voices[voice].kill_pending) + guswave_kill_note(voices[voice].dev_pending, voice, + voices[voice].note_pending, 0); + + if (voices[voice].sample_pending >= 0) + { + guswave_set_instr(voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + } + break; + + default: + restore_flags(flags); + } + restore_flags(flags); +} + +void gus_voice_irq(void) +{ + unsigned long wave_ignore = 0, volume_ignore = 0; + unsigned long voice_bit; + + unsigned char src, voice; + + while (1) + { + src = gus_read8(0x0f); /* + * Get source info + */ + voice = src & 0x1f; + src &= 0xc0; + + if (src == (0x80 | 0x40)) + return; /* + * No interrupt + */ + + voice_bit = 1 << voice; + + if (!(src & 0x80)) /* + * Wave IRQ pending + */ + if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + wave_ignore |= voice_bit; + do_loop_irq(voice); + } + if (!(src & 0x40)) /* + * Volume IRQ pending + */ + if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + volume_ignore |= voice_bit; + do_volume_irq(voice); + } + } +} + +void guswave_dma_irq(void) +{ + unsigned char status; + + status = gus_look8(0x41); /* Get DMA IRQ Status */ + if (status & 0x40) /* DMA interrupt pending */ + switch (active_device) + { + case GUS_DEV_WAVE: + wake_up(&dram_sleeper); + break; + + case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + gus_transfer_output_block(pcm_current_dev, pcm_current_buf, + pcm_current_count, + pcm_current_intrflag, 1); + break; + + case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + if (pcm_qlen < pcm_nblk) + { + dma_active = 0; + if (gus_busy) + { + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + } + break; + + default: + break; + } + status = gus_look8(0x49); /* + * Get Sampling IRQ Status + */ + if (status & 0x40) /* + * Sampling Irq pending + */ + { + DMAbuf_inputintr(gus_devnum); + } +} + +/* + * Timer stuff + */ + +static volatile int select_addr, data_addr; +static volatile int curr_timer = 0; + +void gus_timer_command(unsigned int addr, unsigned int val) +{ + int i; + + outb(((unsigned char) (addr & 0xff)), select_addr); + + for (i = 0; i < 2; i++) + inb(select_addr); + + outb(((unsigned char) (val & 0xff)), data_addr); + + for (i = 0; i < 2; i++) + inb(select_addr); +} + +static void arm_timer(int timer, unsigned int interval) +{ + curr_timer = timer; + + if (timer == 1) + { + gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */ + gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */ + gus_timer_command(0x04, 0x01); /* Start timer 1 */ + } + else + { + gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */ + gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */ + gus_timer_command(0x04, 0x02); /* Start timer 2 */ + } + + gus_timer_enabled = 1; +} + +static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick) +{ + int timer_no, resolution; + int divisor; + + if (usecs_per_tick > (256 * 80)) + { + timer_no = 2; + resolution = 320; /* usec */ + } + else + { + timer_no = 1; + resolution = 80; /* usec */ + } + divisor = (usecs_per_tick + (resolution / 2)) / resolution; + arm_timer(timer_no, divisor); + + return divisor * resolution; +} + +static void gus_tmr_disable(int dev) +{ + gus_write8(0x45, 0); /* Disable both timers */ + gus_timer_enabled = 0; +} + +static void gus_tmr_restart(int dev) +{ + if (curr_timer == 1) + gus_write8(0x45, 0x04); /* Start timer 1 again */ + else + gus_write8(0x45, 0x08); /* Start timer 2 again */ + gus_timer_enabled = 1; +} + +static struct sound_lowlev_timer gus_tmr = +{ + 0, + 1, + gus_tmr_start, + gus_tmr_disable, + gus_tmr_restart +}; + +static void gus_tmr_install(int io_base) +{ + struct sound_lowlev_timer *tmr; + + select_addr = io_base; + data_addr = io_base + 1; + + tmr = &gus_tmr; + +#ifdef THIS_GETS_FIXED + sound_timer_init(&gus_tmr, "GUS"); +#endif +} diff -Nru linux/sound/oss/hex2hex.c linux-2.4.19-pre5-mjc/sound/oss/hex2hex.c --- linux/sound/oss/hex2hex.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/hex2hex.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,101 @@ +/* + * hex2hex reads stdin in Intel HEX format and produces an + * (unsigned char) array which contains the bytes and writes it + * to stdout using C syntax + */ + +#include +#include +#include + +#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); } +#define MAX_SIZE (256*1024) +unsigned char buf[MAX_SIZE]; + +int loadhex(FILE *inf, unsigned char *buf) +{ + int l=0, c, i; + + while ((c=getc(inf))!=EOF) + { + if (c == ':') /* Sync with beginning of line */ + { + int n, check; + unsigned char sum; + int addr; + int linetype; + + if (fscanf(inf, "%02x", &n) != 1) + ABANDON("File format error"); + sum = n; + + if (fscanf(inf, "%04x", &addr) != 1) + ABANDON("File format error"); + sum += addr/256; + sum += addr%256; + + if (fscanf(inf, "%02x", &linetype) != 1) + ABANDON("File format error"); + sum += linetype; + + if (linetype != 0) + continue; + + for (i=0;i= MAX_SIZE) + ABANDON("File too large"); + buf[addr++] = c; + if (addr > l) + l = addr; + sum += c; + } + + if (fscanf(inf, "%02x", &check) != 1) + ABANDON("File format error"); + + sum = ~sum + 1; + if (check != sum) + ABANDON("Line checksum error"); + } + } + + return l; +} + +int main( int argc, const char * argv [] ) +{ + const char * varline; + int i,l; + int id=0; + + if(argv[1] && strcmp(argv[1], "-i")==0) + { + argv++; + argc--; + id=1; + } + if(argv[1]==NULL) + { + fprintf(stderr,"hex2hex: [-i] filename\n"); + exit(1); + } + varline = argv[1]; + l = loadhex(stdin, buf); + + printf("/*\n *\t Computer generated file. Do not edit.\n */\n"); + printf("static int %s_len = %d;\n", varline, l); + printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":""); + + for (i=0;i + * + * Built from: + * Low level code: Zach Brown (original nonworking i810 OSS driver) + * Jaroslav Kysela (working ALSA driver) + * + * Framework: Thomas Sailer + * Extended by: Zach Brown + * and others.. + * + * Hardware Provided By: + * Analog Devices (A major AC97 codec maker) + * Intel Corp (you've probably heard of them already) + * + * AC97 clues and assistance provided by + * Analog Devices + * Zach 'Fufu' Brown + * Jeff Garzik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + * + * Intel 810 theory of operation + * + * The chipset provides three DMA channels that talk to an AC97 + * CODEC (AC97 is a digital/analog mixer standard). At its simplest + * you get 48Khz audio with basic volume and mixer controls. At the + * best you get rate adaption in the codec. We set the card up so + * that we never take completion interrupts but instead keep the card + * chasing its tail around a ring buffer. This is needed for mmap + * mode audio and happens to work rather well for non-mmap modes too. + * + * The board has one output channel for PCM audio (supported) and + * a stereo line in and mono microphone input. Again these are normally + * locked to 48Khz only. Right now recording is not finished. + * + * There is no midi support, no synth support. Use timidity. To get + * esd working you need to use esd -r 48000 as it won't probe 48KHz + * by default. mpg123 can't handle 48Khz only audio so use xmms. + * + * Fix The Sound On Dell + * + * Not everyone uses 48KHz. We know of no way to detect this reliably + * and certainly not to get the right data. If your i810 audio sounds + * stupid you may need to investigate other speeds. According to Analog + * they tend to use a 14.318MHz clock which gives you a base rate of + * 41194Hz. + * + * This is available via the 'ftsodell=1' option. + * + * If you need to force a specific rate set the clocking= option + * + * This driver is cursed. (Ben LaHaise) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PCI_DEVICE_ID_INTEL_82801 +#define PCI_DEVICE_ID_INTEL_82801 0x2415 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82901 +#define PCI_DEVICE_ID_INTEL_82901 0x2425 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH2 +#define PCI_DEVICE_ID_INTEL_ICH2 0x2445 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH3 +#define PCI_DEVICE_ID_INTEL_ICH3 0x2485 +#endif +#ifndef PCI_DEVICE_ID_INTEL_440MX +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#endif +#ifndef PCI_DEVICE_ID_SI_7012 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 +#endif + +static int ftsodell=0; +static int strict_clocking=0; +static unsigned int clocking=0; +static int spdif_locked=0; + +//#define DEBUG +//#define DEBUG2 +//#define DEBUG_INTERRUPTS +//#define DEBUG_MMAP + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 + +#define I810_FMT_16BIT 1 +#define I810_FMT_STEREO 2 +#define I810_FMT_MASK 3 + +#define SPDIF_ON 0x0004 +#define SURR_ON 0x0010 +#define CENTER_LFE_ON 0x0020 +#define VOL_MUTED 0x8000 + +/* the 810's array of pointers to data buffers */ + +struct sg_item { +#define BUSADDR_MASK 0xFFFFFFFE + u32 busaddr; +#define CON_IOC 0x80000000 /* interrupt on completion */ +#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */ +#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */ + u32 control; +}; + +/* an instance of the i810 channel */ +#define SG_LEN 32 +struct i810_channel +{ + /* these sg guys should probably be allocated + seperately as nocache. Must be 8 byte aligned */ + struct sg_item sg[SG_LEN]; /* 32*8 */ + u32 offset; /* 4 */ + u32 port; /* 4 */ + u32 used; + u32 num; +}; + +/* + * we have 3 seperate dma engines. pcm in, pcm out, and mic. + * each dma engine has controlling registers. These goofy + * names are from the datasheet, but make it easy to write + * code while leafing through it. + */ + +#define ENUM_ENGINE(PRE,DIG) \ +enum { \ + PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ + PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ + PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ + PRE##_SR = 0x##DIG##6, /* Status Register */ \ + PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \ + PRE##_PIV = 0x##DIG##a, /* Prefetched Index Value */ \ + PRE##_CR = 0x##DIG##b /* Control Register */ \ +} + +ENUM_ENGINE(OFF,0); /* Offsets */ +ENUM_ENGINE(PI,0); /* PCM In */ +ENUM_ENGINE(PO,1); /* PCM Out */ +ENUM_ENGINE(MC,2); /* Mic In */ + +enum { + GLOB_CNT = 0x2c, /* Global Control */ + GLOB_STA = 0x30, /* Global Status */ + CAS = 0x34 /* Codec Write Semaphore Register */ +}; + +/* interrupts for a dma engine */ +#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */ +#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */ +#define DMA_INT_LVI (1<<2) /* last valid done */ +#define DMA_INT_CELV (1<<1) /* last valid is current */ +#define DMA_INT_DCH (1) /* DMA Controller Halted (happens on LVI interrupts) */ +#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) + +/* interrupts for the whole chip */ +#define INT_SEC (1<<11) +#define INT_PRI (1<<10) +#define INT_MC (1<<7) +#define INT_PO (1<<6) +#define INT_PI (1<<5) +#define INT_MO (1<<2) +#define INT_NI (1<<1) +#define INT_GPI (1<<0) +#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI) + + +#define DRIVER_VERSION "0.21" + +/* magic numbers to protect our data structures */ +#define I810_CARD_MAGIC 0x5072696E /* "Prin" */ +#define I810_STATE_MAGIC 0x63657373 /* "cess" */ +#define I810_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ +#define NR_HW_CH 3 + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */ +/* stream at a minimum for this card to be happy */ +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */ +/* values are one less than might be expected */ +static const unsigned sample_shift[] = { -1, 0, 0, 1 }; + +enum { + ICH82801AA = 0, + ICH82901AB, + INTEL440MX, + INTELICH2, + INTELICH3, + SI7012, + NVIDIA_NFORCE +}; + +static char * card_names[] = { + "Intel ICH 82801AA", + "Intel ICH 82901AB", + "Intel 440MX", + "Intel ICH2", + "Intel ICH3", + "SiS 7012", + "NVIDIA nForce Audio" +}; + +static struct pci_device_id i810_pci_tbl [] __initdata = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82901, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82901AB}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_440MX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTEL440MX}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3}, + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012}, + {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE}, + {0,} +}; + +MODULE_DEVICE_TABLE (pci, i810_pci_tbl); + +#ifdef CONFIG_PM +#define PM_SUSPENDED(card) (card->pm_suspended) +#else +#define PM_SUSPENDED(card) (0) +#endif + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct i810_state { + unsigned int magic; + struct i810_card *card; /* Card info */ + + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + wait_queue_head_t open_wait; + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + +#ifdef CONFIG_PM + unsigned int pm_saved_dac_rate,pm_saved_adc_rate; +#endif + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable, trigger; + + /* hardware channel */ + struct i810_channel *read_channel; + struct i810_channel *write_channel; + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, updated by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be consumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + unsigned error; /* number of over/underruns */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + /* what the hardware uses */ + unsigned dmasize; + unsigned fragsize; + unsigned fragsamples; + + /* what we tell the user to expect */ + unsigned userfrags; + unsigned userfragsize; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned update_flag; + unsigned ossfragsize; + unsigned ossmaxfrags; + unsigned subdivision; + } dmabuf; +}; + + +struct i810_card { + unsigned int magic; + + /* We keep i810 cards in a linked list */ + struct i810_card *next; + + /* The i810 has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + + /* PCI device stuff */ + struct pci_dev * pci_dev; + u16 pci_id; +#ifdef CONFIG_PM + u16 pm_suspended; + u32 pm_save_state[64/sizeof(u32)]; + int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97]; +#endif + /* soundcore stuff */ + int dev_audio; + + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct i810_state *states[NR_HW_CH]; + struct i810_channel *channel; /* 1:1 to states[] but diff. lifetime */ + dma_addr_t chandma; + + u16 ac97_features; + u16 ac97_status; + u16 channels; + + /* hardware resources */ + unsigned long iobase; + unsigned long ac97base; + u32 irq; + + /* Function support */ + struct i810_channel *(*alloc_pcm_channel)(struct i810_card *); + struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *); + struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *); + void (*free_pcm_channel)(struct i810_card *, int chan); + + /* We have a *very* long init time possibly, so use this to block */ + /* attempts to open our devices before we are ready (stops oops'es) */ + int initializing; +}; + +static struct i810_card *devs = NULL; + +static int i810_open_mixdev(struct inode *inode, struct file *file); +static int i810_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg); +static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card) +{ + if(card->channel[1].used==1) + return NULL; + card->channel[1].used=1; + return &card->channel[1]; +} + +static struct i810_channel *i810_alloc_rec_pcm_channel(struct i810_card *card) +{ + if(card->channel[0].used==1) + return NULL; + card->channel[0].used=1; + return &card->channel[0]; +} + +static struct i810_channel *i810_alloc_rec_mic_channel(struct i810_card *card) +{ + if(card->channel[2].used==1) + return NULL; + card->channel[2].used=1; + return &card->channel[2]; +} + +static void i810_free_pcm_channel(struct i810_card *card, int channel) +{ + card->channel[channel].used=0; +} + +static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate ) +{ + unsigned long id = 0L; + + id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16); + id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; +#ifdef DEBUG + printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id); +#endif + switch ( id ) { + case 0x41445361: /* AD1886 */ + if (rate == 48000) { + return 1; + } + break; + default: /* all other codecs, until we know otherwiae */ + if (rate == 48000 || rate == 44100 || rate == 32000) { + return 1; + } + break; + } + return (0); +} + +/* i810_set_spdif_output + * + * Configure the S/PDIF output transmitter. When we turn on + * S/PDIF, we turn off the analog output. This may not be + * the right thing to do. + * + * Assumptions: + * The DSP sample rate must already be set to a supported + * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. + */ +static void i810_set_spdif_output(struct i810_state *state, int slots, int rate) +{ + int vol; + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if(!(state->card->ac97_features & 4)) { +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n"); +#endif + state->card->ac97_status &= ~SPDIF_ON; + } else { + if ( slots == -1 ) { /* Turn off S/PDIF */ + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + + /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ + if ( !(state->card->ac97_status & VOL_MUTED) ) { + aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); + i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED)); + } + state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); + return; + } + + vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); + state->card->ac97_status = vol & VOL_MUTED; + + /* Set S/PDIF transmitter sample rate */ + aud_reg = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + switch ( rate ) { + case 32000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; + break; + case 44100: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; + break; + case 48000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; + break; + default: +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: %d sample rate not supported by S/PDIF.\n", rate); +#endif + /* turn off S/PDIF */ + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + + i810_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); + + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_VRA | AC97_EA_SPDIF; + i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + state->card->ac97_status |= SPDIF_ON; + + /* Check to make sure the configuration is valid */ + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + if ( ! (aud_reg & 0x0400) ) { +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: S/PDIF transmitter configuration not valid (0x%04x).\n", aud_reg); +#endif + + /* turn off S/PDIF */ + i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + /* Mute the analog output */ + /* Should this only mute the PCM volume??? */ + i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED)); + } +} + +/* i810_set_dac_channels + * + * Configure the codec's multi-channel DACs + * + * The logic is backwards. Setting the bit to 1 turns off the DAC. + * + * What about the ICH? We currently configure it using the + * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, + * does that imply that we want the ICH set to support + * these channels? + * + * TODO: + * vailidate that the codec really supports these DACs + * before turning them on. + */ +static void i810_set_dac_channels(struct i810_state *state, int channel) +{ + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; + state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); + + switch ( channel ) { + case 2: /* always enabled */ + break; + case 4: + aud_reg &= ~AC97_EA_PRJ; + state->card->ac97_status |= SURR_ON; + break; + case 6: + aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); + state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; + break; + default: + break; + } + i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + +} + + +/* set playback sample rate */ +static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec=state->card->ac97_codec[0]; + + if(!(state->card->ac97_features&0x0001)) + { + dmabuf->rate = clocking; +#ifdef DEBUG + printk("Asked for %d Hz, but ac97_features says we only do %dHz. Sorry!\n", + rate,clocking); +#endif + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + rate = ( rate * clocking)/48000; + if(strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000)/clocking; + } + + new_rate=ac97_set_dac_rate(codec, rate); + if(new_rate != rate) { + dmabuf->rate = (new_rate * 48000)/clocking; + } +#ifdef DEBUG + printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate); +#endif + rate = new_rate; + return dmabuf->rate; +} + +/* set recording sample rate */ +static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec=state->card->ac97_codec[0]; + + if(!(state->card->ac97_features&0x0001)) + { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + + rate = ( rate * clocking)/48000; + if(strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000)/clocking; + } + + new_rate = ac97_set_adc_rate(codec, rate); + + if(new_rate != rate) { + dmabuf->rate = (new_rate * 48000)/clocking; + rate = new_rate; + } +#ifdef DEBUG + printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate); +#endif + return dmabuf->rate; +} + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int civ, offset, port, port_picb, bytes = 2; + + if (!dmabuf->enable) + return 0; + + if (rec) + port = state->card->iobase + dmabuf->read_channel->port; + else + port = state->card->iobase + dmabuf->write_channel->port; + + if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) { + port_picb = port + OFF_SR; + bytes = 1; + } else + port_picb = port + OFF_PICB; + + do { + civ = inb(port+OFF_CIV) & 31; + offset = inw(port_picb); + /* Must have a delay here! */ + if(offset == 0) + udelay(1); + /* Reread both registers and make sure that that total + * offset from the first reading to the second is 0. + * There is an issue with SiS hardware where it will count + * picb down to 0, then update civ to the next value, + * then set the new picb to fragsize bytes. We can catch + * it between the civ update and the picb update, making + * it look as though we are 1 fragsize ahead of where we + * are. The next to we get the address though, it will + * be back in the right place, and we will suddenly think + * we just went forward dmasize - fragsize bytes, causing + * totally stupid *huge* dma overrun messages. We are + * assuming that the 1us delay is more than long enough + * that we won't have to worry about the chip still being + * out of sync with reality ;-) + */ + } while (civ != (inb(port+OFF_CIV) & 31) || offset != inw(port_picb)); + + return (((civ + 1) * dmabuf->fragsize - (bytes * offset)) + % dmabuf->dmasize); +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + outb(0, card->iobase + PI_CR); + // wait for the card to acknowledge shutdown + while( inb(card->iobase + PI_CR) != 0 ) ; + // now clear any latent interrupt bits (like the halt bit) + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + outb( inb(card->iobase + PI_PICB), card->iobase + PI_PICB ); + else + outb( inb(card->iobase + PI_SR), card->iobase + PI_SR ); + outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA); +} + +static void stop_adc(struct i810_state *state) +{ + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_adc(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->enable |= ADC_RUNNING; + outb((1<<4) | (1<<2) | 1, state->card->iobase + PI_CR); + } +} + +static void start_adc(struct i810_state *state) +{ + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __start_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + outb(0, card->iobase + PO_CR); + // wait for the card to acknowledge shutdown + while( inb(card->iobase + PO_CR) != 0 ) ; + // now clear any latent interrupt bits (like the halt bit) + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB ); + else + outb( inb(card->iobase + PO_SR), card->iobase + PO_SR ); + outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA); +} + +static void stop_dac(struct i810_state *state) +{ + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_dac(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->enable |= DAC_RUNNING; + outb((1<<4) | (1<<2) | 1, state->card->iobase + PO_CR); + } +} +static void start_dac(struct i810_state *state) +{ + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __start_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ +static int alloc_dmabuf(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf= NULL; + int order, size; + struct page *page, *pend; + + /* If we don't have any oss frag params, then use our default ones */ + if(dmabuf->ossmaxfrags == 0) + dmabuf->ossmaxfrags = 4; + if(dmabuf->ossfragsize == 0) + dmabuf->ossfragsize = (PAGE_SIZE<ossmaxfrags; + size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + + if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) + return 0; + /* alloc enough to satisfy the oss params */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + if ( (PAGE_SIZE< size ) + continue; + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + } + if (!rawbuf) + return -ENOMEM; + + +#ifdef DEBUG + printk("i810_audio: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(rawbuf); page <= pend; page++) + mem_map_reserve(page); + + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct page *page, *pend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_handle); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct i810_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_channel *c; + struct sg_item *sg; + unsigned long flags; + int ret; + unsigned fragint; + int i; + + spin_lock_irqsave(&state->card->lock, flags); + if(dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if(dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer, let alloc_dmabuf determine if we are already + * allocated well enough or if we should replace the current buffer + * (assuming one is already allocated, if it isn't, then allocate it). + */ + if ((ret = alloc_dmabuf(state))) + return ret; + + /* FIXME: figure out all this OSS fragment stuff */ + /* I did, it now does what it should according to the OSS API. DL */ + /* We may not have realloced our dmabuf, but the fragment size to + * fragment number ratio may have changed, so go ahead and reprogram + * things + */ + dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; + dmabuf->numfrag = SG_LEN; + dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag; + dmabuf->fragsamples = dmabuf->fragsize >> 1; + dmabuf->userfragsize = dmabuf->ossfragsize; + dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize; + + memset(dmabuf->rawbuf, 0, dmabuf->dmasize); + + if(dmabuf->ossmaxfrags == 4) { + fragint = 8; + dmabuf->fragshift = 2; + } else if (dmabuf->ossmaxfrags == 8) { + fragint = 4; + dmabuf->fragshift = 3; + } else if (dmabuf->ossmaxfrags == 16) { + fragint = 2; + dmabuf->fragshift = 4; + } else { + fragint = 1; + dmabuf->fragshift = 5; + } + /* + * Now set up the ring + */ + if(dmabuf->read_channel) + c = dmabuf->read_channel; + else + c = dmabuf->write_channel; + while(c != NULL) { + sg=&c->sg[0]; + /* + * Load up 32 sg entries and take an interrupt at half + * way (we might want more interrupts later..) + */ + + for(i=0;inumfrag;i++) + { + sg->busaddr=(u32)dmabuf->dma_handle+dmabuf->fragsize*i; + // the card will always be doing 16bit stereo + sg->control=dmabuf->fragsamples; + if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) + sg->control <<= 1; + sg->control|=CON_BUFPAD; + // set us up to get IOC interrupts as often as needed to + // satisfy numfrag requirements, no more + if( ((i+1) % fragint) == 0) { + sg->control|=CON_IOC; + } + sg++; + } + spin_lock_irqsave(&state->card->lock, flags); + outb(2, state->card->iobase+c->port+OFF_CR); /* reset DMA machine */ + outl((u32)state->card->chandma + + c->num*sizeof(struct i810_channel), + state->card->iobase+c->port+OFF_BDBAR); + outb(0, state->card->iobase+c->port+OFF_CIV); + outb(0, state->card->iobase+c->port+OFF_LVI); + + spin_unlock_irqrestore(&state->card->lock, flags); + + if(c != dmabuf->write_channel) + c = dmabuf->write_channel; + else + c = NULL; + } + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + +#ifdef DEBUG + printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, " + "fragsize = %d dmasize = %d\n", + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + + return 0; +} + +static void __i810_update_lvi(struct i810_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int x, port; + + port = state->card->iobase; + if(rec) + port += dmabuf->read_channel->port; + else + port += dmabuf->write_channel->port; + + /* if we are currently stopped, then our CIV is actually set to our + * *last* sg segment and we are ready to wrap to the next. However, + * if we set our LVI to the last sg segment, then it won't wrap to + * the next sg segment, it won't even get a start. So, instead, when + * we are stopped, we set both the LVI value and also we increment + * the CIV value to the next sg segment to be played so that when + * we call start_{dac,adc}, things will operate properly + */ + if (!dmabuf->enable && dmabuf->ready) { + if(rec && dmabuf->count < dmabuf->dmasize && + (dmabuf->trigger & PCM_ENABLE_INPUT)) + { + outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI); + __start_adc(state); + while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ; + } else if (!rec && dmabuf->count && + (dmabuf->trigger & PCM_ENABLE_OUTPUT)) + { + outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI); + __start_dac(state); + while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ; + } + } + + /* swptr - 1 is the tail of our transfer */ + x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize; + x /= dmabuf->fragsize; + outb(x, port+OFF_LVI); +} + +static void i810_update_lvi(struct i810_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + + if(!dmabuf->ready) + return; + spin_lock_irqsave(&state->card->lock, flags); + __i810_update_lvi(state, rec); + spin_unlock_irqrestore(&state->card->lock, flags); +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void i810_update_ptr(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned hwptr; + int diff; + + /* error handling and process wake up for DAC */ + if (dmabuf->enable == ADC_RUNNING) { + /* update hardware pointer */ + hwptr = i810_get_dma_addr(state, 1); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; +#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) + printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#endif + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + if (dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a read */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if((inb(state->card->iobase + PI_CIV) & 31) != + (inb(state->card->iobase + PI_LVI) & 31)) { + printk(KERN_WARNING "i810_audio: DMA overrun on read\n"); + dmabuf->error++; + } + } + if (dmabuf->count > dmabuf->userfragsize) + wake_up(&dmabuf->wait); + } + /* error handling and process wake up for DAC */ + if (dmabuf->enable == DAC_RUNNING) { + /* update hardware pointer */ + hwptr = i810_get_dma_addr(state, 0); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; +#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) + printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#endif + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if((inb(state->card->iobase + PO_CIV) & 31) != + (inb(state->card->iobase + PO_LVI) & 31)) { + printk(KERN_WARNING "i810_audio: DMA overrun on write\n"); + printk("i810_audio: CIV %d, LVI %d, hwptr %x, " + "count %d\n", + inb(state->card->iobase + PO_CIV) & 31, + inb(state->card->iobase + PO_LVI) & 31, + dmabuf->hwptr, dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize-dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } +} + +static inline int i810_get_free_write_space(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int free; + + i810_update_ptr(state); + // catch underruns during playback + if (dmabuf->count < 0) { + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + free = dmabuf->dmasize - dmabuf->count; + free -= (dmabuf->hwptr % dmabuf->fragsize); + if(free < 0) + return(0); + return(free); +} + +static inline int i810_get_available_read_data(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int avail; + + i810_update_ptr(state); + // catch overruns during record + if (dmabuf->count > dmabuf->dmasize) { + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + avail = dmabuf->count; + avail -= (dmabuf->hwptr % dmabuf->fragsize); + if(avail < 0) + return(0); + return(avail); +} + +static int drain_dac(struct i810_state *state, int signals_allowed) +{ + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + + if (!dmabuf->ready) + return 0; + if(dmabuf->mapped) { + stop_dac(state); + return 0; + } + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + if(!dmabuf->enable) { + dmabuf->trigger = PCM_ENABLE_OUTPUT; + i810_update_lvi(state,0); + } + if (signal_pending(current) && signals_allowed) { + break; + } + + /* It seems that we have to set the current state to + * TASK_INTERRUPTIBLE every time to make the process + * really go to sleep. This also has to be *after* the + * update_ptr() call because update_ptr is likely to + * do a wake_up() which will unset this before we ever + * try to sleep, resuling in a tight loop in this code + * instead of actually sleeping and waiting for an + * interrupt to wake us up! + */ + set_current_state(TASK_INTERRUPTIBLE); + /* + * set the timeout to significantly longer than it *should* + * take for the DAC to drain the DMA buffer + */ + tmo = (count * HZ) / (dmabuf->rate); + if (!schedule_timeout(tmo >= 2 ? tmo : 2)){ + printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n"); + count = 0; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &wait); + if(count > 0 && signal_pending(current) && signals_allowed) + return -ERESTARTSYS; + stop_dac(state); + return 0; +} + +static void i810_channel_interrupt(struct i810_card *card) +{ + int i, count; + +#ifdef DEBUG_INTERRUPTS + printk("CHANNEL "); +#endif + for(i=0;istates[i]; + struct i810_channel *c; + struct dmabuf *dmabuf; + unsigned long port = card->iobase; + u16 status; + + if(!state) + continue; + if(!state->dmabuf.ready) + continue; + dmabuf = &state->dmabuf; + if(dmabuf->enable & DAC_RUNNING) { + c=dmabuf->write_channel; + } else if(dmabuf->enable & ADC_RUNNING) { + c=dmabuf->read_channel; + } else /* This can occur going from R/W to close */ + continue; + + port+=c->port; + + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + status = inw(port + OFF_PICB); + else + status = inw(port + OFF_SR); + +#ifdef DEBUG_INTERRUPTS + printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status); +#endif + if(status & DMA_INT_COMPLETE) + { + /* only wake_up() waiters if this interrupt signals + * us being beyond a userfragsize of data open or + * available, and i810_update_ptr() does that for + * us + */ + i810_update_ptr(state); +#ifdef DEBUG_INTERRUPTS + printk("COMP %d ", dmabuf->hwptr / + dmabuf->fragsize); +#endif + } + if(status & (DMA_INT_LVI | DMA_INT_DCH)) + { + /* wake_up() unconditionally on LVI and DCH */ + i810_update_ptr(state); + wake_up(&dmabuf->wait); +#ifdef DEBUG_INTERRUPTS + if(status & DMA_INT_LVI) + printk("LVI "); + if(status & DMA_INT_DCH) + printk("DCH -"); +#endif + if(dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else + count = dmabuf->dmasize - dmabuf->count; + if(count > 0) { + outb(inb(port+OFF_CR) | 1, port+OFF_CR); +#ifdef DEBUG_INTERRUPTS + printk(" CONTINUE "); +#endif + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); +#ifdef DEBUG_INTERRUPTS + printk(" STOP "); +#endif + } + } + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + outw(status & DMA_INT_MASK, port + OFF_PICB); + else + outw(status & DMA_INT_MASK, port + OFF_SR); + } +#ifdef DEBUG_INTERRUPTS + printk(")\n"); +#endif +} + +static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct i810_card *card = (struct i810_card *)dev_id; + u32 status; + + spin_lock(&card->lock); + + status = inl(card->iobase + GLOB_STA); + + if(!(status & INT_MASK)) + { + spin_unlock(&card->lock); + return; /* not for us */ + } + + if(status & (INT_PO|INT_PI|INT_MC)) + i810_channel_interrupt(card); + + /* clear 'em */ + outl(status & INT_MASK, card->iobase + GLOB_STA); + spin_unlock(&card->lock); +} + +/* in this loop, dmabuf.count signifies the amount of data that is + waiting to be copied to the user's buffer. It is filled by the dma + machine and drained by this loop. */ + +static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_card *card=state ? state->card : 0; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; + DECLARE_WAITQUEUE(waita, current); + +#ifdef DEBUG2 + printk("i810_audio: i810_read called, count = %d\n", count); +#endif + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & DAC_RUNNING) + return -ENODEV; + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = card->alloc_rec_pcm_channel(card); + if (!dmabuf->read_channel) { + return -EBUSY; + } + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; + cnt = i810_get_available_read_data(state); + // this is to make the copy_to_user simpler below + if(cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&card->lock, flags); + + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + /* + * Don't let us deadlock. The ADC won't start if + * dmabuf->trigger isn't set. A call to SETTRIGGER + * could have turned it off after we set it to on + * previously. + */ + dmabuf->trigger = PCM_ENABLE_INPUT; + /* + * This does three things. Updates LVI to be correct, + * makes sure the ADC is running, and updates the + * hwptr. + */ + i810_update_lvi(state,1); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto done; + } + /* Set the timeout to how long it would take to fill + * two of our buffers. If we haven't been woke up + * by then, then we know something is wrong. + */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { +#ifdef DEBUG + printk(KERN_ERR "i810_audio: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer overrun, we delay the recovery until next time the + while loop begin and we REALLY have space to record */ + } + if (signal_pending(current)) { + ret = ret ? ret : -ERESTARTSYS; + goto done; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) ret = -EFAULT; + goto done; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&card->lock, flags); + + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + } + done: + i810_update_lvi(state,1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + return ret; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to + the soundcard. it is drained by the dma machine and filled by this loop. */ +static ssize_t i810_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_card *card=state ? state->card : 0; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr = 0; + int cnt, x; + DECLARE_WAITQUEUE(waita, current); + +#ifdef DEBUG2 + printk("i810_audio: i810_write called, count = %d\n", count); +#endif + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & ADC_RUNNING) + return -ENODEV; + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = card->alloc_pcm_channel(card); + if(!dmabuf->write_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + + swptr = dmabuf->swptr; + cnt = i810_get_free_write_space(state); + /* Bound the maximum size to how much we can copy to the + * dma buffer before we hit the end. If we have more to + * copy then it will get done in a second pass of this + * loop starting from the beginning of the buffer. + */ + if(cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&state->card->lock, flags); + +#ifdef DEBUG2 + printk(KERN_INFO "i810_audio: i810_write: %d bytes available space\n", cnt); +#endif + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + // There is data waiting to be played + /* + * Force the trigger setting since we would + * deadlock with it set any other way + */ + dmabuf->trigger = PCM_ENABLE_OUTPUT; + i810_update_lvi(state,0); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto ret; + } + /* Not strictly correct but works */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer underrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { +#ifdef DEBUG + printk(KERN_ERR "i810_audio: playback schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer underrun, we delay the recovery until next time the + while loop begin and we REALLY have data to play */ + //return ret; + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto ret; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf+swptr,buffer,cnt)) { + if (!ret) ret = -EFAULT; + goto ret; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + + dmabuf->swptr = swptr; + dmabuf->count += cnt; + + count -= cnt; + buffer += cnt; + ret += cnt; + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (swptr % dmabuf->fragsize) { + x = dmabuf->fragsize - (swptr % dmabuf->fragsize); + memset(dmabuf->rawbuf + swptr, '\0', x); + } +ret: + i810_update_lvi(state,0); + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int i810_poll(struct file *file, struct poll_table_struct *wait) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned int mask = 0; + + if(!dmabuf->ready) + return 0; + poll_wait(file, &dmabuf->wait, wait); + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable & ADC_RUNNING || + dmabuf->trigger & PCM_ENABLE_INPUT) { + if (i810_get_available_read_data(state) >= + (signed)dmabuf->userfragsize) + mask |= POLLIN | POLLRDNORM; + } + if (dmabuf->enable & DAC_RUNNING || + dmabuf->trigger & PCM_ENABLE_OUTPUT) { + if (i810_get_free_write_space(state) >= + (signed)dmabuf->userfragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&state->card->lock, flags); + return mask; +} + +static int i810_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + int ret = -EINVAL; + unsigned long size; + + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if (!dmabuf->write_channel && + (dmabuf->write_channel = + state->card->alloc_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if (vma->vm_flags & VM_READ) { + if (!dmabuf->read_channel && + (dmabuf->read_channel = + state->card->alloc_rec_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if ((ret = prog_dmabuf(state, 0)) != 0) + goto out; + + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) + goto out; + dmabuf->mapped = 1; + dmabuf->trigger = 0; + ret = 0; +#ifdef DEBUG_MMAP + printk("i810_audio: mmap'ed %ld bytes of data space\n", size); +#endif +out: + unlock_kernel(); + return ret; +} + +static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_channel *c = NULL; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + unsigned int i_glob_cnt; + int val = 0, ret; + struct ac97_codec *codec = state->card->ac97_codec[0]; + +#ifdef DEBUG + printk("i810_audio: i810_ioctl, arg=0x%x, cmd=", arg ? *(int *)arg : 0); +#endif + + switch (cmd) + { + case OSS_GETVERSION: +#ifdef DEBUG + printk("OSS_GETVERSION\n"); +#endif + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: +#ifdef DEBUG + printk("SNDCTL_DSP_RESET\n"); +#endif + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable == DAC_RUNNING) { + c = dmabuf->write_channel; + __stop_dac(state); + } + if (dmabuf->enable == ADC_RUNNING) { + c = dmabuf->read_channel; + __stop_adc(state); + } + if (c != NULL) { + outb(2, state->card->iobase+c->port+OFF_CR); /* reset DMA machine */ + outl((u32)state->card->chandma + + c->num*sizeof(struct i810_channel), + state->card->iobase+c->port+OFF_BDBAR); + outb(0, state->card->iobase+c->port+OFF_CIV); + outb(0, state->card->iobase+c->port+OFF_LVI); + } + + spin_unlock_irqrestore(&state->card->lock, flags); + synchronize_irq(); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + return 0; + + case SNDCTL_DSP_SYNC: +#ifdef DEBUG + printk("SNDCTL_DSP_SYNC\n"); +#endif + if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK) + return 0; + if((val = drain_dac(state, 1))) + return val; + dmabuf->total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ +#ifdef DEBUG + printk("SNDCTL_DSP_SPEED\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + if ( (state->card->ac97_status & SPDIF_ON) ) { /* S/PDIF Enabled */ + /* AD1886 only supports 48000, need to check that */ + if ( i810_valid_spdif_rate ( codec, val ) ) { + /* Set DAC rate */ + i810_set_spdif_output ( state, -1, 0 ); + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, val ); + if ( ! (state->card->ac97_status & SPDIF_ON) ) { + val = dmabuf->rate; + } + } else { /* Not a valid rate for S/PDIF, ignore it */ + val = dmabuf->rate; + } + } else { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + return put_user(dmabuf->rate, (int *)arg); + + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ +#ifdef DEBUG + printk("SNDCTL_DSP_STEREO\n"); +#endif + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + return put_user(1, (int *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0))) + return val; + } + if (file->f_mode & FMODE_READ) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 1))) + return val; + } +#ifdef DEBUG + printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize); +#endif + return put_user(dmabuf->userfragsize, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ +#ifdef DEBUG + printk("SNDCTL_DSP_GETFMTS\n"); +#endif + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETFMT\n"); +#endif + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_CHANNELS: +#ifdef DEBUG + printk("SNDCTL_DSP_CHANNELS\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val > 0) { + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + } else { + return put_user(state->card->channels, (int *)arg); + } + + /* ICH and ICH0 only support 2 channels */ + if ( state->card->pci_id == 0x2415 || state->card->pci_id == 0x2425 ) + return put_user(2, (int *)arg); + + /* Multi-channel support was added with ICH2. Bits in */ + /* Global Status and Global Control register are now */ + /* used to indicate this. */ + + i_glob_cnt = inl(state->card->iobase + GLOB_CNT); + + /* Current # of channels enabled */ + if ( i_glob_cnt & 0x0100000 ) + ret = 4; + else if ( i_glob_cnt & 0x0200000 ) + ret = 6; + else + ret = 2; + + switch ( val ) { + case 2: /* 2 channels is always supported */ + outl(state->card->iobase + GLOB_CNT, (i_glob_cnt & 0xcfffff)); + /* Do we need to change mixer settings???? */ + break; + case 4: /* Supported on some chipsets, better check first */ + if ( state->card->channels >= 4 ) { + outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0100000)); + /* Do we need to change mixer settings??? */ + } else { + val = ret; + } + break; + case 6: /* Supported on some chipsets, better check first */ + if ( state->card->channels >= 6 ) { + outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0200000)); + /* Do we need to change mixer settings??? */ + } else { + val = ret; + } + break; + default: /* nothing else is ever supported by the chipset */ + val = ret; + break; + } + + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */ + /* we update the swptr to the end of the last sg segment then return */ +#ifdef DEBUG + printk("SNDCTL_DSP_POST\n"); +#endif + if(!dmabuf->ready || (dmabuf->enable != DAC_RUNNING)) + return 0; + if((dmabuf->swptr % dmabuf->fragsize) != 0) { + val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize); + dmabuf->swptr += val; + dmabuf->count += val; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; +#ifdef DEBUG + printk("SNDCTL_DSP_SUBDIVIDE %d\n", val); +#endif + dmabuf->subdivision = val; + dmabuf->ready = 0; + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + + dmabuf->ossfragsize = 1<<(val & 0xffff); + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags) + return -EINVAL; + /* + * Bound the frag size into our allowed range of 256 - 4096 + */ + if (dmabuf->ossfragsize < 256) + dmabuf->ossfragsize = 256; + else if (dmabuf->ossfragsize > 4096) + dmabuf->ossfragsize = 4096; + /* + * The numfrags could be something reasonable, or it could + * be 0xffff meaning "Give me as much as possible". So, + * we check the numfrags * fragsize doesn't exceed our + * 64k buffer limit, nor is it less than our 8k minimum. + * If it fails either one of these checks, then adjust the + * number of fragments, not the size of them. It's OK if + * our number of fragments doesn't equal 32 or anything + * like our hardware based number now since we are using + * a different frag count for the hardware. Before we get + * into this though, bound the maxfrags to avoid overflow + * issues. A reasonable bound would be 64k / 256 since our + * maximum buffer size is 64k and our minimum frag size is + * 256. On the other end, our minimum buffer size is 8k and + * our maximum frag size is 4k, so the lower bound should + * be 2. + */ + + if(dmabuf->ossmaxfrags > 256) + dmabuf->ossmaxfrags = 256; + else if (dmabuf->ossmaxfrags < 2) + dmabuf->ossmaxfrags = 2; + + val = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + while (val < 8192) { + val <<= 1; + dmabuf->ossmaxfrags <<= 1; + } + while (val > 65536) { + val >>= 1; + dmabuf->ossmaxfrags >>= 1; + } + dmabuf->ready = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val, + dmabuf->ossfragsize, dmabuf->ossmaxfrags); +#endif + + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; + else + abinfo.bytes = i810_get_free_write_space(state); + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes, + abinfo.fragsize, abinfo.fragments, abinfo.fragstotal); +#endif + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + val = i810_get_free_write_space(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.ptr = dmabuf->hwptr; + cinfo.blocks = val/dmabuf->userfragsize; + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __i810_update_lvi(state, 0); + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + abinfo.bytes = i810_get_available_read_data(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes, + abinfo.fragsize, abinfo.fragments, abinfo.fragstotal); +#endif + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + val = i810_get_available_read_data(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = val/dmabuf->userfragsize; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->count -= val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __i810_update_lvi(state, 1); + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_NONBLOCK: +#ifdef DEBUG + printk("SNDCTL_DSP_NONBLOCK\n"); +#endif + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCAPS\n"); +#endif + return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, + (int *)arg); + + case SNDCTL_DSP_GETTRIGGER: + val = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger); +#endif + return put_user(dmabuf->trigger, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val); +#endif + if( !(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) { + stop_adc(state); + } + if( !(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) { + stop_dac(state); + } + dmabuf->trigger = val; + if(val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) { + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = state->card->alloc_pcm_channel(state->card); + if (!dmabuf->write_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = i810_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __i810_update_lvi(state, 0); + spin_unlock_irqrestore(&state->card->lock, flags); + } else + start_dac(state); + } + if(val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) { + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card); + if (!dmabuf->read_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + } + i810_update_lvi(state, 1); + start_adc(state); + } + return 0; + + case SNDCTL_DSP_SETDUPLEX: +#ifdef DEBUG + printk("SNDCTL_DSP_SETDUPLEX\n"); +#endif + return -EINVAL; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); +#ifdef DEBUG + printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count); +#endif + return put_user(val, (int *)arg); + + case SOUND_PCM_READ_RATE: +#ifdef DEBUG + printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate); +#endif + return put_user(dmabuf->rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: +#ifdef DEBUG + printk("SOUND_PCM_READ_CHANNELS\n"); +#endif + return put_user(2, (int *)arg); + + case SOUND_PCM_READ_BITS: +#ifdef DEBUG + printk("SOUND_PCM_READ_BITS\n"); +#endif + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETSPDIF\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* Check to make sure the codec supports S/PDIF transmitter */ + + if((state->card->ac97_features & 4)) { + /* mask out the transmitter speed bits so the user can't set them */ + val &= ~0x3000; + + /* Add the current transmitter speed bits to the passed value */ + ret = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + val |= (ret & 0x3000); + + i810_ac97_set(codec, AC97_SPDIF_CONTROL, val); + if(i810_ac97_get(codec, AC97_SPDIF_CONTROL) != val ) { + printk(KERN_ERR "i810_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); + return -EFAULT; + } + } +#ifdef DEBUG + else + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n"); +#endif + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETSPDIF\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* Check to make sure the codec supports S/PDIF transmitter */ + + if(!(state->card->ac97_features & 4)) { +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n"); +#endif + val = 0; + } else { + val = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + } + //return put_user((val & 0xcfff), (int *)arg); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETCHANNELMASK: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCHANNELMASK\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* Based on AC'97 DAC support, not ICH hardware */ + val = DSP_BIND_FRONT; + if ( state->card->ac97_features & 0x0004 ) + val |= DSP_BIND_SPDIF; + + if ( state->card->ac97_features & 0x0080 ) + val |= DSP_BIND_SURR; + if ( state->card->ac97_features & 0x0140 ) + val |= DSP_BIND_CENTER_LFE; + + return put_user(val, (int *)arg); + + case SNDCTL_DSP_BIND_CHANNEL: +#ifdef DEBUG + printk("SNDCTL_DSP_BIND_CHANNEL\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + if ( val == DSP_BIND_QUERY ) { + val = DSP_BIND_FRONT; /* Always report this as being enabled */ + if ( state->card->ac97_status & SPDIF_ON ) + val |= DSP_BIND_SPDIF; + else { + if ( state->card->ac97_status & SURR_ON ) + val |= DSP_BIND_SURR; + if ( state->card->ac97_status & CENTER_LFE_ON ) + val |= DSP_BIND_CENTER_LFE; + } + } else { /* Not a query, set it */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if ( dmabuf->enable == DAC_RUNNING ) { + stop_dac(state); + } + if ( val & DSP_BIND_SPDIF ) { /* Turn on SPDIF */ + /* Ok, this should probably define what slots + * to use. For now, we'll only set it to the + * defaults: + * + * non multichannel codec maps to slots 3&4 + * 2 channel codec maps to slots 7&8 + * 4 channel codec maps to slots 6&9 + * 6 channel codec maps to slots 10&11 + * + * there should be some way for the app to + * select the slot assignment. + */ + + i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, dmabuf->rate ); + if ( !(state->card->ac97_status & SPDIF_ON) ) + val &= ~DSP_BIND_SPDIF; + } else { + int mask; + int channels; + + /* Turn off S/PDIF if it was on */ + if ( state->card->ac97_status & SPDIF_ON ) + i810_set_spdif_output ( state, -1, 0 ); + + mask = val & (DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + val = DSP_BIND_FRONT; + channels = 2; + break; + } + i810_set_dac_channels ( state, channels ); + + /* check that they really got turned on */ + if ( !state->card->ac97_status & SURR_ON ) + val &= ~DSP_BIND_SURR; + if ( !state->card->ac97_status & CENTER_LFE_ON ) + val &= ~DSP_BIND_CENTER_LFE; + } + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: +#ifdef DEBUG + printk("SNDCTL_* -EINVAL\n"); +#endif + return -EINVAL; + } + return -EINVAL; +} + +static int i810_open(struct inode *inode, struct file *file) +{ + int i = 0; + struct i810_card *card = devs; + struct i810_state *state = NULL; + struct dmabuf *dmabuf = NULL; + + /* find an avaiable virtual channel (instance of /dev/dsp) */ + while (card != NULL) { + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + } + for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct i810_state *) + kmalloc(sizeof(struct i810_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct i810_state)); + dmabuf = &state->dmabuf; + goto found_virt; + } + } + card = card->next; + } + /* no more virtual channel avaiable */ + if (!state) + return -ENODEV; + +found_virt: + /* initialize the virtual channel */ + state->virt = i; + state->card = card; + state->magic = I810_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = state; + dmabuf->trigger = 0; + + /* allocate hardware channels */ + if(file->f_mode & FMODE_READ) { + if((dmabuf->read_channel = card->alloc_rec_pcm_channel(card)) == NULL) { + kfree (card->states[i]); + card->states[i] = NULL;; + return -EBUSY; + } + dmabuf->trigger |= PCM_ENABLE_INPUT; + i810_set_adc_rate(state, 8000); + } + if(file->f_mode & FMODE_WRITE) { + if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) { + kfree (card->states[i]); + card->states[i] = NULL;; + return -EBUSY; + } + /* Initialize to 8kHz? What if we don't support 8kHz? */ + /* Let's change this to check for S/PDIF stuff */ + + dmabuf->trigger |= PCM_ENABLE_OUTPUT; + if ( spdif_locked ) { + i810_set_dac_rate(state, spdif_locked); + i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked); + } else { + i810_set_dac_rate(state, 8000); + } + } + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample, but we don't support those so we + set it immediately to stereo and 16bit, which is all we do support */ + dmabuf->fmt |= I810_FMT_16BIT | I810_FMT_STEREO; + dmabuf->ossfragsize = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + return 0; +} + +static int i810_release(struct inode *inode, struct file *file) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct i810_card *card = state->card; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + + lock_kernel(); + + /* stop DMA state machine and free DMA buffers/channels */ + if(dmabuf->trigger & PCM_ENABLE_OUTPUT) { + drain_dac(state, 0); + } + if(dmabuf->trigger & PCM_ENABLE_INPUT) { + stop_adc(state); + } + spin_lock_irqsave(&card->lock, flags); + dealloc_dmabuf(state); + if (file->f_mode & FMODE_WRITE) { + state->card->free_pcm_channel(state->card, dmabuf->write_channel->num); + } + if (file->f_mode & FMODE_READ) { + state->card->free_pcm_channel(state->card, dmabuf->read_channel->num); + } + + state->card->states[state->virt] = NULL; + kfree(state); + spin_unlock_irqrestore(&card->lock, flags); + unlock_kernel(); + + return 0; +} + +static /*const*/ struct file_operations i810_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: i810_read, + write: i810_write, + poll: i810_poll, + ioctl: i810_ioctl, + mmap: i810_mmap, + open: i810_open, + release: i810_release, +}; + +/* Write AC97 codec registers */ + +static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct i810_card *card = dev->private_data; + int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + + while(count-- && (inb(card->iobase + CAS) & 1)) + udelay(1); + + return inw(card->ac97base + reg_set); +} + +static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct i810_card *card = dev->private_data; + int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); + + while(count-- && (inb(card->iobase + CAS) & 1)) + udelay(1); + outw(data, card->ac97base + reg_set); +} + + +/* OSS /dev/mixer file operation methods */ + +static int i810_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + unsigned int minor = minor(inode->i_rdev); + struct i810_card *card = devs; + + for (card = devs; card != NULL; card = card->next) { + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + } + for (i = 0; i < NR_AC97 && card && !card->initializing; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) { + file->private_data = card->ac97_codec[i]; + return 0; + } + } + return -ENODEV; +} + +static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations i810_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: i810_ioctl_mixdev, + open: i810_open_mixdev, +}; + +/* AC97 codec initialisation. These small functions exist so we don't + duplicate code between module init and apm resume */ + +static inline int i810_ac97_exists(struct i810_card *card,int ac97_number) +{ + u32 reg = inl(card->iobase + GLOB_STA); + return (reg & (0x100 << ac97_number)); +} + +static inline int i810_ac97_enable_variable_rate(struct ac97_codec *codec) +{ + i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9); + i810_ac97_set(codec,AC97_EXTENDED_STATUS, + i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800); + + return (i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1); +} + + +static int i810_ac97_probe_and_powerup(struct i810_card *card,struct ac97_codec *codec) +{ + /* Returns 0 on failure */ + int i; + + if (ac97_probe_codec(codec) == 0) return 0; + + /* power it all up */ + i810_ac97_set(codec, AC97_POWER_CONTROL, + i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i=10; + i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); + i--) + { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + } + return i; +} + +/* if I knew what this did, I'd give it a better name */ +static int i810_ac97_random_init_stuff(struct i810_card *card) +{ + u32 reg = inl(card->iobase + GLOB_CNT); + int i; + + if((reg&2)==0) /* Cold required */ + reg|=2; + else + reg|=4; /* Warm */ + + reg&=~8; /* ACLink on */ + outl(reg , card->iobase + GLOB_CNT); + + for(i=0;i<10;i++) + { + if((inl(card->iobase+GLOB_CNT)&4)==0) + break; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + } + if(i==10) + { + printk(KERN_ERR "i810_audio: AC'97 reset failed.\n"); + return 0; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/2); + reg = inl(card->iobase + GLOB_STA); + inw(card->ac97base); + return 1; +} + +static int __init i810_ac97_init(struct i810_card *card) +{ + int num_ac97 = 0; + int total_channels = 0; + struct ac97_codec *codec; + u16 eid; + u32 reg; + + if(!i810_ac97_random_init_stuff(card)) return 0; + + /* Number of channels supported */ + /* What about the codec? Just because the ICH supports */ + /* multiple channels doesn't mean the codec does. */ + /* we'll have to modify this in the codec section below */ + /* to reflect what the codec has. */ + /* ICH and ICH0 only support 2 channels so don't bother */ + /* to check.... */ + + card->channels = 2; + reg = inl(card->iobase + GLOB_STA); + if ( reg & 0x0200000 ) + card->channels = 6; + else if ( reg & 0x0100000 ) + card->channels = 4; + printk("i810_audio: Audio Controller supports %d channels.\n", card->channels); + + inw(card->ac97base); + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + + /* Assume codec isn't available until we go through the + * gauntlet below */ + card->ac97_codec[num_ac97] = NULL; + + /* The ICH programmer's reference says you should */ + /* check the ready status before probing. So we chk */ + /* What do we do if it's not ready? Wait and try */ + /* again, or abort? */ + if (!i810_ac97_exists(card,num_ac97)) { + if(num_ac97 == 0) + printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); + break; /* I think this works, if not ready stop */ + } + + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + + codec->codec_read = i810_ac97_get; + codec->codec_write = i810_ac97_set; + + if(!i810_ac97_probe_and_powerup(card,codec)) { + printk("i810_audio: timed out waiting for codec %d analog ready", num_ac97); + kfree(codec); + break; /* it didn't work */ + } + /* Store state information about S/PDIF transmitter */ + card->ac97_status = 0; + + /* Don't attempt to get eid until powerup is complete */ + eid = i810_ac97_get(codec, AC97_EXTENDED_ID); + + if(eid==0xFFFFFF) + { + printk(KERN_WARNING "i810_audio: no codec attached ?\n"); + kfree(codec); + break; + } + + card->ac97_features = eid; + + /* Now check the codec for useful features to make up for + the dumbness of the 810 hardware engine */ + + if(!(eid&0x0001)) + printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n"); + else + { + if(!i810_ac97_enable_variable_rate(codec)) { + printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n"); + card->ac97_features&=~1; + } + } + + /* Determine how many channels the codec(s) support */ + /* - The primary codec always supports 2 */ + /* - If the codec supports AMAP, surround DACs will */ + /* automaticlly get assigned to slots. */ + /* * Check for surround DACs and increment if */ + /* found. */ + /* - Else check if the codec is revision 2.2 */ + /* * If surround DACs exist, assign them to slots */ + /* and increment channel count. */ + + /* All of this only applies to ICH2 and above. ICH */ + /* and ICH0 only support 2 channels. ICH2 will only */ + /* support multiple codecs in a "split audio" config. */ + /* as described above. */ + + /* TODO: Remove all the debugging messages! */ + + if((eid & 0xc000) == 0) /* primary codec */ + total_channels += 2; + + if(eid & 0x200) { /* GOOD, AMAP support */ + if (eid & 0x0080) /* L/R Surround channels */ + total_channels += 2; + if (eid & 0x0140) /* LFE and Center channels */ + total_channels += 2; + printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", num_ac97, total_channels); + } else if (eid & 0x0400) { /* this only works on 2.2 compliant codecs */ + eid &= 0xffcf; + if((eid & 0xc000) != 0) { + switch ( total_channels ) { + case 2: + /* Set dsa1, dsa0 to 01 */ + eid |= 0x0010; + break; + case 4: + /* Set dsa1, dsa0 to 10 */ + eid |= 0x0020; + break; + case 6: + /* Set dsa1, dsa0 to 11 */ + eid |= 0x0030; + break; + } + total_channels += 2; + } + i810_ac97_set(codec, AC97_EXTENDED_ID, eid); + eid = i810_ac97_get(codec, AC97_EXTENDED_ID); + printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", num_ac97, eid); + if (eid & 0x0080) /* L/R Surround channels */ + total_channels += 2; + if (eid & 0x0140) /* LFE and Center channels */ + total_channels += 2; + printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", num_ac97, total_channels); + } else { + printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", num_ac97, total_channels); + } + + if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { + printk(KERN_ERR "i810_audio: couldn't register mixer!\n"); + kfree(codec); + break; + } + + card->ac97_codec[num_ac97] = codec; + } + + /* pick the minimum of channels supported by ICHx or codec(s) */ + card->channels = (card->channels > total_channels)?total_channels:card->channels; + + return num_ac97; +} + +static void __init i810_configure_clocking (void) +{ + struct i810_card *card; + struct i810_state *state; + struct dmabuf *dmabuf; + unsigned int i, offset, new_offset; + unsigned long flags; + + card = devs; + /* We could try to set the clocking for multiple cards, but can you even have + * more than one i810 in a machine? Besides, clocking is global, so unless + * someone actually thinks more than one i810 in a machine is possible and + * decides to rewrite that little bit, setting the rate for more than one card + * is a waste of time. + */ + if(card != NULL) { + state = card->states[0] = (struct i810_state *) + kmalloc(sizeof(struct i810_state), GFP_KERNEL); + if (state == NULL) + return; + memset(state, 0, sizeof(struct i810_state)); + dmabuf = &state->dmabuf; + + dmabuf->write_channel = card->alloc_pcm_channel(card); + state->virt = 0; + state->card = card; + state->magic = I810_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + dmabuf->fmt = I810_FMT_STEREO | I810_FMT_16BIT; + dmabuf->trigger = PCM_ENABLE_OUTPUT; + i810_set_dac_rate(state, 48000); + if(prog_dmabuf(state, 0) != 0) { + goto config_out_nodmabuf; + } + if(dmabuf->dmasize < 16384) { + goto config_out; + } + dmabuf->count = dmabuf->dmasize; + outb(31,card->iobase+dmabuf->write_channel->port+OFF_LVI); + save_flags(flags); + cli(); + start_dac(state); + offset = i810_get_dma_addr(state, 0); + mdelay(50); + new_offset = i810_get_dma_addr(state, 0); + stop_dac(state); + outb(2,card->iobase+dmabuf->write_channel->port+OFF_CR); + restore_flags(flags); + i = new_offset - offset; +#ifdef DEBUG + printk("i810_audio: %d bytes in 50 milliseconds\n", i); +#endif + if(i == 0) + goto config_out; + i = i / 4 * 20; + if (i > 48500 || i < 47500) { + clocking = clocking * clocking / i; + printk("i810_audio: setting clocking to %d\n", clocking); + } +config_out: + dealloc_dmabuf(state); +config_out_nodmabuf: + state->card->free_pcm_channel(state->card,state->dmabuf.write_channel->num); + kfree(state); + card->states[0] = NULL; + } +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ + +static int __init i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct i810_card *card; + + if (pci_enable_device(pci_dev)) + return -EIO; + + if (pci_set_dma_mask(pci_dev, I810_DMA_MASK)) { + printk(KERN_ERR "intel810: architecture does not support" + " 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "i810_audio: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + + card->initializing = 1; + card->iobase = pci_resource_start (pci_dev, 1); + card->ac97base = pci_resource_start (pci_dev, 0); + card->pci_dev = pci_dev; + card->pci_id = pci_id->device; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = I810_CARD_MAGIC; +#ifdef CONFIG_PM + card->pm_suspended=0; +#endif + spin_lock_init(&card->lock); + devs = card; + + pci_set_master(pci_dev); + + printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->ac97base, + card->irq); + + card->alloc_pcm_channel = i810_alloc_pcm_channel; + card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel; + card->alloc_rec_mic_channel = i810_alloc_rec_mic_channel; + card->free_pcm_channel = i810_free_pcm_channel; + + if ((card->channel = pci_alloc_consistent(pci_dev, + sizeof(struct i810_channel)*NR_HW_CH, &card->chandma)) == NULL) { + printk(KERN_ERR "i810: cannot allocate channel DMA memory\n"); + goto out_mem; + } + + { /* We may dispose of this altogether some time soon, so... */ + struct i810_channel *cp = card->channel; + + cp[0].offset = 0; + cp[0].port = 0x00; + cp[0].num = 0; + cp[1].offset = 0; + cp[1].port = 0x10; + cp[1].num = 1; + cp[2].offset = 0; + cp[2].port = 0x20; + cp[2].num = 2; + } + + /* claim our iospace and irq */ + request_region(card->iobase, 64, card_names[pci_id->driver_data]); + request_region(card->ac97base, 256, card_names[pci_id->driver_data]); + + if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + goto out_chan; + } + + /* initialize AC97 codec and register /dev/mixer */ + if (i810_ac97_init(card) <= 0) { + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + free_irq(card->irq, card); + goto out_chan; + } + pci_set_drvdata(pci_dev, card); + + if(clocking == 0) { + clocking = 48000; + i810_configure_clocking(); + } + + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) { + int i; + printk(KERN_ERR "i810_audio: couldn't register DSP device!\n"); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + free_irq(card->irq, card); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree (card->ac97_codec[i]); + } + goto out_chan; + } + card->initializing = 0; + return 0; + + out_chan: + pci_free_consistent(pci_dev, sizeof(struct i810_channel)*NR_HW_CH, + card->channel, card->chandma); + out_mem: + kfree(card); + return -ENODEV; +} + +static void __devexit i810_remove(struct pci_dev *pci_dev) +{ + int i; + struct i810_card *card = pci_get_drvdata(pci_dev); + /* free hardware resources */ + free_irq(card->irq, devs); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree (card->ac97_codec[i]); + card->ac97_codec[i] = NULL; + } + unregister_sound_dsp(card->dev_audio); + kfree(card); +} + +#ifdef CONFIG_PM +static int i810_pm_suspend(struct pci_dev *dev, u32 pm_state) +{ + struct i810_card *card = pci_get_drvdata(dev); + struct i810_state *state; + unsigned long flags; + struct dmabuf *dmabuf; + int i,num_ac97; +#ifdef DEBUG + printk("i810_audio: i810_pm_suspend called\n"); +#endif + if(!card) return 0; + spin_lock_irqsave(&card->lock, flags); + card->pm_suspended=1; + for(i=0;istates[i]; + if(!state) continue; + /* this happens only if there are open files */ + dmabuf = &state->dmabuf; + if(dmabuf->enable & DAC_RUNNING || + (dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) { + state->pm_saved_dac_rate=dmabuf->rate; + stop_dac(state); + } else { + state->pm_saved_dac_rate=0; + } + if(dmabuf->enable & ADC_RUNNING) { + state->pm_saved_adc_rate=dmabuf->rate; + stop_adc(state); + } else { + state->pm_saved_adc_rate=0; + } + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + + spin_unlock_irqrestore(&card->lock, flags); + + /* save mixer settings */ + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + if(!codec) continue; + for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) { + if((supported_mixer(codec,i)) && + (codec->read_mixer)) { + card->pm_saved_mixer_settings[i][num_ac97]= + codec->read_mixer(codec,i); + } + } + } + pci_save_state(dev,card->pm_save_state); /* XXX do we need this? */ + pci_disable_device(dev); /* disable busmastering */ + pci_set_power_state(dev,3); /* Zzz. */ + + return 0; +} + + +static int i810_pm_resume(struct pci_dev *dev) +{ + int num_ac97,i=0; + struct i810_card *card=pci_get_drvdata(dev); + pci_enable_device(dev); + pci_restore_state (dev,card->pm_save_state); + + /* observation of a toshiba portege 3440ct suggests that the + hardware has to be more or less completely reinitialized from + scratch after an apm suspend. Works For Me. -dan */ + + i810_ac97_random_init_stuff(card); + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + /* check they haven't stolen the hardware while we were + away */ + if(!codec || !i810_ac97_exists(card,num_ac97)) { + if(num_ac97) continue; + else BUG(); + } + if(!i810_ac97_probe_and_powerup(card,codec)) BUG(); + + if((card->ac97_features&0x0001)) { + /* at probe time we found we could do variable + rates, but APM suspend has made it forget + its magical powers */ + if(!i810_ac97_enable_variable_rate(codec)) BUG(); + } + /* we lost our mixer settings, so restore them */ + for(i=0;i< SOUND_MIXER_NRDEVICES ;i++) { + if(supported_mixer(codec,i)){ + int val=card-> + pm_saved_mixer_settings[i][num_ac97]; + codec->mixer_state[i]=val; + codec->write_mixer(codec,i, + (val & 0xff) , + ((val >> 8) & 0xff) ); + } + } + } + + /* we need to restore the sample rate from whatever it was */ + for(i=0;istates[i]; + if(state) { + if(state->pm_saved_adc_rate) + i810_set_adc_rate(state,state->pm_saved_adc_rate); + if(state->pm_saved_dac_rate) + i810_set_dac_rate(state,state->pm_saved_dac_rate); + } + } + + + card->pm_suspended = 0; + + /* any processes that were reading/writing during the suspend + probably ended up here */ + for(i=0;istates[i]; + if(state) wake_up(&state->dmabuf.wait); + } + + return 0; +} +#endif /* CONFIG_PM */ + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Intel 810 audio support"); +MODULE_LICENSE("GPL"); +MODULE_PARM(ftsodell, "i"); +MODULE_PARM(clocking, "i"); +MODULE_PARM(strict_clocking, "i"); +MODULE_PARM(spdif_locked, "i"); + +#define I810_MODULE_NAME "intel810_audio" + +static struct pci_driver i810_pci_driver = { + name: I810_MODULE_NAME, + id_table: i810_pci_tbl, + probe: i810_probe, + remove: __devexit_p(i810_remove), +#ifdef CONFIG_PM + suspend: i810_pm_suspend, + resume: i810_pm_resume, +#endif /* CONFIG_PM */ +}; + + +static int __init i810_init_module (void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + + printk(KERN_INFO "Intel 810 + AC97 Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (!pci_register_driver(&i810_pci_driver)) { + pci_unregister_driver(&i810_pci_driver); + return -ENODEV; + } + if(ftsodell != 0) { + printk("i810_audio: ftsodell is now a deprecated option.\n"); + } + if(spdif_locked > 0 ) { + if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) { + printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked); + } else { + printk("i810_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + spdif_locked = 0; + } + } + + return 0; +} + +static void __exit i810_cleanup_module (void) +{ + pci_unregister_driver(&i810_pci_driver); +} + +module_init(i810_init_module); +module_exit(i810_cleanup_module); + +/* +Local Variables: +c-basic-offset: 8 +End: +*/ diff -Nru linux/sound/oss/ics2101.c linux-2.4.19-pre5-mjc/sound/oss/ics2101.c --- linux/sound/oss/ics2101.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ics2101.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,246 @@ +/* + * sound/ics2101.c + * + * Driver for the ICS2101 mixer of GUS v3.7. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init() + */ +#include +#include "sound_config.h" + +#include + +#include "gus.h" +#include "gus_hw.h" + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH| \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +extern int *gus_osp; +extern int gus_base; +static int volumes[ICS_MIXDEVS]; +static int left_fix[ICS_MIXDEVS] = +{1, 1, 1, 2, 1, 2}; +static int right_fix[ICS_MIXDEVS] = +{2, 2, 2, 1, 2, 1}; + +static int scale_vol(int vol) +{ + /* + * Experimental volume scaling by Risto Kankkunen. + * This should give smoother volume response than just + * a plain multiplication. + */ + + int e; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + vol = (31 * vol + 50) / 100; + e = 0; + if (vol) + { + while (vol < 16) + { + vol <<= 1; + e--; + } + vol -= 16; + e += 7; + } + return ((e << 4) + vol); +} + +static void write_mix(int dev, int chn, int vol) +{ + int *selector; + unsigned long flags; + int ctrl_addr = dev << 3; + int attn_addr = dev << 3; + + vol = scale_vol(vol); + + if (chn == CHN_LEFT) + { + selector = left_fix; + ctrl_addr |= 0x00; + attn_addr |= 0x02; + } + else + { + selector = right_fix; + ctrl_addr |= 0x01; + attn_addr |= 0x03; + } + + save_flags(flags); + cli(); + outb((ctrl_addr), u_MixSelect); + outb((selector[dev]), u_MixData); + outb((attn_addr), u_MixSelect); + outb(((unsigned char) vol), u_MixData); + restore_flags(flags); +} + +static int set_volumes(int dev, int vol) +{ + int left = vol & 0x00ff; + int right = (vol >> 8) & 0x00ff; + + if (left < 0) + left = 0; + if (left > 100) + left = 100; + if (right < 0) + right = 0; + if (right > 100) + right = 100; + + write_mix(dev, CHN_LEFT, left); + write_mix(dev, CHN_RIGHT, right); + + vol = left + (right << 8); + volumes[dev] = vol; + return vol; +} + +static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + if (((cmd >> 8) & 0xff) == 'M') { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + + if (get_user(val, (int *)arg)) + return -EFAULT; + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_MIC: + val = set_volumes(DEV_MIC, val); + break; + + case SOUND_MIXER_CD: + val = set_volumes(DEV_CD, val); + break; + + case SOUND_MIXER_LINE: + val = set_volumes(DEV_LINE, val); + break; + + case SOUND_MIXER_SYNTH: + val = set_volumes(DEV_GF1, val); + break; + + case SOUND_MIXER_VOLUME: + val = set_volumes(DEV_VOL, val); + break; + + default: + return -EINVAL; + } + return put_user(val, (int *)arg); + } else { + switch (cmd & 0xff) { + /* + * Return parameters + */ + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = volumes[DEV_MIC]; + break; + + case SOUND_MIXER_LINE: + val = volumes[DEV_LINE]; + break; + + case SOUND_MIXER_CD: + val = volumes[DEV_CD]; + break; + + case SOUND_MIXER_VOLUME: + val = volumes[DEV_VOL]; + break; + + case SOUND_MIXER_SYNTH: + val = volumes[DEV_GF1]; + break; + + default: + return -EINVAL; + } + return put_user(val, (int *)arg); + } + } + return -EINVAL; +} + +static struct mixer_operations ics2101_mixer_operations = +{ + owner: THIS_MODULE, + id: "ICS2101", + name: "ICS2101 Multimedia Mixer", + ioctl: ics2101_mixer_ioctl +}; + +int __init ics2101_mixer_init(void) +{ + int i; + int n; + + if ((n = sound_alloc_mixerdev()) != -1) + { + mixer_devs[n] = &ics2101_mixer_operations; + + /* + * Some GUS v3.7 cards had some channels flipped. Disable + * the flipping feature if the model id is other than 5. + */ + + if (inb(u_MixSelect) != 5) + { + for (i = 0; i < ICS_MIXDEVS; i++) + left_fix[i] = 1; + for (i = 0; i < ICS_MIXDEVS; i++) + right_fix[i] = 2; + } + set_volumes(DEV_GF1, 0x5a5a); + set_volumes(DEV_CD, 0x5a5a); + set_volumes(DEV_MIC, 0x0000); + set_volumes(DEV_LINE, 0x5a5a); + set_volumes(DEV_VOL, 0x5a5a); + set_volumes(DEV_UNUSED, 0x0000); + } + return n; +} diff -Nru linux/sound/oss/ite8172.c linux-2.4.19-pre5-mjc/sound/oss/ite8172.c --- linux/sound/oss/ite8172.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ite8172.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1952 @@ +/* + * ite8172.c -- ITE IT8172G Sound Driver. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Module command line parameters: + * + * Supported devices: + * /dev/dsp standard OSS /dev/dsp device + * /dev/mixer standard OSS /dev/mixer device + * + * Notes: + * + * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are + * taken, slightly modified or not at all, from the ES1371 driver, + * so refer to the credits in es1371.c for those. The rest of the + * code (probe, open, read, write, the ISR, etc.) is new. + * 2. The following support is untested: + * * Memory mapping the audio buffers, and the ioctl controls that go + * with it. + * * S/PDIF output. + * 3. The following is not supported: + * * I2S input. + * * legacy audio mode. + * 4. Support for volume button interrupts is implemented but doesn't + * work yet. + * + * Revision history + * 02.08.2001 0.1 Initial release + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define IT8172_DEBUG +#undef IT8172_VERBOSE_DEBUG +#define DBG(x) {} + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + + +/* + * Audio Controller register bit definitions follow. See + * include/asm/it8172/it8172.h for register offsets. + */ + +/* PCM Out Volume Reg */ +#define PCMOV_PCMOM (1<<15) /* PCM Out Mute default 1: mute */ +#define PCMOV_PCMRCG_BIT 8 /* PCM Right channel Gain */ +#define PCMOV_PCMRCG_MASK (0x1f<= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static void it8172_delay(int msec) +{ + unsigned long tmo; + signed long tmo2; + + if (in_interrupt()) + return; + + tmo = jiffies + (msec*HZ)/1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } +} + + +static unsigned short +get_compat_rate(unsigned* rate) +{ + unsigned rate_out = *rate; + unsigned short sr; + + if (rate_out >= 46050) { + sr = CC_SR_48000; rate_out = 48000; + } else if (rate_out >= 41250) { + sr = CC_SR_44100; rate_out = 44100; + } else if (rate_out >= 35200) { + sr = CC_SR_38400; rate_out = 38400; + } else if (rate_out >= 27025) { + sr = CC_SR_32000; rate_out = 32000; + } else if (rate_out >= 20625) { + sr = CC_SR_22050; rate_out = 22050; + } else if (rate_out >= 17600) { + sr = CC_SR_19200; rate_out = 19200; + } else if (rate_out >= 13513) { + sr = CC_SR_16000; rate_out = 16000; + } else if (rate_out >= 10313) { + sr = CC_SR_11025; rate_out = 11025; + } else if (rate_out >= 8800) { + sr = CC_SR_9600; rate_out = 9600; + } else if (rate_out >= 6750) { + sr = CC_SR_8000; rate_out = 8000; + } else { + sr = CC_SR_5500; rate_out = 5500; + } + + *rate = rate_out; + return sr; +} + +static void set_adc_rate(struct it8172_state *s, unsigned rate) +{ + unsigned long flags; + unsigned short sr; + + sr = get_compat_rate(&rate); + + spin_lock_irqsave(&s->lock, flags); + s->capcc &= ~CC_SR_MASK; + s->capcc |= sr; + outw(s->capcc, s->io+IT_AC_CAPCC); + spin_unlock_irqrestore(&s->lock, flags); + + s->adcrate = rate; +} + + +static void set_dac_rate(struct it8172_state *s, unsigned rate) +{ + unsigned long flags; + unsigned short sr; + + sr = get_compat_rate(&rate); + + spin_lock_irqsave(&s->lock, flags); + s->pcc &= ~CC_SR_MASK; + s->pcc |= sr; + outw(s->pcc, s->io+IT_AC_PCC); + spin_unlock_irqrestore(&s->lock, flags); + + s->dacrate = rate; +} + + +/* --------------------------------------------------------------------- */ + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp, data; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + printk(KERN_INFO PFX "rdcodec: codec ready poll expired!\n"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp |= CIRCP_RWC; // read command + outw(circp, s->io+IT_AC_CIRCP); + + /* now wait for the data */ + for (i = 0; i < POLL_COUNT; i++) + if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF) + break; + if (i == POLL_COUNT) + printk(KERN_INFO PFX "rdcodec: read poll expired!\n"); + + data = inw(s->io+IT_AC_CIRDP); + spin_unlock_irqrestore(&s->lock, flags); + + return data; +} + + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + printk(KERN_INFO PFX "wrcodec: codec ready poll expired!\n"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp &= ~CIRCP_RWC; // write command + + outw(data, s->io+IT_AC_CIRDP); // send data first + outw(circp, s->io+IT_AC_CIRCP); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void waitcodec(struct ac97_codec *codec) +{ + unsigned short temp; + + /* codec_wait is used to wait for a ready state after + an AC97_RESET. */ + it8172_delay(10); + + temp = rdcodec(codec, 0x26); + + // If power down, power up + if (temp & 0x3f00) { + // Power on + wrcodec(codec, 0x26, 0); + it8172_delay(100); + // Reread + temp = rdcodec(codec, 0x26); + } + + // Check if Codec REF,ANL,DAC,ADC ready***/ + if ((temp & 0x3f0f) != 0x000f) { + printk(KERN_INFO PFX "codec reg 26 status (0x%x) not ready!!\n", + temp); + return; + } +} + + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CSP; + outw(s->capcc, s->io+IT_AC_CAPCC); + + // disable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_CCIM, s->io+IT_AC_IMC); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CSP; + outw(s->pcc, s->io+IT_AC_PCC); + + // disable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_PCIM, s->io+IT_AC_IMC); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize + buf1 = virt_to_bus(db->nextOut); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_PCB1STA); + outl(buf2, s->io+IT_AC_PCB2STA); + db->curBufPtr = IT_AC_PCB1STA; + + // enable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC); + + s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CA; + outw(s->pcc, s->io+IT_AC_PCC); + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize + buf1 = virt_to_bus(db->nextIn); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_CAPB1STA); + outl(buf2, s->io+IT_AC_CAPB2STA); + db->curBufPtr = IT_AC_CAPB1STA; + + // enable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC); + + s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CA; + outw(s->capcc, s->io+IT_AC_CAPCC); + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +extern inline void dealloc_dmabuf(struct it8172_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, + db->rawbuf, db->dmaaddr); + } + db->rawbuf = db->nextIn = db->nextOut = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct it8172_state *s, struct dmabuf *db, + unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; + otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + db->count = 0; + db->nextIn = db->nextOut = db->rawbuf; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? + db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, db->dmasize); + + // set data length register + outw(db->fragsize, s->io+reg+2); + db->ready = 1; + + return 0; +} + +extern inline int prog_dmabuf_adc(struct it8172_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, + (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_CAPCC); +} + +extern inline int prog_dmabuf_dac(struct it8172_state *s) +{ + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac, s->dacrate, + (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_PCC); +} + + +/* hold spinlock for the following! */ + +static void it8172_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct it8172_state *s = (struct it8172_state *)dev_id; + struct dmabuf* dac = &s->dma_dac; + struct dmabuf* adc = &s->dma_adc; + unsigned char isc, vs; + unsigned short vol, mute; + unsigned long newptr; + + spin_lock(&s->lock); + + isc = inb(s->io+IT_AC_ISC); + + /* fastpath out, to ease interrupt sharing */ + if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI))) + return; + + /* clear audio interrupts first */ + outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC); + + /* handle volume button events */ + if (isc & ISC_VCI) { + vs = inb(s->io+IT_AC_VS); + outb(0, s->io+IT_AC_VS); + vol = inw(s->io+IT_AC_PCMOV); + mute = vol & PCMOV_PCMOM; + vol &= PCMOV_PCMLCG_MASK; + if ((vs & VS_VUP) && vol > 0) + vol--; + if ((vs & VS_VDP) && vol < 0x1f) + vol++; + vol |= (vol << PCMOV_PCMRCG_BIT); + if (vs & VS_VMP) + vol |= (mute ^ PCMOV_PCMOM); + outw(vol, s->io+IT_AC_PCMOV); + } + + /* update capture pointers */ + if (isc & ISC_CCI) { + if (adc->count > adc->dmasize - adc->fragsize) { + // Overrun. Stop ADC and log the error + stop_adc(s); + adc->error++; + printk(KERN_INFO PFX "adc overrun\n"); + } else { + newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize; + if (newptr >= adc->dmaaddr + adc->dmasize) + newptr -= adc->dmasize; + + outl(newptr, s->io+adc->curBufPtr); + adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ? + IT_AC_CAPB2STA : IT_AC_CAPB1STA; + + adc->nextIn += adc->fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + adc->count += adc->fragsize; + adc->total_bytes += adc->fragsize; + + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) + wake_up_interruptible(&adc->wait); + } + } + + /* update playback pointers */ + if (isc & ISC_PCI) { + newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize; + if (newptr >= dac->dmaaddr + dac->dmasize) + newptr -= dac->dmasize; + + outl(newptr, s->io+dac->curBufPtr); + dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ? + IT_AC_PCB2STA : IT_AC_PCB1STA; + + dac->nextOut += dac->fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + dac->count -= dac->fragsize; + dac->total_bytes += dac->fragsize; + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up_interruptible(&dac->wait); + + if (dac->count <= 0) + stop_dac(s); + } + + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static int it8172_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct it8172_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (s->codec.dev_mixer == minor) + break; + } + file->private_data = s; + return 0; +} + +static int it8172_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + + +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, + unsigned long arg) +{ + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int it8172_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct ac97_codec *codec = &s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations it8172_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: it8172_ioctl_mixdev, + open: it8172_open_mixdev, + release: it8172_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct it8172_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) + return -EBUSY; + tmo = 1000 * count / s->dacrate; + tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; + it8172_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t it8172_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + ssize_t ret; + unsigned long flags; + int cnt, bufcnt, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + // wait for samples in capture buffer + do { + spin_lock_irqsave(&s->lock, flags); + if (db->stopped) + start_adc(s); + avail = db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + cnt = count > avail ? avail : count; + bufcnt = cnt; + if (cnt % db->fragsize) { + // round count up to nearest fragment + int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); + cnt = newcnt; + } + + // copy from nextOut to user + if (copy_to_user(buffer, db->nextOut, bufcnt)) { + if (!ret) + ret = -EFAULT; + return ret; + } + + spin_lock_irqsave(&s->lock, flags); + db->count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + + db->nextOut += cnt; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + + count -= bufcnt; + buffer += bufcnt; + ret += bufcnt; + } // while (count > 0) + + return ret; +} + +static ssize_t it8172_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + ssize_t ret; + unsigned long flags; + int cnt, bufcnt, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = db->dmasize - db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + cnt = count > avail ? avail : count; + // copy to nextIn + if (copy_from_user(db->nextIn, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + return ret; + } + + bufcnt = cnt; + if (cnt % db->fragsize) { + // round count up to nearest fragment, and fill remainder of + // fragment with silence + int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); + memset(db->nextIn + cnt, (s->pcc & CC_DF) ? 0 : 0x80, newcnt - cnt); + cnt = newcnt; + } + + spin_lock_irqsave(&s->lock, flags); + db->count += cnt; + if (db->stopped) + start_dac(s); + spin_unlock_irqrestore(&s->lock, flags); + + db->nextIn += cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; + + count -= bufcnt; + buffer += bufcnt; + ret += bufcnt; + } // while (count > 0) + + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int it8172_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= + s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int it8172_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db; + unsigned long size; + + lock_kernel(); + if (vma->vm_flags & VM_WRITE) + db = &s->dma_dac; + else if (vma->vm_flags & VM_READ) + db = &s->dma_adc; + else { + unlock_kernel(); + return -EINVAL; + } + if (vma->vm_pgoff != 0) { + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + unlock_kernel(); + return -EINVAL; + } + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), + size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + db->mapped = 1; + unlock_kernel(); + return 0; +} + + +#ifdef IT8172_VERBOSE_DEBUG +static struct ioctl_str_t { + unsigned int cmd; + const char* str; +} ioctl_str[] = { + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} +}; +#endif + +static int it8172_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret, diff; + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + +#ifdef IT8172_VERBOSE_DEBUG + for (count=0; countf_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = s->dma_dac.rawbuf; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = s->dma_adc.rawbuf; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val) + s->capcc |= CC_SM; + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val) + s->pcc |= CC_SM; + else + s->pcc &= ~CC_SM; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val >= 2) { + val = 2; + s->capcc |= CC_SM; + } + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + switch (val) { + case 1: + s->pcc &= ~CC_SM; + break; + case 2: + s->pcc |= CC_SM; + break; + default: + // FIX! support multichannel??? + val = 2; + s->pcc |= CC_SM; + break; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val == AFMT_S16_LE) + s->capcc |= CC_DF; + else { + val = AFMT_U8; + s->capcc &= ~CC_DF; + } + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val == AFMT_S16_LE) + s->pcc |= CC_DF; + else { + val = AFMT_U8; + s->pcc &= ~CC_DF; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + if (file->f_mode & FMODE_READ) + val = (s->capcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; + else + val = (s->pcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) + val |= PCM_ENABLE_OUTPUT; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) + start_adc(s); + else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) + start_dac(s); + else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + if (!s->dma_adc.stopped) + count += (s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (!s->dma_adc.stopped) { + diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL); + count += diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - s->dma_adc.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - s->dma_adc.dmaaddr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (!s->dma_dac.stopped) { + diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL); + count -= diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - s->dma_dac.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - s->dma_dac.dmaaddr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragsize, (int *)arg); + else + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.subdivision = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.subdivision = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_SM) ? 2 : 1, (int *)arg); + else + return put_user((s->pcc & CC_SM) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_DF) ? 16 : 8, (int *)arg); + else + return put_user((s->pcc & CC_DF) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + + return mixdev_ioctl(&s->codec, cmd, arg); +} + + +static int it8172_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct it8172_state *s; + int ret; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; + s->capcc &= ~(CC_SM | CC_DF); + set_adc_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->capcc |= CC_DF; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; + s->pcc &= ~(CC_SM | CC_DF); + set_dac_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->pcc |= CC_DF; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + + spin_unlock_irqrestore(&s->lock, flags); + + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + return 0; +} + +static int it8172_release(struct inode *inode, struct file *file) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations it8172_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: it8172_read, + write: it8172_write, + poll: it8172_poll, + ioctl: it8172_ioctl, + mmap: it8172_mmap, + open: it8172_open, + release: it8172_release, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef IT8172_DEBUG +static int proc_it8172_dump (char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct it8172_state *s; + int cnt, len = 0; + + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct it8172_state, devs); + + /* print out header */ + len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf (buf + len, "IT8172 Audio Controller registers\n"); + len += sprintf (buf + len, "---------------------------------\n"); + cnt=0; + while (cnt < 0x72) { + if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA || + cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA || + cnt == IT_AC_PFDP) { + len+= sprintf (buf + len, "reg %02x = %08x\n", + cnt, inl(s->io+cnt)); + cnt += 4; + } else { + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, inw(s->io+cnt)); + cnt += 2; + } + } + + /* print out CODEC state */ + len += sprintf (buf + len, "\nAC97 CODEC registers\n"); + len += sprintf (buf + len, "----------------------\n"); + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, rdcodec(&s->codec, cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* IT8172_DEBUG */ + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int spdif[NR_DEVICE] = { 0, }; + +static unsigned int devindex = 0; + +MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(spdif, "if 1 the S/PDIF digital output is enabled"); + +MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); +MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver"); +MODULE_LICENSE("GPL"); + +/* --------------------------------------------------------------------- */ + +static int __devinit it8172_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + struct it8172_state *s; + int i, val; + unsigned short pcisr, vol; + unsigned char legacy, imc; + char proc_str[80]; + + if (pcidev->irq == 0) + return -1; + + if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) { + printk(KERN_ERR PFX "alloc of device struct failed\n"); + return -1; + } + + memset(s, 0, sizeof(struct it8172_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + s->vendor = pcidev->vendor; + s->device = pcidev->device; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); + s->codec.private_data = s; + s->codec.id = 0; + s->codec.codec_read = rdcodec; + s->codec.codec_write = wrcodec; + s->codec.codec_wait = waitcodec; + + if (!request_region(s->io, pci_resource_len(pcidev,0), + IT8172_MODULE_NAME)) { + printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n", + s->io, s->io + pci_resource_len(pcidev,0)-1); + goto err_region; + } + if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT, + IT8172_MODULE_NAME, s)) { + printk(KERN_ERR PFX "irq %u in use\n", s->irq); + goto err_irq; + } + + printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq); + + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec.dev_mixer = + register_sound_mixer(&it8172_mixer_fops, -1)) < 0) + goto err_dev2; + +#ifdef IT8172_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL, + proc_it8172_dump, NULL); +#endif /* IT8172_DEBUG */ + + /* + * Reset the Audio device using the IT8172 PCI Reset register. This + * creates an audible double click on a speaker connected to Line-out. + */ + IT_IO_READ16(IT_PM_PCISR, pcisr); + pcisr |= IT_PM_PCISR_ACSR; + IT_IO_WRITE16(IT_PM_PCISR, pcisr); + /* wait up to 100msec for reset to complete */ + for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) { + it8172_delay(10); + if (i == 10) + break; + IT_IO_READ16(IT_PM_PCISR, pcisr); + } + if (i == 10) { + printk(KERN_ERR PFX "chip reset timeout!\n"); + goto err_dev3; + } + + /* enable pci io and bus mastering */ + if (pci_enable_device(pcidev)) + goto err_dev3; + pci_set_master(pcidev); + + /* get out of legacy mode */ + pci_read_config_byte (pcidev, 0x40, &legacy); + pci_write_config_byte (pcidev, 0x40, legacy & ~1); + + s->spdif_volume = -1; + /* check to see if s/pdif mode is being requested */ + if (spdif[devindex]) { + printk(KERN_INFO PFX "enabling S/PDIF output\n"); + s->spdif_volume = 0; + outb(GC_SOE, s->io+IT_AC_GC); + } else { + printk(KERN_INFO PFX "disabling S/PDIF output\n"); + outb(0, s->io+IT_AC_GC); + } + + /* cold reset the AC97 */ + outw(CODECC_CR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + /* need to delay around 500msec(bleech) to give + some CODECs enough time to wakeup */ + it8172_delay(500); + + /* AC97 warm reset to start the bitclk */ + outw(CODECC_WR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + + /* codec init */ + if (!ac97_probe_codec(&s->codec)) + goto err_dev3; + + /* Enable Volume button interrupts */ + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC); + + /* Un-mute PCM and FM out on the controller */ + vol = inw(s->io+IT_AC_PCMOV); + outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV); + vol = inw(s->io+IT_AC_FMOV); + outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV); + + /* set channel defaults to 8-bit, mono, 8 Khz */ + s->pcc = 0; + s->capcc = 0; + set_dac_rate(s, 8000); + set_adc_rate(s, 8000); + + /* set mic to be the recording source */ + val = SOUND_MASK_MIC; + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + + /* mute master and PCM when in S/PDIF mode */ + if (s->spdif_volume != -1) { + val = 0x0000; + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, + (unsigned long)&val); + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, + (unsigned long)&val); + } + +#ifdef IT8172_DEBUG + sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, s->codec.id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, &s->codec); +#endif + + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev3: + unregister_sound_mixer(s->codec.dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR PFX "cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, pci_resource_len(pcidev,0)); + err_region: + kfree(s); + return -1; +} + +static void __devinit it8172_remove(struct pci_dev *dev) +{ + struct it8172_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); +#ifdef IT8172_DEBUG + if (s->ps) + remove_proc_entry(IT8172_MODULE_NAME, NULL); +#endif /* IT8172_DEBUG */ + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, pci_resource_len(dev,0)); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec.dev_mixer); + kfree(s); + pci_set_drvdata(dev, NULL); +} + + + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID, + PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver it8172_driver = { + name: IT8172_MODULE_NAME, + id_table: id_table, + probe: it8172_probe, + remove: it8172_remove +}; + +static int __init init_it8172(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk("version v0.26 time " __TIME__ " " __DATE__ "\n"); + return pci_module_init(&it8172_driver); +} + +static void __exit cleanup_it8172(void) +{ + printk(KERN_INFO PFX "unloading\n"); + pci_unregister_driver(&it8172_driver); +} + +module_init(init_it8172); +module_exit(cleanup_it8172); + diff -Nru linux/sound/oss/iwmem.h linux-2.4.19-pre5-mjc/sound/oss/iwmem.h --- linux/sound/oss/iwmem.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/iwmem.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,36 @@ +/* + * sound/iwmem.h + * + * DRAM size encoding table for AMD Interwave chip. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Bartlomiej Zolnierkiewicz : added __initdata to mem_decode + */ + + +#define K 1024 +#define M (1024*K) +static int mem_decode[][4] __initdata = +{ +/* Bank0 Bank1 Bank2 Bank3 Encoding bits */ + {256*K, 0, 0, 0}, /* 0 */ + {256*K, 256*K, 0, 0}, /* 1 */ + {256*K, 256*K, 256*K, 256*K}, /* 2 */ + {256*K, 1*M, 0, 0}, /* 3 */ + {256*K, 1*M, 1*M, 1*M}, /* 4 */ + {256*K, 256*K, 1*M, 0}, /* 5 */ + {256*K, 256*K, 1*M, 1*M}, /* 6 */ + {1*M, 0, 0, 0}, /* 7 */ + {1*M, 1*M, 0, 0}, /* 8 */ + {1*M, 1*M, 1*M, 1*M}, /* 9 */ + {4*M, 0, 0, 0}, /* 10 */ + {4*M, 4*M, 0, 0}, /* 11 */ + {4*M, 4*M, 4*M, 4*M} /* 12 */ +}; diff -Nru linux/sound/oss/mad16.c linux-2.4.19-pre5-mjc/sound/oss/mad16.c --- linux/sound/oss/mad16.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/mad16.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1074 @@ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * mad16.c + * + * Initialization code for OPTi MAD16 compatible audio chips. Including + * + * OPTi 82C928 MAD16 (replaced by C929) + * OAK OTI-601D Mozart + * OAK OTI-605 Mozart (later version with MPU401 Midi) + * OPTi 82C929 MAD16 Pro + * OPTi 82C930 + * OPTi 82C924 + * + * These audio interface chips don't produce sound themselves. They just + * connect some other components (OPL-[234] and a WSS compatible codec) + * to the PC bus and perform I/O, DMA and IRQ address decoding. There is + * also a UART for the MPU-401 mode (not 82C928/Mozart). + * The Mozart chip appears to be compatible with the 82C928, although later + * issues of the card, using the OTI-605 chip, have an MPU-401 compatable Midi + * port. This port is configured differently to that of the OPTi audio chips. + * + * Changes + * + * Alan Cox Clean up, added module selections. + * + * A. Wik Added support for Opti924 PnP. + * Improved debugging support. 16-May-1998 + * Fixed bug. 16-Jun-1998 + * + * Torsten Duwe Made Opti924 PnP support non-destructive + * 23-Dec-1998 + * + * Paul Grayson Added support for Midi on later Mozart cards. + * 25-Nov-1999 + * Christoph Hellwig Adapted to module_init/module_exit. + * Arnaldo C. de Melo got rid of attach_uart401 21-Sep-2000 + * + * Pavel Rabel Clean up Nov-2000 + */ + +#include +#include +#include + +#include "sound_config.h" + +#include "ad1848.h" +#include "sb.h" +#include "mpu401.h" + +static int mad16_conf; +static int mad16_cdsel; + +static int already_initialized = 0; + +#define C928 1 +#define MOZART 2 +#define C929 3 +#define C930 4 +#define C924 5 + +/* + * Registers + * + * The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations). + * All ports are inactive by default. They can be activated by + * writing 0xE2 or 0xE3 to the password register. The password is valid + * only until the next I/O read or write. + * + * 82C930 uses 0xE4 as the password and indirect addressing to access + * the config registers. + */ + +#define MC0_PORT 0xf8c /* Dummy port */ +#define MC1_PORT 0xf8d /* SB address, CD-ROM interface type, joystick */ +#define MC2_PORT 0xf8e /* CD-ROM address, IRQ, DMA, plus OPL4 bit */ +#define MC3_PORT 0xf8f +#define PASSWD_REG 0xf8f +#define MC4_PORT 0xf90 +#define MC5_PORT 0xf91 +#define MC6_PORT 0xf92 +#define MC7_PORT 0xf93 +#define MC8_PORT 0xf94 +#define MC9_PORT 0xf95 +#define MC10_PORT 0xf96 +#define MC11_PORT 0xf97 +#define MC12_PORT 0xf98 + +static int board_type = C928; + +static int *mad16_osp; +static int c931_detected; /* minor differences from C930 */ +static char c924pnp = 0; /* " " " C924 */ +static int debug = 0; /* debugging output */ + +#ifdef DDB +#undef DDB +#endif +#define DDB(x) {if (debug) x;} + +static unsigned char mad_read(int port) +{ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + /* the c924 has its ports relocated by -128 if + PnP is enabled -aw */ + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); + break; + } + + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + tmp = inb(0xe0f); /* Read from data reg */ + } + else + if (!c924pnp) + tmp = inb(port); else + tmp = inb(port-0x80); + restore_flags(flags); + + return tmp; +} + +static void mad_write(int port, int value) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); + break; + } + + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + outb(((unsigned char) (value & 0xff)), 0xe0f); + } + else + if (!c924pnp) + outb(((unsigned char) (value & 0xff)), port); else + outb(((unsigned char) (value & 0xff)), port-0x80); + restore_flags(flags); +} + +static int __init detect_c930(void) +{ + unsigned char tmp = mad_read(MC1_PORT); + + if ((tmp & 0x06) != 0x06) + { + DDB(printk("Wrong C930 signature (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, 0); + + if (mad_read(MC1_PORT) != 0x06) + { + DDB(printk("Wrong C930 signature2 (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, tmp); /* Restore bits */ + + mad_write(MC7_PORT, 0); + if ((tmp = mad_read(MC7_PORT)) != 0) + { + DDB(printk("MC7 not writable (%x)\n", tmp)); + return 0; + } + mad_write(MC7_PORT, 0xcb); + if ((tmp = mad_read(MC7_PORT)) != 0xcb) + { + DDB(printk("MC7 not writable2 (%x)\n", tmp)); + return 0; + } + + tmp = mad_read(MC0_PORT+18); + if (tmp == 0xff || tmp == 0x00) + return 1; + /* We probably have a C931 */ + DDB(printk("Detected C931 config=0x%02x\n", tmp)); + c931_detected = 1; + + /* + * We cannot configure the chip if it is in PnP mode. + * If we have a CSN assigned (bit 8 in MC13) we first try + * a software reset, then a software power off, finally + * Clearing PnP mode. The last option is not + * Bit 8 in MC13 + */ + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Software reset */ + mad_write(MC9_PORT, 0x02); + mad_write(MC9_PORT, 0x00); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Power off, and on again */ + mad_write(MC9_PORT, 0xc2); + mad_write(MC9_PORT, 0xc0); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + +#if 0 + /* Force off PnP mode. This is not recommended because + * the PnP bios will not recognize the chip on the next + * warm boot and may assignd different resources to other + * PnP/PCI cards. + */ + mad_write(MC0_PORT+17, 0x04); +#endif + return 1; +} + +static int __init detect_mad16(void) +{ + unsigned char tmp, tmp2, bit; + int i, port; + + /* + * Check that reading a register doesn't return bus float (0xff) + * when the card is accessed using password. This may fail in case + * the card is in low power mode. Normally at least the power saving + * mode bit should be 0. + */ + + if ((tmp = mad_read(MC1_PORT)) == 0xff) + { + DDB(printk("MC1_PORT returned 0xff\n")); + return 0; + } + for (i = 0xf8d; i <= 0xf98; i++) + if (!c924pnp) + DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))) else + DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i))); + + if (board_type == C930) + return detect_c930(); + + /* + * Now check that the gate is closed on first I/O after writing + * the password. (This is how a MAD16 compatible card works). + */ + + if ((tmp2 = inb(MC1_PORT)) == tmp) /* It didn't close */ + { + DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); + return 0; + } + + bit = (c924pnp) ? 0x20 : 0x80; + port = (c924pnp) ? MC2_PORT : MC1_PORT; + + tmp = mad_read(port); + mad_write(port, tmp ^ bit); /* Toggle a bit */ + if ((tmp2 = mad_read(port)) != (tmp ^ bit)) /* Compare the bit */ + { + mad_write(port, tmp); /* Restore */ + DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); + return 0; + } + mad_write(port, tmp); /* Restore */ + return 1; /* Bingo */ +} + +static int __init wss_init(struct address_info *hw_config) +{ + int ad_flags = 0; + + /* + * Verify the WSS parameters + */ + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return 0; + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00. + */ + + if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 && + (inb(hw_config->io_base + 3) & 0x3f) != 0x00) + { + DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); + return 0; + } + if (hw_config->irq > 11) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk("MSS: Can't use DMA0 with a 8 bit card/slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 1; +} + +static int __init init_c930(struct address_info *hw_config) +{ + unsigned char cfg = 0; + + if(c931_detected) + { + /* Bit 0 has reversd meaning. Bits 1 and 2 sese + reversed on write. + Support only IDE cdrom. IDE port programmed + somewhere else. */ + cfg = (cfg & 0x09) ^ 0x07; + } + + switch (hw_config->io_base) + { + case 0x530: + cfg |= 0x00; + break; + case 0xe80: + cfg |= 0x10; + break; + case 0xf40: + cfg |= 0x20; + break; + case 0x604: + cfg |= 0x30; + break; + default: + printk(KERN_ERR "MAD16: Invalid codec port %x\n", hw_config->io_base); + return 0; + } + mad_write(MC1_PORT, cfg); + + /* MC2 is CD configuration. Don't touch it. */ + + mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ + + /* bit 2 of MC4 reverses it's meaning between the C930 + and the C931. */ + cfg = c931_detected ? 0x04 : 0x00; + + mad_write(MC4_PORT, 0x52|cfg); + + mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ + mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ + mad_write(MC7_PORT, 0xCB); + mad_write(MC10_PORT, 0x11); + + return wss_init(hw_config); +} + +static int __init chip_detect(void) +{ + int i; + + /* + * Then try to detect with the old password + */ + board_type = C924; + + DDB(printk("Detect using password = 0xE5\n")); + + if (detect_mad16()) { + return 1; + } + + board_type = C928; + + DDB(printk("Detect using password = 0xE2\n")); + + if (detect_mad16()) + { + unsigned char model; + + if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) { + DDB(printk("mad16.c: Mozart detected\n")); + board_type = MOZART; + } else { + DDB(printk("mad16.c: 82C928 detected???\n")); + board_type = C928; + } + return 1; + } + + board_type = C929; + + DDB(printk("Detect using password = 0xE3\n")); + + if (detect_mad16()) + { + DDB(printk("mad16.c: 82C929 detected\n")); + return 1; + } + + if (inb(PASSWD_REG) != 0xff) + return 0; + + /* + * First relocate MC# registers to 0xe0e/0xe0f, disable password + */ + + outb((0xE4), PASSWD_REG); + outb((0x80), PASSWD_REG); + + board_type = C930; + + DDB(printk("Detect using password = 0xE4\n")); + + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printk("port %03x = %02x\n", i, mad_read(i))); + + if(detect_mad16()) { + DDB(printk("mad16.c: 82C930 detected\n")); + return 1; + } + + /* The C931 has the password reg at F8D */ + outb((0xE4), 0xF8D); + outb((0x80), 0xF8D); + DDB(printk("Detect using password = 0xE4 for C931\n")); + + if (detect_mad16()) { + return 1; + } + + board_type = C924; + c924pnp++; + DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n")); + if (detect_mad16()) { + DDB(printk("mad16.c: 82C924 PnP detected\n")); + return 1; + } + + c924pnp=0; + + return 0; +} + +static int __init probe_mad16(struct address_info *hw_config) +{ + int i; + static int valid_ports[] = + { + 0x530, 0xe80, 0xf40, 0x604 + }; + unsigned char tmp; + unsigned char cs4231_mode = 0; + + int ad_flags = 0; + + if (already_initialized) + return 0; + + mad16_osp = hw_config->osp; + + /* + * Check that all ports return 0xff (bus float) when no password + * is written to the password register. + */ + + DDB(printk("--- Detecting MAD16 / Mozart ---\n")); + if (!chip_detect()) + return 0; + + if (board_type == C930) + return init_c930(hw_config); + + + for (i = 0xf8d; i <= 0xf93; i++) + if (!c924pnp) + DDB(printk("port %03x = %02x\n", i, mad_read(i))) else + DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i))); + +/* + * Set the WSS address + */ + + tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */ + + for (i = 0; i < 5; i++) + { + if (i > 3) /* Not a valid port */ + { + printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); + return 0; + } + if (valid_ports[i] == hw_config->io_base) + { + tmp |= i << 4; /* WSS port select bits */ + break; + } + } + + /* + * Set optional CD-ROM and joystick settings. + */ + + tmp &= ~0x0f; + mad_write(MC1_PORT, tmp); + + tmp = mad_read(MC2_PORT); + + mad_write(MC2_PORT, tmp); + mad_write(MC3_PORT, 0xf0); /* Disable SB */ + + if (board_type == C924) /* Specific C924 init values */ + { + mad_write(MC4_PORT, 0xA0); + mad_write(MC5_PORT, 0x05); + mad_write(MC6_PORT, 0x03); + } + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return 0; + + if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) + cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ + + if (board_type == C929) + { + mad_write(MC4_PORT, 0xa2); + mad_write(MC5_PORT, 0xA5 | cs4231_mode); + mad_write(MC6_PORT, 0x03); /* Disable MPU401 */ + } + else + { + mad_write(MC4_PORT, 0x02); + mad_write(MC5_PORT, 0x30 | cs4231_mode); + } + + for (i = 0xf8d; i <= 0xf93; i++) if (!c924pnp) + DDB(printk("port %03x after init = %02x\n", i, mad_read(i))) else + DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i))); + wss_init(hw_config); + + return 1; +} + +static void __init attach_mad16(struct address_info *hw_config) +{ + + static signed char interrupt_bits[12] = { + -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 + }; + signed char bits; + + static char dma_bits[4] = { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; + int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2; + unsigned char dma2_bit = 0; + + already_initialized = 1; + + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return; + + /* + * Set the IRQ and DMA addresses. + */ + + if (board_type == C930 || c924pnp) + interrupt_bits[5] = 0x28; /* Also IRQ5 is possible on C930 */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + return; + + outb((bits | 0x40), config_port); + if ((inb(version_port) & 0x40) == 0) + printk(KERN_ERR "[IRQ Conflict?]\n"); + + /* + * Handle the capture DMA channel + */ + + if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) + { + if (!((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0))) + { /* Unsupported combination. Try to swap channels */ + int tmp = dma; + + dma = dma2; + dma2 = tmp; + } + if ((dma == 0 && dma2 == 1) || (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) + { + dma2_bit = 0x04; /* Enable capture DMA */ + } + else + { + printk("MAD16: Invalid capture DMA\n"); + dma2 = dma; + } + } + else dma2 = dma; + + outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ + + hw_config->slots[0] = ad1848_init("MAD16 WSS", hw_config->io_base + 4, + hw_config->irq, + dma, + dma2, 0, + hw_config->osp, + THIS_MODULE); + request_region(hw_config->io_base, 4, "MAD16 WSS config"); +} + +static int __init probe_mad16_mpu(struct address_info *hw_config) +{ + static int mpu_attached = 0; + unsigned char tmp; + + if (!already_initialized) /* The MSS port must be initialized first */ + return 0; + + if (mpu_attached) /* Don't let them call this twice */ + return 0; + mpu_attached = 1; + + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + +#ifdef CONFIG_MAD16_OLDCARD + + tmp = mad_read(MC3_PORT); + + /* + * MAD16 SB base is defined by the WSS base. It cannot be changed + * alone. + * Ignore configured I/O base. Use the active setting. + */ + + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + switch (hw_config->irq) + { + case 5: + tmp = (tmp & 0x3f) | 0x80; + break; + case 7: + tmp = (tmp & 0x3f); + break; + case 11: + tmp = (tmp & 0x3f) | 0x40; + break; + default: + printk(KERN_ERR "mad16/Mozart: Invalid MIDI IRQ\n"); + return 0; + } + + mad_write(MC3_PORT, tmp | 0x04); + hw_config->driver_use_1 = SB_MIDI_ONLY; + if (!sb_dsp_detect(hw_config, 0, 0, NULL)) + return 0; + + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + hw_config->name = "Mad16/Mozart"; + sb_dsp_init(hw_config, THIS_MODULE); + return 1; +#else + /* assuming all later Mozart cards are identified as + * either 82C928 or Mozart. If so, following code attempts + * to set MPU register. TODO - add probing + */ + + tmp = mad_read(MC8_PORT); + + switch (hw_config->irq) + { + case 5: + tmp |= 0x08; + break; + case 7: + tmp |= 0x10; + break; + case 9: + tmp |= 0x18; + break; + case 10: + tmp |= 0x20; + break; + case 11: + tmp |= 0x28; + break; + default: + printk(KERN_ERR "mad16/MOZART: invalid mpu_irq\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x300: + tmp |= 0x01; + break; + case 0x310: + tmp |= 0x03; + break; + case 0x320: + tmp |= 0x05; + break; + case 0x330: + tmp |= 0x07; + break; + default: + printk(KERN_ERR "mad16/MOZART: invalid mpu_io\n"); + return 0; + } + + mad_write(MC8_PORT, tmp); /* write MPU port parameters */ + goto probe_401; +#endif + } + tmp = mad_read(MC6_PORT) & 0x83; + tmp |= 0x80; /* MPU-401 enable */ + + /* Set the MPU base bits */ + + switch (hw_config->io_base) + { + case 0x300: + tmp |= 0x60; + break; + case 0x310: + tmp |= 0x40; + break; + case 0x320: + tmp |= 0x20; + break; + case 0x330: + tmp |= 0x00; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base); + return 0; + } + + /* Set the MPU IRQ bits */ + + switch (hw_config->irq) + { + case 5: + tmp |= 0x10; + break; + case 7: + tmp |= 0x18; + break; + case 9: + tmp |= 0x00; + break; + case 10: + tmp |= 0x08; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq); + break; + } + + mad_write(MC6_PORT, tmp); /* Write MPU401 config */ + +#ifndef CONFIG_MAD16_OLDCARD +probe_401: +#endif + hw_config->driver_use_1 = SB_MIDI_ONLY; + hw_config->name = "Mad16/Mozart"; + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_mad16(struct address_info *hw_config) +{ + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0); + release_region(hw_config->io_base, 4); + sound_unload_audiodev(hw_config->slots[0]); +} + +static void __exit unload_mad16_mpu(struct address_info *hw_config) +{ +#ifdef CONFIG_MAD16_OLDCARD + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + sb_dsp_unload(hw_config, 0); + return; + } +#endif + + unload_uart401(hw_config); +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int found_mpu; + +static int __initdata mpu_io = 0; +static int __initdata mpu_irq = 0; +static int __initdata io = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata irq = -1; +static int __initdata cdtype = 0; +static int __initdata cdirq = 0; +static int __initdata cdport = 0x340; +static int __initdata cddma = -1; +static int __initdata opl4 = 0; +static int __initdata joystick = 0; + +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM(io,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma16,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(cdtype,"i"); +MODULE_PARM(cdirq,"i"); +MODULE_PARM(cdport,"i"); +MODULE_PARM(cddma,"i"); +MODULE_PARM(opl4,"i"); +MODULE_PARM(joystick,"i"); +MODULE_PARM(debug,"i"); + +static int __initdata dma_map[2][8] = +{ + {0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02}, + {0x03, -1, 0x01, 0x00, -1, -1, -1, -1} +}; + +static int __initdata irq_map[16] = +{ + 0x00, -1, -1, 0x0A, + -1, 0x04, -1, 0x08, + -1, 0x10, 0x14, 0x18, + -1, -1, -1, -1 +}; + +static int __init init_mad16(void) +{ + int dmatype = 0; + + printk(KERN_INFO "MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + printk(KERN_INFO "CDROM "); + switch (cdtype) + { + case 0x00: + printk("Disabled"); + cdirq = 0; + break; + case 0x02: + printk("Sony CDU31A"); + dmatype = 1; + if(cddma == -1) cddma = 3; + break; + case 0x04: + printk("Mitsumi"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + case 0x06: + printk("Panasonic Lasermate"); + dmatype = 1; + if(cddma == -1) cddma = 3; + break; + case 0x08: + printk("Secondary IDE"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + case 0x0A: + printk("Primary IDE"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + default: + printk("\n"); + printk(KERN_ERR "Invalid CDROM type\n"); + return -EINVAL; + } + + /* + * Build the config words + */ + + mad16_conf = (joystick ^ 1) | cdtype; + mad16_cdsel = 0; + if (opl4) + mad16_cdsel |= 0x20; + + if(cdtype){ + if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) + { + printk("\n"); + printk(KERN_ERR "Invalid CDROM DMA\n"); + return -EINVAL; + } + if (cddma) + printk(", DMA %d", cddma); + else + printk(", no DMA"); + + if (!cdirq) + printk(", no IRQ"); + else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) + { + printk(", invalid IRQ (disabling)"); + cdirq = 0; + } + else printk(", IRQ %d", cdirq); + + mad16_cdsel |= dma_map[dmatype][cddma]; + + if (cdtype < 0x08) + { + switch (cdport) + { + case 0x340: + mad16_cdsel |= 0x00; + break; + case 0x330: + mad16_cdsel |= 0x40; + break; + case 0x360: + mad16_cdsel |= 0x80; + break; + case 0x320: + mad16_cdsel |= 0xC0; + break; + default: + printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); + return -EINVAL; + } + } + mad16_cdsel |= irq_map[cdirq]; + } + + printk(".\n"); + printk(KERN_INFO "Joystick port "); + if (joystick == 1) + printk("enabled.\n"); + else + { + joystick = 0; + printk("disabled.\n"); + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_ERR "I/O, DMA and irq are mandatory\n"); + return -EINVAL; + } + + if (!probe_mad16(&cfg)) + return -ENODEV; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + attach_mad16(&cfg); + + found_mpu = probe_mad16_mpu(&cfg_mpu); + return 0; +} + +static void __exit cleanup_mad16(void) +{ + if (found_mpu) + unload_mad16_mpu(&cfg_mpu); + unload_mad16(&cfg); +} + +module_init(init_mad16); +module_exit(cleanup_mad16); + +#ifndef MODULE +static int __init setup_mad16(char *str) +{ + /* io, irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + + return 1; +} + +__setup("mad16=", setup_mad16); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/maestro.c linux-2.4.19-pre5-mjc/sound/oss/maestro.c --- linux/sound/oss/maestro.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/maestro.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,3807 @@ +/***************************************************************************** + * + * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + * (c) Copyright 1999 Alan Cox + * + * Based heavily on SonicVibes.c: + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * Heavily modified by Zach Brown based on lunch + * with ESS engineers. Many thanks to Howard Kim for providing + * contacts and hardware. Honorable mention goes to Eric + * Brombaugh for all sorts of things. Best regards to the + * proprietors of Hack Central for fine lodging. + * + * Supported devices: + * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + * + * This driver has brute force APM suspend support. We catch suspend + * notifications and stop all work being done on the chip. Any people + * that try between this shutdown and the real suspend operation will + * be put to sleep. When we resume we restore our software state on + * the chip and wake up the people that were using it. The code thats + * being used now is quite dirty and assumes we're on a uni-processor + * machine. Much of it will need to be cleaned up for SMP ACPI or + * similar. + * + * We also pay attention to PCI power management now. The driver + * will power down units of the chip that it knows aren't needed. + * The WaveProcessor and company are only powered on when people + * have /dev/dsp*s open. On removal the driver will + * power down the maestro entirely. There could still be + * trouble with BIOSen that magically change power states + * themselves, but we'll see. + * + * History + * v0.15 - May 21 2001 - Marcus Meissner + * Ported to Linux 2.4 PCI API. Some clean ups, global devs list + * removed (now using pci device driver data). + * PM needs to be polished still. Bumped version. + * (still kind of v0.14) May 13 2001 - Ben Pfaff + * Add support for 978 docking and basic hardware volume control + * (still kind of v0.14) Nov 23 - Alan Cox + * Add clocking= for people with seriously warped hardware + * (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz + * add __init to maestro_ac97_init() and maestro_install() + * (still based on v0.14) Mar 29 2000 - Zach Brown + * move to 2.3 power management interface, which + * required hacking some suspend/resume/check paths + * make static compilation work + * v0.14 - Jan 28 2000 - Zach Brown + * add PCI power management through ACPI regs. + * we now shut down on machine reboot/halt + * leave scary PCI config items alone (isa stuff, mostly) + * enable 1921s, it seems only mine was broke. + * fix swapped left/right pcm dac. har har. + * up bob freq, increase buffers, fix pointers at underflow + * silly compilation problems + * v0.13 - Nov 18 1999 - Zach Brown + * fix nec Versas? man would that be cool. + * v0.12 - Nov 12 1999 - Zach Brown + * brown bag volume max fix.. + * v0.11 - Nov 11 1999 - Zach Brown + * use proper stereo apu decoding, mmap/write should work. + * make volume sliders more useful, tweak rate calculation. + * fix lame 8bit format reporting bug. duh. apm apu saving buglet also + * fix maestro 1 clock freq "bug", remove pt101 support + * v0.10 - Oct 28 1999 - Zach Brown + * aha, so, sometimes the WP writes a status word to offset 0 + * from one of the PCMBARs. rearrange allocation accordingly.. + * cheers again to Eric for being a good hacker in investigating this. + * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) + * v0.09 - Oct 23 1999 - Zach Brown + * added APM support. + * re-order something such that some 2Es now work. Magic! + * new codec reset routine. made some codecs come to life. + * fix clear_advance, sync some control with ESS. + * now write to all base regs to be paranoid. + * v0.08 - Oct 20 1999 - Zach Brown + * Fix initial buflen bug. I am so smart. also smp compiling.. + * I owe Eric yet another beer: fixed recmask, igain, + * muting, and adc sync consistency. Go Team. + * v0.07 - Oct 4 1999 - Zach Brown + * tweak adc/dac, formating, and stuff to allow full duplex + * allocate dsps memory at open() so we can fit in the wavecache window + * fix wavecache braindamage. again. no more scribbling? + * fix ess 1921 codec bug on some laptops. + * fix dumb pci scanning bug + * started 2.3 cleanup, redid spinlocks, little cleanups + * v0.06 - Sep 20 1999 - Zach Brown + * fix wavecache thinkos. limit to 1 /dev/dsp. + * eric is wearing his thinking toque this week. + * spotted apu mode bugs and gain ramping problem + * don't touch weird mixer regs, make recmask optional + * fixed igain inversion, defaults for mixers, clean up rec_start + * make mono recording work. + * report subsystem stuff, please send reports. + * littles: parallel out, amp now + * v0.05 - Sep 17 1999 - Zach Brown + * merged and fixed up Eric's initial recording code + * munged format handling to catch misuse, needs rewrite. + * revert ring bus init, fixup shared int, add pci busmaster setting + * fix mixer oss interface, fix mic mute and recmask + * mask off unsupported mixers, reset with all 1s, modularize defaults + * make sure bob is running while we need it + * got rid of device limit, initial minimal apm hooks + * pull out dead code/includes, only allow multimedia/audio maestros + * v0.04 - Sep 01 1999 - Zach Brown + * copied memory leak fix from sonicvibes driver + * different ac97 reset, play with 2.0 ac97, simplify ring bus setup + * bob freq code, region sanity, jitter sync fix; all from Eric + * + * TODO + * fix bob frequency + * endianness + * do smart things with ac97 2.0 bits. + * dual codecs + * leave 54->61 open + * + * it also would be fun to have a mode that would not use pci dma at all + * but would copy into the wavecache on board memory and use that + * on architectures that don't like the maestro's pci dma ickiness. + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +static int maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *d); + +#include "maestro.h" + +static struct pci_driver maestro_pci_driver; + +/* --------------------------------------------------------------------- */ + +#define M_DEBUG 1 + +#ifdef M_DEBUG +static int debug=0; +#define M_printk(args...) {if (debug) printk(args);} +#else +#define M_printk(x) +#endif + +/* we try to setup 2^(dsps_order) /dev/dsp devices */ +static int dsps_order=0; +/* wether or not we mess around with power management */ +static int use_pm=2; /* set to 1 for force */ +/* clocking for broken hardware - a few laptops seem to use a 50Khz clock + ie insmod with clocking=50000 or so */ + +static int clocking=48000; + +MODULE_AUTHOR("Zach Brown , Alan Cox "); +MODULE_DESCRIPTION("ESS Maestro Driver"); +MODULE_LICENSE("GPL"); + +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(dsps_order,"i"); +MODULE_PARM(use_pm,"i"); +MODULE_PARM(clocking, "i"); + +/* --------------------------------------------------------------------- */ +#define DRIVER_VERSION "0.15" + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ + +#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, + the people the maestro + was bought from */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ +#endif /* PCI_VENDOR_ESS */ + +#define ESS_CHAN_HARD 0x100 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + + +/* changed so that I could actually find all the + references and fix them up. its a little more readable now. */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define ESS_STATE_MAGIC 0x125D1968 +#define ESS_CARD_MAGIC 0x19283746 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define MAX_DSP_ORDER 2 +#define MAX_DSPS (1<src buffer page */ + void *mixbuf; + +}; + +struct ess_card { + unsigned int magic; + + /* We keep maestro cards in a linked list */ + struct ess_card *next; + + int dev_mixer; + + int card_type; + + /* as most of this is static, + perhaps it should be a pointer to a global struct */ + struct mixer_goo { + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + /* the caller must guarantee arg sanity before calling these */ +/* int (*read_mixer)(struct ess_card *card, int index);*/ + void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); + int (*recmask_io)(struct ess_card *card,int rw,int mask); + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + } mix; + + int power_regs; + + int in_suspend; + wait_queue_head_t suspend_queue; + + struct ess_state channels[MAX_DSPS]; + u16 maestro_map[NR_IDRS]; /* Register map */ + /* we have to store this junk so that we can come back from a + suspend */ + u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* memory for this card.. wavecache limited :(*/ + void *dmapages; + int dmaorder; + + /* hardware resources */ + struct pci_dev *pcidev; + u32 iobase; + u32 irq; + + int bob_freq; + char dsps_open; + + int dock_mute_vol; +}; + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ); + +static unsigned +ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + + +/* --------------------------------------------------------------------- */ + +static void check_suspend(struct ess_card *card); + +/* --------------------------------------------------------------------- */ + + +/* + * ESS Maestro AC97 codec programming interface. + */ + +static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val) +{ + int io = card->iobase; + int i; + /* + * Wait for the codec bus to be free + */ + + check_suspend(card); + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + /* + * Write the bus + */ + outw(val, io+ESS_AC97_DATA); + mdelay(1); + outb(cmd, io+ESS_AC97_INDEX); + mdelay(1); +} + +static u16 maestro_ac97_get(struct ess_card *card, u8 cmd) +{ + int io = card->iobase; + int sanity=10000; + u16 data; + int i; + + check_suspend(card); + /* + * Wait for the codec bus to be free + */ + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + + outb(cmd|0x80, io+ESS_AC97_INDEX); + mdelay(1); + + while(inb(io+ESS_AC97_INDEX)&1) + { + sanity--; + if(!sanity) + { + printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); + return 0; + } + } + data=inw(io+ESS_AC97_DATA); + mdelay(1); + return data; +} + +/* OSS interface to the ac97s.. */ + +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ + SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ + SOUND_MASK_SPEAKER) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<offset); + + if(AC97_STEREO_MASK & (1<> 8) & 0x7f; + right = val & 0x7f; + + if (mixer == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + else { + right = 100 - ((right * 100) / mh->scale); + left = 100 - ((left * 100) / mh->scale); + } + + ret = left | (right << 8); + } else if (mixer == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + + M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); + + return ret; +} +#endif + +/* write the OSS encoded volume to the given OSS encoded mixer, + again caller's job to make sure all is well in arg land, + call with spinlock held */ + +/* linear scale -> log */ +static unsigned char lin2log[101] = +{ +0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , +50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , +63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 , +72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 , +78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 , +83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 , +87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 , +90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 , +93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 , +95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 , +97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 +}; + +static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right) +{ + u16 val=0; + struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + + M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); + + if(AC97_STEREO_MASK & (1<scale) / 100; + left = (left * mh->scale) / 100; + if ((left == 0) && (right == 0)) + val |= 0x8000; + } else { + /* log conversion for the stereo controls */ + if((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - lin2log[right]) * mh->scale) / 100; + left = ((100 - lin2log[left]) * mh->scale) / 100; + } + + val |= (left << 8) | right; + + } else if (mixer == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (mixer == SOUND_MIXER_MIC) { + val = maestro_ac97_get(card, mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + val = maestro_ac97_get(card , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (mixer == SOUND_MIXER_TREBLE) { + val = maestro_ac97_get(card , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + + maestro_ac97_set(card , mh->offset, val); + + M_printk(" -> %x\n",val); +} + +/* the following tables allow us to go from + OSS <-> ac97 quickly. */ + +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static unsigned int ac97_oss_mask[] = { + [AC97_REC_MIC] = SOUND_MASK_MIC, + [AC97_REC_CD] = SOUND_MASK_CD, + [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, + [AC97_REC_AUX] = SOUND_MASK_LINE1, + [AC97_REC_LINE] = SOUND_MASK_LINE, + [AC97_REC_PHONE] = SOUND_MASK_PHONEIN +}; + +/* indexed by bit position */ +static unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* read or write the recmask + the ac97 can really have left and right recording + inputs independantly set, but OSS doesn't seem to + want us to express that to the user. + the caller guarantees that we have a supported bit set, + and they must be holding the card's spinlock */ +static int +ac97_recmask_io(struct ess_card *card, int read, int mask) +{ + unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ]; + + if (read) return val; + + /* oss can have many inputs, maestro cant. try + to pick the 'new' one */ + + if (mask != val) mask &= ~val; + + val = ffs(mask) - 1; + val = ac97_oss_rm[val]; + val |= val << 8; /* set both channels */ + + M_printk("maestro: setting ac97 recmask to 0x%x\n",val); + + maestro_ac97_set(card,0x1a,val); + + return 0; +}; + +/* + * The Maestro can be wired to a standard AC97 compliant codec + * (see www.intel.com for the pdf's on this), or to a PT101 codec + * which appears to be the ES1918 (data sheet on the esstech.com.tw site) + * + * The PT101 setup is untested. + */ + +static u16 __init maestro_ac97_init(struct ess_card *card) +{ + u16 vend1, vend2, caps; + + card->mix.supported_mixers = AC97_SUPPORTED_MASK; + card->mix.stereo_mixers = AC97_STEREO_MASK; + card->mix.record_sources = AC97_RECORD_MASK; +/* card->mix.read_mixer = ac97_read_mixer;*/ + card->mix.write_mixer = ac97_write_mixer; + card->mix.recmask_io = ac97_recmask_io; + + vend1 = maestro_ac97_get(card, 0x7c); + vend2 = maestro_ac97_get(card, 0x7e); + + caps = maestro_ac97_get(card, 0x00); + + printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", + vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf); + + if (! (caps & 0x4) ) { + /* no bass/treble nobs */ + card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + } + + /* XXX endianness, dork head. */ + /* vendor specifc bits.. */ + switch ((long)(vend1 << 16) | vend2) { + case 0x545200ff: /* TriTech */ + /* no idea what this does */ + maestro_ac97_set(card,0x2a,0x0001); + maestro_ac97_set(card,0x2c,0x0000); + maestro_ac97_set(card,0x2c,0xffff); + break; +#if 0 /* i thought the problems I was seeing were with + the 1921, but apparently they were with the pci board + it was on, so this code is commented out. + lets see if this holds true. */ + case 0x83847609: /* ESS 1921 */ + /* writing to 0xe (mic) or 0x1a (recmask) seems + to hang this codec */ + card->mix.supported_mixers &= ~(SOUND_MASK_MIC); + card->mix.record_sources = 0; + card->mix.recmask_io = NULL; +#if 0 /* don't ask. I have yet to see what these actually do. */ + maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ + udelay(20); + maestro_ac97_set(card,0x78,0x3002); + udelay(20); + maestro_ac97_set(card,0x78,0x3802); + udelay(20); +#endif + break; +#endif + default: break; + } + + maestro_ac97_set(card, 0x1E, 0x0404); + /* null misc stuff */ + maestro_ac97_set(card, 0x20, 0x0000); + + return 0; +} + +#if 0 /* there has been 1 person on the planet with a pt101 that we + know of. If they care, they can put this back in :) */ +static u16 maestro_pt101_init(struct ess_card *card,int iobase) +{ + printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + maestro_ac97_set(iobase, 0x2A, 0x0001); + maestro_ac97_set(iobase, 0x2C, 0x0000); + maestro_ac97_set(iobase, 0x2C, 0xFFFF); + maestro_ac97_set(iobase, 0x10, 0x9F1F); + maestro_ac97_set(iobase, 0x12, 0x0808); + maestro_ac97_set(iobase, 0x14, 0x9F1F); + maestro_ac97_set(iobase, 0x16, 0x9F1F); + maestro_ac97_set(iobase, 0x18, 0x0404); + maestro_ac97_set(iobase, 0x1A, 0x0000); + maestro_ac97_set(iobase, 0x1C, 0x0000); + maestro_ac97_set(iobase, 0x02, 0x0404); + maestro_ac97_set(iobase, 0x04, 0x0808); + maestro_ac97_set(iobase, 0x0C, 0x801F); + maestro_ac97_set(iobase, 0x0E, 0x801F); + return 0; +} +#endif + +/* this is very magic, and very slow.. */ +static void +maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) +{ + u16 save_68; + u16 w; + u32 vend; + + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* reset the first codec */ + outw(0x0000, ioaddr+0x36); + save_68 = inw(ioaddr+0x68); + pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if( w & 0x1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ + outw(0x0001, ioaddr + 0x68); + outw(0x0000, ioaddr + 0x60); + udelay(20); + outw(0x0001, ioaddr + 0x60); + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); + outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); + outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); + + /* now the second codec */ + outw(0x0000, ioaddr+0x36); + outw(0xfff7, ioaddr + 0x64); + save_68 = inw(ioaddr+0x68); + outw(0x0009, ioaddr + 0x68); + outw(0x0001, ioaddr + 0x60); + udelay(20); + outw(0x0009, ioaddr + 0x60); + mdelay(500); /* .. ouch.. */ + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + M_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80|0x7c, ioaddr + 0x30); + for (w=0; ; w++) { + if ((inw(ioaddr+ 0x30) & 1) == 0) { + if(inb(ioaddr + 0x32) !=0) break; + + outb(0x80|0x7d, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + outb(0x80|0x7f, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + } + + if( w > 10000) { + outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); + udelay(1); + outw( 0x80, ioaddr+0x30); + for(w = 0 ; w < 10000; w++) { + if((inw(ioaddr + 0x30) & 1) ==0) break; + } + } + } +#endif + if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); +} +/* + * Indirect register access. Not all registers are readable so we + * need to keep register state ourselves + */ + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* + * The Maestro engineers were a little indirection happy. These indirected + * registers themselves include indirect registers at another layer + */ + +static void __maestro_write(struct ess_card *card, u16 reg, u16 data) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + outw(data, ioaddr+0x00); + if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); + else card->maestro_map[reg]=data; + +} + +static void maestro_write(struct ess_state *s, u16 reg, u16 data) +{ + unsigned long flags; + + check_suspend(s->card); + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_write(s->card,reg,data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 __maestro_read(struct ess_card *card, u16 reg) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + return card->maestro_map[reg]=inw(ioaddr+0x00); +} + +static u16 maestro_read(struct ess_state *s, u16 reg) +{ + if(READABLE_MAP & (1<card); + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_read(s->card,reg); + + spin_unlock_irqrestore(&s->card->lock,flags); + } + return s->card->maestro_map[reg]; +} + +/* + * These routines handle accessing the second level indirections to the + * wave ram. + */ + +/* + * The register names are the ones ESS uses (see 104T31.ZIP) + */ + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +static void apu_index_set(struct ess_card *card, u16 index) +{ + int i; + __maestro_write(card, IDR1_CRAM_POINTER, index); + for(i=0;i<1000;i++) + if(__maestro_read(card, IDR1_CRAM_POINTER)==index) + return; + printk(KERN_WARNING "maestro: APU register select failed.\n"); +} + +static void apu_data_set(struct ess_card *card, u16 data) +{ + int i; + for(i=0;i<1000;i++) + { + if(__maestro_read(card, IDR0_DATA_PORT)==data) + return; + __maestro_write(card, IDR0_DATA_PORT, data); + } +} + +/* + * This is the public interface for APU manipulation. It handles the + * interlock to avoid two APU writes in parallel etc. Don't diddle + * directly with the stuff above. + */ + +static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + + check_suspend(s->card); + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + { + if(channel>5) + printk("BAD CHANNEL %d.\n",channel); + else + channel = s->apu[channel]; + /* store based on real hardware apu/reg */ + s->card->apu_map[channel][reg]=data; + } + reg|=(channel<<4); + + /* hooray for double indirection!! */ + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + apu_data_set(s->card, data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + + check_suspend(s->card); + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + channel = s->apu[channel]; + + reg|=(channel<<4); + + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + v=__maestro_read(s->card, IDR0_DATA_PORT); + + spin_unlock_irqrestore(&s->card->lock,flags); + return v; +} + + +/* + * The wavecache buffers between the APUs and + * pci bus mastering + */ + +static void wave_set_register(struct ess_state *s, u16 reg, u16 value) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + check_suspend(s->card); + + spin_lock_irqsave(&s->card->lock,flags); + + outw(reg, ioaddr+0x10); + outw(value, ioaddr+0x12); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 wave_get_register(struct ess_state *s, u16 reg) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + u16 value; + check_suspend(s->card); + + spin_lock_irqsave(&s->card->lock,flags); + outw(reg, ioaddr+0x10); + value=inw(ioaddr+0x12); + spin_unlock_irqrestore(&s->card->lock,flags); + + return value; +} + +static void sound_reset(int ioaddr) +{ + outw(0x2000, 0x18+ioaddr); + udelay(1); + outw(0x0000, 0x18+ioaddr); + udelay(1); +} + +/* sets the play formats of these apus, should be passed the already shifted format */ +static void set_apu_fmt(struct ess_state *s, int apu, int mode) +{ + int apu_fmt = 0x10; + + if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; + if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; + s->apu_mode[apu] = apu_fmt; + s->apu_mode[apu+1] = apu_fmt; +} + +/* this only fixes the output apu mode to be later set by start_dac and + company. output apu modes are set in ess_rec_setup */ +static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data) +{ + s->fmt = (s->fmt & mask) | data; + set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK); +} + +/* this is off by a little bit.. */ +static u32 compute_rate(struct ess_state *s, u32 freq) +{ + u32 clock = clock_freq[s->card->card_type]; + + freq = (freq * clocking)/48000; + + if (freq == 48000) + return 0x10000; + + return ((freq / clock) <<16 )+ + (((freq % clock) << 16) / clock); +} + +static void set_dac_rate(struct ess_state *s, unsigned int rate) +{ + u32 freq; + int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->ratedac = rate; + + if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO)) + rate >>= 1; + +/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 0, 3, freq>>8); + apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 1, 3, freq>>8); +} + +static void set_adc_rate(struct ess_state *s, unsigned rate) +{ + u32 freq; + + /* Sample Rate conversion APUs don't like 0x10000 for their rate */ + if (rate > 47999) + rate = 47999; + if (rate < 4000) + rate = 4000; + + s->rateadc = rate; + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 2, 3, freq>>8); + apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 3, 3, freq>>8); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + + apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 4, 3, freq>>8); + apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 5, 3, freq>>8); +} + +/* Stop our host of recording apus */ +static inline void stop_adc(struct ess_state *s) +{ + /* XXX lets hope we don't have to lock around this */ + if (! (s->enable & ADC_RUNNING)) return; + + s->enable &= ~ADC_RUNNING; + apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); + apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F); +} + +/* stop output apus */ +static void stop_dac(struct ess_state *s) +{ + /* XXX have to lock around this? */ + if (! (s->enable & DAC_RUNNING)) return; + + s->enable &= ~DAC_RUNNING; + apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); + apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F); +} + +static void start_dac(struct ess_state *s) +{ + /* XXX locks? */ + if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && + s->dma_dac.ready && + (! (s->enable & DAC_RUNNING)) ) { + + s->enable |= DAC_RUNNING; + + apu_set_register(s, 0, 0, + (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); + + if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) + apu_set_register(s, 1, 0, + (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); + } +} + +static void start_adc(struct ess_state *s) +{ + /* XXX locks? */ + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) { + + s->enable |= ADC_RUNNING; + apu_set_register(s, 2, 0, + (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); + apu_set_register(s, 4, 0, + (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); + + if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + apu_set_register(s, 3, 0, + (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); + apu_set_register(s, 5, 0, + (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); + } + + } +} + + +/* + * Native play back driver + */ + +/* the mode passed should be already shifted and masked */ +static void +ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + u32 pa; + u32 tmpval; + int high_apu = 0; + int channel; + + M_printk("mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + if(mode&ESS_FMT_STEREO) { + high_apu++; + /* only 16/stereo gets size divided */ + if(mode&ESS_FMT_16BIT) + size>>=1; + } + + for(channel=0; channel <= high_apu; channel++) + { + pa = virt_to_bus(buffer); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + if(!(mode & ESS_FMT_16BIT)) tmpval |= 4; + if(mode & ESS_FMT_STEREO) tmpval |= 2; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on the left one */ + if(!channel) ess->dma_dac.base = pa&0xFFFF; + + pa|=0x00400000; /* System RAM */ + + /* XXX the 16bit here might not be needed.. */ + if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) { + if(channel) + pa|=0x00800000; /* Stereo */ + pa>>=1; + } + +/* XXX think about endianess when writing these registers */ + M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); + /* start of sample */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + apu_set_register(ess, channel, 5, pa&0xFFFF); + /* sample end */ + apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); + /* setting loop len == sample len */ + apu_set_register(ess, channel, 7, size); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(ess, channel, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(ess, channel, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + apu_set_register(ess, channel, 0, 0x400F); + + if(mode&ESS_FMT_16BIT) + ess->apu_mode[channel]=0x10; + else + ess->apu_mode[channel]=0x30; + + if(mode&ESS_FMT_STEREO) { + /* set panning: left or right */ + apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10)); + ess->apu_mode[channel] += 0x10; + } else + apu_set_register(ess, channel, 10, 0x8F08); + } + + /* clear WP interrupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* go team! */ + set_dac_rate(ess,rate); + start_dac(ess); +} + +/* + * Native record driver + */ + +/* again, passed mode is alrady shifted/masked */ +static void +ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + int apu_step = 2; + int channel; + + M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if(mode&ESS_FMT_STEREO) { + size >>=1; + apu_step = 1; + } + + /* APU assignments: 2 = mono/left SRC + 3 = right SRC + 4 = mono/left Input Mixer + 5 = right Input Mixer */ + for(channel=2;channel<6;channel+=apu_step) + { + int i; + int bsize, route; + u32 pa; + u32 tmpval; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if(channel & 0x04) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if(!(channel & 0x01)) { + pa = virt_to_bus(ess->mixbuf); + } else { + pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); + } + + /* we source from a 'magic' apu */ + bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ + route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ + ess->apu_mode[channel] = 0x90; /* Input Mixer */ + + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if(!(channel & 0x01)) { + pa = virt_to_bus(buffer); + } else { + /* right channel records its split half. + *2 accomodates for rampant shifting earlier */ + pa = virt_to_bus(buffer + size*2); + } + + ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = channel + 2; + } + + M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + if(channel==2) ess->dma_adc.base = pa&0xFFFF; + + pa|=0x00400000; /* bit 22 -> System RAM */ + + M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", + ess->apu[channel], pa, bsize, route); + + /* Begin loading the APU */ + for(i=0;i<15;i++) /* clear all PBRs */ + apu_set_register(ess, channel, i, 0x0000); + + apu_set_register(ess, channel, 0, 0x400F); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(ess, channel, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + /* XXX reg is little endian.. */ + apu_set_register(ess, channel, 5, pa&0xFFFF); + apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); + apu_set_register(ess, channel, 7, bsize); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x00F0); + + /* amplitude now? sure. why not. */ + apu_set_register(ess, channel, 9, 0x0000); + + /* set filter tune, radius, polar pan */ + apu_set_register(ess, channel, 10, 0x8F08); + + /* route input */ + apu_set_register(ess, channel, 11, route); + } + + /* clear WP interrupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* let 'er rip */ + set_adc_rate(ess,rate); + start_adc(ess); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmaa??\n"); +} + +static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmac??\n"); +} + +/* Playback pointer */ +static inline unsigned get_dmaa(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,0,5); + +/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ + + offset-=s->dma_dac.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* Record pointer */ +static inline unsigned get_dmac(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,2,5); + +/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ + + /* The offset is an address not a position relative to base */ + offset-=s->dma_adc.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* + * Meet Bob, the timer... + */ + +static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static void stop_bob(struct ess_state *s) +{ + /* Mask IDR 11,17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); + maestro_write(s, 0x17, maestro_read(s, 0x17)&~1); +} + +/* eventually we could be clever and limit bob ints + to the frequency at which our smallest duration + chunks may expire */ +#define ESS_SYSCLK 50000000 +static void start_bob(struct ess_state *s) +{ + int prescale; + int divide; + + /* XXX make freq selector much smarter, see calc_bob_rate */ + int freq = 200; + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for(prescale=5;prescale<12;prescale++) + if(freq > (ESS_SYSCLK>>(prescale+9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide=1; + while((prescale > 5) && (divide<32)) + { + prescale--; + divide <<=1; + } + divide>>=1; + + /* now fine-tune the divider for best match */ + for(;divide<31;divide++) + if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) + break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if(divide == 0) + { + divide++; + if(prescale>5) + prescale--; + } + + maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)|1); + maestro_write(s, 0x17, maestro_read(s, 0x17)|1); +} +/* --------------------------------------------------------------------- */ + +/* this quickly calculates the frequency needed for bob + and sets it if its different than what bob is + currently running at. its called often so + needs to be fairly quick. */ +#define BOB_MIN 50 +#define BOB_MAX 400 +static void calc_bob_rate(struct ess_state *s) { +#if 0 /* this thing tries to set the frequency of bob such that + there are 2 interrupts / buffer walked by the dac/adc. That + is probably very wrong for people who actually care about + mid buffer positioning. it should be calculated as bytes/interrupt + and that needs to be decided :) so for now just use the static 150 + in start_bob.*/ + + unsigned int dac_rate=2,adc_rate=1,newrate; + static int israte=-1; + + if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; + else { + dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_dac.fragsize) ; + } + + if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; + else { + adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_adc.fragsize) ; + } + + if(dac_rate > adc_rate) newrate = adc_rate; + else newrate=dac_rate; + + if(newrate > BOB_MAX) newrate = BOB_MAX; + else { + if(newrate < BOB_MIN) + newrate = BOB_MIN; + } + + if( israte != newrate) { + printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); + israte=newrate; + } +#endif + +} + +static int +prog_dmabuf(struct ess_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + spin_unlock_irqrestore(&s->lock, flags); + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + /* this algorithm is a little nuts.. where did /1000 come from? */ + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + + M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + spin_lock_irqsave(&s->lock, flags); + if (rec) + ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); + else + ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); + + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + + return 0; +} + +static __inline__ void +clear_advance(struct ess_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void +ess_update_ptr(struct ess_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + /* oh boy should this all be re-written. everything in the current code paths think + that the various counters/pointers are expressed in bytes to the user but we have + two apus doing stereo stuff so we fix it up here.. it propogates to all the various + counters from here. */ + if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; + } else { + hwptr = get_dmac(s) % s->dma_adc.dmasize; + } + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmaa(s) % s->dma_dac.dmasize; + /* the apu only reports the length it has seen, not the + length of the memory that has been used (the WP + knows that) */ + if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT)) + hwptr<<=1; + + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; +/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/ + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { + wake_up(&s->dma_dac.wait); + } + } else { + s->dma_dac.count -= diff; +/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ + if (s->dma_dac.count <= 0) { + M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count, + hwptr, s->dma_dac.swptr); + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + /* XXX how on earth can calling this with the lock held work.. */ + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = hwptr; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); +/* printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr, + hwptr);*/ + } + } + } +} + +static void +ess_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ess_state *s; + struct ess_card *c = (struct ess_card *)dev_id; + int i; + u32 event; + + if ( ! (event = inb(c->iobase+0x1A)) ) return; + + outw(inw(c->iobase+4)&1, c->iobase+4); + +/* M_printk("maestro int: %x\n",event);*/ + if(event&(1<<6)) + { + int x; + enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt; + int volume; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(c->iobase+0x1c); + if (x&1) vol_evt = MUTE_EVT; + else if (((x>>1)&7) > 4) vol_evt = UP_EVT; + else vol_evt = DOWN_EVT; + + /* Reset the volume control registers. */ + outb(0x88, c->iobase+0x1c); + outb(0x88, c->iobase+0x1d); + outb(0x88, c->iobase+0x1e); + outb(0x88, c->iobase+0x1f); + + /* Deal with the button press in a hammer-handed + manner by adjusting the master mixer volume. */ + volume = c->mix.mixer_state[0] & 0xff; + if (vol_evt == UP_EVT) { + volume += 10; + if (volume > 100) + volume = 100; + } + else if (vol_evt == DOWN_EVT) { + volume -= 10; + if (volume < 0) + volume = 0; + } else { + /* vol_evt == MUTE_EVT */ + if (volume == 0) + volume = c->dock_mute_vol; + else { + c->dock_mute_vol = volume; + volume = 0; + } + } + set_mixer (c, 0, (volume << 8) | volume); + } + + /* Ack all the interrupts. */ + outb(0xFF, c->iobase+0x1A); + + /* + * Update the pointers for all APU's we are running. + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + break; + spin_lock(&s->lock); + ess_update_ptr(s); + spin_unlock(&s->lock); + } +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC) + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) +{ + unsigned int left,right; + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if(right > 100) right = 100; + if(left > 100) left = 100; + + card->mix.mixer_state[mixer]=(right << 8) | left; + card->mix.write_mixer(card,mixer,left,right); +} + +static void +mixer_push_state(struct ess_card *card) +{ + int i; + for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { + if( ! supported_mixer(card,i)) continue; + + set_mixer(card,i,card->mix.mixer_state[i]); + } +} + +static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg) +{ + int i, val=0; + unsigned long flags; + + VALIDATE_CARD(card); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name,card_names[card->card_type],sizeof(info.name)); + info.modify_counter = card->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name,card_names[card->card_type],sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + + if(!card->mix.recmask_io) { + val = 0; + } else { + spin_lock_irqsave(&card->lock, flags); + val = card->mix.recmask_io(card,1,0); + spin_unlock_irqrestore(&card->lock, flags); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = card->mix.supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = card->mix.record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = card->mix.stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ +/* spin_lock_irqsave(&card->lock, flags); + val = card->mix.read_mixer(card,i); + spin_unlock_irqrestore(&card->lock, flags);*/ + + val = card->mix.mixer_state[i]; +/* M_printk("returned 0x%x for mixer %d\n",val,i);*/ + + break; + } + return put_user(val,(int *)arg); + } + + if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) + return -EINVAL; + + card->mix.modcnt++; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + + if (!card->mix.recmask_io) return -EINVAL; + if(!val) return 0; + if(! (val &= card->mix.record_sources)) return -EINVAL; + + spin_lock_irqsave(&card->lock, flags); + card->mix.recmask_io(card,0,val); + spin_unlock_irqrestore(&card->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + spin_lock_irqsave(&card->lock, flags); + set_mixer(card,i,val); + spin_unlock_irqrestore(&card->lock, flags); + + return 0; + } +} + +/* --------------------------------------------------------------------- */ +static int ess_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct ess_card *card = NULL; + struct pci_dev *pdev; + struct pci_driver *drvr; + + pci_for_each_dev(pdev) { + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + card = (struct ess_card*)pci_get_drvdata (pdev); + if (!card) + continue; + if (card->dev_mixer == minor) + break; + } + } + if (!card) + return -ENODEV; + file->private_data = card; + return 0; +} + +static int ess_release_mixdev(struct inode *inode, struct file *file) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return 0; +} + +static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return mixer_ioctl(card, cmd, arg); +} + +static /*const*/ struct file_operations ess_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: ess_ioctl_mixdev, + open: ess_open_mixdev, + release: ess_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct ess_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int count; + signed long tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + /* XXX uhm.. questionable locking*/ + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; + /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. + or something. who cares. - zach */ + if (!schedule_timeout(tmo ? tmo : 1) && tmo) + M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* Zach sez: "god this is gross.." */ +static int +comb_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, + int count, int bufsize) +{ + /* No such thing as stereo recording, so we + use dual input mixers. which means we have to + combine mono to stereo buffer. yuck. + + but we don't have to be able to work a byte at a time..*/ + + unsigned char *so,*left,*right; + int i; + + so = tmp_buffer; + left = real_buffer + offset; + right = real_buffer + bufsize/2 + offset; + +/* M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/ + + for(i=count/4; i ; i--) { + (*(so+2)) = *(right++); + (*(so+3)) = *(right++); + (*so) = *(left++); + (*(so+1)) = *(left++); + so+=4; + } + + return 0; +} + +/* in this loop, dma_adc.count signifies the amount of data thats waiting + to be copied to the user's buffer. it is filled by the interrupt + handler and drained by this loop. */ +static ssize_t +ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned char *combbuf = NULL; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if(!(combbuf = kmalloc(count,GFP_KERNEL))) + return -ENOMEM; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + /* remember, all these things are expressed in bytes to be + sent to the user.. hence the evil / 2 down below */ + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if ( cnt > 0 ) cnt &= ~3; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto rec_return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + /* FILL ME */ +/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */ + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto rec_return_free; + } + continue; + } + + if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + /* swptr/2 so that we know the real offset in each apu's buffer */ + comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize); + if (copy_to_user(buffer, combbuf, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } else { + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +rec_return_free: + if(combbuf) kfree(combbuf); + return ret; +} + +static ssize_t +ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + cnt = s->dma_dac.dmasize-swptr; + + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ +/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */ + /* FILL ME */ + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto return_free; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } +/* printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/ + + swptr = (swptr + cnt) % s->dma_dac.dmasize; + + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +return_free: + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + +/* In 0.14 prog_dmabuf always returns success anyway ... */ + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf(s, 0)) + return 0; + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf(s, 1)) + return 0; + } + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int ess_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_dac; + } else +#if 0 + /* if we can have the wp/wc do the combining + we can turn this back on. */ + if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_adc; + } else +#endif + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + +/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/ + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + /* fixed at 16bit for now */ + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; +#if 0 + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); +#endif + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_U8, + (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + M_printk("maestro: SETFRAGMENT: %0x\n",val); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static void +set_base_registers(struct ess_state *s,void *vaddr) +{ + unsigned long packed_phys = virt_to_bus(vaddr)>>12; + wave_set_register(s, 0x01FC , packed_phys); + wave_set_register(s, 0x01FD , packed_phys); + wave_set_register(s, 0x01FE , packed_phys); + wave_set_register(s, 0x01FF , packed_phys); +} + +/* + * this guy makes sure we're in the right power + * state for what we want to be doing + */ +static void maestro_power(struct ess_card *card, int tostate) +{ + u16 active_mask = acpi_state_mask[tostate]; + u8 state; + + if(!use_pm) return; + + pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state); + state&=3; + + /* make sure we're in the right state */ + if(state != tostate) { + M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n", + card->pcidev->bus->number, + PCI_SLOT(card->pcidev->devfn), + PCI_FUNC(card->pcidev->devfn), + state,tostate); + pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate); + } + + /* and make sure the units we care about are on + XXX we might want to do this before state flipping? */ + pci_write_config_word(card->pcidev, 0x54, ~ active_mask); + pci_write_config_word(card->pcidev, 0x56, ~ active_mask); +} + +/* we allocate a large power of two for all our memory. + this is cut up into (not to scale :): + |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | +*/ +static int +allocate_buffers(struct ess_state *s) +{ + void *rawbuf=NULL; + int order,i; + struct page *page, *pend; + + /* alloc as big a chunk as we can */ + for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) + if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) + break; + + if (!rawbuf) + return 1; + + M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<card->dmapages = rawbuf; + s->card->dmaorder = order; + + for(i=0;icard->channels[i]; + + if(ess->dev_audio == -1) + continue; + + ess->dma_dac.ready = s->dma_dac.mapped = 0; + ess->dma_adc.ready = s->dma_adc.mapped = 0; + ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1; + + /* offset dac and adc buffers starting half way through and then at each [da][ad]c's + order's intervals.. */ + ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 ))); + ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder); + /* offset mixbuf by a mixbuf so that the lame status fifo can + happily scribble away.. */ + ess->mixbuf = rawbuf + (512 * (i+1)); + + M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf, + ess->dma_adc.rawbuf, ess->mixbuf); + + } + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(rawbuf); page <= pend; page++) + mem_map_reserve(page); + + return 0; +} +static void +free_buffers(struct ess_state *s) +{ + struct page *page, *pend; + + s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL; + s->dma_dac.mapped = s->dma_adc.mapped = 0; + s->dma_dac.ready = s->dma_adc.ready = 0; + + M_printk("maestro: freeing %p\n",s->card->dmapages); + /* undo marking the pages as reserved */ + + pend = virt_to_page(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1); + for (page = virt_to_page(s->card->dmapages); page <= pend; page++) + mem_map_unreserve(page); + + free_pages((unsigned long)s->card->dmapages,s->card->dmaorder); + s->card->dmapages = NULL; +} + +static int +ess_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct ess_state *s = NULL; + unsigned char fmtm = ~0, fmts = 0; + struct pci_dev *pdev; + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + + pci_for_each_dev(pdev) { + struct ess_card *c; + struct pci_driver *drvr; + + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + int i; + struct ess_state *sp; + + c = (struct ess_card*)pci_get_drvdata (pdev); + if (!c) + continue; + for(i=0;ichannels[i]; + if(sp->dev_audio < 0) + continue; + if((sp->dev_audio ^ minor) & ~0xf) + continue; + s=sp; + } + } + } + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EWOULDBLOCK; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + /* under semaphore.. */ + if ((s->card->dmapages==NULL) && allocate_buffers(s)) { + up(&s->open_sem); + return -ENOMEM; + } + + /* we're covered by the open_sem */ + if( ! s->card->dsps_open ) { + maestro_power(s->card,ACPI_D0); + start_bob(s); + } + s->card->dsps_open++; + M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); + + /* ok, lets write WC base regs now that we've + powered up the chip */ + M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages), + ((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12); + set_base_registers(s,s->card->dmapages); + + if (file->f_mode & FMODE_READ) { +/* + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */ + + fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT); + fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + up(&s->open_sem); + return 0; +} + +static int +ess_release(struct inode *inode, struct file *file) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + /* we're covered by the open_sem */ + M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); + if( --s->card->dsps_open <= 0) { + s->card->dsps_open = 0; + stop_bob(s); + free_buffers(s); + maestro_power(s->card,ACPI_D2); + } + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static struct file_operations ess_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: ess_read, + write: ess_write, + poll: ess_poll, + ioctl: ess_ioctl, + mmap: ess_mmap, + open: ess_open, + release: ess_release, +}; + +static int +maestro_config(struct ess_card *card) +{ + struct pci_dev *pcidev = card->pcidev; + struct ess_state *ess = &card->channels[0]; + int apu,iobase = card->iobase; + u16 w; + u32 n; + + /* We used to muck around with pci config space that + * we had no business messing with. We don't know enough + * about the machine to know which DMA mode is appropriate, + * etc. We were guessing wrong on some machines and making + * them unhappy. We now trust in the BIOS to do things right, + * which almost certainly means a new host of problems will + * arise with broken BIOS implementations. screw 'em. + * We're already intolerant of machines that don't assign + * IRQs. + */ + + /* do config work at full power */ + maestro_power(card,ACPI_D0); + + pci_read_config_word(pcidev, 0x50, &w); + + w&=~(1<<5); /* Don't swap left/right (undoc)*/ + + pci_write_config_word(pcidev, 0x50, w); + + pci_read_config_word(pcidev, 0x52, &w); + w&=~(1<<15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w&=~(1<<14); /* External clock */ + + w|= (1<<7); /* Hardware volume control on */ + w|= (1<<6); /* Debounce off: easier to push the HWV buttons. */ + w&=~(1<<5); /* GPIO 4:5 */ + w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ + w&=~(1<<2); /* MIDI fix off (undoc) */ + w&=~(1<<1); /* reserved, always write 0 */ + pci_write_config_word(pcidev, 0x52, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pcidev, 0x40, &w); + w|=(1<<15); /* legacy decode off */ + w&=~(1<<14); /* Disable SIRQ */ + w&=~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pcidev, 0x40, w); + + /* Set up 978 docking control chip. */ + pci_read_config_word(pcidev, 0x58, &w); + w|=1<<2; /* Enable 978. */ + w|=1<<3; /* Turn on 978 hardware volume control. */ + w&=~(1<<11); /* Turn on 978 mixer volume control. */ + pci_write_config_word(pcidev, 0x58, w); + + sound_reset(iobase); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase+0x34); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase+0x36); /* direct sound, stereo */ + udelay(20); + + + /* + * Reset the CODEC + */ + + maestro_ac97_reset(iobase,pcidev); + + /* + * Ring Bus Setup + */ + + n=inl(iobase+0x34); + n&=~0xF000; + n|=12<<12; /* Direct Sound, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x0F00; /* Modem off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F0; + n|=9<<4; /* DAC, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F; /* ASSP off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<29); /* Enable ring bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<28); /* Enable serial bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F00000; /* MIC off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F0000; /* I2S off */ + outl(n, iobase+0x34); + + + w=inw(iobase+0x18); + w&=~(1<<7); /* ClkRun off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<6); /* Hardware volume control interrupt off... for now. */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<4); /* ASSP irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<3); /* ISDN irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<2); /* Direct Sound IRQ on */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<1); /* MPU401 IRQ off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<0); /* SB IRQ on */ + outw(w, iobase+0x18); + + /* Set hardware volume control registers to midpoints. + We can tell which button was pushed based on how they change. */ + outb(0x88, iobase+0x1c); + outb(0x88, iobase+0x1d); + outb(0x88, iobase+0x1e); + outb(0x88, iobase+0x1f); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of wether we use the assp or not. */ + + outb(0, iobase+0xA4); + outb(3, iobase+0xA2); + outb(0, iobase+0xA6); + + for(apu=0;apu<16;apu++) + { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + + /* + * The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too. + */ + outw(0x01D0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + } + +#if 1 + wave_set_register(ess, IDR7_WAVE_ROMRAM, + (wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400); +#else + maestro_write(ess, IDR7_WAVE_ROMRAM, + (maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400); +#endif + + maestro_write(ess, IDR2_CRAM_DATA, 0x0000); + maestro_write(ess, 0x08, 0xB004); + /* Now back to the DirectSound stuff */ + maestro_write(ess, 0x09, 0x001B); + maestro_write(ess, 0x0A, 0x8000); + maestro_write(ess, 0x0B, 0x3F37); + maestro_write(ess, 0x0C, 0x0098); + + /* parallel out ?? */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0xF000)|0x8000); + /* parallel in, has something to do with recording :) */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0x0F00)|0x0500); + + maestro_write(ess, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + outw(inw(0x14+iobase)|(1<<8),0x14+iobase); + outw(inw(0x14+iobase)&0xFE03,0x14+iobase); + outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase); + outw(inw(0x14+iobase)|(1<<7),0x14+iobase); + + outw(0xA1A0, 0x14+iobase); /* 0300 ? */ + + /* Now clear the APU control ram */ + for(apu=0;apupower_regs = 0; + + /* check to see if we have a capabilities list in + the config register */ + pci_read_config_word(pcidev, PCI_STATUS, &w); + if(! w & PCI_STATUS_CAP_LIST) return 0; + + /* walk the list, starting at the head. */ + pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next); + + while(next && max--) { + pci_read_config_dword(pcidev, next & ~3, &n); + if((n & 0xff) == PCI_CAP_ID_PM) { + card->power_regs = next; + break; + } + next = ((n>>8) & 0xff); + } + + return card->power_regs ? 1 : 0; +} + +static int __init +maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid) +{ + int card_type = pdid->driver_data; + u32 n; + int iobase; + int i, ret; + struct ess_card *card; + struct ess_state *ess; + struct pm_dev *pmdev; + int num = 0; + +/* when built into the kernel, we only print version if device is found */ +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(version); +#endif + + /* don't pick up weird modem maestros */ + if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return -ENODEV; + + + if ((ret=pci_enable_device(pcidev))) + return ret; + + iobase = pci_resource_start(pcidev,0); + if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO)) + return -ENODEV; + + if(pcidev->irq == 0) + return -ENODEV; + + /* stake our claim on the iospace */ + if( request_region(iobase, 256, card_names[card_type]) == NULL ) + { + printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); + return -EBUSY; + } + + /* just to be sure */ + pci_set_master(pcidev); + + card = kmalloc(sizeof(struct ess_card), GFP_KERNEL); + if(card == NULL) + { + printk(KERN_WARNING "maestro: out of memory\n"); + release_region(iobase, 256); + return -ENOMEM; + } + + memset(card, 0, sizeof(*card)); + card->pcidev = pcidev; + + pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), + maestro_pm_callback); + if (pmdev) + pmdev->data = card; + + card->iobase = iobase; + card->card_type = card_type; + card->irq = pcidev->irq; + card->magic = ESS_CARD_MAGIC; + spin_lock_init(&card->lock); + init_waitqueue_head(&card->suspend_queue); + + card->dock_mute_vol = 50; + + /* init our groups of 6 apus */ + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + spin_lock_init(&s->lock); + init_MUTEX(&s->open_sem); + s->magic = ESS_STATE_MAGIC; + + s->apu[0] = 6*i; + s->apu[1] = (6*i)+1; + s->apu[2] = (6*i)+2; + s->apu[3] = (6*i)+3; + s->apu[4] = (6*i)+4; + s->apu[5] = (6*i)+5; + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk("maestro: BOTCH!\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0) + break; + } + + num = i; + + /* clear the rest if we ran out of slots to register */ + for(;ichannels[i]; + s->dev_audio = -1; + } + + ess = &card->channels[0]; + + /* + * Ok card ready. Begin setup proper + */ + + printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", + card_names[card_type],iobase,card->irq); + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); + + /* turn off power management unless: + * - the user explicitly asks for it + * or + * - we're not a 2e, lesser chipps seem to have problems. + * - we're not on our _very_ small whitelist. some implemenetations + * really dont' like the pm code, others require it. + * feel free to expand this as required. + */ +#define SUBSYSTEM_VENDOR(x) (x&0xffff) + if( (use_pm != 1) && + ((card_type != TYPE_MAESTRO2E) || (SUBSYSTEM_VENDOR(n) != 0x1028))) + use_pm = 0; + + if(!use_pm) + printk(KERN_INFO "maestro: not attempting power management.\n"); + else { + if(!parse_power(card,pcidev)) + printk(KERN_INFO "maestro: no PCI power management interface found.\n"); + else { + pci_read_config_dword(pcidev, card->power_regs, &n); + printk(KERN_INFO "maestro: PCI power management capability: 0x%x\n",n>>16); + } + } + + maestro_config(card); + + if(maestro_ac97_get(card, 0x00)==0x0080) { + printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n" + "\tyou should tell someone about this.\n"); + } else { + maestro_ac97_init(card); + } + + if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { + printk("maestro: couldn't register mixer!\n"); + } else { + memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state)); + mixer_push_state(card); + } + + if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card))) + { + printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + release_region(card->iobase, 256); + unregister_reboot_notifier(&maestro_nb); + kfree(card); + return ret; + } + + /* Turn on hardware volume control interrupt. + This has to come after we grab the IRQ above, + or a crash will result on installation if a button has been pressed, + because in that case we'll get an immediate interrupt. */ + n = inw(iobase+0x18); + n|=(1<<6); + outw(n, iobase+0x18); + + pci_set_drvdata(pcidev,card); + /* now go to sleep 'till something interesting happens */ + maestro_power(card,ACPI_D2); + + printk(KERN_INFO "maestro: %d channels configured.\n", num); + return 0; +} + +static void maestro_remove(struct pci_dev *pcidev) { + struct ess_card *card = pci_get_drvdata(pcidev); + int i; + + /* XXX maybe should force stop bob, but should be all + stopped by _release by now */ + free_irq(card->irq, card); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(ess->dev_audio != -1) + unregister_sound_dsp(ess->dev_audio); + } + /* Goodbye, Mr. Bond. */ + maestro_power(card,ACPI_D3); + release_region(card->iobase, 256); + kfree(card); + pci_set_drvdata(pcidev,NULL); +} + +static struct pci_device_id maestro_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2}, + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2E}, + {PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, maestro_pci_tbl); + +static struct pci_driver maestro_pci_driver = { + name:"maestro", + id_table:maestro_pci_tbl, + probe:maestro_probe, + remove:maestro_remove, +}; + +int __init init_maestro(void) +{ + int rc; + + rc = pci_module_init(&maestro_pci_driver); + if (rc < 0) + return rc; + + if (register_reboot_notifier(&maestro_nb)) + printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); +#ifdef MODULE + printk(version); +#endif + if (dsps_order < 0) { + dsps_order = 1; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + else if (dsps_order > MAX_DSP_ORDER) { + dsps_order = MAX_DSP_ORDER; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + return 0; +} + +static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + /* this notifier is called when the kernel is really shut down. */ + M_printk("maestro: shutting down\n"); + /* this will remove all card instances too */ + pci_unregister_driver(&maestro_pci_driver); + /* XXX dunno about power management */ + return NOTIFY_OK; +} + +/* --------------------------------------------------------------------- */ + + +void cleanup_maestro(void) { + M_printk("maestro: unloading\n"); + pci_unregister_driver(&maestro_pci_driver); + pm_unregister_all(maestro_pm_callback); + unregister_reboot_notifier(&maestro_nb); +} + +/* --------------------------------------------------------------------- */ + +void +check_suspend(struct ess_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!card->in_suspend) return; + + card->in_suspend++; + add_wait_queue(&(card->suspend_queue), &wait); + current->state = TASK_UNINTERRUPTIBLE; + schedule(); + remove_wait_queue(&(card->suspend_queue), &wait); + current->state = TASK_RUNNING; +} + +static int +maestro_suspend(struct ess_card *card) +{ + unsigned long flags; + int i,j; + + save_flags(flags); + cli(); /* over-kill */ + + M_printk("maestro: apm in dev %p\n",card); + + /* we have to read from the apu regs, need + to power it up */ + maestro_power(card,ACPI_D0); + + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; + + M_printk("maestro: stopping apus for device %d\n",i); + stop_dac(s); + stop_adc(s); + for(j=0;j<6;j++) + card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5); + + } + + /* get rid of interrupts? */ + if( card->dsps_open > 0) + stop_bob(&card->channels[0]); + + card->in_suspend++; + + restore_flags(flags); + + /* we trust in the bios to power down the chip on suspend. + * XXX I'm also not sure that in_suspend will protect + * against all reg accesses from here on out. + */ + return 0; +} +static int +maestro_resume(struct ess_card *card) +{ + unsigned long flags; + int i; + + save_flags(flags); + cli(); /* over-kill */ + + card->in_suspend = 0; + + M_printk("maestro: resuming card at %p\n",card); + + /* restore all our config */ + maestro_config(card); + /* need to restore the base pointers.. */ + if(card->dmapages) + set_base_registers(&card->channels[0],card->dmapages); + + mixer_push_state(card); + + /* set each channels' apu control registers before + * restoring audio + */ + for(i=0;ichannels[i]; + int chan,reg; + + if(s->dev_audio == -1) + continue; + + for(chan = 0 ; chan < 6 ; chan++) { + wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]); + for(reg = 1 ; reg < NR_APU_REGS ; reg++) + apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]); + } + for(chan = 0 ; chan < 6 ; chan++) + apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F); + } + + /* now we flip on the music */ + + if( card->dsps_open <= 0) { + /* this card's idle */ + maestro_power(card,ACPI_D2); + } else { + /* ok, we're actually playing things on + this card */ + maestro_power(card,ACPI_D0); + start_bob(&card->channels[0]); + for(i=0;ichannels[i]; + + /* these use the apu_mode, and can handle + spurious calls */ + start_dac(s); + start_adc(s); + } + } + + restore_flags(flags); + + /* all right, we think things are ready, + wake up people who were using the device + when we suspended */ + wake_up(&(card->suspend_queue)); + + return 0; +} + +int +maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct ess_card *card = (struct ess_card*) dev->data; + + if ( ! card ) goto out; + + M_printk("maestro: pm event 0x%x received for card %p\n", rqst, card); + + switch (rqst) { + case PM_SUSPEND: + maestro_suspend(card); + break; + case PM_RESUME: + maestro_resume(card); + break; + /* + * we'd also like to find out about + * power level changes because some biosen + * do mean things to the maestro when they + * change their power state. + */ + } +out: + return 0; +} + +module_init(init_maestro); +module_exit(cleanup_maestro); diff -Nru linux/sound/oss/maestro.h linux-2.4.19-pre5-mjc/sound/oss/maestro.h --- linux/sound/oss/maestro.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/maestro.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,60 @@ +/* + * Registers for the ESS PCI cards + */ + +/* + * Memory access + */ + +#define ESS_MEM_DATA 0x00 +#define ESS_MEM_INDEX 0x02 + +/* + * AC-97 Codec port. Delay 1uS after each write. This is used to + * talk AC-97 (see intel.com). Write data then register. + */ + +#define ESS_AC97_INDEX 0x30 /* byte wide */ +#define ESS_AC97_DATA 0x32 + +/* + * Reading is a bit different. You write register|0x80 to ubdex + * delay 1uS poll the low bit of index, when it clears read the + * data value. + */ + +/* + * Control port. Not yet fully understood + * The value 0xC090 gets loaded to it then 0x0000 and 0x2800 + * to the data port. Then after 4uS the value 0x300 is written + */ + +#define RING_BUS_CTRL_L 0x34 +#define RING_BUS_CTRL_H 0x36 + +/* + * This is also used during setup. The value 0x17 is written to it + */ + +#define ESS_SETUP_18 0x18 + +/* + * And this one gets 0x000b + */ + +#define ESS_SETUP_A2 0xA2 + +/* + * And this 0x0000 + */ + +#define ESS_SETUP_A4 0xA4 +#define ESS_SETUP_A6 0xA6 + +/* + * Stuff to do with Harpo - the wave stuff + */ + +#define ESS_WAVETABLE_SIZE 0x14 +#define ESS_WAVETABLE_2M 0xA180 + diff -Nru linux/sound/oss/maestro3.c linux-2.4.19-pre5-mjc/sound/oss/maestro3.c --- linux/sound/oss/maestro3.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/maestro3.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2962 @@ +/***************************************************************************** + * + * ESS Maestro3/Allegro driver for Linux 2.4.x + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + * (c) Copyright 2000 Zach Brown + * + * I need to thank many people for helping make this driver happen. + * As always, Eric Brombaugh was a hacking machine and killed many bugs + * that I was too dumb to notice. Howard Kim at ESS provided reference boards + * and as much docs as he could. Todd and Mick at Dell tested snapshots on + * an army of laptops. msw and deviant at Red Hat also humoured me by hanging + * their laptops every few hours in the name of science. + * + * Shouts go out to Mike "DJ XPCom" Ang. + * + * History + * v1.22 - Feb 28 2001 - Zach Brown + * allocate mem at insmod/setup, rather than open + * limit pci dma addresses to 28bit, thanks guys. + * v1.21 - Feb 04 2001 - Zach Brown + * fix up really dumb notifier -> suspend oops + * v1.20 - Jan 30 2001 - Zach Brown + * get rid of pm callback and use pci_dev suspend/resume instead + * m3_probe cleanups, including pm oops think-o + * v1.10 - Jan 6 2001 - Zach Brown + * revert to lame remap_page_range mmap() just to make it work + * record mmap fixed. + * fix up incredibly broken open/release resource management + * duh. fix record format setting. + * add SMP locking and cleanup formatting here and there + * v1.00 - Dec 16 2000 - Zach Brown + * port to sexy 2.4 interfaces + * properly align instance allocations so recording works + * clean up function namespace a little :/ + * update PCI IDs based on mail from ESS + * arbitrarily bump version number to show its 2.4 now, + * 2.2 will stay 0., oss_audio port gets 2. + * v0.03 - Nov 05 2000 - Zach Brown + * disable recording but allow dsp to be opened read + * pull out most silly compat defines + * v0.02 - Nov 04 2000 - Zach Brown + * changed clocking setup for m3, slowdown fixed. + * codec reset is hopefully reliable now + * rudimentary apm/power management makes suspend/resume work + * v0.01 - Oct 31 2000 - Zach Brown + * first release + * v0.00 - Sep 09 2000 - Zach Brown + * first pass derivation from maestro.c + * + * TODO + * in/out allocated contiguously so fullduplex mmap will work? + * no beep on init (mute) + * resetup msrc data memory if freq changes? + * + * -- + * + * Allow me to ramble a bit about the m3 architecture. The core of the + * chip is the 'assp', the custom ESS dsp that runs the show. It has + * a small amount of code and data ram. ESS drops binary dsp code images + * on our heads, but we don't get to see specs on the dsp. + * + * The constant piece of code on the dsp is the 'kernel'. It also has a + * chunk of the dsp memory that is statically set aside for its control + * info. This is the KDATA defines in maestro3.h. Part of its core + * data is a list of code addresses that point to the pieces of DSP code + * that it should walk through in its loop. These other pieces of code + * do the real work. The kernel presumably jumps into each of them in turn. + * These code images tend to have their own data area, and one can have + * multiple data areas representing different states for each of the 'client + * instance' code portions. There is generally a list in the kernel data + * that points to the data instances for a given piece of code. + * + * We've only been given the binary image for the 'minisrc', mini sample + * rate converter. This is rather annoying because it limits the work + * we can do on the dsp, but it also greatly simplifies the job of managing + * dsp data memory for the code and data for our playing streams :). We + * statically allocate the minisrc code into a region we 'know' to be free + * based on the map of the binary kernel image we're loading. We also + * statically allocate the data areas for the maximum number of pcm streams + * we can be dealing with. This max is set by the length of the static list + * in the kernel data that records the number of minisrc data regions we + * can have. Thats right, all software dsp mixing with static code list + * limits. Rock. + * + * How sound goes in and out is still a relative mystery. It appears + * that the dsp has the ability to get input and output through various + * 'connections'. To do IO from or to a connection, you put the address + * of the minisrc client area in the static kernel data lists for that + * input or output. so for pcm -> dsp -> mixer, we put the minisrc data + * instance in the DMA list and also in the list for the mixer. I guess + * it Just Knows which is in/out, and we give some dma control info that + * helps. There are all sorts of cool inputs/outputs that it seems we can't + * use without dsp code images that know how to use them. + * + * So at init time we preload all the memory allocation stuff and set some + * system wide parameters. When we really get a sound to play we build + * up its minisrc header (stream parameters, buffer addresses, input/output + * settings). Then we throw its header on the various lists. We also + * tickle some KDATA settings that ask the assp to raise clock interrupts + * and do some amount of software mixing before handing data to the ac97. + * + * Sorry for the vague details. Feel free to ask Eric or myself if you + * happen to be trying to use this driver elsewhere. Please accept my + * apologies for the quality of the OSS support code, its passed through + * too many hands now and desperately wants to be rethought. + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + /* + * for crizappy mmap() + */ +#include + +#include "maestro3.h" + +#define M_DEBUG 1 + +#define DRIVER_VERSION "1.22" +#define M3_MODULE_NAME "maestro3" +#define PFX M3_MODULE_NAME ": " + +#define M3_STATE_MAGIC 0x734d724d +#define M3_CARD_MAGIC 0x646e6f50 + +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define SND_DEV_DSP16 5 + +#ifdef M_DEBUG +static int debug; +#define DPMOD 1 /* per module load */ +#define DPSTR 2 /* per 'stream' */ +#define DPSYS 3 /* per syscall */ +#define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */ +#define DPINT 5 /* per interrupt, LOTS */ +#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);} +#else +#define DPRINTK(x) +#endif + +struct m3_list { + int curlen; + u16 mem_addr; + int max; +}; + +int external_amp = 1; + +struct m3_state { + unsigned int magic; + struct m3_card *card; + unsigned char fmt, enable; + + int index; + + /* this locks around the oss state in the driver */ + spinlock_t lock; + + struct semaphore open_sem; + wait_queue_head_t open_wait; + mode_t open_mode; + + int dev_audio; + + struct assp_instance { + u16 code, data; + } dac_inst, adc_inst; + + /* should be in dmabuf */ + unsigned int rateadc, ratedac; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + /* new in m3 */ + int mixer_index, dma_index, msrc_index, adc1_index; + int in_lists; + /* 2.4.. */ + dma_addr_t handle; + + } dma_dac, dma_adc; +}; + +struct m3_card { + unsigned int magic; + + struct m3_card *next; + + struct ac97_codec *ac97; + spinlock_t ac97_lock; + + int card_type; + +#define NR_DSPS 1 +#define MAX_DSPS NR_DSPS + struct m3_state channels[MAX_DSPS]; + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* hardware resources */ + struct pci_dev *pcidev; + u32 iobase; + u32 irq; + + int dacs_active; + + int timer_users; + + struct m3_list msrc_list, + mixer_list, + adc1_list, + dma_list; + + /* for storing reset state..*/ + u8 reset_state; + + u16 *suspend_mem; + int in_suspend; + wait_queue_head_t suspend_queue; +}; + +/* + * an arbitrary volume we set the internal + * volume settings to so that the ac97 volume + * range is a little less insane. 0x7fff is + * max. + */ +#define ARB_VOLUME ( 0x6800 ) + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +enum { + ESS_ALLEGRO, + ESS_MAESTRO3, + /* + * a maestro3 with 'hardware strapping', only + * found inside ESS? + */ + ESS_MAESTRO3HW, +}; + +static char *card_names[] = { + [ESS_ALLEGRO] = "Allegro", + [ESS_MAESTRO3] = "Maestro3(i)", + [ESS_MAESTRO3HW] = "Maestro3(i)hw" +}; + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#endif + +#define M3_DEVICE(DEV, TYPE) \ +{ \ +vendor: PCI_VENDOR_ESS, \ +device: DEV, \ +subvendor: PCI_ANY_ID, \ +subdevice: PCI_ANY_ID, \ +class: PCI_CLASS_MULTIMEDIA_AUDIO << 8, \ +class_mask: 0xffff << 8, \ +driver_data: TYPE, \ +} + +static struct pci_device_id m3_id_table[] __initdata = { + M3_DEVICE(0x1988, ESS_ALLEGRO), + M3_DEVICE(0x1998, ESS_MAESTRO3), + M3_DEVICE(0x199a, ESS_MAESTRO3HW), + {0,} +}; + +MODULE_DEVICE_TABLE (pci, m3_id_table); + +/* + * reports seem to indicate that the m3 is limited + * to 28bit bus addresses. aaaargggh... + */ +#define M3_PCI_DMA_MASK 0x0fffffff + +static unsigned +ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +static struct m3_card *devs; + +/* + * I'm not very good at laying out functions in a file :) + */ +static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf); +static int m3_suspend(struct pci_dev *pci_dev, u32 state); +static void check_suspend(struct m3_card *card); + +struct notifier_block m3_reboot_nb = {m3_notifier, NULL, 0}; + +static void m3_outw(struct m3_card *card, + u16 value, unsigned long reg) +{ + check_suspend(card); + outw(value, card->iobase + reg); +} + +static u16 m3_inw(struct m3_card *card, unsigned long reg) +{ + check_suspend(card); + return inw(card->iobase + reg); +} +static void m3_outb(struct m3_card *card, + u8 value, unsigned long reg) +{ + check_suspend(card); + outb(value, card->iobase + reg); +} +static u8 m3_inb(struct m3_card *card, unsigned long reg) +{ + check_suspend(card); + return inb(card->iobase + reg); +} + +/* + * access 16bit words to the code or data regions of the dsp's memory. + * index addresses 16bit words. + */ +static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index) +{ + m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + m3_outw(card, index, DSP_PORT_MEMORY_INDEX); + return m3_inw(card, DSP_PORT_MEMORY_DATA); +} +static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index) +{ + unsigned long flags; + u16 ret; + + spin_lock_irqsave(&(card->lock), flags); + ret = __m3_assp_read(card, region, index); + spin_unlock_irqrestore(&(card->lock), flags); + + return ret; +} + +static void __m3_assp_write(struct m3_card *card, + u16 region, u16 index, u16 data) +{ + m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + m3_outw(card, index, DSP_PORT_MEMORY_INDEX); + m3_outw(card, data, DSP_PORT_MEMORY_DATA); +} +static void m3_assp_write(struct m3_card *card, + u16 region, u16 index, u16 data) +{ + unsigned long flags; + + spin_lock_irqsave(&(card->lock), flags); + __m3_assp_write(card, region, index, data); + spin_unlock_irqrestore(&(card->lock), flags); +} + +static void m3_assp_halt(struct m3_card *card) +{ + card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; + mdelay(10); + m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +static void m3_assp_continue(struct m3_card *card) +{ + m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +/* + * This makes me sad. the maestro3 has lists + * internally that must be packed.. 0 terminates, + * apparently, or maybe all unused entries have + * to be 0, the lists have static lengths set + * by the binary code images. + */ + +static int m3_add_list(struct m3_card *card, + struct m3_list *list, u16 val) +{ + DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n", + val, list, list->curlen); + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + list->curlen, + val); + + return list->curlen++; + +} + +static void m3_remove_list(struct m3_card *card, + struct m3_list *list, int index) +{ + u16 val; + int lastindex = list->curlen - 1; + + DPRINTK(DPSTR, "removing ind %d from list 0x%p\n", + index, list); + + if(index != lastindex) { + val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex); + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + index, + val); + } + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex, + 0); + + list->curlen--; +} + +static void set_fmt(struct m3_state *s, unsigned char mask, unsigned char data) +{ + int tmp; + + s->fmt = (s->fmt & mask) | data; + + tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + /* write to 'mono' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1, + (tmp & ESS_FMT_STEREO) ? 0 : 1); + /* write to '8bit' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2, + (tmp & ESS_FMT_16BIT) ? 0 : 1); + + tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK; + + /* write to 'mono' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1, + (tmp & ESS_FMT_STEREO) ? 0 : 1); + /* write to '8bit' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2, + (tmp & ESS_FMT_16BIT) ? 0 : 1); +} + +static void set_dac_rate(struct m3_state *s, unsigned int rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + + s->ratedac = rate; + + freq = ((rate << 15) + 24000 ) / 48000; + if(freq) + freq--; + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_FREQUENCY, + freq); +} + +static void set_adc_rate(struct m3_state *s, unsigned int rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + + s->rateadc = rate; + + freq = ((rate << 15) + 24000 ) / 48000; + if(freq) + freq--; + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_FREQUENCY, + freq); +} + +static void inc_timer_users(struct m3_card *card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + card->timer_users++; + DPRINTK(DPSYS, "inc timer users now %d\n", + card->timer_users); + if(card->timer_users != 1) + goto out; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 240 ) ; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 240 ) ; + + m3_outw(card, + m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +out: + spin_unlock_irqrestore(&card->lock, flags); +} + +static void dec_timer_users(struct m3_card *card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + card->timer_users--; + DPRINTK(DPSYS, "dec timer users now %d\n", + card->timer_users); + if(card->timer_users > 0 ) + goto out; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 0 ) ; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 0 ) ; + + m3_outw(card, m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +out: + spin_unlock_irqrestore(&card->lock, flags); +} + +/* + * {start,stop}_{adc,dac} should be called + * while holding the 'state' lock and they + * will try to grab the 'card' lock.. + */ +static void stop_adc(struct m3_state *s) +{ + if (! (s->enable & ADC_RUNNING)) + return; + + s->enable &= ~ADC_RUNNING; + dec_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_INSTANCE_READY, 0); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 0); +} + +static void stop_dac(struct m3_state *s) +{ + if (! (s->enable & DAC_RUNNING)) + return; + + DPRINTK(DPSYS, "stop_dac()\n"); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_INSTANCE_READY, 0); + + s->enable &= ~DAC_RUNNING; + s->card->dacs_active--; + dec_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + s->card->dacs_active ) ; +} + +static void start_dac(struct m3_state *s) +{ + if( (!s->dma_dac.mapped && s->dma_dac.count < 1) || + !s->dma_dac.ready || + (s->enable & DAC_RUNNING)) + return; + + DPRINTK(DPSYS, "start_dac()\n"); + + s->enable |= DAC_RUNNING; + s->card->dacs_active++; + inc_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_INSTANCE_READY, 1); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + s->card->dacs_active ) ; +} + +static void start_adc(struct m3_state *s) +{ + if ((! s->dma_adc.mapped && + s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + || !s->dma_adc.ready + || (s->enable & ADC_RUNNING) ) + return; + + DPRINTK(DPSYS, "start_adc()\n"); + + s->enable |= ADC_RUNNING; + inc_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 1); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_INSTANCE_READY, 1); +} + +static struct play_vals { + u16 addr, val; +} pv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 0} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ + {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ +}; + + +/* the mode passed should be already shifted and masked */ +static void m3_play_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) +{ + int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); + int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); + int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + struct dmabuf *db = &s->dma_dac; + int i; + + DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_ADDRL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_ADDRH, + HI(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_CURRENTL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_CURRENTH, + HI(virt_to_bus(buffer))); +#undef LO +#undef HI + + /* dsp buffers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); + + /* + * some per client initializers + */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12, + s->dac_inst.data + 40 + 8); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19, + s->dac_inst.code + MINISRC_COEF_LOC); + + /* enable or disable low pass filter? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22, + s->ratedac > 45000 ? 0xff : 0 ); + + /* tell it which way dma is going? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_DMA_CONTROL, + DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + pv[i].addr, pv[i].val); + + /* + * put us in the lists if we're not already there + */ + + if(db->in_lists == 0) { + + db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->dma_index = m3_add_list(s->card, &s->card->dma_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->mixer_index = m3_add_list(s->card, &s->card->mixer_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->in_lists = 1; + } + + set_dac_rate(s,rate); + start_dac(s); +} + +/* + * Native record driver + */ +static struct rec_vals { + u16 addr, val; +} rv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 1} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ + {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ + {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ +}; + +/* again, passed mode is alrady shifted/masked */ +static void m3_rec_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) +{ + int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2); + int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); + int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + struct dmabuf *db = &s->dma_adc; + int i; + + DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_ADDRL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_ADDRH, + HI(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_CURRENTL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_CURRENTH, + HI(virt_to_bus(buffer))); +#undef LO +#undef HI + + /* dsp buffers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); + + /* + * some per client initializers + */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12, + s->adc_inst.data + 40 + 8); + + /* tell it which way dma is going? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_DMA_CONTROL, + DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + rv[i].addr, rv[i].val); + + /* + * put us in the lists if we're not already there + */ + + if(db->in_lists == 0) { + + db->adc1_index = m3_add_list(s->card, &s->card->adc1_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->dma_index = m3_add_list(s->card, &s->card->dma_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->in_lists = 1; + } + + set_adc_rate(s,rate); + start_adc(s); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct m3_state *s, unsigned int addr, unsigned int count) +{ + DPRINTK(DPINT,"set_dmaa??\n"); +} + +static void set_dmac(struct m3_state *s, unsigned int addr, unsigned int count) +{ + DPRINTK(DPINT,"set_dmac??\n"); +} + +u32 get_dma_pos(struct m3_card *card, + int instance_addr) +{ + u16 hi = 0, lo = 0; + int retry = 10; + + /* + * try and get a valid answer + */ + while(retry--) { + hi = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTH); + + lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTL); + + if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTH)) + break; + } + return lo | (hi<<16); +} + +u32 get_dmaa(struct m3_state *s) +{ + u32 offset; + + offset = get_dma_pos(s->card, s->dac_inst.data) - + virt_to_bus(s->dma_dac.rawbuf); + + DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset); + + return offset; +} + +u32 get_dmac(struct m3_state *s) +{ + u32 offset; + + offset = get_dma_pos(s->card, s->adc_inst.data) - + virt_to_bus(s->dma_adc.rawbuf); + + DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset); + + return offset; + +} + +static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static int +prog_dmabuf(struct m3_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + + DPRINTK(DPSTR,"prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + if (rec) + m3_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); + else + m3_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); + + db->ready = 1; + + spin_unlock_irqrestore(&s->lock, flags); + + return 0; +} + +static void clear_advance(struct m3_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void m3_update_ptr(struct m3_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = get_dmac(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmaa(s) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + + DPRINTK(DPINT,"updating dac: hwptr: %6d diff: %6d count: %6d\n", + hwptr,diff,s->dma_dac.count); + + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + + if (s->dma_dac.mapped) { + + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { + wake_up(&s->dma_dac.wait); + } + } else { + + s->dma_dac.count -= diff; + + if (s->dma_dac.count <= 0) { + DPRINTK(DPCRAP,"underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x)\n", + diff, diff, + s->dma_dac.count, + s->dma_dac.count, + hwptr, hwptr, + s->dma_dac.swptr, + s->dma_dac.swptr); + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = hwptr; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); + DPRINTK(DPINT,"waking up DAC count: %d sw: %d hw: %d\n", + s->dma_dac.count, s->dma_dac.swptr, hwptr); + } + } + } +} + +static void m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct m3_card *c = (struct m3_card *)dev_id; + struct m3_state *s = &c->channels[0]; + u8 status; + + status = inb(c->iobase+0x1A); + + if(status == 0xff) return; + + /* presumably acking the ints? */ + outw(status, c->iobase+0x1A); + + if(c->in_suspend) + return; + + /* + * ack an assp int if its running + * and has an int pending + */ + if( status & ASSP_INT_PENDING) { + u8 ctl = inb(c->iobase + ASSP_CONTROL_B); + if( !(ctl & STOP_ASSP_CLOCK)) { + ctl = inb(c->iobase + ASSP_HOST_INT_STATUS ); + if(ctl & DSP2HOST_REQ_TIMER) { + outb( DSP2HOST_REQ_TIMER, c->iobase + ASSP_HOST_INT_STATUS); + /* update adc/dac info if it was a timer int */ + spin_lock(&s->lock); + m3_update_ptr(s); + spin_unlock(&s->lock); + } + } + } + + /* XXX is this needed? */ + if(status & 0x40) + outb(0x40, c->iobase+0x1A); +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC) + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct m3_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int count; + signed long tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; + /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. + or something. who cares. - zach */ + if (!schedule_timeout(tmo ? tmo : 1) && tmo) + DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static ssize_t m3_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + spin_lock_irqsave(&s->lock, flags); + + while (count > 0) { + int timed_out; + + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto out; + } + + spin_unlock_irqrestore(&s->lock, flags); + timed_out = interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ) == 0; + spin_lock_irqsave(&s->lock, flags); + + if(timed_out) { + printk("read: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto out; + } + continue; + } + + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + return ret; + } + spin_lock_irqsave(&s->lock, flags); + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +out: + spin_unlock_irqrestore(&s->lock, flags); + return ret; +} + +static ssize_t m3_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + spin_lock_irqsave(&s->lock, flags); + + while (count > 0) { + int timed_out; + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + cnt = s->dma_dac.dmasize-swptr; + + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto out; + } + spin_unlock_irqrestore(&s->lock, flags); + timed_out = interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ) == 0; + spin_lock_irqsave(&s->lock, flags); + if(timed_out) { + DPRINTK(DPCRAP,"write: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto out; + } + continue; + } + spin_unlock_irqrestore(&s->lock, flags); + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + return ret; + } + spin_lock_irqsave(&s->lock, flags); + + DPRINTK(DPSYS,"wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d\n", + cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr); + + swptr = (swptr + cnt) % s->dma_dac.dmasize; + + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +out: + spin_unlock_irqrestore(&s->lock, flags); + return ret; +} + +static unsigned int m3_poll(struct file *file, struct poll_table_struct *wait) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int m3_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long max_size, size, start, offset; + struct dmabuf *db; + int ret = -EINVAL; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_dac; + } else + if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 1)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + + max_size = db->dmasize; + + start = vma->vm_start; + offset = (vma->vm_pgoff << PAGE_SHIFT); + size = vma->vm_end - vma->vm_start; + + if(size > max_size) + goto out; + if(offset > max_size - size) + goto out; + + /* + * this will be ->nopage() once I can + * ask Jeff what the hell I'm doing wrong. + */ + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + + db->mapped = 1; + ret = 0; + +out: + return ret; +} + +/* + * this function is a disaster.. + */ +#define get_user_ret(x, ptr, ret) ({ if(get_user(x, ptr)) return ret; }) +static int m3_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + VALIDATE_STATE(s); + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + + DPRINTK(DPSYS,"m3_ioctl: cmd %d\n", cmd); + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + spin_unlock_irqrestore(&s->lock, flags); + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + spin_unlock_irqrestore(&s->lock, flags); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + spin_unlock_irqrestore(&s->lock, flags); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_U8, + (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + m3_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static int +allocate_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) +{ + int order; + + DPRINTK(DPSTR,"allocating for dmabuf %p\n", db); + + /* + * alloc as big a chunk as we can, start with + * 64k 'cause we're insane. based on order cause + * the amazingly complicated prog_dmabuf wants it. + * + * pci_alloc_sonsistent guarantees that it won't cross a natural + * boundry; the m3 hardware can't have dma cross a 64k bus + * address boundry. + */ + for (order = 16-PAGE_SHIFT; order >= 1; order--) { + db->rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order, + &(db->handle)); + if(db->rawbuf) + break; + } + + if (!db->rawbuf) + return 1; + + DPRINTK(DPSTR,"allocated %ld (%d) bytes at %p\n", + PAGE_SIZE<rawbuf); + + { + struct page *page, *pend; + + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + + db->buforder = order; + db->ready = 0; + db->mapped = 0; + + return 0; +} + +static void +nuke_lists(struct m3_card *card, struct dmabuf *db) +{ + m3_remove_list(card, &(card->dma_list), db->dma_index); + m3_remove_list(card, &(card->msrc_list), db->msrc_index); + db->in_lists = 0; +} + +static void +free_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) +{ + if(db->rawbuf == NULL) + return; + + DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db); + + { + struct page *page, *pend; + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + } + + + pci_free_consistent(pci_dev, PAGE_SIZE << db->buforder, + db->rawbuf, db->handle); + + db->rawbuf = NULL; + db->buforder = 0; + db->mapped = 0; + db->ready = 0; +} + +static int m3_open(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct m3_card *c; + struct m3_state *s = NULL; + int i; + unsigned char fmtm = ~0, fmts = 0; + unsigned long flags; + + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + for(c = devs ; c != NULL ; c = c->next) { + + for(i=0;ichannels[i].dev_audio < 0) + continue; + if((c->channels[i].dev_audio ^ minor) & ~0xf) + continue; + + s = &c->channels[i]; + break; + } + } + + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + + file->private_data = s; + + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EWOULDBLOCK; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + up(&s->open_sem); + spin_unlock_irqrestore(&s->lock, flags); + return 0; +} + +static int m3_release(struct inode *inode, struct file *file) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long flags; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + + down(&s->open_sem); + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if(s->dma_dac.in_lists) { + m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index); + nuke_lists(s->card, &(s->dma_dac)); + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if(s->dma_adc.in_lists) { + m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index); + nuke_lists(s->card, &(s->dma_adc)); + } + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + + return 0; +} + +/* + * Wait for the ac97 serial bus to be free. + * return nonzero if the bus is still busy. + */ +static int m3_ac97_wait(struct m3_card *card) +{ + int i = 10000; + + while( (m3_inb(card, 0x30) & 1) && i--) ; + + return i == 0; +} + +u16 m3_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 ret = 0; + struct m3_card *card = codec->private_data; + + spin_lock(&card->ac97_lock); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy reading reg 0x%x\n",reg); + goto out; + } + + m3_outb(card, 0x80 | (reg & 0x7f), 0x30); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy finishing read reg 0x%x\n",reg); + goto out; + } + + ret = m3_inw(card, 0x32); + DPRINTK(DPCRAP,"reading 0x%04x from 0x%02x\n",ret, reg); + +out: + spin_unlock(&card->ac97_lock); + return ret; +} + +void m3_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct m3_card *card = codec->private_data; + + spin_lock(&card->ac97_lock); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy writing 0x%x to 0x%x\n",val, reg); + goto out; + } + DPRINTK(DPCRAP,"writing 0x%04x to 0x%02x\n", val, reg); + + m3_outw(card, val, 0x32); + m3_outb(card, reg & 0x7f, 0x30); +out: + spin_unlock(&card->ac97_lock); +} +/* OSS /dev/mixer file operation methods */ +static int m3_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = minor(inode->i_rdev); + struct m3_card *card = devs; + + for (card = devs; card != NULL; card = card->next) { + if((card->ac97 != NULL) && (card->ac97->dev_mixer == minor)) + break; + } + + if (!card) { + return -ENODEV; + } + + file->private_data = card->ac97; + + return 0; +} + +static int m3_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static int m3_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static struct file_operations m3_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: m3_ioctl_mixdev, + open: m3_open_mixdev, + release: m3_release_mixdev, +}; + +void remote_codec_config(int io, int isremote) +{ + isremote = isremote ? 1 : 0; + + outw( (inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, + io + RING_BUS_CTRL_B); + outw( (inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, + io + SDO_OUT_DEST_CTRL); + outw( (inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, + io + SDO_IN_DEST_CTRL); +} + +/* + * hack, returns non zero on err + */ +static int try_read_vendor(struct m3_card *card) +{ + u16 ret; + + if(m3_ac97_wait(card)) + return 1; + + m3_outb(card, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); + + if(m3_ac97_wait(card)) + return 1; + + ret = m3_inw(card, 0x32); + + return (ret == 0) || (ret == 0xffff); +} + +static void m3_codec_reset(struct m3_card *card, int busywait) +{ + u16 dir; + int delay1 = 0, delay2 = 0, i; + int io = card->iobase; + + switch (card->card_type) { + /* + * the onboard codec on the allegro seems + * to want to wait a very long time before + * coming back to life + */ + case ESS_ALLEGRO: + delay1 = 50; + delay2 = 800; + break; + case ESS_MAESTRO3: + case ESS_MAESTRO3HW: + delay1 = 20; + delay2 = 500; + break; + } + + for(i = 0; i < 5; i ++) { + dir = inw(io + GPIO_DIRECTION); + dir |= 0x10; /* assuming pci bus master? */ + + remote_codec_config(io, 0); + + outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); + udelay(20); + + outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); + outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); + outw(0, io + GPIO_DATA); + outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); + + if(busywait) { + mdelay(delay1); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay1 * HZ) / 1000); + } + + outw(GPO_PRIMARY_AC97, io + GPIO_DATA); + udelay(5); + /* ok, bring back the ac-link */ + outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); + outw(~0, io + GPIO_MASK); + + if(busywait) { + mdelay(delay2); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay2 * HZ) / 1000); + } + if(! try_read_vendor(card)) + break; + + delay1 += 10; + delay2 += 100; + + DPRINTK(DPMOD, "retrying codec reset with delays of %d and %d ms\n", + delay1, delay2); + } + +#if 0 + /* more gung-ho reset that doesn't + * seem to work anywhere :) + */ + tmp = inw(io + RING_BUS_CTRL_A); + outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); + mdelay(20); + outw(tmp, io + RING_BUS_CTRL_A); + mdelay(50); +#endif +} + +static int __init m3_codec_install(struct m3_card *card) +{ + struct ac97_codec *codec; + + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + codec->private_data = card; + codec->codec_read = m3_ac97_read; + codec->codec_write = m3_ac97_write; + /* someday we should support secondary codecs.. */ + codec->id = 0; + + if (ac97_probe_codec(codec) == 0) { + printk(KERN_ERR PFX "codec probe failed\n"); + kfree(codec); + return -1; + } + + if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) { + printk(KERN_ERR PFX "couldn't register mixer!\n"); + kfree(codec); + return -1; + } + + card->ac97 = codec; + + return 0; +} + + +#define MINISRC_LPF_LEN 10 +static u16 minisrc_lpf[MINISRC_LPF_LEN] = { + 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, + 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F +}; +static void m3_assp_init(struct m3_card *card) +{ + int i; + + /* zero kernel data */ + for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR + i, 0); + + /* zero mixer data? */ + for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR2 + i, 0); + + /* init dma pointer */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_CURRENT_DMA, + KDATA_DMA_XFER0); + + /* write kernel into code memory.. */ + for(i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + REV_B_CODE_MEMORY_BEGIN + i, + assp_kernel_image[i]); + } + + /* + * We only have this one client and we know that 0x400 + * is free in our kernel's mem map, so lets just + * drop it there. It seems that the minisrc doesn't + * need vectors, so we won't bother with them.. + */ + for(i = 0 ; i < sizeof(assp_minisrc_image) / 2; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + i, + assp_minisrc_image[i]); + } + + /* + * write the coefficients for the low pass filter? + */ + for(i = 0; i < MINISRC_LPF_LEN ; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + i, + minisrc_lpf[i]); + } + + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, + 0x8000); + + /* + * the minisrc is the only thing on + * our task list.. + */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TASK0, + 0x400); + + /* + * init the mixer number.. + */ + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER,0); + + /* + * EXTREME KERNEL MASTER VOLUME + */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); + + card->mixer_list.mem_addr = KDATA_MIXER_XFER0; + card->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; + card->adc1_list.mem_addr = KDATA_ADC1_XFER0; + card->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; + card->dma_list.mem_addr = KDATA_DMA_XFER0; + card->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; + card->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; + card->msrc_list.max = MAX_INSTANCE_MINISRC; +} + +static int setup_msrc(struct m3_card *card, + struct assp_instance *inst, int index) +{ + int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + + MINISRC_IN_BUFFER_SIZE / 2 + + 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); + int address, i; + + /* + * the revb memory map has 0x1100 through 0x1c00 + * free. + */ + + /* + * align instance address to 256 bytes so that it's + * shifted list address is aligned. + * list address = (mem address >> 1) >> 7; + */ + data_bytes = (data_bytes + 255) & ~255; + address = 0x1100 + ((data_bytes/2) * index); + + if((address + (data_bytes/2)) >= 0x1c00) { + printk(KERN_ERR PFX "no memory for %d bytes at ind %d (addr 0x%x)\n", + data_bytes, index, address); + return -1; + } + + for(i = 0; i < data_bytes/2 ; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + address + i, 0); + + inst->code = 0x400; + inst->data = address; + + return 0; +} + +static int m3_assp_client_init(struct m3_state *s) +{ + setup_msrc(s->card, &(s->dac_inst), s->index * 2); + setup_msrc(s->card, &(s->adc_inst), (s->index * 2) + 1); + + return 0; +} + +static void m3_amp_enable(struct m3_card *card, int enable) +{ + /* + * this works for the reference board, have to find + * out about others + * + * this needs more magic for 4 speaker, but.. + */ + int io = card->iobase; + u16 gpo, polarity_port, polarity; + + if(!external_amp) + return; + + switch (card->card_type) { + case ESS_ALLEGRO: + polarity_port = 0x1800; + break; + default: + /* presumably this is for all 'maestro3's.. */ + polarity_port = 0x1100; + break; + } + + gpo = (polarity_port >> 8) & 0x0F; + polarity = polarity_port >> 12; + if ( enable ) + polarity = !polarity; + polarity = polarity << gpo; + gpo = 1 << gpo; + + outw(~gpo , io + GPIO_MASK); + + outw( inw(io + GPIO_DIRECTION) | gpo , + io + GPIO_DIRECTION); + + outw( (GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity) , + io + GPIO_DATA); + + outw(0xffff , io + GPIO_MASK); +} + +static int +maestro_config(struct m3_card *card) +{ + struct pci_dev *pcidev = card->pcidev; + u32 n; + u8 t; /* makes as much sense as 'n', no? */ + + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= REDUCED_DEBOUNCE; + n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + outb(RESET_ASSP, card->iobase + ASSP_CONTROL_B); + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= ~INT_CLK_SELECT; + if(card->card_type >= ESS_MAESTRO3) { + n &= ~INT_CLK_MULT_ENABLE; + n |= INT_CLK_SRC_NOT_PCI; + } + n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + if(card->card_type <= ESS_ALLEGRO) { + pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); + n |= IN_CLK_12MHZ_SELECT; + pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); + } + + t = inb(card->iobase + ASSP_CONTROL_A); + t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); + t |= ASSP_CLK_49MHZ_SELECT; + t |= ASSP_0_WS_ENABLE; + outb(t, card->iobase + ASSP_CONTROL_A); + + outb(RUN_ASSP, card->iobase + ASSP_CONTROL_B); + + return 0; +} + +static void m3_enable_ints(struct m3_card *card) +{ + unsigned long io = card->iobase; + + outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); + outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, + io + ASSP_CONTROL_C); +} + +static struct file_operations m3_audio_fops = { + owner: THIS_MODULE, + llseek: &no_llseek, + read: &m3_read, + write: &m3_write, + poll: &m3_poll, + ioctl: &m3_ioctl, + mmap: &m3_mmap, + open: &m3_open, + release: &m3_release, +}; + +#ifdef CONFIG_PM +int alloc_dsp_suspendmem(struct m3_card *card) +{ + int len = sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); + + if( (card->suspend_mem = vmalloc(len)) == NULL) + return 1; + + return 0; +} +void free_dsp_suspendmem(struct m3_card *card) +{ + if(card->suspend_mem) + vfree(card->suspend_mem); +} + +#else +#define alloc_dsp_suspendmem(args...) 0 +#define free_dsp_suspendmem(args...) +#endif + +/* + * great day! this function is ugly as hell. + */ +static int __init m3_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + u32 n; + int i; + struct m3_card *card = NULL; + int ret = 0; + int card_type = pci_id->driver_data; + + DPRINTK(DPMOD, "in maestro_install\n"); + + if (pci_enable_device(pci_dev)) + return -EIO; + + if (pci_set_dma_mask(pci_dev, M3_PCI_DMA_MASK)) { + printk(KERN_ERR PFX "architecture does not support limiting to 28bit PCI bus addresses\n"); + return -ENODEV; + } + + pci_set_master(pci_dev); + + if( (card = kmalloc(sizeof(struct m3_card), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING PFX "out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(struct m3_card)); + card->pcidev = pci_dev; + init_waitqueue_head(&card->suspend_queue); + + if ( ! request_region(pci_resource_start(pci_dev, 0), + pci_resource_len (pci_dev, 0), M3_MODULE_NAME)) { + + printk(KERN_WARNING PFX "unable to reserve I/O space.\n"); + ret = -EBUSY; + goto out; + } + + card->iobase = pci_resource_start(pci_dev, 0); + + if(alloc_dsp_suspendmem(card)) { + printk(KERN_WARNING PFX "couldn't alloc %d bytes for saving dsp state on suspend\n", + REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); + ret = -ENOMEM; + goto out; + } + + card->card_type = card_type; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = M3_CARD_MAGIC; + spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); + devs = card; + for(i = 0; ichannels[i]); + s->dev_audio = -1; + } + + printk(KERN_INFO PFX "Configuring ESS %s found at IO 0x%04X IRQ %d\n", + card_names[card->card_type], card->iobase, card->irq); + + pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO PFX " subvendor id: 0x%08x\n",n); + + maestro_config(card); + m3_assp_halt(card); + + m3_codec_reset(card, 0); + + if(m3_codec_install(card)) { + ret = -EIO; + goto out; + } + + m3_assp_init(card); + m3_amp_enable(card, 1); + + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + spin_lock_init(&s->lock); + init_MUTEX(&(s->open_sem)); + s->magic = M3_STATE_MAGIC; + + m3_assp_client_init(s); + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) { + break; + } + + if( allocate_dmabuf(card->pcidev, &(s->dma_adc)) || + allocate_dmabuf(card->pcidev, &(s->dma_dac))) { + ret = -ENOMEM; + goto out; + } + } + + if(request_irq(card->irq, m3_interrupt, SA_SHIRQ, card_names[card->card_type], card)) { + + printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq); + + ret = -EIO; + goto out; + } + + pci_set_drvdata(pci_dev, card); + + m3_enable_ints(card); + m3_assp_continue(card); + +out: + if(ret) { + if(card->iobase) + release_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); + free_dsp_suspendmem(card); + if(card->ac97) { + unregister_sound_mixer(card->ac97->dev_mixer); + kfree(card->ac97); + } + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + kfree(card); + } + + return ret; +} + +static void m3_remove(struct pci_dev *pci_dev) +{ + struct m3_card *card; + + unregister_reboot_notifier(&m3_reboot_nb); + + while ((card = devs)) { + int i; + devs = devs->next; + + free_irq(card->irq, card); + unregister_sound_mixer(card->ac97->dev_mixer); + kfree(card->ac97); + + for(i=0;ichannels[i]; + if(s->dev_audio < 0) + continue; + + unregister_sound_dsp(s->dev_audio); + free_dmabuf(card->pcidev, &s->dma_adc); + free_dmabuf(card->pcidev, &s->dma_dac); + } + + release_region(card->iobase, 256); + free_dsp_suspendmem(card); + kfree(card); + } + devs = NULL; +} + +/* + * some bioses like the sound chip to be powered down + * at shutdown. We're just calling _suspend to + * achieve that.. + */ +static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + struct m3_card *card; + + DPRINTK(DPMOD, "notifier suspending all cards\n"); + + for(card = devs; card != NULL; card = card->next) { + if(!card->in_suspend) + m3_suspend(card->pcidev, 3); /* XXX legal? */ + } + return 0; +} + +static int m3_suspend(struct pci_dev *pci_dev, u32 state) +{ + unsigned long flags; + int i; + struct m3_card *card = pci_get_drvdata(pci_dev); + + /* must be a better way.. */ + save_flags(flags); + cli(); + + DPRINTK(DPMOD, "pm in dev %p\n",card); + + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; + + DPRINTK(DPMOD, "stop_adc/dac() device %d\n",i); + stop_dac(s); + stop_adc(s); + } + + mdelay(10); /* give the assp a chance to idle.. */ + + m3_assp_halt(card); + + if(card->suspend_mem) { + int index = 0; + + DPRINTK(DPMOD, "saving code\n"); + for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) + card->suspend_mem[index++] = + m3_assp_read(card, MEMTYPE_INTERNAL_CODE, i); + DPRINTK(DPMOD, "saving data\n"); + for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + card->suspend_mem[index++] = + m3_assp_read(card, MEMTYPE_INTERNAL_DATA, i); + } + + DPRINTK(DPMOD, "powering down apci regs\n"); + m3_outw(card, 0xffff, 0x54); + m3_outw(card, 0xffff, 0x56); + + card->in_suspend = 1; + + restore_flags(flags); + + return 0; +} + +static int m3_resume(struct pci_dev *pci_dev) +{ + unsigned long flags; + int index; + int i; + struct m3_card *card = pci_get_drvdata(pci_dev); + + save_flags(flags); /* paranoia */ + cli(); + card->in_suspend = 0; + + DPRINTK(DPMOD, "resuming\n"); + + /* first lets just bring everything back. .*/ + + DPRINTK(DPMOD, "bringing power back on card 0x%p\n",card); + m3_outw(card, 0, 0x54); + m3_outw(card, 0, 0x56); + + DPRINTK(DPMOD, "restoring pci configs and reseting codec\n"); + maestro_config(card); + m3_assp_halt(card); + m3_codec_reset(card, 1); + + DPRINTK(DPMOD, "restoring dsp code card\n"); + index = 0; + for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, i, + card->suspend_mem[index++]); + for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, i, + card->suspend_mem[index++]); + + /* tell the dma engine to restart itself */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DMA_ACTIVE, 0); + + DPRINTK(DPMOD, "resuming dsp\n"); + m3_assp_continue(card); + + DPRINTK(DPMOD, "enabling ints\n"); + m3_enable_ints(card); + + /* bring back the old school flavor */ + for(i = 0; i < SOUND_MIXER_NRDEVICES ; i++) { + int state = card->ac97->mixer_state[i]; + if (!supported_mixer(card->ac97, i)) + continue; + + card->ac97->write_mixer(card->ac97, i, + state & 0xff, (state >> 8) & 0xff); + } + + m3_amp_enable(card, 1); + + /* + * now we flip on the music + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + continue; + /* + * db->ready makes it so these guys can be + * called unconditionally.. + */ + DPRINTK(DPMOD, "turning on dacs ind %d\n",i); + start_dac(s); + start_adc(s); + } + + restore_flags(flags); + + /* + * all right, we think things are ready, + * wake up people who were using the device + * when we suspended + */ + wake_up(&card->suspend_queue); + + return 0; +} + +MODULE_AUTHOR("Zach Brown "); +MODULE_DESCRIPTION("ESS Maestro3/Allegro Driver"); +MODULE_LICENSE("GPL"); + +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(external_amp,"i"); + +static struct pci_driver m3_pci_driver = { + name: "ess_m3_audio", + id_table: m3_id_table, + probe: m3_probe, + remove: m3_remove, + suspend: m3_suspend, + resume: m3_resume, +}; + +static int __init m3_init_module(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + + printk(KERN_INFO PFX "version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ "\n"); + + if (register_reboot_notifier(&m3_reboot_nb)) { + printk(KERN_WARNING PFX "reboot notifier registration failed\n"); + return -ENODEV; /* ? */ + } + + if (!pci_register_driver(&m3_pci_driver)) { + pci_unregister_driver(&m3_pci_driver); + unregister_reboot_notifier(&m3_reboot_nb); + return -ENODEV; + } + return 0; +} + +static void __exit m3_cleanup_module(void) +{ + pci_unregister_driver(&m3_pci_driver); +} + +module_init(m3_init_module); +module_exit(m3_cleanup_module); + +void check_suspend(struct m3_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!card->in_suspend) + return; + + card->in_suspend++; + add_wait_queue(&card->suspend_queue, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + remove_wait_queue(&card->suspend_queue, &wait); + set_current_state(TASK_RUNNING); +} diff -Nru linux/sound/oss/maestro3.h linux-2.4.19-pre5-mjc/sound/oss/maestro3.h --- linux/sound/oss/maestro3.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/maestro3.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,821 @@ +/* + * ESS Technology allegro audio driver. + * + * Copyright (C) 1992-2000 Don Kim (don.kim@esstech.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Hacked for the maestro3 driver by zab + */ + +// Allegro PCI configuration registers +#define PCI_LEGACY_AUDIO_CTRL 0x40 +#define SOUND_BLASTER_ENABLE 0x00000001 +#define FM_SYNTHESIS_ENABLE 0x00000002 +#define GAME_PORT_ENABLE 0x00000004 +#define MPU401_IO_ENABLE 0x00000008 +#define MPU401_IRQ_ENABLE 0x00000010 +#define ALIAS_10BIT_IO 0x00000020 +#define SB_DMA_MASK 0x000000C0 +#define SB_DMA_0 0x00000040 +#define SB_DMA_1 0x00000040 +#define SB_DMA_R 0x00000080 +#define SB_DMA_3 0x000000C0 +#define SB_IRQ_MASK 0x00000700 +#define SB_IRQ_5 0x00000000 +#define SB_IRQ_7 0x00000100 +#define SB_IRQ_9 0x00000200 +#define SB_IRQ_10 0x00000300 +#define MIDI_IRQ_MASK 0x00003800 +#define SERIAL_IRQ_ENABLE 0x00004000 +#define DISABLE_LEGACY 0x00008000 + +#define PCI_ALLEGRO_CONFIG 0x50 +#define SB_ADDR_240 0x00000004 +#define MPU_ADDR_MASK 0x00000018 +#define MPU_ADDR_330 0x00000000 +#define MPU_ADDR_300 0x00000008 +#define MPU_ADDR_320 0x00000010 +#define MPU_ADDR_340 0x00000018 +#define USE_PCI_TIMING 0x00000040 +#define POSTED_WRITE_ENABLE 0x00000080 +#define DMA_POLICY_MASK 0x00000700 +#define DMA_DDMA 0x00000000 +#define DMA_TDMA 0x00000100 +#define DMA_PCPCI 0x00000200 +#define DMA_WBDMA16 0x00000400 +#define DMA_WBDMA4 0x00000500 +#define DMA_WBDMA2 0x00000600 +#define DMA_WBDMA1 0x00000700 +#define DMA_SAFE_GUARD 0x00000800 +#define HI_PERF_GP_ENABLE 0x00001000 +#define PIC_SNOOP_MODE_0 0x00002000 +#define PIC_SNOOP_MODE_1 0x00004000 +#define SOUNDBLASTER_IRQ_MASK 0x00008000 +#define RING_IN_ENABLE 0x00010000 +#define SPDIF_TEST_MODE 0x00020000 +#define CLK_MULT_MODE_SELECT_2 0x00040000 +#define EEPROM_WRITE_ENABLE 0x00080000 +#define CODEC_DIR_IN 0x00100000 +#define HV_BUTTON_FROM_GD 0x00200000 +#define REDUCED_DEBOUNCE 0x00400000 +#define HV_CTRL_ENABLE 0x00800000 +#define SPDIF_ENABLE 0x01000000 +#define CLK_DIV_SELECT 0x06000000 +#define CLK_DIV_BY_48 0x00000000 +#define CLK_DIV_BY_49 0x02000000 +#define CLK_DIV_BY_50 0x04000000 +#define CLK_DIV_RESERVED 0x06000000 +#define PM_CTRL_ENABLE 0x08000000 +#define CLK_MULT_MODE_SELECT 0x30000000 +#define CLK_MULT_MODE_SHIFT 28 +#define CLK_MULT_MODE_0 0x00000000 +#define CLK_MULT_MODE_1 0x10000000 +#define CLK_MULT_MODE_2 0x20000000 +#define CLK_MULT_MODE_3 0x30000000 +#define INT_CLK_SELECT 0x40000000 +#define INT_CLK_MULT_RESET 0x80000000 + +// M3 +#define INT_CLK_SRC_NOT_PCI 0x00100000 +#define INT_CLK_MULT_ENABLE 0x80000000 + +#define PCI_ACPI_CONTROL 0x54 +#define PCI_ACPI_D0 0x00000000 +#define PCI_ACPI_D1 0xB4F70000 +#define PCI_ACPI_D2 0xB4F7B4F7 + +#define PCI_USER_CONFIG 0x58 +#define EXT_PCI_MASTER_ENABLE 0x00000001 +#define SPDIF_OUT_SELECT 0x00000002 +#define TEST_PIN_DIR_CTRL 0x00000004 +#define AC97_CODEC_TEST 0x00000020 +#define TRI_STATE_BUFFER 0x00000080 +#define IN_CLK_12MHZ_SELECT 0x00000100 +#define MULTI_FUNC_DISABLE 0x00000200 +#define EXT_MASTER_PAIR_SEL 0x00000400 +#define PCI_MASTER_SUPPORT 0x00000800 +#define STOP_CLOCK_ENABLE 0x00001000 +#define EAPD_DRIVE_ENABLE 0x00002000 +#define REQ_TRI_STATE_ENABLE 0x00004000 +#define REQ_LOW_ENABLE 0x00008000 +#define MIDI_1_ENABLE 0x00010000 +#define MIDI_2_ENABLE 0x00020000 +#define SB_AUDIO_SYNC 0x00040000 +#define HV_CTRL_TEST 0x00100000 +#define SOUNDBLASTER_TEST 0x00400000 + +#define PCI_USER_CONFIG_C 0x5C + +#define PCI_DDMA_CTRL 0x60 +#define DDMA_ENABLE 0x00000001 + + +// Allegro registers +#define HOST_INT_CTRL 0x18 +#define SB_INT_ENABLE 0x0001 +#define MPU401_INT_ENABLE 0x0002 +#define ASSP_INT_ENABLE 0x0010 +#define RING_INT_ENABLE 0x0020 +#define HV_INT_ENABLE 0x0040 +#define CLKRUN_GEN_ENABLE 0x0100 +#define HV_CTRL_TO_PME 0x0400 +#define SOFTWARE_RESET_ENABLE 0x8000 + +/* + * should be using the above defines, probably. + */ +#define REGB_ENABLE_RESET 0x01 +#define REGB_STOP_CLOCK 0x10 + +#define HOST_INT_STATUS 0x1A +#define SB_INT_PENDING 0x01 +#define MPU401_INT_PENDING 0x02 +#define ASSP_INT_PENDING 0x10 +#define RING_INT_PENDING 0x20 +#define HV_INT_PENDING 0x40 + +#define HARDWARE_VOL_CTRL 0x1B +#define SHADOW_MIX_REG_VOICE 0x1C +#define HW_VOL_COUNTER_VOICE 0x1D +#define SHADOW_MIX_REG_MASTER 0x1E +#define HW_VOL_COUNTER_MASTER 0x1F + +#define CODEC_COMMAND 0x30 +#define CODEC_READ_B 0x80 + +#define CODEC_STATUS 0x30 +#define CODEC_BUSY_B 0x01 + +#define CODEC_DATA 0x32 + +#define RING_BUS_CTRL_A 0x36 +#define RAC_PME_ENABLE 0x0100 +#define RAC_SDFS_ENABLE 0x0200 +#define LAC_PME_ENABLE 0x0400 +#define LAC_SDFS_ENABLE 0x0800 +#define SERIAL_AC_LINK_ENABLE 0x1000 +#define IO_SRAM_ENABLE 0x2000 +#define IIS_INPUT_ENABLE 0x8000 + +#define RING_BUS_CTRL_B 0x38 +#define SECOND_CODEC_ID_MASK 0x0003 +#define SPDIF_FUNC_ENABLE 0x0010 +#define SECOND_AC_ENABLE 0x0020 +#define SB_MODULE_INTF_ENABLE 0x0040 +#define SSPE_ENABLE 0x0040 +#define M3I_DOCK_ENABLE 0x0080 + +#define SDO_OUT_DEST_CTRL 0x3A +#define COMMAND_ADDR_OUT 0x0003 +#define PCM_LR_OUT_LOCAL 0x0000 +#define PCM_LR_OUT_REMOTE 0x0004 +#define PCM_LR_OUT_MUTE 0x0008 +#define PCM_LR_OUT_BOTH 0x000C +#define LINE1_DAC_OUT_LOCAL 0x0000 +#define LINE1_DAC_OUT_REMOTE 0x0010 +#define LINE1_DAC_OUT_MUTE 0x0020 +#define LINE1_DAC_OUT_BOTH 0x0030 +#define PCM_CLS_OUT_LOCAL 0x0000 +#define PCM_CLS_OUT_REMOTE 0x0040 +#define PCM_CLS_OUT_MUTE 0x0080 +#define PCM_CLS_OUT_BOTH 0x00C0 +#define PCM_RLF_OUT_LOCAL 0x0000 +#define PCM_RLF_OUT_REMOTE 0x0100 +#define PCM_RLF_OUT_MUTE 0x0200 +#define PCM_RLF_OUT_BOTH 0x0300 +#define LINE2_DAC_OUT_LOCAL 0x0000 +#define LINE2_DAC_OUT_REMOTE 0x0400 +#define LINE2_DAC_OUT_MUTE 0x0800 +#define LINE2_DAC_OUT_BOTH 0x0C00 +#define HANDSET_OUT_LOCAL 0x0000 +#define HANDSET_OUT_REMOTE 0x1000 +#define HANDSET_OUT_MUTE 0x2000 +#define HANDSET_OUT_BOTH 0x3000 +#define IO_CTRL_OUT_LOCAL 0x0000 +#define IO_CTRL_OUT_REMOTE 0x4000 +#define IO_CTRL_OUT_MUTE 0x8000 +#define IO_CTRL_OUT_BOTH 0xC000 + +#define SDO_IN_DEST_CTRL 0x3C +#define STATUS_ADDR_IN 0x0003 +#define PCM_LR_IN_LOCAL 0x0000 +#define PCM_LR_IN_REMOTE 0x0004 +#define PCM_LR_RESERVED 0x0008 +#define PCM_LR_IN_BOTH 0x000C +#define LINE1_ADC_IN_LOCAL 0x0000 +#define LINE1_ADC_IN_REMOTE 0x0010 +#define LINE1_ADC_IN_MUTE 0x0020 +#define MIC_ADC_IN_LOCAL 0x0000 +#define MIC_ADC_IN_REMOTE 0x0040 +#define MIC_ADC_IN_MUTE 0x0080 +#define LINE2_DAC_IN_LOCAL 0x0000 +#define LINE2_DAC_IN_REMOTE 0x0400 +#define LINE2_DAC_IN_MUTE 0x0800 +#define HANDSET_IN_LOCAL 0x0000 +#define HANDSET_IN_REMOTE 0x1000 +#define HANDSET_IN_MUTE 0x2000 +#define IO_STATUS_IN_LOCAL 0x0000 +#define IO_STATUS_IN_REMOTE 0x4000 + +#define SPDIF_IN_CTRL 0x3E +#define SPDIF_IN_ENABLE 0x0001 + +#define GPIO_DATA 0x60 +#define GPIO_DATA_MASK 0x0FFF +#define GPIO_HV_STATUS 0x3000 +#define GPIO_PME_STATUS 0x4000 + +#define GPIO_MASK 0x64 +#define GPIO_DIRECTION 0x68 +#define GPO_PRIMARY_AC97 0x0001 +#define GPI_LINEOUT_SENSE 0x0004 +#define GPO_SECONDARY_AC97 0x0008 +#define GPI_VOL_DOWN 0x0010 +#define GPI_VOL_UP 0x0020 +#define GPI_IIS_CLK 0x0040 +#define GPI_IIS_LRCLK 0x0080 +#define GPI_IIS_DATA 0x0100 +#define GPI_DOCKING_STATUS 0x0100 +#define GPI_HEADPHONE_SENSE 0x0200 +#define GPO_EXT_AMP_SHUTDOWN 0x1000 + +// M3 +#define GPO_M3_EXT_AMP_SHUTDN 0x0002 + +#define ASSP_INDEX_PORT 0x80 +#define ASSP_MEMORY_PORT 0x82 +#define ASSP_DATA_PORT 0x84 + +#define MPU401_DATA_PORT 0x98 +#define MPU401_STATUS_PORT 0x99 + +#define CLK_MULT_DATA_PORT 0x9C + +#define ASSP_CONTROL_A 0xA2 +#define ASSP_0_WS_ENABLE 0x01 +#define ASSP_CTRL_A_RESERVED1 0x02 +#define ASSP_CTRL_A_RESERVED2 0x04 +#define ASSP_CLK_49MHZ_SELECT 0x08 +#define FAST_PLU_ENABLE 0x10 +#define ASSP_CTRL_A_RESERVED3 0x20 +#define DSP_CLK_36MHZ_SELECT 0x40 + +#define ASSP_CONTROL_B 0xA4 +#define RESET_ASSP 0x00 +#define RUN_ASSP 0x01 +#define ENABLE_ASSP_CLOCK 0x00 +#define STOP_ASSP_CLOCK 0x10 +#define RESET_TOGGLE 0x40 + +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOST_INT_ENABLE 0x01 +#define FM_ADDR_REMAP_DISABLE 0x02 +#define HOST_WRITE_PORT_ENABLE 0x08 + +#define ASSP_HOST_INT_STATUS 0xAC +#define DSP2HOST_REQ_PIORECORD 0x01 +#define DSP2HOST_REQ_I2SRATE 0x02 +#define DSP2HOST_REQ_TIMER 0x04 + +// AC97 registers +// XXX fix this crap up +/*#define AC97_RESET 0x00*/ + +#define AC97_VOL_MUTE_B 0x8000 +#define AC97_VOL_M 0x1F +#define AC97_LEFT_VOL_S 8 + +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_MASTER_MONO_VOL 0x06 +#define AC97_PC_BEEP_VOL 0x0A +#define AC97_PC_BEEP_VOL_M 0x0F +#define AC97_SROUND_MASTER_VOL 0x38 +#define AC97_PC_BEEP_VOL_S 1 + +/*#define AC97_PHONE_VOL 0x0C +#define AC97_MIC_VOL 0x0E*/ +#define AC97_MIC_20DB_ENABLE 0x40 + +/*#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16*/ +#define AC97_PCM_OUT_VOL 0x18 +/*#define AC97_RECORD_SELECT 0x1A*/ +#define AC97_RECORD_MIC 0x00 +#define AC97_RECORD_CD 0x01 +#define AC97_RECORD_VIDEO 0x02 +#define AC97_RECORD_AUX 0x03 +#define AC97_RECORD_MONO_MUX 0x02 +#define AC97_RECORD_DIGITAL 0x03 +#define AC97_RECORD_LINE 0x04 +#define AC97_RECORD_STEREO 0x05 +#define AC97_RECORD_MONO 0x06 +#define AC97_RECORD_PHONE 0x07 + +/*#define AC97_RECORD_GAIN 0x1C*/ +#define AC97_RECORD_VOL_M 0x0F + +/*#define AC97_GENERAL_PURPOSE 0x20*/ +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_ADC_READY 0x0001 +#define AC97_DAC_READY 0x0002 +#define AC97_ANALOG_READY 0x0004 +#define AC97_VREF_ON 0x0008 +#define AC97_PR0 0x0100 +#define AC97_PR1 0x0200 +#define AC97_PR2 0x0400 +#define AC97_PR3 0x0800 +#define AC97_PR4 0x1000 + +#define AC97_RESERVED1 0x28 + +#define AC97_VENDOR_TEST 0x5A + +#define AC97_CLOCK_DELAY 0x5C +#define AC97_LINEOUT_MUX_SEL 0x0001 +#define AC97_MONO_MUX_SEL 0x0002 +#define AC97_CLOCK_DELAY_SEL 0x1F +#define AC97_DAC_CDS_SHIFT 6 +#define AC97_ADC_CDS_SHIFT 11 + +#define AC97_MULTI_CHANNEL_SEL 0x74 + +/*#define AC97_VENDOR_ID1 0x7C +#define AC97_VENDOR_ID2 0x7E*/ + +/* + * ASSP control regs + */ +#define DSP_PORT_TIMER_COUNT 0x06 + +#define DSP_PORT_MEMORY_INDEX 0x80 + +#define DSP_PORT_MEMORY_TYPE 0x82 +#define MEMTYPE_INTERNAL_CODE 0x0002 +#define MEMTYPE_INTERNAL_DATA 0x0003 +#define MEMTYPE_MASK 0x0003 + +#define DSP_PORT_MEMORY_DATA 0x84 + +#define DSP_PORT_CONTROL_REG_A 0xA2 +#define DSP_PORT_CONTROL_REG_B 0xA4 +#define DSP_PORT_CONTROL_REG_C 0xA6 + +#define REV_A_CODE_MEMORY_BEGIN 0x0000 +#define REV_A_CODE_MEMORY_END 0x0FFF +#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) + +#define REV_B_CODE_MEMORY_BEGIN 0x0000 +#define REV_B_CODE_MEMORY_END 0x0BFF +#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) + +#define REV_A_DATA_MEMORY_BEGIN 0x1000 +#define REV_A_DATA_MEMORY_END 0x2FFF +#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) + +#define REV_B_DATA_MEMORY_BEGIN 0x1000 +#define REV_B_DATA_MEMORY_END 0x2BFF +#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) + + +#define NUM_UNITS_KERNEL_CODE 16 +#define NUM_UNITS_KERNEL_DATA 2 + +#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 +#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 + +/* + * Kernel data layout + */ + +#define DP_SHIFT_COUNT 7 + +#define KDATA_BASE_ADDR 0x1000 +#define KDATA_BASE_ADDR2 0x1080 + +#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) +#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) +#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) +#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) +#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) +#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) +#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) +#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) +#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) + +#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) +#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) + +#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) +#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) +#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) +#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) +#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) +#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) +#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) +#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) +#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) +#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) + +#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) +#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) + +#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) +#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) + +#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) +#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) + +#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) +#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) +#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) + +#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) +#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) +#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) +#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) +#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) + +#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) +#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) +#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) + +#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) +#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) +#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) + +#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) +#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) +#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) +#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) +#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) +#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) +#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) +#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) +#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) +#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) + +#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) +#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) +#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) + +#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) +#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) + +#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) +#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) +#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) + +#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) +#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) +#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) +#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) +#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) +#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) + +#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) +#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) +#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) +#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) +#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) +#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) + +#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) +#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) +#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) +#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) +#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) +#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) + +#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) +#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) +#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) +#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) + +#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) +#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) + +#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) +#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) + +#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) +#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) +#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) +#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) +#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) + +#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) +#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) + +#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) +#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) +#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) + +#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) +#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) + +#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) + +#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) +#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) +#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) +#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) +#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) +#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) +#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) +#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) +#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) +#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) +#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) +#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) + +#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) +#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) +#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) +#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) + +#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) +#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) + +#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) +#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) +#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) +#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) + +#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) +#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) +#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) +#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) +#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) + +/* + * second 'segment' (?) reserved for mixer + * buffers.. + */ + +#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) +#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) +#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) +#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) +#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) +#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) +#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) +#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) +#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) +#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) +#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) +#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) +#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) +#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) +#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) +#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) + +#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) +#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) +#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) +#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) +#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) +#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) +#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) +#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) +#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) +#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) +#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) + +#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) +#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) +#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) +#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) +#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) +#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) + +#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) +#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) +#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) +#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) + +/* + * client data area offsets + */ +#define CDATA_INSTANCE_READY 0x00 + +#define CDATA_HOST_SRC_ADDRL 0x01 +#define CDATA_HOST_SRC_ADDRH 0x02 +#define CDATA_HOST_SRC_END_PLUS_1L 0x03 +#define CDATA_HOST_SRC_END_PLUS_1H 0x04 +#define CDATA_HOST_SRC_CURRENTL 0x05 +#define CDATA_HOST_SRC_CURRENTH 0x06 + +#define CDATA_IN_BUF_CONNECT 0x07 +#define CDATA_OUT_BUF_CONNECT 0x08 + +#define CDATA_IN_BUF_BEGIN 0x09 +#define CDATA_IN_BUF_END_PLUS_1 0x0A +#define CDATA_IN_BUF_HEAD 0x0B +#define CDATA_IN_BUF_TAIL 0x0C +#define CDATA_OUT_BUF_BEGIN 0x0D +#define CDATA_OUT_BUF_END_PLUS_1 0x0E +#define CDATA_OUT_BUF_HEAD 0x0F +#define CDATA_OUT_BUF_TAIL 0x10 + +#define CDATA_DMA_CONTROL 0x11 +#define CDATA_RESERVED 0x12 + +#define CDATA_FREQUENCY 0x13 +#define CDATA_LEFT_VOLUME 0x14 +#define CDATA_RIGHT_VOLUME 0x15 +#define CDATA_LEFT_SUR_VOL 0x16 +#define CDATA_RIGHT_SUR_VOL 0x17 + +#define CDATA_HEADER_LEN 0x18 + +#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN +#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) +#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) +#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) +#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) +#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) +#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) +#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) + +#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) +#define MINISRC_BIQUAD_STAGE 2 +#define MINISRC_COEF_LOC 0X175 + +#define DMACONTROL_BLOCK_MASK 0x000F +#define DMAC_BLOCK0_SELECTOR 0x0000 +#define DMAC_BLOCK1_SELECTOR 0x0001 +#define DMAC_BLOCK2_SELECTOR 0x0002 +#define DMAC_BLOCK3_SELECTOR 0x0003 +#define DMAC_BLOCK4_SELECTOR 0x0004 +#define DMAC_BLOCK5_SELECTOR 0x0005 +#define DMAC_BLOCK6_SELECTOR 0x0006 +#define DMAC_BLOCK7_SELECTOR 0x0007 +#define DMAC_BLOCK8_SELECTOR 0x0008 +#define DMAC_BLOCK9_SELECTOR 0x0009 +#define DMAC_BLOCKA_SELECTOR 0x000A +#define DMAC_BLOCKB_SELECTOR 0x000B +#define DMAC_BLOCKC_SELECTOR 0x000C +#define DMAC_BLOCKD_SELECTOR 0x000D +#define DMAC_BLOCKE_SELECTOR 0x000E +#define DMAC_BLOCKF_SELECTOR 0x000F +#define DMACONTROL_PAGE_MASK 0x00F0 +#define DMAC_PAGE0_SELECTOR 0x0030 +#define DMAC_PAGE1_SELECTOR 0x0020 +#define DMAC_PAGE2_SELECTOR 0x0010 +#define DMAC_PAGE3_SELECTOR 0x0000 +#define DMACONTROL_AUTOREPEAT 0x1000 +#define DMACONTROL_STOPPED 0x2000 +#define DMACONTROL_DIRECTION 0x0100 + + +/* + * DSP Code images + */ + +u16 assp_kernel_image[] = { + 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, + 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, + 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, + 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, + 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, + 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, + 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, + 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, + 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, + 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, + 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, + 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, + 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, + 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, + 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, + 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, + 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, + 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, + 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, + 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, + 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, + 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, + 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, + 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, + 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, + 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, + 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, + 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, + 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, + 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, + 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, + 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, + 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, + 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, + 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, + 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, + 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, + 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, + 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, + 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, + 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, + 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, + 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, + 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, + 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, + 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, + 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, + 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, + 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, + 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, + 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, + 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, + 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, + 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, + 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, + 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, + 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, + 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, + 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, + 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, + 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, + 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, + 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, + 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, + 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, + 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, + 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, + 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, + 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, + 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, + 0xBE3A, +}; + +/* + * Mini sample rate converter code image + * that is to be loaded at 0x400 on the DSP. + */ +u16 assp_minisrc_image[] = { + + 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, + 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, + 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, + 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, + 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, + 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, + 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, + 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, + 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, + 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, + 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, + 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, + 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, + 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, + 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, + 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, + 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, + 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, + 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, + 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, + 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, + 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, + 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, + 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, + 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, + 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, + 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, + 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, + 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, + 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, + 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, + 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + diff -Nru linux/sound/oss/maestro_tables.h linux-2.4.19-pre5-mjc/sound/oss/maestro_tables.h --- linux/sound/oss/maestro_tables.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/maestro_tables.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,333 @@ +/* + * Set up data block for the ESS soundblaster emulation. This is like + * the example code from ftp.esstech.com.tw (104T31.ZIP) + */ + +u16 asp_block_0[]= { + 0x7980, 0x003a, 0x7980, 0x007d, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, + 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, 0xbe3a, 0x8b00, + 0x7980, 0x0be6, 0x7980, 0x0069, 0xbe3a, 0x8b00, 0x8048, 0x6945, + 0xb801, 0x9045, 0x6941, 0xe388, 0x0031, 0x6944, 0xba50, 0xe38c, + 0x0031, 0x8b88, 0x6942, 0x304a, 0xe308, 0x0028, 0x6949, 0x9042, + 0x0042, 0xafa0, 0x8000, 0xafa0, 0x8000, 0x8042, 0x6944, 0xb801, + 0x9044, 0x0048, 0xbe3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xbe41, 0xbf80, 0x0101, 0x8806, 0x8804, 0xb912, + 0x8807, 0xbc26, 0xbe40, 0xb92f, 0x9001, 0x9002, 0x003b, 0x0122, + 0x7a88, 0x0055, 0x003a, 0x013c, 0x7a88, 0x0055, 0x4902, 0xe100, + 0x0800, 0x0c02, 0xa000, 0x7980, 0x004e, 0xb908, 0x8809, 0xbec6, + 0x0067, 0x4a80, 0xe200, 0x0065, 0x4880, 0xe200, 0x0063, 0xb97f, + 0x6e80, 0x7980, 0x0066, 0x1089, 0x9088, 0xb900, 0x90a9, 0x8ba8, + 0xef00, 0x803d, 0x0003, 0x1007, 0x881f, 0x8b88, 0xbb0f, 0xada0, + 0x0810, 0x9003, 0x4903, 0xe200, 0x007b, 0x9002, 0x6a05, 0x6d04, + 0x9803, 0x9804, 0x9005, 0x003d, 0xbe3a, 0xaf06, 0x0000, 0x803d, + 0x8b88, 0x6906, 0x8810, 0xaf06, 0x0001, 0xbfb0, 0x00ff, 0xbaf5, + 0xe3cc, 0x009a, 0x5e06, 0x003f, 0xbf80, 0x0080, 0x4a06, 0xe200, + 0x0095, 0x6e80, 0x6d06, 0x7980, 0x009b, 0x9080, 0x0810, 0xba46, + 0x8810, 0x8b00, 0x1006, 0x9080, 0x003d, 0xbe3a, 0x1024, 0x2009, + 0x8810, 0x8b88, 0x8b00, 0x1089, 0xbfb0, 0x000e, 0xbe0a, 0x880d, + 0x903e, 0x1038, 0x2008, 0x8810, 0x1037, 0x2008, 0x8811, 0x5f3e, + 0x0000, 0x8b00, 0xf500, 0x5e80, 0xfffe, 0x1039, 0x2008, 0x8815, + 0x8b8d, 0x0231, 0x0333, 0x108a, 0x90ad, 0x1025, 0x200a, 0x8815, + 0x0605, 0xb91f, 0x8809, 0x4f16, 0x1080, 0xf600, 0xbfb0, + 0x0003, 0xbfb0, 0x0007, 0x9089, 0xbe47, 0xbe43, 0xbec6, + 0x00f6, 0x4f8b, 0x14a8, 0xe500, 0x6380, 0x8b89, 0x4e8a, + 0xbfe3, 0xe500, 0x2480, 0xbe46, 0x8b9d, 0xbfe7, 0xbfb0, + 0x00ff, 0x288c, 0x2026, 0x8814, 0xbe47, 0x8b00, 0x738f, + 0x54aa, 0xbe03, 0xbe0a, 0x2fa8, 0x988a, 0x8da9, 0xbe03, + 0x4d8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe03, + 0x4c8e, 0xbfe1, 0xf500, 0x6180, 0x9880, 0x8ba9, 0xbe42, + 0x1039, 0x2008, 0x8815, 0x8b8d, 0x8b00, 0x8d8a, 0x7980, + 0x0bcf}; + +u16 asp_block_1[]={ + 0x0005, 0xb900, 0x9008, 0x9009, 0x900a, 0xbb3f, 0x90a0, + 0x001a, 0x011b, 0x021c, 0x0323, 0x1089, 0x9015, 0x108a, + 0x9016, 0x108b, 0x9017, 0x1088, 0x9018, 0x0137, 0x0524, + 0x8b8d, 0x4f16, 0xe200, 0x0827, 0x4f15, 0xe200, 0x0827, + 0x7a80, 0x08e6, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb900, + 0x90a0, 0x908d, 0x7980, 0x0833, 0x7a80, 0x0913, 0x7803, + 0x7a80, 0x0913, 0x102c, 0xb802, 0x8813, 0x8b8b, 0xb903, + 0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, 0x0845, 0x4e15, + 0xe200, 0x0845, 0x7a80, 0x08e6, 0x102c, 0xb806, 0x8813, + 0x8b8b, 0xb901, 0x90a0, 0x908d, 0x7980, 0x0851, 0x7a80, + 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb806, 0x8813, + 0x8b8b, 0xb904, 0x90a0, 0x908d, 0x7c02, 0x4f16, 0xe200, + 0x0863, 0x4d15, 0xe200, 0x0863, 0x7a80, 0x08e6, 0x102c, + 0xb80a, 0x8813, 0x8b8b, 0xb902, 0x90a0, 0x908d, 0x7980, + 0x086f, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, + 0xb80a, 0x8813, 0x8b8b, 0xb905, 0x90a0, 0x908d, 0x7801, + 0x7a80, 0x0913, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, + 0x0913, 0x1024, 0xbf90, 0x0100, 0x8815, 0x4f16, 0xe200, + 0x088e, 0x4c15, 0xe200, 0x088e, 0x7a80, 0x08e6, 0x102c, + 0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0100, 0x90a0, 0x908d, + 0x7980, 0x089b, 0x7a80, 0x0913, 0x7803, 0x7a80, 0x0913, + 0x102c, 0xb814, 0x8813, 0x8b8b, 0xbf80, 0x0103, 0x90a0, + 0x908d, 0x7c02, 0x4f16, 0xe200, 0x08ae, 0x4b15, 0xe200, + 0x08ae, 0x7a80, 0x08e6, 0x102c, 0xb818, 0x8813, 0x8b8b, + 0xbf80, 0x0101, 0x90a0, 0x908d, 0x7980, 0x08bb, 0x7a80, + 0x0913, 0x7803, 0x7a80, 0x0913, 0x102c, 0xb818, 0x8813, + 0x8b8b, 0xbf80, 0x0104, 0x90a0, 0x908d, 0x7c02, 0x4f16, + 0xe200, 0x08ce, 0x4a15, 0xe200, 0x08ce, 0x7a80, 0x08e6, + 0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0102, 0x90a0, + 0x908d, 0x7980, 0x08db, 0x7a80, 0x0913, 0x7803, 0x7a80, + 0x0913, 0x102c, 0xb81c, 0x8813, 0x8b8b, 0xbf80, 0x0105, + 0x90a0, 0x908d, 0x7801, 0x7a80, 0x0913, 0x7801, 0x7a80, + 0x0913, 0x7801, 0x7a80, 0x0913, 0x7980, 0x0920, 0x4f80, + 0x7803, 0xe100, 0x08fe, 0x4f89, 0xe100, 0x08f5, 0xb901, + 0x90a0, 0xb902, 0x90a0, 0x90a0, 0xb906, 0x90ad, 0xef00, + 0xb901, 0x90a0, 0xb906, 0x90a0, 0xb900, 0x90a0, 0xb906, + 0x90ad, 0xef00, 0x4f89, 0xe100, 0x090a, 0xb905, 0x90a0, + 0xb900, 0x90a0, 0xb902, 0x90a0, 0xb906, 0x90ad, 0xef00, + 0xb905, 0x90a0, 0xb900, 0x90a0, 0xb906, 0x90a0, 0xb904, + 0x90ad, 0xef00, 0x4f89, 0xe100, 0x091b, 0xb901, 0x90a0, + 0xb906, 0x90ad, 0xef00, 0xb905, 0x90a0, 0xb904, 0x90ad, + 0xef00, 0xb91f, 0x8809, 0x0034, 0x8b88, 0xbec6, 0x0932, + 0x1313, 0xbe1e, 0x1014, 0xbe1a, 0xbe01, 0xbfe8, 0xbe17, + 0x6a13, 0x6214, 0xbe14, 0x9813, 0x9014, 0x98a0, 0xbe47, + 0x5f0f, 0x002e, 0xe200, 0x093d, 0xbf80, 0xffd2, 0x900f, + 0x7980, 0x0940, 0x100f, 0xb801, 0x900f, 0x400f, 0x8b00, + 0xe500, 0xbe01, 0xbe09, 0x9010, 0xbe46, 0x5f11, 0x003f, + 0xe200, 0x094f, 0xb900, 0x9011, 0x7980, 0x0952, 0x1011, + 0xb801, 0x9011, 0x1001, 0xe388, 0x0bcf, 0x1021, 0x2009, + 0x8813, 0x8b8b, 0x8b00, 0x1080, 0xbfe6, 0x7810, 0x8b00, + 0x8b00, 0x2180, 0xbfb0, 0x0007, 0x4c11, 0x8b00, 0xe100, + 0x096c, 0x4b11, 0x8b00, 0xe600, 0xb900, 0x7980, 0x096d, + 0xbe0a, 0x4a11, 0x8b00, 0xe500, 0xbe01, 0x9012, 0x1037, + 0x2008, 0x8811, 0x102e, 0x2008, 0x8812, 0x4a89, 0xb901, + 0xe500, 0x9019, 0xe100, 0x09c9, 0xb900, 0x9019, 0x4a18, + 0xe200, 0x09c9, 0x5f0a, 0x0010, 0xe200, 0x098d, 0x4b18, + 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0013, + 0xe200, 0x0997, 0x4b18, 0xb901, 0xe500, 0x9019, 0x7980, + 0x09c9, 0x5f0a, 0x0011, 0xe200, 0x09a4, 0x4f18, 0xb905, + 0xe500, 0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, + 0x5f0a, 0x0014, 0xe200, 0x09b1, 0x4c18, 0xb904, 0xe500, + 0x9080, 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, + 0x0012, 0xe200, 0x09be, 0x4d18, 0xb905, 0xe500, 0x9080, + 0xb901, 0xe500, 0x9019, 0x7980, 0x09c9, 0x5f0a, 0x0015, + 0xe200, 0x09c9, 0x4e18, 0xb904, 0xe500, 0x9080, 0xb901, + 0xe500, 0x9019, 0x8b8a, 0x4f19, 0xe200, 0x09d4, 0x4b80, + 0xe200, 0x09d6, 0x5d80, 0x0020, 0x7980, 0x09d9, 0x5d80, + 0x0010, 0x4a80, 0xe200, 0x0bca, 0x1001, 0xba01, 0x9001, + 0x1024, 0x2009, 0x8815, 0x8b89, 0x4d8d, 0xe200, 0x09f8, + 0x4f16, 0xe100, 0x09ed, 0x8b89, 0x1080, 0xbfc0, 0x000c, + 0x908c, 0x7980, 0x09f8, 0x4b89, 0x108d, 0xf600, 0xbfb0, + 0x0003, 0x4a89, 0x8b00, 0xf500, 0xbfc0, 0x0008, 0x908c, + 0x1022, 0x2009, 0x8811, 0x102e, 0x2008, 0x8812, 0x102f, + 0x2008, 0x8813, 0x1020, 0x200a, 0x8814, 0x101d, 0x200a, + 0x8815, 0x8b8a, 0x4f19, 0xe100, 0x0a11, 0x5e80, 0x0020, + 0x5d80, 0x0018, 0x7980, 0x0a3e, 0x4f80, 0xe200, 0x0a1e, + 0x8b8b, 0x108a, 0xbfa0, 0x7fc0, 0x8b00, 0xf704, 0x5c80, + 0x0003, 0x7980, 0x0a3e, 0x4e80, 0xe200, 0x0a36, 0x8b8c, + 0x178b, 0xbe01, 0xbfb7, 0x00f0, 0x308a, 0xe344, 0x0a3e, + 0x8b8d, 0x4a8a, 0x8b00, 0xe200, 0x0a32, 0x5c80, 0x0006, + 0x7980, 0x0a3e, 0x5c80, 0x000a, 0x7980, 0x0a3e, 0x4c80, + 0xe200, 0x0a3e, 0x4b80, 0x8b00, 0xf500, 0x5c80, 0x0019, + 0x4f80, 0xe200, 0x0a4d, 0x101f, 0x200a, 0x8816, 0x8b00, + 0x8b00, 0x8b8e, 0x1780, 0xbfb7, 0x00f0, 0x900b, 0x7980, + 0x0a64, 0x4e80, 0xe200, 0x0a5c, 0x101f, 0x200a, 0x8816, + 0x8b00, 0x8b00, 0x8b8e, 0x1b80, 0xbfbb, 0x000f, 0x900b, + 0x7980, 0x0a64, 0x4c80, 0xe200, 0x0a64, 0x8b8c, 0x1b80, + 0xbfbb, 0x000f, 0x900b, 0x8b89, 0x1880, 0xbfb8, 0x001c, + 0x4917, 0xe100, 0x0a6e, 0x4f80, 0x7980, 0x0a70, 0x4e80, + 0x8b00, 0xf500, 0xbf98, 0x0002, 0x8b8d, 0x4b89, 0x8b00, + 0xe600, 0xbfe1, 0x903e, 0xbe43, 0x6a0b, 0x613e, 0xbfe8, + 0x980c, 0xbe42, 0x7c10, 0x1080, 0xbfe5, 0xbe1e, 0x7810, + 0x1280, 0xbfb2, 0x001f, 0xbe11, 0x2027, 0x8811, 0x101e, + 0x200a, 0x8816, 0x8b00, 0x128e, 0x4980, 0xe100, 0x0a9b, + 0x4880, 0xe100, 0x0a98, 0xb900, 0x7980, 0x0a9f, 0xbe0a, + 0x7980, 0x0a9f, 0x4880, 0xe200, 0x0a9f, 0xbe09, 0xbe1e, + 0x158d, 0xbfb5, 0x003f, 0xbe11, 0x4889, 0xe200, 0x0aae, + 0xbe1e, 0x1010, 0x4818, 0x8b00, 0xe600, 0xbfe1, 0xbe11, + 0xbfe1, 0x8811, 0xbe1e, 0xb9ff, 0x8819, 0x8b00, 0x8b00, + 0xbf46, 0x8b00, 0xe600, 0xbe1f, 0xbe01, 0xbfb0, 0x00ff, + 0x202a, 0x8811, 0x8b00, 0x8b00, 0x108a, 0x900d, 0xb91f, + 0x8809, 0x0732, 0x730d, 0x4f80, 0xe200, 0x0ad8, 0x1028, + 0x200c, 0x8815, 0xbe43, 0x8b8b, 0x6a8d, 0xbec6, 0x0ad4, + 0x618b, 0x9880, 0x548f, 0x8dad, 0xbe42, 0x7980, 0x0b24, + 0x4e80, 0xe200, 0x0af9, 0x8b8c, 0x178b, 0xbe01, 0xbfb7, + 0x00f0, 0x903e, 0x1029, 0x200c, 0x8815, 0x108d, 0xbec6, + 0x0af6, 0x308b, 0xbe1e, 0x303e, 0x903f, 0x403f, 0xbe1f, + 0xe500, 0x103e, 0x9080, 0xbfe6, 0x202a, 0x8811, 0x8b00, + 0x1089, 0x548f, 0x8dad, 0x7980, 0x0b24, 0x4c8b, 0xe200, + 0x0b1b, 0x1029, 0x200c, 0x8815, 0xbf80, 0x007f, 0x903e, + 0x108d, 0xbec6, 0x0b14, 0x308b, 0xbe1e, 0x303e, 0x903f, + 0x403f, 0xbe1f, 0xe500, 0xb900, 0x9080, 0xbfe6, 0x202a, + 0x8811, 0x8b00, 0x1089, 0x548f, 0x8dad, 0x8b8a, 0xf500, + 0x5e80, 0xffdf, 0x7980, 0x0b24, 0x1089, 0xbfe6, 0x202a, + 0x8811, 0x8b00, 0x8b00, 0x548f, 0xbb1f, 0x8da0, 0x8b8f, + 0x0732, 0x1021, 0x2009, 0x8811, 0x8b89, 0x101d, 0x200a, + 0x8812, 0x1080, 0x7810, 0x8b00, 0xbe47, 0x288a, 0xbfb0, + 0x03ff, 0x4989, 0xe200, 0x0b3e, 0xbe1e, 0x1012, 0x4918, + 0x8b00, 0xe600, 0xbe0a, 0xbe11, 0x900e, 0xbe46, 0x108a, + 0xbfb0, 0x001c, 0xbfe1, 0x880d, 0x8b00, 0x6b0e, 0xbe0a, + 0x880c, 0x108c, 0xbfb0, 0x000f, 0x202d, 0x8814, 0x8b00, + 0x8b00, 0x5589, 0xbf03, 0x8c0e, 0xbf00, 0x1030, 0x2008, + 0x8811, 0xb91f, 0x8809, 0x108b, 0x0333, 0xbec6, 0x0b5f, + 0x200e, 0x90a0, 0x8b00, 0x8b89, 0x9080, 0x4a18, 0xe200, + 0x0b7a, 0x5f0a, 0x0011, 0xe200, 0x0b6c, 0x4f18, 0xe900, + 0x0b7c, 0x5f0a, 0x0014, 0xe200, 0x0b73, 0x4c18, 0xe900, + 0x0b9b, 0x5f0a, 0x0015, 0xe200, 0x0b7a, 0x4e18, 0xe900, + 0x0bb2, 0x7980, 0x009e, 0x0034, 0xb91f, 0x8809, 0x0333, + 0x8b8b, 0xbec6, 0x0b99, 0x1280, 0x6c80, 0xbfea, 0xbe1e, + 0x1580, 0x6c88, 0xbfec, 0xbe13, 0x903e, 0x6cab, 0x903f, + 0x4f3e, 0xb900, 0xf500, 0xbf80, 0x8000, 0x4f3f, 0xbf90, + 0x0d00, 0xf500, 0xbf90, 0x2700, 0x90a0, 0xef00, 0x0034, + 0xb91f, 0x8809, 0x0333, 0x8b88, 0xbec6, 0x0bb0, 0x1eab, + 0x6c80, 0xbfed, 0x903e, 0x4180, 0xb900, 0xf500, 0xbf80, + 0x8000, 0x4f3e, 0x8b00, 0xf500, 0xbf90, 0x4000, 0x90a8, + 0xef00, 0x0034, 0xb91f, 0x8809, 0x0333, 0x8b8b, 0xbec6, + 0x0bc8, 0x1280, 0x6c8b, 0xbfea, 0xbe1e, 0x1580, 0x6c80, + 0xbfec, 0xbe13, 0x903e, 0x4f3e, 0xbf80, 0x4000, 0xf500, + 0xbf90, 0x8000, 0x90a0, 0xef00, 0x0231, 0x8b8a, 0xb900, + 0xbb20, 0x90a0, 0x5f08, 0x0023, 0xe100, 0x0043, 0x1008, + 0xb801, 0x9008, 0x102b, 0x2008, 0x8812, 0x8b00, 0x8b00, + 0x1080, 0x900a, 0x102c, 0x2008, 0x8812, 0x8b00, 0x8b00, + 0x1080, 0x9009, 0x7980, 0x0952, 0x8148, 0x6946, 0xb801, + 0x9046, 0xb901, 0x9041, 0x6944, 0xba08, 0xe344, 0x0bfe, + 0x9044, 0x8b89, 0x0143, 0x694b, 0x881f, 0xbb0f, 0xada0, + 0x8143, 0x0811, 0x304a, 0xe308, 0x0bfe, 0x6949, 0x9043, + 0x0148, 0xbe3a +}; + +u16 asp_block_2[]={ + 0x0000, 0x0000, 0x0003, 0x0003, 0x0001, 0x0001, + 0x0004, 0x0004, 0x0002, 0x0002, 0x0005, 0x0005, + 0x0006, 0x0006, 0x0007, 0x0007, 0x0008, 0x0008, + 0x0100, 0x0100, 0x0103, 0x0103, 0x0101, 0x0101, + 0x0104, 0x0104, 0x0102, 0x0102, 0x0105, 0x0105, + 0x0106, 0x0106, 0x0107, 0x0107, 0x0108, 0x0108 +}; + +u16 asp_block_3[]={ + 0x0000, 0x0000, 0x0000, 0x1200, 0x1200, 0x1280, 0x0000, 0x05d0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x1104, 0x1105, 0x1008, 0x1020, 0x1040, 0x1060, + 0x1080, 0x10a0, 0x10b0, 0x100d, 0x1010, 0x10e0, 0x2000, 0x2980, + 0x2b00, 0x2b40, 0x2a00, 0x2b90, 0x13dc, 0x2b80, 0x11bc, 0x134c, + 0x1370, 0x12e0, 0x1240, 0x1260, 0x12c0, 0x009e, 0x0045, 0x10bc, + 0x1394, 0x13b8, 0x11f6, 0x10f6, 0x11b0, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0815, 0x0956, 0x09df, 0x0be5, 0x0a19, 0x0a48, 0x0b37, + 0x0b5d, 0x0a8b, 0x0aae, 0x0ad2 }; + + +u16 asp_block_4[] = { + 0x0192, 0x04b6, 0x07d9, 0x0afb, 0x0e1c, 0x113a, 0x1455, 0x176e, + 0x1a83, 0x1d93, 0x209f, 0x23a7, 0x26a8, 0x29a4, 0x2c99, 0x2f87, + 0x326e, 0x354e, 0x3825, 0x3af3, 0x3db8, 0x4074, 0x4326, 0x45cd, + 0x486a, 0x4afb, 0x4d81, 0x4ffb, 0x5269, 0x54ca, 0x571e, 0x5964, + 0x5b9d, 0x5dc8, 0x5fe4, 0x61f1, 0x63ef, 0x65de, 0x67bd, 0x698c, + 0x6b4b, 0x6cf9, 0x6e97, 0x7023, 0x719e, 0x7308, 0x7460, 0x75a6, + 0x76d9, 0x77fb, 0x790a, 0x7a06, 0x7aef, 0x7bc6, 0x7c89, 0x7d3a, + 0x7dd6, 0x7e60, 0x7ed6, 0x7f38, 0x7f87, 0x7fc2, 0x7fea, 0x7ffe, + 0x7ffe, 0x7fea, 0x7fc2, 0x7f87, 0x7f38, 0x7ed6, 0x7e60, 0x7dd6, + 0x7d3a, 0x7c89, 0x7bc6, 0x7aef, 0x7a06, 0x790a, 0x77fb, 0x76d9, + 0x75a6, 0x7460, 0x7308, 0x719e, 0x7023, 0x6e97, 0x6cf9, 0x6b4b, + 0x698c, 0x67bd, 0x65de, 0x63ef, 0x61f1, 0x5fe4, 0x5dc8, 0x5b9d, + 0x5964, 0x571e, 0x54ca, 0x5269, 0x4ffb, 0x4d81, 0x4afb, 0x486a, + 0x45cd, 0x4326, 0x4074, 0x3db8, 0x3af3, 0x3825, 0x354e, 0x326e, + 0x2f87, 0x2c99, 0x29a4, 0x26a8, 0x23a7, 0x209f, 0x1d93, 0x1a83, + 0x176e, 0x1455, 0x113a, 0x0e1c, 0x0afb, 0x07d9, 0x04b6, 0x0192, + 0xfe6f, 0xfb4b, 0xf828, 0xf506, 0xf1e5, 0xeec7, 0xebac, 0xe893, + 0xe57e, 0xe26e, 0xdf62, 0xdc5a, 0xd959, 0xd65d, 0xd368, 0xd07a, + 0xcd93, 0xcab3, 0xc7dc, 0xc50e, 0xc249, 0xbf8d, 0xbcdb, 0xba34, + 0xb797, 0xb506, 0xb280, 0xb006, 0xad98, 0xab37, 0xa8e3, 0xa69d, + 0xa464, 0xa239, 0xa01d, 0x9e10, 0x9c12, 0x9a23, 0x9844, 0x9675, + 0x94b6, 0x9308, 0x916a, 0x8fde, 0x8e63, 0x8cf9, 0x8ba1, 0x8a5b, + 0x8928, 0x8806, 0x86f7, 0x85fb, 0x8512, 0x843b, 0x8378, 0x82c7, + 0x822b, 0x81a1, 0x812b, 0x80c9, 0x807a, 0x803f, 0x8017, 0x8003, + 0x8003, 0x8017, 0x803f, 0x807a, 0x80c9, 0x812b, 0x81a1, 0x822b, + 0x82c7, 0x8378, 0x843b, 0x8512, 0x85fb, 0x86f7, 0x8806, 0x8928, + 0x8a5b, 0x8ba1, 0x8cf9, 0x8e63, 0x8fde, 0x916a, 0x9308, 0x94b6, + 0x9675, 0x9844, 0x9a23, 0x9c12, 0x9e10, 0xa01d, 0xa239, 0xa464, + 0xa69d, 0xa8e3, 0xab37, 0xad98, 0xb006, 0xb280, 0xb506, 0xb797, + 0xba34, 0xbcdb, 0xbf8d, 0xc249, 0xc50e, 0xc7dc, 0xcab3, 0xcd93, + 0xd07a, 0xd368, 0xd65d, 0xd959, 0xdc5a, 0xdf62, 0xe26e, 0xe57e, + 0xe893, 0xebac, 0xeec7, 0xf1e5, 0xf506, 0xf828, 0xfb4b, 0xfe6f, + 0x7cc3, 0x725e, 0x68d5, 0x6017, 0x5813, 0x50b9, 0x49fb, 0x43cd, + 0x3e22, 0x38ef, 0x342b, 0x2fcc, 0x2bc9, 0x281c, 0x24be, 0x21a6, + 0x1ed1, 0x1c37, 0x19d5, 0x17a6, 0x15a5, 0x13ce, 0x121f, 0x1093, + 0x0f28, 0x0ddc, 0x0cab, 0x0b93, 0x0a92, 0x09a7, 0x08cf, 0x080a, + 0x0754, 0x06ae, 0x0615, 0x0589, 0x0509, 0x0494, 0x0428, 0x03c5, + 0x036a, 0x0317, 0x02cb, 0x0285, 0x0245, 0x020a, 0x01d4, 0x01a2, + 0x0175, 0x014b, 0x0125, 0x0102, 0x00e2, 0x00c5, 0x00aa, 0x0091, + 0x007b, 0x0066, 0x0053, 0x0041, 0x0031, 0x0022, 0x0015, 0x0009, + 0xfffe, 0xfff2, 0xffe5, 0xffd7, 0xffc8, 0xffb7, 0xffa5, 0xff91, + 0xff7b, 0xff64, 0xff4a, 0xff2e, 0xff0f, 0xfeee, 0xfec9, 0xfea1, + 0xfe76, 0xfe46, 0xfe13, 0xfdda, 0xfd9d, 0xfd5a, 0xfd11, 0xfcc1, + 0xfc6b, 0xfc0c, 0xfba5, 0xfb34, 0xfab9, 0xfa33, 0xf9a1, 0xf902, + 0xf854, 0xf797, 0xf6c8, 0xf5e7, 0xf4f1, 0xf3e5, 0xf2c1, 0xf183, + 0xf027, 0xeeac, 0xed0f, 0xeb4d, 0xe961, 0xe74a, 0xe501, 0xe284, + 0xdfcd, 0xdcd8, 0xd99d, 0xd618, 0xd242, 0xce12, 0xc982, 0xc487, + 0xbf1a, 0xb92e, 0xb2ba, 0xabaf, 0xa402, 0x9ba3, 0x9282, 0x888d, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, 0x0010, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x000a, 0x000e, + 0x0010, 0x0014, 0x0016, 0x0018, 0x001a, 0x001c, 0x001e, 0x0020, + 0x0000, 0x0000, 0x0000, 0x000a, 0x0010, 0x0016, 0x001a, 0x001e, + 0x0020, 0x0024, 0x0026, 0x0028, 0x002a, 0x002c, 0x002e, 0x0030, + 0x0000, 0x0000, 0x0010, 0x001a, 0x0020, 0x0026, 0x002a, 0x002e, + 0x0030, 0x0034, 0x0036, 0x0038, 0x003a, 0x003c, 0x003e, 0x0040, + 0x0000, 0x0010, 0x0020, 0x002a, 0x0030, 0x0036, 0x003a, 0x003e, + 0x0040, 0x0044, 0x0046, 0x0048, 0x004a, 0x004c, 0x004e, 0x0050, + 0x0000, 0x0020, 0x0030, 0x003a, 0x0040, 0x0046, 0x004a, 0x004e, + 0x0050, 0x0054, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060, + 0x0000, 0x0030, 0x0040, 0x004a, 0x0050, 0x0056, 0x005a, 0x005e, + 0x0060, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, 0x0070, + 0x0008, 0x0021, 0x0042, 0x0064, 0x0086, 0x00a8, 0x00cb, 0x00ee, + 0x0111, 0x0135, 0x0158, 0x017d, 0x01a1, 0x01c6, 0x01eb, 0x0211, + 0x0236, 0x025d, 0x0283, 0x02aa, 0x02d1, 0x02f9, 0x0321, 0x0349, + 0x0372, 0x039b, 0x03c4, 0x03ee, 0x0418, 0x0442, 0x046d, 0x0499, + 0x04c4, 0x04f0, 0x051d, 0x054a, 0x0577, 0x05a5, 0x05d3, 0x0601, + 0x0630, 0x0660, 0x0690, 0x06c0, 0x06f1, 0x0722, 0x0753, 0x0785, + 0x07b8, 0x07eb, 0x081e, 0x0852, 0x0886, 0x08bb, 0x08f0, 0x0926, + 0x095c, 0x0993, 0x09ca, 0x0a02, 0x0a3a, 0x0a73, 0x0aac, 0x0ae6, + 0x0b20, 0x0b5b, 0x0b96, 0x0bd2, 0x0c0e, 0x0c4b, 0x0c89, 0x0cc7, + 0x0d05, 0x0d44, 0x0d84, 0x0dc5, 0x0e05, 0x0e47, 0x0e89, 0x0ecc, + 0x0f0f, 0x0f53, 0x0f97, 0x0fdd, 0x1022, 0x1069, 0x10b0, 0x10f7, + 0x1140, 0x1189, 0x11d2, 0x121c, 0x1267, 0x12b3, 0x12ff, 0x134c, + 0x139a, 0x13e8, 0x1438, 0x1487, 0x14d8, 0x1529, 0x157b, 0x15ce, + 0x1621, 0x1676, 0x16cb, 0x1720, 0x1777, 0x17ce, 0x1826, 0x187f, + 0x18d9, 0x1934, 0x198f, 0x19eb, 0x1a48, 0x1aa6, 0x1b05, 0x1b64, + 0x1bc5, 0x1c26, 0x1c88, 0x1ceb, 0x1d4f, 0x1db4, 0x1e1a, 0x1e80, + 0x1ee8, 0x1f51, 0x1fba, 0x2025, 0x2090, 0x20fc, 0x216a, 0x21d8, + 0x2247, 0x22b8, 0x2329, 0x239b, 0x240f, 0x2483, 0x24f9, 0x256f, + 0x25e7, 0x2660, 0x26da, 0x2755, 0x27d1, 0x284e, 0x28cc, 0x294b, + 0x29cc, 0x2a4e, 0x2ad1, 0x2b55, 0x2bda, 0x2c61, 0x2ce8, 0x2d71, + 0x2dfb, 0x2e87, 0x2f13, 0x2fa1, 0x3031, 0x30c1, 0x3153, 0x31e6, + 0x327b, 0x3310, 0x33a8, 0x3440, 0x34da, 0x3575, 0x3612, 0x36b0, + 0x3750, 0x37f1, 0x3893, 0x3937, 0x39dc, 0x3a83, 0x3b2c, 0x3bd6, + 0x3c81, 0x3d2e, 0x3ddd, 0x3e8d, 0x3f3f, 0x3ff2, 0x40a7, 0x415d, + 0x4216, 0x42d0, 0x438b, 0x4448, 0x4507, 0x45c8, 0x468b, 0x474f, + 0x4815, 0x48dd, 0x49a6, 0x4a72, 0x4b3f, 0x4c0e, 0x4cdf, 0x4db2, + 0x4e87, 0x4f5d, 0x5036, 0x5111, 0x51ed, 0x52cc, 0x53ac, 0x548f, + 0x5573, 0x565a, 0x5743, 0x582e, 0x591b, 0x5a0a, 0x5afb, 0x5bef, + 0x5ce4, 0x5ddc, 0x5ed7, 0x5fd3, 0x60d2, 0x61d3, 0x62d6, 0x63dc, + 0x64e4, 0x65ee, 0x66fb, 0x680a, 0x691c, 0x6a30, 0x6b47, 0x6c60, + 0x6d7c, 0x6e9a, 0x6fbb, 0x70de, 0x7204, 0x732d, 0x7459, 0x7587, + 0x76b8, 0x77eb, 0x7922, 0x7a5b, 0x7b97, 0x7cd6, 0x7e18, 0x7f5d, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, + 0x0003, 0x0004, 0x0004, 0x0005, 0x0006, 0x0008, 0x0009, 0x000a, + 0x000c, 0x0010, 0x0012, 0x0015, 0x0019, 0x0022, 0x0024, 0x002a, + 0x0031, 0x003e, 0x0049, 0x0055, 0x0062, 0x007c, 0x0092, 0x00a9, + 0x00c4, 0x00fc, 0x0125, 0x0152, 0x0187, 0x01f2, 0x024a, 0x02a4, + 0x030d, 0x03e3, 0x0492, 0x0547, 0x061b, 0x07c7, 0x0923, 0x0a8d, + 0x0c19, 0x0eb3, 0x1228, 0x14c1, 0x17fb, 0x1d17, 0x22f2, 0x2835, + 0x2dd4, 0x3cd0, 0x4cf5, 0x51cc, 0x7fff, 0x7fff, 0x7fff, 0x7fff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, + 0x0002, 0x0003, 0x0004, 0x0005, 0x0005, 0x0007, 0x0008, 0x000a, + 0x000b, 0x000e, 0x0011, 0x0014, 0x0017, 0x001c, 0x0022, 0x0028, + 0x002e, 0x0039, 0x0045, 0x0050, 0x005c, 0x0073, 0x008a, 0x00a0, + 0x00b9, 0x00e7, 0x0114, 0x0141, 0x0172, 0x01ce, 0x0228, 0x0283, + 0x02e3, 0x039b, 0x0454, 0x0501, 0x05bf, 0x072a, 0x0899, 0x0a18, + 0x0b7e, 0x0e55, 0x10d3, 0x1404, 0x16c3, 0x16c3, 0x16c3, 0x16c3, + 0x0012, 0x0024, 0x0048, 0x006c, 0x0090, 0x00b4, 0x00d8, 0x00fc, + 0x0120, 0x0144, 0x0168, 0x0168, 0x01b0, 0x01b0, 0x021c, 0x021c, + 0x0000, 0x0003, 0x0008, 0x000b, 0x0001, 0x0004, 0x0009, 0x000c, + 0x0002, 0x0005, 0x000a, 0x000d, 0x0010, 0x0013, 0x0011, 0x0014, + 0x0012, 0x0015, 0x0100, 0x0103, 0x0108, 0x010b, 0x0101, 0x0104, + 0x0109, 0x010c, 0x0102, 0x0105, 0x010a, 0x010d, 0x0110, 0x0113, + 0x0111, 0x0114, 0x0112, 0x0115 }; diff -Nru linux/sound/oss/maui.c linux-2.4.19-pre5-mjc/sound/oss/maui.c --- linux/sound/oss/maui.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/maui.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,477 @@ +/* + * sound/maui.c + * + * The low level driver for Turtle Beach Maui and Tropez. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox General clean up, use kernel IRQ + * system + * Christoph Hellwig Adapted to module_init/module_exit + * Bartlomiej Zolnierkiewicz + * Added __init to download_code() + * + * Status: + * Andrew J. Kroll Tested 06/01/1999 with: + * * OSWF.MOT File Version: 1.15 + * * OSWF.MOT File Dated: 09/12/94 + * * Older versions will cause problems. + */ + +#include +#include +#include + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" +#include "sound_firmware.h" + +#include "mpu401.h" + +static int maui_base = 0x330; + +static volatile int irq_ok = 0; +static int *maui_osp; + +#define HOST_DATA_PORT (maui_base + 2) +#define HOST_STAT_PORT (maui_base + 3) +#define HOST_CTRL_PORT (maui_base + 3) + +#define STAT_TX_INTR 0x40 +#define STAT_TX_AVAIL 0x20 +#define STAT_TX_IENA 0x10 +#define STAT_RX_INTR 0x04 +#define STAT_RX_AVAIL 0x02 +#define STAT_RX_IENA 0x01 + +static int (*orig_load_patch) (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) = NULL; + +#include "maui_boot.h" + +static int maui_wait(int mask) +{ + int i; + + /* + * Perform a short initial wait without sleeping + */ + + for (i = 0; i < 100; i++) + if (inb(HOST_STAT_PORT) & mask) + return 1; + + /* + * Wait up to 15 seconds with sleeping + */ + + for (i = 0; i < 150; i++) { + if (inb(HOST_STAT_PORT) & mask) + return 1; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 10); + if (signal_pending(current)) + return 0; + } + return 0; +} + +static int maui_read(void) +{ + if (maui_wait(STAT_RX_AVAIL)) + return inb(HOST_DATA_PORT); + return -1; +} + +static int maui_write(unsigned char data) +{ + if (maui_wait(STAT_TX_AVAIL)) { + outb((data), HOST_DATA_PORT); + return 1; + } + printk(KERN_WARNING "Maui: Write timeout\n"); + return 0; +} + +static void mauiintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + irq_ok = 1; +} + +static int __init download_code(void) +{ + int i, lines = 0; + int eol_seen = 0, done = 0; + int skip = 1; + + printk(KERN_INFO "Code download (%d bytes): ", maui_osLen); + + for (i = 0; i < maui_osLen; i++) { + if (maui_os[i] != '\r') { + if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) { + skip = 0; + + if (maui_os[i] == '\n') + eol_seen = skip = 1; + else if (maui_os[i] == 'S') { + if (maui_os[i + 1] == '8') + done = 1; + if (!maui_write(0xF1)) + goto failure; + if (!maui_write('S')) + goto failure; + } else { + if (!maui_write(maui_os[i])) + goto failure; + } + + if (eol_seen) { + int c = 0; + int n; + + eol_seen = 0; + + for (n = 0; n < 2; n++) { + if (maui_wait(STAT_RX_AVAIL)) { + c = inb(HOST_DATA_PORT); + break; + } + } + if (c != 0x80) { + printk("Download not acknowledged\n"); + return 0; + } + else if (!(lines++ % 10)) + printk("."); + + if (done) { + printk("\n"); + printk(KERN_INFO "Download complete\n"); + return 1; + } + } + } + } + } + +failure: + printk("\n"); + printk(KERN_ERR "Download failed!!!\n"); + return 0; +} + +static int __init maui_init(int irq) +{ + unsigned char bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq); + return 0; + } + outb((0x00), HOST_CTRL_PORT); /* Reset */ + outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */ + outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */ + +#ifdef CONFIG_SMP + { + int i; + for (i = 0; i < 1000000 && !irq_ok; i++) + ; + if (!irq_ok) + return 0; + } +#endif + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + + printk(KERN_INFO "Turtle Beach Maui initialization\n"); + + if (!download_code()) + return 0; + + outb((0xE0), HOST_CTRL_PORT); /* Normal operation */ + + /* Select mpu401 mode */ + + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) { + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) + printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n"); + } + printk(KERN_INFO "Maui initialized OK\n"); + return 1; +} + +static int maui_short_wait(int mask) { + int i; + + for (i = 0; i < 1000; i++) { + if (inb(HOST_STAT_PORT) & mask) { + return 1; + } + } + return 0; +} + +static int maui_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + + struct sysex_info header; + unsigned long left, src_offs; + int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; + int i; + + if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ + return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); + + if (format != MAUI_PATCH) + { + printk(KERN_WARNING "Maui: Unknown patch format\n"); + } + if (count < hdr_size) { +/* printk("Maui error: Patch header too short\n");*/ + return -EINVAL; + } + count -= hdr_size; + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs)) + return -EFAULT; + + if (count < header.len) { + printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); + header.len = count; + } + left = header.len; + src_offs = 0; + + for (i = 0; i < left; i++) { + unsigned char data; + + if(get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]))) + return -EFAULT; + if (i == 0 && !(data & 0x80)) + return -EINVAL; + + if (maui_write(data) == -1) + return -EIO; + } + + if ((i = maui_read()) != 0x80) { + if (i != -1) + printk("Maui: Error status %02x\n", i); + return -EIO; + } + return 0; +} + +static int __init probe_maui(struct address_info *hw_config) +{ + int i; + int tmp1, tmp2, ret; + + if (check_region(hw_config->io_base, 8)) + return 0; + + maui_base = hw_config->io_base; + maui_osp = hw_config->osp; + + if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) + return 0; + + /* + * Initialize the processor if necessary + */ + + if (maui_osLen > 0) { + if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) || + !maui_write(0x9F) || /* Report firmware version */ + !maui_short_wait(STAT_RX_AVAIL) || + maui_read() == -1 || maui_read() == -1) + if (!maui_init(hw_config->irq)) { + free_irq(hw_config->irq, NULL); + return 0; + } + } + if (!maui_write(0xCF)) /* Report hardware version */ { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + free_irq(hw_config->irq, NULL); + return 0; + } + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + free_irq(hw_config->irq, NULL); + return 0; + } + if (tmp1 == 0xff || tmp2 == 0xff) { + free_irq(hw_config->irq, NULL); + return 0; + } + printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); + + if (!maui_write(0x9F)) /* Report firmware version */ + return 0; + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) + return 0; + + printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); + + if (!maui_write(0x85)) /* Report free DRAM */ + return 0; + tmp1 = 0; + for (i = 0; i < 4; i++) { + tmp1 |= maui_read() << (7 * i); + } + printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); + + for (i = 0; i < 1000; i++) + if (probe_mpu401(hw_config)) + break; + + ret = probe_mpu401(hw_config); + + if (ret) + request_region(hw_config->io_base + 2, 6, "Maui"); + + return ret; +} + +static void __init attach_maui(struct address_info *hw_config) +{ + int this_dev; + + conf_printf("Maui", hw_config); + + hw_config->irq *= -1; + hw_config->name = "Maui"; + attach_mpu401(hw_config, THIS_MODULE); + + if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */ { + struct synth_operations *synth; + + this_dev = hw_config->slots[1]; + + /* + * Intercept patch loading calls so that they can be handled + * by the Maui driver. + */ + + synth = midi_devs[this_dev]->converter; + synth->id = "MAUI"; + + if (synth != NULL) { + orig_load_patch = synth->load_patch; + synth->load_patch = &maui_load_patch; + } else + printk(KERN_ERR "Maui: Can't install patch loader\n"); + } +} + +static void __exit unload_maui(struct address_info *hw_config) +{ + int irq = hw_config->irq; + release_region(hw_config->io_base + 2, 6); + unload_mpu401(hw_config); + + if (irq < 0) + irq = -irq; + if (irq > 0) + free_irq(irq, NULL); +} + +static int fw_load = 0; + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); + +/* + * Install a Maui card. Needs mpu401 loaded already. + */ + +static int __init init_maui(void) +{ + printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + + if (cfg.io_base == -1 || cfg.irq == -1) { + printk(KERN_INFO "maui: irq and io must be set.\n"); + return -EINVAL; + } + + if (maui_os == NULL) { + fw_load = 1; + maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os); + } + if (probe_maui(&cfg) == 0) + return -ENODEV; + attach_maui(&cfg); + + return 0; +} + +static void __exit cleanup_maui(void) +{ + if (fw_load && maui_os) + vfree(maui_os); + unload_maui(&cfg); +} + +module_init(init_maui); +module_exit(cleanup_maui); + +#ifndef MODULE +static int __init setup_maui(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} + +__setup("maui=", setup_maui); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/midi_ctrl.h linux-2.4.19-pre5-mjc/sound/oss/midi_ctrl.h --- linux/sound/oss/midi_ctrl.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/midi_ctrl.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,22 @@ +static unsigned char ctrl_def_values[128] = +{ + 0x40,0x00,0x40,0x40, 0x40,0x40,0x40,0x7f, /* 0 to 7 */ + 0x40,0x40,0x40,0x7f, 0x40,0x40,0x40,0x40, /* 8 to 15 */ + 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */ + 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */ + + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */ + + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */ + + 0x00,0x00,0x7f,0x7f, 0x7f,0x7f,0x00,0x00, /* 96 to 103 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */ +}; diff -Nru linux/sound/oss/midi_syms.c linux-2.4.19-pre5-mjc/sound/oss/midi_syms.c --- linux/sound/oss/midi_syms.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/midi_syms.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,31 @@ +/* + * Exported symbols for midi driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char midi_syms_symbol; + +#include "sound_config.h" +#define _MIDI_SYNTH_C_ +#include "midi_synth.h" + +EXPORT_SYMBOL(do_midi_msg); +EXPORT_SYMBOL(midi_synth_open); +EXPORT_SYMBOL(midi_synth_close); +EXPORT_SYMBOL(midi_synth_ioctl); +EXPORT_SYMBOL(midi_synth_kill_note); +EXPORT_SYMBOL(midi_synth_start_note); +EXPORT_SYMBOL(midi_synth_set_instr); +EXPORT_SYMBOL(midi_synth_reset); +EXPORT_SYMBOL(midi_synth_hw_control); +EXPORT_SYMBOL(midi_synth_aftertouch); +EXPORT_SYMBOL(midi_synth_controller); +EXPORT_SYMBOL(midi_synth_panning); +EXPORT_SYMBOL(midi_synth_setup_voice); +EXPORT_SYMBOL(midi_synth_send_sysex); +EXPORT_SYMBOL(midi_synth_bender); +EXPORT_SYMBOL(midi_synth_load_patch); +EXPORT_SYMBOL(MIDIbuf_avail); diff -Nru linux/sound/oss/midi_synth.c linux-2.4.19-pre5-mjc/sound/oss/midi_synth.c --- linux/sound/oss/midi_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/midi_synth.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,697 @@ +/* + * sound/midi_synth.c + * + * High level midi sequencer manager for dumb MIDI interfaces. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Andrew Veliath : fixed running status in MIDI input state machine + */ +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" + +#define _MIDI_SYNTH_C_ + +#include "midi_synth.h" + +static int midi2synth[MAX_MIDI_DEV]; +static int sysex_state[MAX_MIDI_DEV] = +{0}; +static unsigned char prev_out_status[MAX_MIDI_DEV]; + +#define STORE(cmd) \ +{ \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ +} + +#define _seqbuf obuf +#define _seqbufptr 0 +#define _SEQ_ADVBUF(x) len=x + +void +do_midi_msg(int synthno, unsigned char *msg, int mlen) +{ + switch (msg[0] & 0xf0) + { + case 0x90: + if (msg[2] != 0) + { + STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + } + msg[2] = 64; + + case 0x80: + STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xA0: + STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xB0: + STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f, + msg[1], msg[2])); + break; + + case 0xC0: + STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xD0: + STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xE0: + STORE(SEQ_BENDER(synthno, msg[0] & 0x0f, + (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7))); + break; + + default: + /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */ + ; + } +} + +static void +midi_outc(int midi_dev, int data) +{ + int timeout; + + for (timeout = 0; timeout < 3200; timeout++) + if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff))) + { + if (data & 0x80) /* + * Status byte + */ + prev_out_status[midi_dev] = + (unsigned char) (data & 0xff); /* + * Store for running status + */ + return; /* + * Mission complete + */ + } + /* + * Sorry! No space on buffers. + */ + printk("Midi send timed out\n"); +} + +static int +prefix_cmd(int midi_dev, unsigned char status) +{ + if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) + return 1; + + return midi_devs[midi_dev]->prefix_cmd(midi_dev, status); +} + +static void +midi_synth_input(int orig_dev, unsigned char data) +{ + int dev; + struct midi_input_info *inc; + + static unsigned char len_tab[] = /* # of data bytes following a status + */ + { + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ + }; + + if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL) + return; + + if (data == 0xfe) /* Ignore active sensing */ + return; + + dev = midi2synth[orig_dev]; + inc = &midi_devs[orig_dev]->in_info; + + switch (inc->m_state) + { + case MST_INIT: + if (data & 0x80) /* MIDI status byte */ + { + if ((data & 0xf0) == 0xf0) /* Common message */ + { + switch (data) + { + case 0xf0: /* Sysex */ + inc->m_state = MST_SYSEX; + break; /* Sysex */ + + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* Song select */ + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = 1; + inc->m_buf[0] = data; + break; + + case 0xf2: /* Song position pointer */ + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = 2; + inc->m_buf[0] = data; + break; + + default: + inc->m_buf[0] = data; + inc->m_ptr = 1; + do_midi_msg(dev, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + inc->m_left = 0; + } + } else + { + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = len_tab[(data >> 4) - 8]; + inc->m_buf[0] = inc->m_prev_status = data; + } + } else if (inc->m_prev_status & 0x80) { + /* Data byte (use running status) */ + inc->m_ptr = 2; + inc->m_buf[1] = data; + inc->m_buf[0] = inc->m_prev_status; + inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1; + if (inc->m_left > 0) + inc->m_state = MST_DATA; /* Not done yet */ + else { + inc->m_state = MST_INIT; + do_midi_msg(dev, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + } + } + break; /* MST_INIT */ + + case MST_DATA: + inc->m_buf[inc->m_ptr++] = data; + if (--inc->m_left <= 0) + { + inc->m_state = MST_INIT; + do_midi_msg(dev, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + } + break; /* MST_DATA */ + + case MST_SYSEX: + if (data == 0xf7) /* Sysex end */ + { + inc->m_state = MST_INIT; + inc->m_left = 0; + inc->m_ptr = 0; + } + break; /* MST_SYSEX */ + + default: + printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data); + inc->m_state = MST_INIT; + } +} + +static void +leave_sysex(int dev) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int timeout = 0; + + if (!sysex_state[dev]) + return; + + sysex_state[dev] = 0; + + while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) && + timeout < 1000) + timeout++; + + sysex_state[dev] = 0; +} + +static void +midi_synth_output(int dev) +{ + /* + * Currently NOP + */ +} + +int midi_synth_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + /* + * int orig_dev = synth_devs[dev]->midi_dev; + */ + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + default: + return -EINVAL; + } +} + +int +midi_synth_kill_note(int dev, int channel, int note, int velocity) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (note < 0 || note > 127) + return 0; + if (channel < 0 || channel > 15) + return 0; + if (velocity < 0) + velocity = 0; + if (velocity > 127) + velocity = 127; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) + { /* + * Use running status + */ + if (!prefix_cmd(orig_dev, note)) + return 0; + + midi_outc(orig_dev, note); + + if (msg == 0x90) /* + * Running status = Note on + */ + midi_outc(orig_dev, 0); /* + * Note on with velocity 0 == note + * off + */ + else + midi_outc(orig_dev, velocity); + } else + { + if (velocity == 64) + { + if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* + * Note on + */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, 0); /* + * Zero G + */ + } else + { + if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /* + * Note off + */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } + } + + return 0; +} + +int +midi_synth_set_instr(int dev, int channel, int instr_no) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + if (instr_no < 0 || instr_no > 127) + instr_no = 0; + if (channel < 0 || channel > 15) + return 0; + + leave_sysex(dev); + + if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /* + * Program change + */ + midi_outc(orig_dev, instr_no); + + return 0; +} + +int +midi_synth_start_note(int dev, int channel, int note, int velocity) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (note < 0 || note > 127) + return 0; + if (channel < 0 || channel > 15) + return 0; + if (velocity < 0) + velocity = 0; + if (velocity > 127) + velocity = 127; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (chn == channel && msg == 0x90) + { /* + * Use running status + */ + if (!prefix_cmd(orig_dev, note)) + return 0; + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } else + { + if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* + * Note on + */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } + return 0; +} + +void +midi_synth_reset(int dev) +{ + + leave_sysex(dev); +} + +int +midi_synth_open(int dev, int mode) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int err; + unsigned long flags; + struct midi_input_info *inc; + + if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL) + return -ENXIO; + + midi2synth[orig_dev] = dev; + sysex_state[dev] = 0; + prev_out_status[orig_dev] = 0; + + if ((err = midi_devs[orig_dev]->open(orig_dev, mode, + midi_synth_input, midi_synth_output)) < 0) + return err; + inc = &midi_devs[orig_dev]->in_info; + + save_flags(flags); + cli(); + inc->m_busy = 0; + inc->m_state = MST_INIT; + inc->m_ptr = 0; + inc->m_left = 0; + inc->m_prev_status = 0x00; + restore_flags(flags); + + return 1; +} + +void +midi_synth_close(int dev) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + leave_sysex(dev); + + /* + * Shut up the synths by sending just single active sensing message. + */ + midi_devs[orig_dev]->outputc(orig_dev, 0xfe); + + midi_devs[orig_dev]->close(orig_dev); +} + +void +midi_synth_hw_control(int dev, unsigned char *event) +{ +} + +int +midi_synth_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + struct sysex_info sysex; + int i; + unsigned long left, src_offs, eox_seen = 0; + int first_byte = 1; + int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex; + + leave_sysex(dev); + + if (!prefix_cmd(orig_dev, 0xf0)) + return 0; + + if (format != SYSEX_PATCH) + { +/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < hdr_size) + { +/* printk("MIDI Error: Patch header too short\n");*/ + return -EINVAL; + } + count -= hdr_size; + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs)) + return -EFAULT; + + if (count < sysex.len) + { +/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/ + sysex.len = count; + } + left = sysex.len; + src_offs = 0; + + for (i = 0; i < left && !signal_pending(current); i++) + { + unsigned char data; + + get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i])); + + eox_seen = (i > 0 && data & 0x80); /* End of sysex */ + + if (eox_seen && data != 0xf7) + data = 0xf7; + + if (i == 0) + { + if (data != 0xf0) + { + printk(KERN_WARNING "midi_synth: Sysex start missing\n"); + return -EINVAL; + } + } + while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) && + !signal_pending(current)) + schedule(); + + if (!first_byte && data & 0x80) + return 0; + first_byte = 0; + } + + if (!eox_seen) + midi_outc(orig_dev, 0xf7); + return 0; +} + +void midi_synth_panning(int dev, int channel, int pressure) +{ +} + +void midi_synth_aftertouch(int dev, int channel, int pressure) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (pressure < 0 || pressure > 127) + return; + if (channel < 0 || channel > 15) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xd0 || chn != channel) /* + * Test for running status + */ + { + if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /* + * Channel pressure + */ + } else if (!prefix_cmd(orig_dev, pressure)) + return; + + midi_outc(orig_dev, pressure); +} + +void +midi_synth_controller(int dev, int channel, int ctrl_num, int value) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int chn, msg; + + if (ctrl_num < 0 || ctrl_num > 127) + return; + if (channel < 0 || channel > 15) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xb0 || chn != channel) + { + if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xb0 | (channel & 0x0f)); + } else if (!prefix_cmd(orig_dev, ctrl_num)) + return; + + midi_outc(orig_dev, ctrl_num); + midi_outc(orig_dev, value & 0x7f); +} + +void +midi_synth_bender(int dev, int channel, int value) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, prev_chn; + + if (channel < 0 || channel > 15) + return; + + if (value < 0 || value > 16383) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + prev_chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xd0 || prev_chn != channel) /* + * Test for running status + */ + { + if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xe0 | (channel & 0x0f)); + } else if (!prefix_cmd(orig_dev, value & 0x7f)) + return; + + midi_outc(orig_dev, value & 0x7f); + midi_outc(orig_dev, (value >> 7) & 0x7f); +} + +void +midi_synth_setup_voice(int dev, int voice, int channel) +{ +} + +int +midi_synth_send_sysex(int dev, unsigned char *bytes, int len) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int i; + + for (i = 0; i < len; i++) + { + switch (bytes[i]) + { + case 0xf0: /* Start sysex */ + if (!prefix_cmd(orig_dev, 0xf0)) + return 0; + sysex_state[dev] = 1; + break; + + case 0xf7: /* End sysex */ + if (!sysex_state[dev]) /* Orphan sysex end */ + return 0; + sysex_state[dev] = 0; + break; + + default: + if (!sysex_state[dev]) + return 0; + + if (bytes[i] & 0x80) /* Error. Another message before sysex end */ + { + bytes[i] = 0xf7; /* Sysex end */ + sysex_state[dev] = 0; + } + } + + if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i])) + { +/* + * Hardware level buffer is full. Abort the sysex message. + */ + + int timeout = 0; + + bytes[i] = 0xf7; + sysex_state[dev] = 0; + + while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) && + timeout < 1000) + timeout++; + } + if (!sysex_state[dev]) + return 0; + } + + return 0; +} diff -Nru linux/sound/oss/midi_synth.h linux-2.4.19-pre5-mjc/sound/oss/midi_synth.h --- linux/sound/oss/midi_synth.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/midi_synth.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,47 @@ +int midi_synth_ioctl (int dev, + unsigned int cmd, caddr_t arg); +int midi_synth_kill_note (int dev, int channel, int note, int velocity); +int midi_synth_set_instr (int dev, int channel, int instr_no); +int midi_synth_start_note (int dev, int channel, int note, int volume); +void midi_synth_reset (int dev); +int midi_synth_open (int dev, int mode); +void midi_synth_close (int dev); +void midi_synth_hw_control (int dev, unsigned char *event); +int midi_synth_load_patch (int dev, int format, const char * addr, + int offs, int count, int pmgr_flag); +void midi_synth_panning (int dev, int channel, int pressure); +void midi_synth_aftertouch (int dev, int channel, int pressure); +void midi_synth_controller (int dev, int channel, int ctrl_num, int value); +void midi_synth_bender (int dev, int chn, int value); +void midi_synth_setup_voice (int dev, int voice, int chn); +int midi_synth_send_sysex(int dev, unsigned char *bytes,int len); + +#ifndef _MIDI_SYNTH_C_ +static struct synth_info std_synth_info = +{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS}; + +static struct synth_operations std_midi_synth = +{ + owner: THIS_MODULE, + id: "MIDI", + info: &std_synth_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_MIDI, + synth_subtype: 0, + open: midi_synth_open, + close: midi_synth_close, + ioctl: midi_synth_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + hw_control: midi_synth_hw_control, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice, + send_sysex: midi_synth_send_sysex +}; +#endif diff -Nru linux/sound/oss/midibuf.c linux-2.4.19-pre5-mjc/sound/oss/midibuf.c --- linux/sound/oss/midibuf.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/midibuf.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,438 @@ +/* + * sound/midibuf.c + * + * Device file manager for /dev/midi# + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + */ +#include +#include + +#define MIDIBUF_C + +#include "sound_config.h" + + +/* + * Don't make MAX_QUEUE_SIZE larger than 4000 + */ + +#define MAX_QUEUE_SIZE 4000 + +static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV]; +static wait_queue_head_t input_sleeper[MAX_MIDI_DEV]; + +struct midi_buf +{ + int len, head, tail; + unsigned char queue[MAX_QUEUE_SIZE]; +}; + +struct midi_parms +{ + long prech_timeout; /* + * Timeout before the first ch + */ +}; + +static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL}; +static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL}; +static struct midi_parms parms[MAX_MIDI_DEV]; + +static void midi_poll(unsigned long dummy); + + +static struct timer_list poll_timer = { + function: midi_poll +}; + +static volatile int open_devs = 0; + +#define DATA_AVAIL(q) (q->len) +#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) + +#define QUEUE_BYTE(q, data) \ + if (SPACE_AVAIL(q)) \ + { \ + unsigned long flags; \ + save_flags( flags);cli(); \ + q->queue[q->tail] = (data); \ + q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ + restore_flags(flags); \ + } + +#define REMOVE_BYTE(q, data) \ + if (DATA_AVAIL(q)) \ + { \ + unsigned long flags; \ + save_flags( flags);cli(); \ + data = q->queue[q->head]; \ + q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ + restore_flags(flags); \ + } + +static void drain_midi_queue(int dev) +{ + + /* + * Give the Midi driver time to drain its output queues + */ + + if (midi_devs[dev]->buffer_status != NULL) + while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev)) + interruptible_sleep_on_timeout(&midi_sleeper[dev], + HZ/10); +} + +static void midi_input_intr(int dev, unsigned char data) +{ + if (midi_in_buf[dev] == NULL) + return; + + if (data == 0xfe) /* + * Active sensing + */ + return; /* + * Ignore + */ + + if (SPACE_AVAIL(midi_in_buf[dev])) { + QUEUE_BYTE(midi_in_buf[dev], data); + wake_up(&input_sleeper[dev]); + } +} + +static void midi_output_intr(int dev) +{ + /* + * Currently NOP + */ +} + +static void midi_poll(unsigned long dummy) +{ + unsigned long flags; + int dev; + + save_flags(flags); + cli(); + if (open_devs) + { + for (dev = 0; dev < num_midis; dev++) + if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL) + { + int ok = 1; + + while (DATA_AVAIL(midi_out_buf[dev]) && ok) + { + int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head]; + + restore_flags(flags); /* Give some time to others */ + ok = midi_devs[dev]->outputc(dev, c); + cli(); + midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; + midi_out_buf[dev]->len--; + } + + if (DATA_AVAIL(midi_out_buf[dev]) < 100) + wake_up(&midi_sleeper[dev]); + } + poll_timer.expires = (1) + jiffies; + add_timer(&poll_timer); + /* + * Come back later + */ + } + restore_flags(flags); +} + +int MIDIbuf_open(int dev, struct file *file) +{ + int mode, err; + + dev = dev >> 4; + mode = translate_mode(file); + + if (num_midis > MAX_MIDI_DEV) + { + printk(KERN_ERR "midi: Too many midi interfaces\n"); + num_midis = MAX_MIDI_DEV; + } + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return -ENXIO; + /* + * Interrupts disabled. Be careful + */ + + if (midi_devs[dev]->owner) + __MOD_INC_USE_COUNT (midi_devs[dev]->owner); + + if ((err = midi_devs[dev]->open(dev, mode, + midi_input_intr, midi_output_intr)) < 0) + return err; + + parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT; + midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); + + if (midi_in_buf[dev] == NULL) + { + printk(KERN_WARNING "midi: Can't allocate buffer\n"); + midi_devs[dev]->close(dev); + return -EIO; + } + midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; + + midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); + + if (midi_out_buf[dev] == NULL) + { + printk(KERN_WARNING "midi: Can't allocate buffer\n"); + midi_devs[dev]->close(dev); + vfree(midi_in_buf[dev]); + midi_in_buf[dev] = NULL; + return -EIO; + } + midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; + open_devs++; + + init_waitqueue_head(&midi_sleeper[dev]); + init_waitqueue_head(&input_sleeper[dev]); + + if (open_devs < 2) /* This was first open */ + { + poll_timer.expires = 1 + jiffies; + add_timer(&poll_timer); /* Start polling */ + } + return err; +} + +void MIDIbuf_release(int dev, struct file *file) +{ + int mode; + unsigned long flags; + + dev = dev >> 4; + mode = translate_mode(file); + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return; + + save_flags(flags); + cli(); + + /* + * Wait until the queue is empty + */ + + if (mode != OPEN_READ) + { + midi_devs[dev]->outputc(dev, 0xfe); /* + * Active sensing to shut the + * devices + */ + + while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev])) + interruptible_sleep_on(&midi_sleeper[dev]); + /* + * Sync + */ + + drain_midi_queue(dev); /* + * Ensure the output queues are empty + */ + } + restore_flags(flags); + + midi_devs[dev]->close(dev); + + open_devs--; + if (open_devs == 0) + del_timer_sync(&poll_timer); + vfree(midi_in_buf[dev]); + vfree(midi_out_buf[dev]); + midi_in_buf[dev] = NULL; + midi_out_buf[dev] = NULL; + + if (midi_devs[dev]->owner) + __MOD_DEC_USE_COUNT (midi_devs[dev]->owner); +} + +int MIDIbuf_write(int dev, struct file *file, const char *buf, int count) +{ + unsigned long flags; + int c, n, i; + unsigned char tmp_data; + + dev = dev >> 4; + + if (!count) + return 0; + + save_flags(flags); + cli(); + + c = 0; + + while (c < count) + { + n = SPACE_AVAIL(midi_out_buf[dev]); + + if (n == 0) { /* + * No space just now. + */ + + if (file->f_flags & O_NONBLOCK) { + restore_flags(flags); + return -EAGAIN; + } + + interruptible_sleep_on(&midi_sleeper[dev]); + if (signal_pending(current)) + { + restore_flags(flags); + return -EINTR; + } + n = SPACE_AVAIL(midi_out_buf[dev]); + } + if (n > (count - c)) + n = count - c; + + for (i = 0; i < n; i++) + { + /* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */ + copy_from_user((char *) &tmp_data, &(buf)[c], 1); + QUEUE_BYTE(midi_out_buf[dev], tmp_data); + c++; + } + } + restore_flags(flags); + return c; +} + + +int MIDIbuf_read(int dev, struct file *file, char *buf, int count) +{ + int n, c = 0; + unsigned long flags; + unsigned char tmp_data; + + dev = dev >> 4; + + save_flags(flags); + cli(); + + if (!DATA_AVAIL(midi_in_buf[dev])) { /* + * No data yet, wait + */ + if (file->f_flags & O_NONBLOCK) { + restore_flags(flags); + return -EAGAIN; + } + interruptible_sleep_on_timeout(&input_sleeper[dev], + parms[dev].prech_timeout); + + if (signal_pending(current)) + c = -EINTR; /* The user is getting restless */ + } + if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /* + * Got some bytes + */ + { + n = DATA_AVAIL(midi_in_buf[dev]); + if (n > count) + n = count; + c = 0; + + while (c < n) + { + char *fixit; + REMOVE_BYTE(midi_in_buf[dev], tmp_data); + fixit = (char *) &tmp_data; + /* BROKE BROKE BROKE */ + copy_to_user(&(buf)[c], fixit, 1); + c++; + } + } + restore_flags(flags); + return c; +} + +int MIDIbuf_ioctl(int dev, struct file *file, + unsigned int cmd, caddr_t arg) +{ + int val; + + dev = dev >> 4; + + if (((cmd >> 8) & 0xff) == 'C') + { + if (midi_devs[dev]->coproc) /* Coprocessor ioctl */ + return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0); +/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/ + return -ENXIO; + } + else + { + switch (cmd) + { + case SNDCTL_MIDI_PRETIME: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 0) + val = 0; + val = (HZ * val) / 10; + parms[dev].prech_timeout = val; + return put_user(val, (int *)arg); + + default: + if (!midi_devs[dev]->ioctl) + return -EINVAL; + return midi_devs[dev]->ioctl(dev, cmd, arg); + } + } +} + +/* No kernel lock - fine */ +unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + + dev = dev >> 4; + + /* input */ + poll_wait(file, &input_sleeper[dev], wait); + if (DATA_AVAIL(midi_in_buf[dev])) + mask |= POLLIN | POLLRDNORM; + + /* output */ + poll_wait(file, &midi_sleeper[dev], wait); + if (!SPACE_AVAIL(midi_out_buf[dev])) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + + +void MIDIbuf_init(void) +{ + /* drag in midi_syms.o */ + { + extern char midi_syms_symbol; + midi_syms_symbol = 0; + } +} + +int MIDIbuf_avail(int dev) +{ + if (midi_in_buf[dev]) + return DATA_AVAIL (midi_in_buf[dev]); + return 0; +} diff -Nru linux/sound/oss/mpu401.c linux-2.4.19-pre5-mjc/sound/oss/mpu401.c --- linux/sound/oss/mpu401.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/mpu401.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1797 @@ +/* + * sound/mpu401.c + * + * The low level driver for Roland MPU-401 compatible Midi cards. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, use normal request_irq, use dev_id + * Bartlomiej Zolnierkiewicz removed some __init to allow using many drivers + * Chris Rankin Update the module-usage counter for the coprocessor + */ + +#include +#include + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" + +#include "coproc.h" +#include "mpu401.h" + +static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; + +struct mpu_config +{ + int base; /* + * I/O base + */ + int irq; + int opened; /* + * Open mode + */ + int devno; + int synthno; + int uart_mode; + int initialized; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + unsigned char version, revision; + unsigned int capabilities; +#define MPU_CAP_INTLG 0x10000000 +#define MPU_CAP_SYNC 0x00000010 +#define MPU_CAP_FSK 0x00000020 +#define MPU_CAP_CLS 0x00000040 +#define MPU_CAP_SMPTE 0x00000080 +#define MPU_CAP_2PORT 0x00000001 + int timer_flag; + +#define MBUF_MAX 10 +#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ + {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} + int m_busy; + unsigned char m_buf[MBUF_MAX]; + int m_ptr; + int m_state; + int m_left; + unsigned char last_status; + void (*inputintr) (int dev, unsigned char data); + int shared_irq; + int *osp; + }; + +#define DATAPORT(base) (base) +#define COMDPORT(base) (base+1) +#define STATPORT(base) (base+1) + + +static void mpu401_close(int dev); + +static int mpu401_status(struct mpu_config *devc) +{ + return inb(STATPORT(devc->base)); +} + +#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL)) +#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY)) + +static void write_command(struct mpu_config *devc, unsigned char cmd) +{ + outb(cmd, COMDPORT(devc->base)); +} + +static int read_data(struct mpu_config *devc) +{ + return inb(DATAPORT(devc->base)); +} + +static void write_data(struct mpu_config *devc, unsigned char byte) +{ + outb(byte, DATAPORT(devc->base)); +} + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static struct mpu_config dev_conf[MAX_MIDI_DEV] = +{ + {0} +}; + +static int n_mpu_devs = 0; + +static int reset_mpu401(struct mpu_config *devc); +static void set_uart_mode(int dev, struct mpu_config *devc, int arg); + +static int mpu_timer_init(int midi_dev); +static void mpu_timer_interrupt(void); +static void timer_ext_event(struct mpu_config *devc, int event, int parm); + +static struct synth_info mpu_synth_info_proto = { + "MPU-401 MIDI interface", + 0, + SYNTH_TYPE_MIDI, + MIDI_TYPE_MPU401, + 0, 128, + 0, 128, + SYNTH_CAP_INPUT +}; + +static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; + +/* + * States for the input scanner + */ + +#define ST_INIT 0 /* Ready for timing byte or msg */ +#define ST_TIMED 1 /* Leading timing byte rcvd */ +#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */ + +#define ST_SYSMSG 100 /* System message (sysx etc). */ +#define ST_SYSEX 101 /* System exclusive msg */ +#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define ST_SONGSEL 103 /* Song select */ +#define ST_SONGPOS 104 /* Song position pointer */ + +static unsigned char len_tab[] = /* # of data bytes following a status + */ +{ + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ +}; + +#define STORE(cmd) \ +{ \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ +} + +#define _seqbuf obuf +#define _seqbufptr 0 +#define _SEQ_ADVBUF(x) len=x + +static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic) +{ + + switch (devc->m_state) + { + case ST_INIT: + switch (midic) + { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + printk(""); + break; + + case 0xfd: + if (devc->timer_flag) + mpu_timer_interrupt(); + break; + + case 0xfe: + return MPU_ACK; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + printk("", midic & 0x0f); + break; + + case 0xf9: + printk(""); + break; + + case 0xff: + devc->m_state = ST_SYSMSG; + break; + + default: + if (midic <= 0xef) + { + /* printk( "mpu time: %d ", midic); */ + devc->m_state = ST_TIMED; + } + else + printk(" ", midic); + } + break; + + case ST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + devc->m_state = ST_DATABYTE; + + if (msg < 8) /* Data byte */ + { + /* printk( "midi msg (running status) "); */ + msg = ((int) (devc->last_status & 0xf0) >> 4); + msg -= 8; + devc->m_left = len_tab[msg] - 1; + + devc->m_ptr = 2; + devc->m_buf[0] = devc->last_status; + devc->m_buf[1] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + else if (msg == 0xf) /* MPU MARK */ + { + devc->m_state = ST_INIT; + + switch (midic) + { + case 0xf8: + /* printk( "NOP "); */ + break; + + case 0xf9: + /* printk( "meas end "); */ + break; + + case 0xfc: + /* printk( "data end "); */ + break; + + default: + printk("Unknown MPU mark %02x\n", midic); + } + } + else + { + devc->last_status = midic; + /* printk( "midi msg "); */ + msg -= 8; + devc->m_left = len_tab[msg]; + + devc->m_ptr = 1; + devc->m_buf[0] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + } + break; + + case ST_SYSMSG: + switch (midic) + { + case 0xf0: + printk(""); + devc->m_state = ST_SYSEX; + break; + + case 0xf1: + devc->m_state = ST_MTC; + break; + + case 0xf2: + devc->m_state = ST_SONGPOS; + devc->m_ptr = 0; + break; + + case 0xf3: + devc->m_state = ST_SONGSEL; + break; + + case 0xf6: + /* printk( "tune_request\n"); */ + devc->m_state = ST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CLOCK, 0); + break; + + case 0xfA: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_START, 0); + break; + + case 0xFB: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CONTINUE, 0); + break; + + case 0xFC: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_STOP, 0); + break; + + case 0xFE: + /* active sensing */ + devc->m_state = ST_INIT; + break; + + case 0xff: + /* printk( "midi hard reset"); */ + devc->m_state = ST_INIT; + break; + + default: + printk("unknown MIDI sysmsg %0x\n", midic); + devc->m_state = ST_INIT; + } + break; + + case ST_MTC: + devc->m_state = ST_INIT; + printk("MTC frame %x02\n", midic); + break; + + case ST_SYSEX: + if (midic == 0xf7) + { + printk(""); + devc->m_state = ST_INIT; + } + else + printk("%02x ", midic); + break; + + case ST_SONGPOS: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if (devc->m_ptr == 2) + { + devc->m_state = ST_INIT; + devc->m_ptr = 0; + timer_ext_event(devc, TMR_SPP, + ((devc->m_buf[1] & 0x7f) << 7) | + (devc->m_buf[0] & 0x7f)); + } + break; + + case ST_DATABYTE: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if ((--devc->m_left) <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + break; + + default: + printk("Bad state %d ", devc->m_state); + devc->m_state = ST_INIT; + } + return 1; +} + +static void mpu401_input_loop(struct mpu_config *devc) +{ + unsigned long flags; + int busy; + int n; + + save_flags(flags); + cli(); + busy = devc->m_busy; + devc->m_busy = 1; + restore_flags(flags); + + if (busy) /* Already inside the scanner */ + return; + + n = 50; + + while (input_avail(devc) && n-- > 0) + { + unsigned char c = read_data(devc); + + if (devc->mode == MODE_SYNTH) + { + mpu_input_scanner(devc, c); + } + else if (devc->opened & OPEN_READ && devc->inputintr != NULL) + devc->inputintr(devc->devno, c); + } + devc->m_busy = 0; +} + +int intchk_mpu401(void *dev_id) +{ + struct mpu_config *devc; + int dev = (int) dev_id; + + devc = &dev_conf[dev]; + return input_avail(devc); +} + +void mpuintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + struct mpu_config *devc; + int dev = (int) dev_id; + + sti(); + devc = &dev_conf[dev]; + + if (input_avail(devc)) + { + if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) + mpu401_input_loop(devc); + else + { + /* Dummy read (just to acknowledge the interrupt) */ + read_data(devc); + } + } +} + +static int mpu401_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int err; + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return -ENXIO; + + devc = &dev_conf[dev]; + + if (devc->opened) + return -EBUSY; + /* + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. + */ + + if (!devc->initialized) + { + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); + } + + if ( (coprocessor = midi_devs[dev]->coproc) != NULL ) + { + if (coprocessor->owner) + __MOD_INC_USE_COUNT(coprocessor->owner); + + if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) + { + printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n"); + mpu401_close(dev); + return err; + } + } + + set_uart_mode(dev, devc, 1); + devc->mode = MODE_MIDI; + devc->synthno = 0; + + mpu401_input_loop(devc); + + devc->inputintr = input; + devc->opened = mode; + + return 0; +} + +static void mpu401_close(int dev) +{ + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + devc = &dev_conf[dev]; + if (devc->uart_mode) + reset_mpu401(devc); /* + * This disables the UART mode + */ + devc->mode = 0; + devc->inputintr = NULL; + + coprocessor = midi_devs[dev]->coproc; + if (coprocessor) { + coprocessor->close(coprocessor->devc, COPR_MIDI); + + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + } + devc->opened = 0; +} + +static int mpu401_out(int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + + save_flags(flags); + cli(); + if (!output_ready(devc)) + { + printk(KERN_WARNING "mpu401: Send data timeout\n"); + restore_flags(flags); + return 0; + } + write_data(devc, midi_byte); + restore_flags(flags); + return 1; +} + +static int mpu401_command(int dev, mpu_command_rec * cmd) +{ + int i, timeout, ok; + int ret = 0; + unsigned long flags; + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + if (devc->uart_mode) /* + * Not possible in UART mode + */ + { + printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n"); + return -EINVAL; + } + /* + * Test for input since pending input seems to block the output. + */ + if (input_avail(devc)) + mpu401_input_loop(devc); + + /* + * Sometimes it takes about 50000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + timeout = 50000; +retry: + if (timeout-- <= 0) + { + printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd); + return -EIO; + } + save_flags(flags); + cli(); + + if (!output_ready(devc)) + { + restore_flags(flags); + goto retry; + } + write_command(devc, cmd->cmd); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + { + if (input_avail(devc)) + { + if (devc->opened && devc->mode == MODE_SYNTH) + { + if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) + ok = 1; + } + else + { + /* Device is not currently open. Use simpler method */ + if (read_data(devc) == MPU_ACK) + ok = 1; + } + } + } + if (!ok) + { + restore_flags(flags); + return -EIO; + } + if (cmd->nr_args) + { + for (i = 0; i < cmd->nr_args; i++) + { + for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); + + if (!mpu401_out(dev, cmd->data[i])) + { + restore_flags(flags); + printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd); + return -EIO; + } + } + } + ret = 0; + cmd->data[0] = 0; + + if (cmd->nr_returns) + { + for (i = 0; i < cmd->nr_returns; i++) + { + ok = 0; + for (timeout = 5000; timeout > 0 && !ok; timeout--) + if (input_avail(devc)) + { + cmd->data[i] = read_data(devc); + ok = 1; + } + if (!ok) + { + restore_flags(flags); + return -EIO; + } + } + } + restore_flags(flags); + return ret; +} + +static int mpu_cmd(int dev, int cmd, int data) +{ + int ret; + + static mpu_command_rec rec; + + rec.cmd = cmd & 0xff; + rec.nr_args = ((cmd & 0xf0) == 0xE0); + rec.nr_returns = ((cmd & 0xf0) == 0xA0); + rec.data[0] = data & 0xff; + + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + return (unsigned char) rec.data[0]; +} + +static int mpu401_prefix_cmd(int dev, unsigned char status) +{ + struct mpu_config *devc = &dev_conf[dev]; + + if (devc->uart_mode) + return 1; + + if (status < 0xf0) + { + if (mpu_cmd(dev, 0xD0, 0) < 0) + return 0; + return 1; + } + switch (status) + { + case 0xF0: + if (mpu_cmd(dev, 0xDF, 0) < 0) + return 0; + return 1; + + default: + return 0; + } +} + +static int mpu401_start_read(int dev) +{ + return 0; +} + +static int mpu401_end_read(int dev) +{ + return 0; +} + +static int mpu401_ioctl(int dev, unsigned cmd, caddr_t arg) +{ + struct mpu_config *devc; + mpu_command_rec rec; + int val, ret; + + devc = &dev_conf[dev]; + switch (cmd) + { + case SNDCTL_MIDI_MPUMODE: + if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ + printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n"); + return -EINVAL; + } + if (get_user(val, (int *)arg)) + return -EFAULT; + set_uart_mode(dev, devc, !val); + return 0; + + case SNDCTL_MIDI_MPUCMD: + if (copy_from_user(&rec, arg, sizeof(rec))) + return -EFAULT; + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + if (copy_to_user(arg, &rec, sizeof(rec))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } +} + +static void mpu401_kick(int dev) +{ +} + +static int mpu401_buffer_status(int dev) +{ + return 0; /* + * No data in buffers + */ +} + +static int mpu_synth_ioctl(int dev, + unsigned int cmd, caddr_t arg) +{ + int midi_dev; + struct mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) + return -ENXIO; + + devc = &dev_conf[midi_dev]; + + switch (cmd) + { + + case SNDCTL_SYNTH_INFO: + memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info)); + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + default: + return -EINVAL; + } +} + +static int mpu_synth_open(int dev, int mode) +{ + int midi_dev, err; + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) + return -ENXIO; + + devc = &dev_conf[midi_dev]; + + /* + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. + */ + + if (!devc->initialized) + { + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); + } + if (devc->opened) + return -EBUSY; + devc->mode = MODE_SYNTH; + devc->synthno = dev; + + devc->inputintr = NULL; + + coprocessor = midi_devs[midi_dev]->coproc; + if (coprocessor) { + if (coprocessor->owner) + __MOD_INC_USE_COUNT(coprocessor->owner); + + if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) + { + printk(KERN_WARNING "mpu401: Can't access coprocessor device\n"); + return err; + } + } + devc->opened = mode; + reset_mpu401(devc); + + if (mode & OPEN_READ) + { + mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ + mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ + mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ + } + return 0; +} + +static void mpu_synth_close(int dev) +{ + int midi_dev; + struct mpu_config *devc; + struct coproc_operations *coprocessor; + + midi_dev = synth_devs[dev]->midi_dev; + + devc = &dev_conf[midi_dev]; + mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ + mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */ + + devc->inputintr = NULL; + + coprocessor = midi_devs[midi_dev]->coproc; + if (coprocessor) { + coprocessor->close(coprocessor->devc, COPR_MIDI); + + if (coprocessor->owner) + __MOD_DEC_USE_COUNT(coprocessor->owner); + } + devc->opened = 0; + devc->mode = 0; +} + +#define MIDI_SYNTH_NAME "MPU-401 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations mpu401_synth_proto = +{ + owner: THIS_MODULE, + id: "MPU401", + info: NULL, + midi_dev: 0, + synth_type: SYNTH_TYPE_MIDI, + synth_subtype: 0, + open: mpu_synth_open, + close: mpu_synth_close, + ioctl: mpu_synth_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + hw_control: midi_synth_hw_control, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice, + send_sysex: midi_synth_send_sysex +}; + +static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV]; + +static struct midi_operations mpu401_midi_proto = +{ + owner: THIS_MODULE, + info: {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + in_info: {0}, + open: mpu401_open, + close: mpu401_close, + ioctl: mpu401_ioctl, + outputc: mpu401_out, + start_read: mpu401_start_read, + end_read: mpu401_end_read, + kick: mpu401_kick, + buffer_status: mpu401_buffer_status, + prefix_cmd: mpu401_prefix_cmd +}; + +static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; + +static void mpu401_chk_version(int n, struct mpu_config *devc) +{ + int tmp; + unsigned long flags; + + devc->version = devc->revision = 0; + + save_flags(flags); + cli(); + if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0) + { + restore_flags(flags); + return; + } + if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ + { + restore_flags(flags); + return; + } + devc->version = tmp; + + if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) + { + devc->version = 0; + restore_flags(flags); + return; + } + devc->revision = tmp; + restore_flags(flags); +} + +void attach_mpu401(struct address_info *hw_config, struct module *owner) +{ + unsigned long flags; + char revision_char; + + int m; + struct mpu_config *devc; + + hw_config->slots[1] = -1; + m = sound_alloc_mididev(); + if (m == -1) + { + printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); + return; + } + devc = &dev_conf[m]; + devc->base = hw_config->io_base; + devc->osp = hw_config->osp; + devc->irq = hw_config->irq; + devc->opened = 0; + devc->uart_mode = 0; + devc->initialized = 0; + devc->version = 0; + devc->revision = 0; + devc->capabilities = 0; + devc->timer_flag = 0; + devc->m_busy = 0; + devc->m_state = ST_INIT; + devc->shared_irq = hw_config->always_detect; + devc->irq = hw_config->irq; + + if (devc->irq < 0) + { + devc->irq *= -1; + devc->shared_irq = 1; + } + + if (!hw_config->always_detect) + { + /* Verify the hardware again */ + if (!reset_mpu401(devc)) + { + printk(KERN_WARNING "mpu401: Device didn't respond\n"); + sound_unload_mididev(m); + return; + } + if (!devc->shared_irq) + { + if (request_irq(devc->irq, mpuintr, 0, "mpu401", (void *)m) < 0) + { + printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); + sound_unload_mididev(m); + return; + } + } + save_flags(flags); + cli(); + mpu401_chk_version(m, devc); + if (devc->version == 0) + mpu401_chk_version(m, devc); + restore_flags(flags); + } + request_region(hw_config->io_base, 2, "mpu401"); + + if (devc->version != 0) + if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */ + if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */ + devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ + + + mpu401_synth_operations[m] = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + + if (mpu401_synth_operations[m] == NULL) + { + sound_unload_mididev(m); + printk(KERN_ERR "mpu401: Can't allocate memory\n"); + return; + } + if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &std_midi_synth, + sizeof(struct synth_operations)); + } + else + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &mpu401_synth_proto, + sizeof(struct synth_operations)); + } + if (owner) + mpu401_synth_operations[m]->owner = owner; + + memcpy((char *) &mpu401_midi_operations[m], + (char *) &mpu401_midi_proto, + sizeof(struct midi_operations)); + + mpu401_midi_operations[m].converter = mpu401_synth_operations[m]; + + memcpy((char *) &mpu_synth_info[m], + (char *) &mpu_synth_info_proto, + sizeof(struct synth_info)); + + n_mpu_devs++; + + if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ + { + int ports = (devc->revision & 0x08) ? 32 : 16; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | + MPU_CAP_CLS | MPU_CAP_2PORT; + + revision_char = (devc->revision == 0x7f) ? 'M' : ' '; + sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d", + ports, + revision_char, + n_mpu_devs); + } + else + { + revision_char = devc->revision ? devc->revision + '@' : ' '; + if ((int) devc->revision > ('Z' - '@')) + revision_char = '+'; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; + + if (hw_config->name) + sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); + else + sprintf(mpu_synth_info[m].name, + "MPU-401 %d.%d%c Midi interface #%d", + (int) (devc->version & 0xf0) >> 4, + devc->version & 0x0f, + revision_char, + n_mpu_devs); + } + + strcpy(mpu401_midi_operations[m].info.name, + mpu_synth_info[m].name); + + conf_printf(mpu_synth_info[m].name, hw_config); + + mpu401_synth_operations[m]->midi_dev = devc->devno = m; + mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno]; + + if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ + hw_config->slots[2] = mpu_timer_init(m); + + midi_devs[m] = &mpu401_midi_operations[devc->devno]; + + if (owner) + midi_devs[m]->owner = owner; + + hw_config->slots[1] = m; + sequencer_init(); +} + +static int reset_mpu401(struct mpu_config *devc) +{ + unsigned long flags; + int ok, timeout, n; + int timeout_limit; + + /* + * Send the RESET command. Try again if no success at the first time. + * (If the device is in the UART mode, it will not ack the reset cmd). + */ + + ok = 0; + + timeout_limit = devc->initialized ? 30000 : 100000; + devc->initialized = 1; + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) + ok = output_ready(devc); + + write_command(devc, MPU_RESET); /* + * Send MPU-401 RESET Command + */ + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) + { + save_flags(flags); + cli(); + if (input_avail(devc)) + if (read_data(devc) == MPU_ACK) + ok = 1; + restore_flags(flags); + } + + } + + devc->m_state = ST_INIT; + devc->m_ptr = 0; + devc->m_left = 0; + devc->last_status = 0; + devc->uart_mode = 0; + + return ok; +} + +static void set_uart_mode(int dev, struct mpu_config *devc, int arg) +{ + if (!arg && (devc->capabilities & MPU_CAP_INTLG)) + return; + if ((devc->uart_mode == 0) == (arg == 0)) + return; /* Already set */ + reset_mpu401(devc); /* This exits the uart mode */ + + if (arg) + { + if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) + { + printk(KERN_ERR "mpu401: Can't enter UART mode\n"); + devc->uart_mode = 0; + return; + } + } + devc->uart_mode = arg; + +} + +int probe_mpu401(struct address_info *hw_config) +{ + int ok = 0; + struct mpu_config tmp_devc; + + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base); + return 0; + } + tmp_devc.base = hw_config->io_base; + tmp_devc.irq = hw_config->irq; + tmp_devc.initialized = 0; + tmp_devc.opened = 0; + tmp_devc.osp = hw_config->osp; + + if (hw_config->always_detect) + return 1; + + if (inb(hw_config->io_base + 1) == 0xff) + { + DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base)); + return 0; /* Just bus float? */ + } + ok = reset_mpu401(&tmp_devc); + + if (!ok) + { + DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base)); + } + return ok; +} + +void unload_mpu401(struct address_info *hw_config) +{ + void *p; + int n=hw_config->slots[1]; + + release_region(hw_config->io_base, 2); + if (hw_config->always_detect == 0 && hw_config->irq > 0) + free_irq(hw_config->irq, (void *)n); + p=mpu401_synth_operations[n]; + sound_unload_mididev(n); + sound_unload_timerdev(hw_config->slots[2]); + if(p) + kfree(p); +} + +/***************************************************** + * Timer stuff + ****************************************************/ + +static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; +static volatile int curr_tempo, curr_timebase, hw_timebase; +static int max_timebase = 8; /* 8*24=192 ppqn */ +static volatile unsigned long next_event_time; +static volatile unsigned long curr_ticks, curr_clocks; +static unsigned long prev_event_time; +static int metronome_mode; + +static unsigned long clocks2ticks(unsigned long clocks) +{ + /* + * The MPU-401 supports just a limited set of possible timebase values. + * Since the applications require more choices, the driver has to + * program the HW to do its best and to convert between the HW and + * actual timebases. + */ + return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; +} + +static void set_timebase(int midi_dev, int val) +{ + int hw_val; + + if (val < 48) + val = 48; + if (val > 1000) + val = 1000; + + hw_val = val; + hw_val = (hw_val + 12) / 24; + if (hw_val > max_timebase) + hw_val = max_timebase; + + if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) + { + printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24); + return; + } + hw_timebase = hw_val * 24; + curr_timebase = val; + +} + +static void tmr_reset(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = curr_clocks = 0; + restore_flags(flags); +} + +static void set_timer_mode(int midi_dev) +{ + if (timer_mode & TMR_MODE_CLS) + mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ + + if (timer_mode & TMR_INTERNAL) + { + mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */ + } + else + { + if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) + { + mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */ + mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ + } + else if (timer_mode & TMR_MODE_FSK) + mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */ + } +} + +static void stop_metronome(int midi_dev) +{ + mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ +} + +static void setup_metronome(int midi_dev) +{ + int numerator, denominator; + int clks_per_click, num_32nds_per_beat; + int beats_per_measure; + + numerator = ((unsigned) metronome_mode >> 24) & 0xff; + denominator = ((unsigned) metronome_mode >> 16) & 0xff; + clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; + num_32nds_per_beat = (unsigned) metronome_mode & 0xff; + beats_per_measure = (numerator * 4) >> denominator; + + if (!metronome_mode) + mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ + else + { + mpu_cmd(midi_dev, 0xE4, clks_per_click); + mpu_cmd(midi_dev, 0xE6, beats_per_measure); + mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */ + } +} + +static int mpu_start_timer(int midi_dev) +{ + tmr_reset(); + set_timer_mode(midi_dev); + + if (tmr_running) + return TIMER_NOT_ARMED; /* Already running */ + + if (timer_mode & TMR_INTERNAL) + { + mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */ + tmr_running = 1; + return TIMER_NOT_ARMED; + } + else + { + mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */ + mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */ + mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */ + mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ + } + return TIMER_ARMED; +} + +static int mpu_timer_open(int dev, int mode) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + if (timer_open) + return -EBUSY; + + tmr_reset(); + curr_tempo = 50; + mpu_cmd(midi_dev, 0xE0, 50); + curr_timebase = hw_timebase = 120; + set_timebase(midi_dev, 120); + timer_open = 1; + metronome_mode = 0; + set_timer_mode(midi_dev); + + mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */ + mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */ + + return 0; +} + +static void mpu_timer_close(int dev) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + timer_open = tmr_running = 0; + mpu_cmd(midi_dev, 0x15, 0); /* Stop all */ + mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */ + mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */ + stop_metronome(midi_dev); +} + +static int mpu_timer_event(int dev, unsigned char *event) +{ + unsigned char command = event[1]; + unsigned long parm = *(unsigned int *) &event[4]; + int midi_dev = sound_timer_devs[dev]->devlink; + + switch (command) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + if (tmr_running) + break; + return mpu_start_timer(midi_dev); + + case TMR_STOP: + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + tmr_running = 0; + break; + + case TMR_CONTINUE: + if (tmr_running) + break; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + setup_metronome(midi_dev); + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + if (mpu_cmd(midi_dev, 0xE0, parm) < 0) + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm); + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + case TMR_TIMESIG: + if (metronome_mode) /* Metronome enabled */ + { + metronome_mode = parm; + setup_metronome(midi_dev); + } + break; + + default:; + } + return TIMER_NOT_ARMED; +} + +static unsigned long mpu_timer_get_time(int dev) +{ + if (!timer_open) + return 0; + + return curr_ticks; +} + +static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + switch (command) + { + case SNDCTL_TMR_SOURCE: + { + int parm; + + parm = *(int *) arg; + parm &= timer_caps; + + if (parm != 0) + { + timer_mode = parm; + + if (timer_mode & TMR_MODE_CLS) + mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ + } + return (*(int *) arg = timer_mode); + } + break; + + case SNDCTL_TMR_START: + mpu_start_timer(midi_dev); + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + return 0; + + case SNDCTL_TMR_CONTINUE: + if (tmr_running) + return 0; + tmr_running = 1; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + return 0; + + case SNDCTL_TMR_TIMEBASE: + { + int val; + + val = *(int *) arg; + if (val) + set_timebase(midi_dev, val); + return (*(int *) arg = curr_timebase); + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val; + int ret; + + val = *(int *) arg; + + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) + { + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val); + return ret; + } + curr_tempo = val; + } + return (*(int *) arg = curr_tempo); + } + break; + + case SNDCTL_SEQ_CTRLRATE: + { + int val; + + val = *(int *) arg; + if (val != 0) /* Can't change */ + return -EINVAL; + return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60); + } + break; + + case SNDCTL_SEQ_GETTIME: + return (*(int *) arg = curr_ticks); + + case SNDCTL_TMR_METRONOME: + metronome_mode = *(int *) arg; + setup_metronome(midi_dev); + return 0; + + default:; + } + return -EINVAL; +} + +static void mpu_timer_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + next_event_time = prev_event_time = time; + return; +} + +static struct sound_timer_operations mpu_timer = +{ + owner: THIS_MODULE, + info: {"MPU-401 Timer", 0}, + priority: 10, /* Priority */ + devlink: 0, /* Local device link */ + open: mpu_timer_open, + close: mpu_timer_close, + event: mpu_timer_event, + get_time: mpu_timer_get_time, + ioctl: mpu_timer_ioctl, + arm_timer: mpu_timer_arm +}; + +static void mpu_timer_interrupt(void) +{ + if (!timer_open) + return; + + if (!tmr_running) + return; + + curr_clocks++; + curr_ticks = clocks2ticks(curr_clocks); + + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } +} + +static void timer_ext_event(struct mpu_config *devc, int event, int parm) +{ + int midi_dev = devc->devno; + + if (!devc->timer_flag) + return; + + switch (event) + { + case TMR_CLOCK: + printk(""); + break; + + case TMR_START: + printk("Ext MIDI start\n"); + if (!tmr_running) + { + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome(midi_dev); + next_event_time = 0; + STORE(SEQ_START_TIMER()); + } + } + break; + + case TMR_STOP: + printk("Ext MIDI stop\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 0; + stop_metronome(midi_dev); + STORE(SEQ_STOP_TIMER()); + } + break; + + case TMR_CONTINUE: + printk("Ext MIDI continue\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome(midi_dev); + STORE(SEQ_CONTINUE_TIMER()); + } + break; + + case TMR_SPP: + printk("Songpos: %d\n", parm); + if (timer_mode & TMR_EXTERNAL) + { + STORE(SEQ_SONGPOS(parm)); + } + break; + } +} + +static int mpu_timer_init(int midi_dev) +{ + struct mpu_config *devc; + int n; + + devc = &dev_conf[midi_dev]; + + if (timer_initialized) + return -1; /* There is already a similar timer */ + + timer_initialized = 1; + + mpu_timer.devlink = midi_dev; + dev_conf[midi_dev].timer_flag = 1; + + n = sound_alloc_timerdev(); + if (n == -1) + n = 0; + sound_timer_devs[n] = &mpu_timer; + + if (devc->version < 0x20) /* Original MPU-401 */ + timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; + else + { + /* + * The version number 2.0 is used (at least) by the + * MusicQuest cards and the Roland Super-MPU. + * + * MusicQuest has given a special meaning to the bits of the + * revision number. The Super-MPU returns 0. + */ + + if (devc->revision) + timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; + + if (devc->revision & 0x02) + timer_caps |= TMR_MODE_CLS; + + + if (devc->revision & 0x40) + max_timebase = 10; /* Has the 216 and 240 ppqn modes */ + } + + timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; + return n; + +} + +EXPORT_SYMBOL(probe_mpu401); +EXPORT_SYMBOL(attach_mpu401); +EXPORT_SYMBOL(unload_mpu401); +EXPORT_SYMBOL(intchk_mpu401); +EXPORT_SYMBOL(mpuintr); + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(irq, "i"); +MODULE_PARM(io, "i"); + +int __init init_mpu401(void) +{ + /* Can be loaded either for module use or to provide functions + to others */ + if (io != -1 && irq != -1) { + cfg.irq = irq; + cfg.io_base = io; + if (probe_mpu401(&cfg) == 0) + return -ENODEV; + attach_mpu401(&cfg, THIS_MODULE); + } + + return 0; +} + +void __exit cleanup_mpu401(void) +{ + if (io != -1 && irq != -1) { + /* Check for use by, for example, sscape driver */ + unload_mpu401(&cfg); + } +} + +module_init(init_mpu401); +module_exit(cleanup_mpu401); + +#ifndef MODULE +static int __init setup_mpu401(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} + +__setup("mpu401=", setup_mpu401); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/mpu401.h linux-2.4.19-pre5-mjc/sound/oss/mpu401.h --- linux/sound/oss/mpu401.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/mpu401.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,20 @@ +/* + * uart401.h + * + * Copyright: Christoph Hellwig + * + */ + +/* From uart401.c */ +int probe_uart401 (struct address_info *hw_config, struct module *owner); +void unload_uart401 (struct address_info *hw_config); + +void uart401intr (int irq, void *dev_id, struct pt_regs * dummy); + +/* From mpu401.c */ +int probe_mpu401(struct address_info *hw_config); +void attach_mpu401(struct address_info * hw_config, struct module *owner); +void unload_mpu401(struct address_info *hw_info); + +int intchk_mpu401(void *dev_id); +void mpuintr(int irq, void *dev_id, struct pt_regs * dummy); diff -Nru linux/sound/oss/msnd.c linux-2.4.19-pre5-mjc/sound/oss/msnd.c --- linux/sound/oss/msnd.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/msnd.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,406 @@ +/********************************************************************* + * + * msnd.c - Driver Base + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Copyright (C) 1998 Andrew Veliath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $ + * + ********************************************************************/ + +#include +#if LINUX_VERSION_CODE < 0x020101 +# define LINUX20 +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef LINUX20 +# include +# include +# include +# include +# include "sound_config.h" +#else +# include +# include +# include +# include +#endif +#include +#include "msnd.h" + +#define LOGNAME "msnd" + +#define MSND_MAX_DEVS 4 + +static multisound_dev_t *devs[MSND_MAX_DEVS]; +static int num_devs; + +int __init msnd_register(multisound_dev_t *dev) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS; ++i) + if (devs[i] == NULL) + break; + + if (i == MSND_MAX_DEVS) + return -ENOMEM; + + devs[i] = dev; + ++num_devs; + + MOD_INC_USE_COUNT; + + return 0; +} + +void msnd_unregister(multisound_dev_t *dev) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS; ++i) + if (devs[i] == dev) + break; + + if (i == MSND_MAX_DEVS) { + printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n"); + return; + } + + devs[i] = NULL; + --num_devs; + + MOD_DEC_USE_COUNT; +} + +int msnd_get_num_devs(void) +{ + return num_devs; +} + +multisound_dev_t *msnd_get_dev(int j) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS && j; ++i) + if (devs[i] != NULL) + --j; + + if (i == MSND_MAX_DEVS || j != 0) + return NULL; + + return devs[i]; +} + +void msnd_init_queue(unsigned long base, int start, int size) +{ + isa_writew(PCTODSP_BASED(start), base + JQS_wStart); + isa_writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize); + isa_writew(0, base + JQS_wHead); + isa_writew(0, base + JQS_wTail); +} + +void msnd_fifo_init(msnd_fifo *f) +{ + f->data = NULL; +} + +void msnd_fifo_free(msnd_fifo *f) +{ + if (f->data) { + vfree(f->data); + f->data = NULL; + } +} + +int msnd_fifo_alloc(msnd_fifo *f, size_t n) +{ + msnd_fifo_free(f); + f->data = (char *)vmalloc(n); + f->n = n; + f->tail = 0; + f->head = 0; + f->len = 0; + + if (!f->data) + return -ENOMEM; + + return 0; +} + +void msnd_fifo_make_empty(msnd_fifo *f) +{ + f->len = f->tail = f->head = 0; +} + +int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == f->n) + return 0; + + while ((count < len) && (f->len != f->n)) { + + int nwritten; + + if (f->head <= f->tail) { + nwritten = len - count; + if (nwritten > f->n - f->tail) + nwritten = f->n - f->tail; + } + else { + nwritten = f->head - f->tail; + if (nwritten > len - count) + nwritten = len - count; + } + + if (user) { + if (copy_from_user(f->data + f->tail, buf, nwritten)) + return -EFAULT; + } else + isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten); + + count += nwritten; + buf += nwritten; + f->len += nwritten; + f->tail += nwritten; + f->tail %= f->n; + } + + return count; +} + +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == 0) + return f->len; + + while ((count < len) && (f->len > 0)) { + + int nread; + + if (f->tail <= f->head) { + nread = len - count; + if (nread > f->n - f->head) + nread = f->n - f->head; + } + else { + nread = f->tail - f->head; + if (nread > len - count) + nread = len - count; + } + + if (user) { + if (copy_to_user(buf, f->data + f->head, nread)) + return -EFAULT; + } else + isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread); + + count += nread; + buf += nread; + f->len -= nread; + f->head += nread; + f->head %= f->n; + } + + return count; +} + +int msnd_wait_TXDE(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 1000; + + while(timeout-- > 0) + if (inb(io + HP_ISR) & HPISR_TXDE) + return 0; + + return -EIO; +} + +int msnd_wait_HC0(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 1000; + + while(timeout-- > 0) + if (!(inb(io + HP_CVR) & HPCVR_HC)) + return 0; + + return -EIO; +} + +int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (msnd_wait_HC0(dev) == 0) { + outb(cmd, dev->io + HP_CVR); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n"); + + return -EIO; +} + +int msnd_send_word(multisound_dev_t *dev, unsigned char high, + unsigned char mid, unsigned char low) +{ + register unsigned int io = dev->io; + + if (msnd_wait_TXDE(dev) == 0) { + outb(high, io + HP_TXH); + outb(mid, io + HP_TXM); + outb(low, io + HP_TXL); + return 0; + } + + printk(KERN_DEBUG LOGNAME ": Send host word timeout\n"); + + return -EIO; +} + +int msnd_upload_host(multisound_dev_t *dev, char *bin, int len) +{ + int i; + + if (len % 3 != 0) { + printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n"); + return -EINVAL; + } + + for (i = 0; i < len; i += 3) + if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0) + return -EIO; + + inb(dev->io + HP_RXL); + inb(dev->io + HP_CVR); + + return 0; +} + +int msnd_enable_irq(multisound_dev_t *dev) +{ + unsigned long flags; + + if (dev->irq_ref++) + return 0; + + printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n"); + + spin_lock_irqsave(&dev->lock, flags); + if (msnd_wait_TXDE(dev) == 0) { + outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); + if (dev->type == msndClassic) + outb(dev->irqid, dev->io + HP_IRQM); + outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); + outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); + enable_irq(dev->irq); + msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n"); + + return -EIO; +} + +int msnd_disable_irq(multisound_dev_t *dev) +{ + unsigned long flags; + + if (--dev->irq_ref > 0) + return 0; + + if (dev->irq_ref < 0) + printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref); + + printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n"); + + spin_lock_irqsave(&dev->lock, flags); + if (msnd_wait_TXDE(dev) == 0) { + outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); + if (dev->type == msndClassic) + outb(HPIRQ_NONE, dev->io + HP_IRQM); + disable_irq(dev->irq); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n"); + + return -EIO; +} + +#ifndef LINUX20 +EXPORT_SYMBOL(msnd_register); +EXPORT_SYMBOL(msnd_unregister); +EXPORT_SYMBOL(msnd_get_num_devs); +EXPORT_SYMBOL(msnd_get_dev); + +EXPORT_SYMBOL(msnd_init_queue); + +EXPORT_SYMBOL(msnd_fifo_init); +EXPORT_SYMBOL(msnd_fifo_free); +EXPORT_SYMBOL(msnd_fifo_alloc); +EXPORT_SYMBOL(msnd_fifo_make_empty); +EXPORT_SYMBOL(msnd_fifo_write); +EXPORT_SYMBOL(msnd_fifo_read); + +EXPORT_SYMBOL(msnd_wait_TXDE); +EXPORT_SYMBOL(msnd_wait_HC0); +EXPORT_SYMBOL(msnd_send_dsp_cmd); +EXPORT_SYMBOL(msnd_send_word); +EXPORT_SYMBOL(msnd_upload_host); + +EXPORT_SYMBOL(msnd_enable_irq); +EXPORT_SYMBOL(msnd_disable_irq); +#endif + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath "); +MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base"); +MODULE_LICENSE("GPL"); + + +int init_module(void) +{ + return 0; +} + +void cleanup_module(void) +{ +} +#endif diff -Nru linux/sound/oss/msnd.h linux-2.4.19-pre5-mjc/sound/oss/msnd.h --- linux/sound/oss/msnd.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/msnd.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,281 @@ +/********************************************************************* + * + * msnd.h + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Some parts of this header file were derived from the Turtle Beach + * MultiSound Driver Development Kit. + * + * Copyright (C) 1998 Andrew Veliath + * Copyright (C) 1993 Turtle Beach Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: msnd.h,v 1.36 1999/03/21 17:05:42 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_H +#define __MSND_H + +#define VERSION "0.8.3.1" + +#define DEFSAMPLERATE DSP_DEFAULT_SPEED +#define DEFSAMPLESIZE AFMT_U8 +#define DEFCHANNELS 1 + +#define DEFFIFOSIZE 128 + +#define SNDCARD_MSND 38 + +#define SRAM_BANK_SIZE 0x8000 +#define SRAM_CNTL_START 0x7F00 + +#define DSP_BASE_ADDR 0x4000 +#define DSP_BANK_BASE 0x4000 + +#define HP_ICR 0x00 +#define HP_CVR 0x01 +#define HP_ISR 0x02 +#define HP_IVR 0x03 +#define HP_NU 0x04 +#define HP_INFO 0x04 +#define HP_TXH 0x05 +#define HP_RXH 0x05 +#define HP_TXM 0x06 +#define HP_RXM 0x06 +#define HP_TXL 0x07 +#define HP_RXL 0x07 + +#define HP_ICR_DEF 0x00 +#define HP_CVR_DEF 0x12 +#define HP_ISR_DEF 0x06 +#define HP_IVR_DEF 0x0f +#define HP_NU_DEF 0x00 + +#define HP_IRQM 0x09 + +#define HPR_BLRC 0x08 +#define HPR_SPR1 0x09 +#define HPR_SPR2 0x0A +#define HPR_TCL0 0x0B +#define HPR_TCL1 0x0C +#define HPR_TCL2 0x0D +#define HPR_TCL3 0x0E +#define HPR_TCL4 0x0F + +#define HPICR_INIT 0x80 +#define HPICR_HM1 0x40 +#define HPICR_HM0 0x20 +#define HPICR_HF1 0x10 +#define HPICR_HF0 0x08 +#define HPICR_TREQ 0x02 +#define HPICR_RREQ 0x01 + +#define HPCVR_HC 0x80 + +#define HPISR_HREQ 0x80 +#define HPISR_DMA 0x40 +#define HPISR_HF3 0x10 +#define HPISR_HF2 0x08 +#define HPISR_TRDY 0x04 +#define HPISR_TXDE 0x02 +#define HPISR_RXDF 0x01 + +#define HPIO_290 0 +#define HPIO_260 1 +#define HPIO_250 2 +#define HPIO_240 3 +#define HPIO_230 4 +#define HPIO_220 5 +#define HPIO_210 6 +#define HPIO_3E0 7 + +#define HPMEM_NONE 0 +#define HPMEM_B000 1 +#define HPMEM_C800 2 +#define HPMEM_D000 3 +#define HPMEM_D400 4 +#define HPMEM_D800 5 +#define HPMEM_E000 6 +#define HPMEM_E800 7 + +#define HPIRQ_NONE 0 +#define HPIRQ_5 1 +#define HPIRQ_7 2 +#define HPIRQ_9 3 +#define HPIRQ_10 4 +#define HPIRQ_11 5 +#define HPIRQ_12 6 +#define HPIRQ_15 7 + +#define HIMT_PLAY_DONE 0x00 +#define HIMT_RECORD_DONE 0x01 +#define HIMT_MIDI_EOS 0x02 +#define HIMT_MIDI_OUT 0x03 + +#define HIMT_MIDI_IN_UCHAR 0x0E +#define HIMT_DSP 0x0F + +#define HDEX_BASE 0x92 +#define HDEX_PLAY_START (0 + HDEX_BASE) +#define HDEX_PLAY_STOP (1 + HDEX_BASE) +#define HDEX_PLAY_PAUSE (2 + HDEX_BASE) +#define HDEX_PLAY_RESUME (3 + HDEX_BASE) +#define HDEX_RECORD_START (4 + HDEX_BASE) +#define HDEX_RECORD_STOP (5 + HDEX_BASE) +#define HDEX_MIDI_IN_START (6 + HDEX_BASE) +#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE) +#define HDEX_MIDI_OUT_START (8 + HDEX_BASE) +#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE) +#define HDEX_AUX_REQ (10 + HDEX_BASE) + +#define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF)) +#define LOWORD(l) ((WORD)(DWORD)(l)) +#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) +#define LOBYTE(w) ((BYTE)(w)) +#define MAKELONG(low,hi) ((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16))) +#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) + +#define PCTODSP_OFFSET(w) (USHORT)((w)/2) +#define PCTODSP_BASED(w) (USHORT)(((w)/2) + DSP_BASE_ADDR) +#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2) + +#ifdef SLOWIO +# undef outb +# undef inb +# define outb outb_p +# define inb inb_p +#endif + +/* JobQueueStruct */ +#define JQS_wStart 0x00 +#define JQS_wSize 0x02 +#define JQS_wHead 0x04 +#define JQS_wTail 0x06 +#define JQS__size 0x08 + +/* DAQueueDataStruct */ +#define DAQDS_wStart 0x00 +#define DAQDS_wSize 0x02 +#define DAQDS_wFormat 0x04 +#define DAQDS_wSampleSize 0x06 +#define DAQDS_wChannels 0x08 +#define DAQDS_wSampleRate 0x0A +#define DAQDS_wIntMsg 0x0C +#define DAQDS_wFlags 0x0E +#define DAQDS__size 0x10 + +typedef u8 BYTE; +typedef u16 USHORT; +typedef u16 WORD; +typedef u32 DWORD; +typedef unsigned long LPDAQD; + +/* Generic FIFO */ +typedef struct { + size_t n, len; + char *data; + int head, tail; +} msnd_fifo; + +typedef struct multisound_dev { + /* Linux device info */ + char *name; + int dsp_minor, mixer_minor; + int ext_midi_dev, hdr_midi_dev; + + /* Hardware resources */ + int io, numio; + int memid, irqid; + int irq, irq_ref; + unsigned char info; + unsigned long base; + + /* Motorola 56k DSP SMA */ + unsigned long SMA; + unsigned long DAPQ, DARQ, MODQ, MIDQ, DSPQ; + unsigned long pwDSPQData, pwMIDQData, pwMODQData; + int dspq_data_buff, dspq_buff_size; + + /* State variables */ + enum { msndClassic, msndPinnacle } type; + mode_t mode; + unsigned long flags; +#define F_RESETTING 0 +#define F_HAVEDIGITAL 1 +#define F_AUDIO_WRITE_INUSE 2 +#define F_WRITING 3 +#define F_WRITEBLOCK 4 +#define F_WRITEFLUSH 5 +#define F_AUDIO_READ_INUSE 6 +#define F_READING 7 +#define F_READBLOCK 8 +#define F_EXT_MIDI_INUSE 9 +#define F_HDR_MIDI_INUSE 10 +#define F_DISABLE_WRITE_NDELAY 11 + wait_queue_head_t writeblock; + wait_queue_head_t readblock; + wait_queue_head_t writeflush; + spinlock_t lock; + int nresets; + unsigned long recsrc; + int left_levels[16]; + int right_levels[16]; + int mixer_mod_count; + int calibrate_signal; + int play_sample_size, play_sample_rate, play_channels; + int play_ndelay; + int rec_sample_size, rec_sample_rate, rec_channels; + int rec_ndelay; + BYTE bCurrentMidiPatch; + + /* Digital audio FIFOs */ + msnd_fifo DAPF, DARF; + int fifosize; + int last_playbank, last_recbank; + + /* MIDI in callback */ + void (*midi_in_interrupt)(struct multisound_dev *); +} multisound_dev_t; + +#ifndef mdelay +# define mdelay(a) udelay((a) * 1000) +#endif + +int msnd_register(multisound_dev_t *dev); +void msnd_unregister(multisound_dev_t *dev); +int msnd_get_num_devs(void); +multisound_dev_t * msnd_get_dev(int i); + +void msnd_init_queue(unsigned long, int start, int size); + +void msnd_fifo_init(msnd_fifo *f); +void msnd_fifo_free(msnd_fifo *f); +int msnd_fifo_alloc(msnd_fifo *f, size_t n); +void msnd_fifo_make_empty(msnd_fifo *f); +int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user); +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user); + +int msnd_wait_TXDE(multisound_dev_t *dev); +int msnd_wait_HC0(multisound_dev_t *dev); +int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd); +int msnd_send_word(multisound_dev_t *dev, unsigned char high, + unsigned char mid, unsigned char low); +int msnd_upload_host(multisound_dev_t *dev, char *bin, int len); +int msnd_enable_irq(multisound_dev_t *dev); +int msnd_disable_irq(multisound_dev_t *dev); + +#endif /* __MSND_H */ diff -Nru linux/sound/oss/msnd_classic.c linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.c --- linux/sound/oss/msnd_classic.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,3 @@ +/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */ +#define MSND_CLASSIC +#include "msnd_pinnacle.c" diff -Nru linux/sound/oss/msnd_classic.h linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.h --- linux/sound/oss/msnd_classic.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/msnd_classic.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,188 @@ +/********************************************************************* + * + * msnd_classic.h + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Some parts of this header file were derived from the Turtle Beach + * MultiSound Driver Development Kit. + * + * Copyright (C) 1998 Andrew Veliath + * Copyright (C) 1993 Turtle Beach Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: msnd_classic.h,v 1.10 1999/03/21 17:36:09 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_CLASSIC_H +#define __MSND_CLASSIC_H + +#include + +#define DSP_NUMIO 0x10 + +#define HP_MEMM 0x08 + +#define HP_BITM 0x0E +#define HP_WAIT 0x0D +#define HP_DSPR 0x0A +#define HP_PROR 0x0B +#define HP_BLKS 0x0C + +#define HPPRORESET_OFF 0 +#define HPPRORESET_ON 1 + +#define HPDSPRESET_OFF 0 +#define HPDSPRESET_ON 1 + +#define HPBLKSEL_0 0 +#define HPBLKSEL_1 1 + +#define HPWAITSTATE_0 0 +#define HPWAITSTATE_1 1 + +#define HPBITMODE_16 0 +#define HPBITMODE_8 1 + +#define HIDSP_INT_PLAY_UNDER 0x00 +#define HIDSP_INT_RECORD_OVER 0x01 +#define HIDSP_INPUT_CLIPPING 0x02 +#define HIDSP_MIDI_IN_OVER 0x10 +#define HIDSP_MIDI_OVERRUN_ERR 0x13 + +#define HDEXAR_CLEAR_PEAKS 1 +#define HDEXAR_IN_SET_POTS 2 +#define HDEXAR_AUX_SET_POTS 3 +#define HDEXAR_CAL_A_TO_D 4 +#define HDEXAR_RD_EXT_DSP_BITS 5 + +#define TIME_PRO_RESET_DONE 0x028A +#define TIME_PRO_SYSEX 0x0040 +#define TIME_PRO_RESET 0x0032 + +#define AGND 0x01 +#define SIGNAL 0x02 + +#define EXT_DSP_BIT_DCAL 0x0001 +#define EXT_DSP_BIT_MIDI_CON 0x0002 + +#define BUFFSIZE 0x8000 +#define HOSTQ_SIZE 0x40 + +#define SRAM_CNTL_START 0x7F00 +#define SMA_STRUCT_START 0x7F40 + +#define DAP_BUFF_SIZE 0x2400 +#define DAR_BUFF_SIZE 0x2000 + +#define DAPQ_STRUCT_SIZE 0x10 +#define DARQ_STRUCT_SIZE 0x10 +#define DAPQ_BUFF_SIZE (3 * 0x10) +#define DARQ_BUFF_SIZE (3 * 0x10) +#define MODQ_BUFF_SIZE 0x400 +#define MIDQ_BUFF_SIZE 0x200 +#define DSPQ_BUFF_SIZE 0x40 + +#define DAPQ_DATA_BUFF 0x6C00 +#define DARQ_DATA_BUFF 0x6C30 +#define MODQ_DATA_BUFF 0x6C60 +#define MIDQ_DATA_BUFF 0x7060 +#define DSPQ_DATA_BUFF 0x7260 + +#define DAPQ_OFFSET SRAM_CNTL_START +#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) +#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) +#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) +#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) + +#define MOP_SYNTH 0x10 +#define MOP_EXTOUT 0x32 +#define MOP_EXTTHRU 0x02 +#define MOP_OUTMASK 0x01 + +#define MIP_EXTIN 0x01 +#define MIP_SYNTH 0x00 +#define MIP_INMASK 0x32 + +/* Classic SMA Common Data */ +#define SMA_wCurrPlayBytes 0x0000 +#define SMA_wCurrRecordBytes 0x0002 +#define SMA_wCurrPlayVolLeft 0x0004 +#define SMA_wCurrPlayVolRight 0x0006 +#define SMA_wCurrInVolLeft 0x0008 +#define SMA_wCurrInVolRight 0x000a +#define SMA_wUser_3 0x000c +#define SMA_wUser_4 0x000e +#define SMA_dwUser_5 0x0010 +#define SMA_dwUser_6 0x0014 +#define SMA_wUser_7 0x0018 +#define SMA_wReserved_A 0x001a +#define SMA_wReserved_B 0x001c +#define SMA_wReserved_C 0x001e +#define SMA_wReserved_D 0x0020 +#define SMA_wReserved_E 0x0022 +#define SMA_wReserved_F 0x0024 +#define SMA_wReserved_G 0x0026 +#define SMA_wReserved_H 0x0028 +#define SMA_wCurrDSPStatusFlags 0x002a +#define SMA_wCurrHostStatusFlags 0x002c +#define SMA_wCurrInputTagBits 0x002e +#define SMA_wCurrLeftPeak 0x0030 +#define SMA_wCurrRightPeak 0x0032 +#define SMA_wExtDSPbits 0x0034 +#define SMA_bExtHostbits 0x0036 +#define SMA_bBoardLevel 0x0037 +#define SMA_bInPotPosRight 0x0038 +#define SMA_bInPotPosLeft 0x0039 +#define SMA_bAuxPotPosRight 0x003a +#define SMA_bAuxPotPosLeft 0x003b +#define SMA_wCurrMastVolLeft 0x003c +#define SMA_wCurrMastVolRight 0x003e +#define SMA_bUser_12 0x0040 +#define SMA_bUser_13 0x0041 +#define SMA_wUser_14 0x0042 +#define SMA_wUser_15 0x0044 +#define SMA_wCalFreqAtoD 0x0046 +#define SMA_wUser_16 0x0048 +#define SMA_wUser_17 0x004a +#define SMA__size 0x004c + +#ifdef HAVE_DSPCODEH +# include "msndperm.c" +# include "msndinit.c" +# define PERMCODE msndperm +# define INITCODE msndinit +# define PERMCODESIZE sizeof(msndperm) +# define INITCODESIZE sizeof(msndinit) +#else +# ifndef CONFIG_MSNDCLAS_INIT_FILE +# define CONFIG_MSNDCLAS_INIT_FILE \ + "/etc/sound/msndinit.bin" +# endif +# ifndef CONFIG_MSNDCLAS_PERM_FILE +# define CONFIG_MSNDCLAS_PERM_FILE \ + "/etc/sound/msndperm.bin" +# endif +# define PERMCODEFILE CONFIG_MSNDCLAS_PERM_FILE +# define INITCODEFILE CONFIG_MSNDCLAS_INIT_FILE +# define PERMCODE dspini +# define INITCODE permini +# define PERMCODESIZE sizeof_dspini +# define INITCODESIZE sizeof_permini +#endif +#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)" + +#endif /* __MSND_CLASSIC_H */ diff -Nru linux/sound/oss/msnd_pinnacle.c linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.c --- linux/sound/oss/msnd_pinnacle.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1894 @@ +/********************************************************************* + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * Linux 2.0/2.2 Version + * + * msnd_pinnacle.c / msnd_classic.c + * + * -- If MSND_CLASSIC is defined: + * + * -> driver for Turtle Beach Classic/Monterey/Tahiti + * + * -- Else + * + * -> driver for Turtle Beach Pinnacle/Fiji + * + * Copyright (C) 1998 Andrew Veliath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $ + * + * 12-3-2000 Modified IO port validation Steve Sycamore + * + * + * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $ + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "sound_firmware.h" +#ifdef MSND_CLASSIC +# ifndef __alpha__ +# define SLOWIO +# endif +#endif +#include "msnd.h" +#ifdef MSND_CLASSIC +# ifdef CONFIG_MSNDCLAS_HAVE_BOOT +# define HAVE_DSPCODEH +# endif +# include "msnd_classic.h" +# define LOGNAME "msnd_classic" +#else +# ifdef CONFIG_MSNDPIN_HAVE_BOOT +# define HAVE_DSPCODEH +# endif +# include "msnd_pinnacle.h" +# define LOGNAME "msnd_pinnacle" +#endif + +#ifndef CONFIG_MSND_WRITE_NDELAY +# define CONFIG_MSND_WRITE_NDELAY 1 +#endif + +#define get_play_delay_jiffies(size) ((size) * HZ * \ + dev.play_sample_size / 8 / \ + dev.play_sample_rate / \ + dev.play_channels) + +#define get_rec_delay_jiffies(size) ((size) * HZ * \ + dev.rec_sample_size / 8 / \ + dev.rec_sample_rate / \ + dev.rec_channels) + +static multisound_dev_t dev; + +#ifndef HAVE_DSPCODEH +static char *dspini, *permini; +static int sizeof_dspini, sizeof_permini; +#endif + +static int dsp_full_reset(void); +static void dsp_write_flush(void); + +static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd) +{ + if (msnd_send_dsp_cmd(dev, cmd) == 0) + return 0; + dsp_full_reset(); + return msnd_send_dsp_cmd(dev, cmd); +} + +static void reset_play_queue(void) +{ + int n; + LPDAQD lpDAQ; + + dev.last_playbank = -1; + isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead); + isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail); + + for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { + isa_writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart); + isa_writew(0, lpDAQ + DAQDS_wSize); + isa_writew(1, lpDAQ + DAQDS_wFormat); + isa_writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize); + isa_writew(dev.play_channels, lpDAQ + DAQDS_wChannels); + isa_writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate); + isa_writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); + isa_writew(n, lpDAQ + DAQDS_wFlags); + } +} + +static void reset_record_queue(void) +{ + int n; + LPDAQD lpDAQ; + unsigned long flags; + + dev.last_recbank = 2; + isa_writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead); + isa_writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail); + + /* Critical section: bank 1 access */ + spin_lock_irqsave(&dev.lock, flags); + outb(HPBLKSEL_1, dev.io + HP_BLKS); + isa_memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + spin_unlock_irqrestore(&dev.lock, flags); + + for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { + isa_writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart); + isa_writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize); + isa_writew(1, lpDAQ + DAQDS_wFormat); + isa_writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize); + isa_writew(dev.rec_channels, lpDAQ + DAQDS_wChannels); + isa_writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate); + isa_writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); + isa_writew(n, lpDAQ + DAQDS_wFlags); + } +} + +static void reset_queues(void) +{ + if (dev.mode & FMODE_WRITE) { + msnd_fifo_make_empty(&dev.DAPF); + reset_play_queue(); + } + if (dev.mode & FMODE_READ) { + msnd_fifo_make_empty(&dev.DARF); + reset_record_queue(); + } +} + +static int dsp_set_format(struct file *file, int val) +{ + int data, i; + LPDAQD lpDAQ, lpDARQ; + + lpDAQ = dev.base + DAPQ_DATA_BUFF; + lpDARQ = dev.base + DARQ_DATA_BUFF; + + switch (val) { + case AFMT_U8: + case AFMT_S16_LE: + data = val; + break; + default: + data = DEFSAMPLESIZE; + break; + } + + for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { + if (file->f_mode & FMODE_WRITE) + isa_writew(data, lpDAQ + DAQDS_wSampleSize); + if (file->f_mode & FMODE_READ) + isa_writew(data, lpDARQ + DAQDS_wSampleSize); + } + if (file->f_mode & FMODE_WRITE) + dev.play_sample_size = data; + if (file->f_mode & FMODE_READ) + dev.rec_sample_size = data; + + return data; +} + +static int dsp_get_frag_size(void) +{ + int size; + size = dev.fifosize / 4; + if (size > 32 * 1024) + size = 32 * 1024; + return size; +} + +static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int val, i, data, tmp; + LPDAQD lpDAQ, lpDARQ; + audio_buf_info abinfo; + unsigned long flags; + + lpDAQ = dev.base + DAPQ_DATA_BUFF; + lpDARQ = dev.base + DARQ_DATA_BUFF; + + switch (cmd) { + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + case SNDCTL_DSP_SETDUPLEX: + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return -EINVAL; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&dev.lock, flags); + abinfo.fragsize = dsp_get_frag_size(); + abinfo.bytes = dev.DAPF.n - dev.DAPF.len; + abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize; + abinfo.fragments = abinfo.bytes / abinfo.fragsize; + spin_unlock_irqrestore(&dev.lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&dev.lock, flags); + abinfo.fragsize = dsp_get_frag_size(); + abinfo.bytes = dev.DARF.n - dev.DARF.len; + abinfo.fragstotal = dev.DARF.n / abinfo.fragsize; + abinfo.fragments = abinfo.bytes / abinfo.fragsize; + spin_unlock_irqrestore(&dev.lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_RESET: + dev.nresets = 0; + reset_queues(); + return 0; + + case SNDCTL_DSP_SYNC: + dsp_write_flush(); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + tmp = dsp_get_frag_size(); + if (put_user(tmp, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETFMTS: + val = AFMT_S16_LE | AFMT_U8; + if (put_user(val, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) + data = val == AFMT_QUERY + ? dev.play_sample_size + : dsp_set_format(file, val); + else + data = val == AFMT_QUERY + ? dev.rec_sample_size + : dsp_set_format(file, val); + + if (put_user(data, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_NONBLOCK: + if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) && + file->f_mode & FMODE_WRITE) + dev.play_ndelay = 1; + if (file->f_mode & FMODE_READ) + dev.rec_ndelay = 1; + return 0; + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; + if (put_user(val, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val < 8000) + val = 8000; + + if (val > 48000) + val = 48000; + + data = val; + + for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { + if (file->f_mode & FMODE_WRITE) + isa_writew(data, lpDAQ + DAQDS_wSampleRate); + if (file->f_mode & FMODE_READ) + isa_writew(data, lpDARQ + DAQDS_wSampleRate); + } + if (file->f_mode & FMODE_WRITE) + dev.play_sample_rate = data; + if (file->f_mode & FMODE_READ) + dev.rec_sample_rate = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + return 0; + + case SNDCTL_DSP_CHANNELS: + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (cmd == SNDCTL_DSP_CHANNELS) { + switch (val) { + case 1: + case 2: + data = val; + break; + default: + val = data = 2; + break; + } + } else { + switch (val) { + case 0: + data = 1; + break; + default: + val = 1; + case 1: + data = 2; + break; + } + } + + for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { + if (file->f_mode & FMODE_WRITE) + isa_writew(data, lpDAQ + DAQDS_wChannels); + if (file->f_mode & FMODE_READ) + isa_writew(data, lpDARQ + DAQDS_wChannels); + } + if (file->f_mode & FMODE_WRITE) + dev.play_channels = data; + if (file->f_mode & FMODE_READ) + dev.rec_channels = data; + + if (put_user(val, (int *)arg)) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + +static int mixer_get(int d) +{ + if (d > 31) + return -EINVAL; + + switch (d) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_IMIX: + case SOUND_MIXER_LINE1: +#ifndef MSND_CLASSIC + case SOUND_MIXER_MIC: + case SOUND_MIXER_SYNTH: +#endif + return (dev.left_levels[d] >> 8) * 100 / 0xff | + (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); + default: + return 0; + } +} + +#define update_volm(a,b) \ + isa_writew((dev.left_levels[a] >> 1) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ + dev.SMA + SMA_##b##Left); \ + isa_writew((dev.right_levels[a] >> 1) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ + dev.SMA + SMA_##b##Right); + +#define update_potm(d,s,ar) \ + isa_writeb((dev.left_levels[d] >> 8) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ + dev.SMA + SMA_##s##Left); \ + isa_writeb((dev.right_levels[d] >> 8) * \ + isa_readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ + dev.SMA + SMA_##s##Right); \ + if (msnd_send_word(&dev, 0, 0, ar) == 0) \ + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + +#define update_pot(d,s,ar) \ + isa_writeb(dev.left_levels[d] >> 8, \ + dev.SMA + SMA_##s##Left); \ + isa_writeb(dev.right_levels[d] >> 8, \ + dev.SMA + SMA_##s##Right); \ + if (msnd_send_word(&dev, 0, 0, ar) == 0) \ + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + +static int mixer_set(int d, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int bLeft, bRight; + int wLeft, wRight; + int updatemaster = 0; + + if (d > 31) + return -EINVAL; + + bLeft = left * 0xff / 100; + wLeft = left * 0xffff / 100; + + bRight = right * 0xff / 100; + wRight = right * 0xffff / 100; + + dev.left_levels[d] = wLeft; + dev.right_levels[d] = wRight; + + switch (d) { + /* master volume unscaled controls */ + case SOUND_MIXER_LINE: /* line pot control */ + /* scaled by IMIX in digital mix */ + isa_writeb(bLeft, dev.SMA + SMA_bInPotPosLeft); + isa_writeb(bRight, dev.SMA + SMA_bInPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; +#ifndef MSND_CLASSIC + case SOUND_MIXER_MIC: /* mic pot control */ + /* scaled by IMIX in digital mix */ + isa_writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft); + isa_writeb(bRight, dev.SMA + SMA_bMicPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; +#endif + case SOUND_MIXER_VOLUME: /* master volume */ + isa_writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft); + isa_writew(wRight, dev.SMA + SMA_wCurrMastVolRight); + /* fall through */ + + case SOUND_MIXER_LINE1: /* aux pot control */ + /* scaled by master volume */ + /* fall through */ + + /* digital controls */ + case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */ + case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */ + case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */ + /* scaled by master volume */ + updatemaster = 1; + break; + + default: + return 0; + } + + if (updatemaster) { + /* update master volume scaled controls */ + update_volm(SOUND_MIXER_PCM, wCurrPlayVol); + update_volm(SOUND_MIXER_IMIX, wCurrInVol); +#ifndef MSND_CLASSIC + update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); +#endif + update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); + } + + return mixer_get(d); +} + +static void mixer_setup(void) +{ + update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); + update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); + update_volm(SOUND_MIXER_PCM, wCurrPlayVol); + update_volm(SOUND_MIXER_IMIX, wCurrInVol); +#ifndef MSND_CLASSIC + update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); + update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); +#endif +} + +static unsigned long set_recsrc(unsigned long recsrc) +{ + if (dev.recsrc == recsrc) + return dev.recsrc; +#ifdef HAVE_NORECSRC + else if (recsrc == 0) + dev.recsrc = 0; +#endif + else + dev.recsrc ^= recsrc; + +#ifndef MSND_CLASSIC + if (dev.recsrc & SOUND_MASK_IMIX) { + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + } + else if (dev.recsrc & SOUND_MASK_SYNTH) { + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + } + else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) { + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); + } + else { +#ifdef HAVE_NORECSRC + /* Select no input (?) */ + dev.recsrc = 0; +#else + dev.recsrc = SOUND_MASK_IMIX; + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); +#endif + } +#endif /* MSND_CLASSIC */ + + return dev.recsrc; +} + +static unsigned long force_recsrc(unsigned long recsrc) +{ + dev.recsrc = 0; + return set_recsrc(recsrc); +} + +#define set_mixer_info() \ + strncpy(info.id, "MSNDMIXER", sizeof(info.id)); \ + strncpy(info.name, "MultiSound Mixer", sizeof(info.name)); + +static int mixer_ioctl(unsigned int cmd, unsigned long arg) +{ + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + set_mixer_info(); + info.modify_counter = dev.mixer_mod_count; + return copy_to_user((void *)arg, &info, sizeof(info)); + } else if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + set_mixer_info(); + return copy_to_user((void *)arg, &info, sizeof(info)); + } else if (cmd == SOUND_MIXER_PRIVATE1) { + dev.nresets = 0; + dsp_full_reset(); + return 0; + } else if (((cmd >> 8) & 0xff) == 'M') { + int val = 0; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = set_recsrc(val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = mixer_set(cmd & 0xff, val); + break; + } + ++dev.mixer_mod_count; + return put_user(val, (int *)arg); + } else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + val = dev.recsrc; + break; + + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_PCM | + SOUND_MASK_LINE | + SOUND_MASK_IMIX | + SOUND_MASK_LINE1 | +#ifndef MSND_CLASSIC + SOUND_MASK_MIC | + SOUND_MASK_SYNTH | +#endif + SOUND_MASK_VOLUME; + break; + + case SOUND_MIXER_RECMASK: +#ifdef MSND_CLASSIC + val = 0; +#else + val = SOUND_MASK_IMIX | + SOUND_MASK_SYNTH; + if (test_bit(F_HAVEDIGITAL, &dev.flags)) + val |= SOUND_MASK_DIGITAL1; +#endif + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: + if ((val = mixer_get(cmd & 0xff)) < 0) + return -EINVAL; + break; + } + } + + return put_user(val, (int *)arg); + } + + return -EINVAL; +} + +static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int minor = MINOR(inode->i_rdev); + + if (cmd == OSS_GETVERSION) { + int sound_version = SOUND_VERSION; + return put_user(sound_version, (int *)arg); + } + + if (minor == dev.dsp_minor) + return dsp_ioctl(file, cmd, arg); + else if (minor == dev.mixer_minor) + return mixer_ioctl(cmd, arg); + + return -EINVAL; +} + +static void dsp_write_flush(void) +{ + if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags)) + return; + set_bit(F_WRITEFLUSH, &dev.flags); + interruptible_sleep_on_timeout( + &dev.writeflush, + get_play_delay_jiffies(dev.DAPF.len)); + clear_bit(F_WRITEFLUSH, &dev.flags); + if (!signal_pending(current)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE)); + } + clear_bit(F_WRITING, &dev.flags); +} + +static void dsp_halt(struct file *file) +{ + if ((file ? file->f_mode : dev.mode) & FMODE_READ) { + clear_bit(F_READING, &dev.flags); + chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP); + msnd_disable_irq(&dev); + if (file) { + printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file); + dev.mode &= ~FMODE_READ; + } + clear_bit(F_AUDIO_READ_INUSE, &dev.flags); + } + if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { + if (test_bit(F_WRITING, &dev.flags)) { + dsp_write_flush(); + chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP); + } + msnd_disable_irq(&dev); + if (file) { + printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file); + dev.mode &= ~FMODE_WRITE; + } + clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags); + } +} + +static int dsp_release(struct file *file) +{ + dsp_halt(file); + return 0; +} + +static int dsp_open(struct file *file) +{ + if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { + set_bit(F_AUDIO_WRITE_INUSE, &dev.flags); + clear_bit(F_WRITING, &dev.flags); + msnd_fifo_make_empty(&dev.DAPF); + reset_play_queue(); + if (file) { + printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file); + dev.mode |= FMODE_WRITE; + } + msnd_enable_irq(&dev); + } + if ((file ? file->f_mode : dev.mode) & FMODE_READ) { + set_bit(F_AUDIO_READ_INUSE, &dev.flags); + clear_bit(F_READING, &dev.flags); + msnd_fifo_make_empty(&dev.DARF); + reset_record_queue(); + if (file) { + printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file); + dev.mode |= FMODE_READ; + } + msnd_enable_irq(&dev); + } + return 0; +} + +static void set_default_play_audio_parameters(void) +{ + dev.play_sample_size = DEFSAMPLESIZE; + dev.play_sample_rate = DEFSAMPLERATE; + dev.play_channels = DEFCHANNELS; +} + +static void set_default_rec_audio_parameters(void) +{ + dev.rec_sample_size = DEFSAMPLESIZE; + dev.rec_sample_rate = DEFSAMPLERATE; + dev.rec_channels = DEFCHANNELS; +} + +static void set_default_audio_parameters(void) +{ + set_default_play_audio_parameters(); + set_default_rec_audio_parameters(); +} + +static int dev_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int err = 0; + + if (minor == dev.dsp_minor) { + if ((file->f_mode & FMODE_WRITE && + test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) || + (file->f_mode & FMODE_READ && + test_bit(F_AUDIO_READ_INUSE, &dev.flags))) + return -EBUSY; + + if ((err = dsp_open(file)) >= 0) { + dev.nresets = 0; + if (file->f_mode & FMODE_WRITE) { + set_default_play_audio_parameters(); + if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags)) + dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; + else + dev.play_ndelay = 0; + } + if (file->f_mode & FMODE_READ) { + set_default_rec_audio_parameters(); + dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; + } + } + } + else if (minor == dev.mixer_minor) { + /* nothing */ + } else + err = -EINVAL; + + return err; +} + +static int dev_release(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int err = 0; + + lock_kernel(); + if (minor == dev.dsp_minor) + err = dsp_release(file); + else if (minor == dev.mixer_minor) { + /* nothing */ + } else + err = -EINVAL; + unlock_kernel(); + return err; +} + +static __inline__ int pack_DARQ_to_DARF(register int bank) +{ + register int size, n, timeout = 3; + register WORD wTmp; + LPDAQD DAQD; + + /* Increment the tail and check for queue wrap */ + wTmp = isa_readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size); + if (wTmp > isa_readw(dev.DARQ + JQS_wSize)) + wTmp = 0; + while (wTmp == isa_readw(dev.DARQ + JQS_wHead) && timeout--) + udelay(1); + isa_writew(wTmp, dev.DARQ + JQS_wTail); + + /* Get our digital audio queue struct */ + DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF; + + /* Get length of data */ + size = isa_readw(DAQD + DAQDS_wSize); + + /* Read data from the head (unprotected bank 1 access okay + since this is only called inside an interrupt) */ + outb(HPBLKSEL_1, dev.io + HP_BLKS); + if ((n = msnd_fifo_write( + &dev.DARF, + (char *)(dev.base + bank * DAR_BUFF_SIZE), + size, 0)) <= 0) { + outb(HPBLKSEL_0, dev.io + HP_BLKS); + return n; + } + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + return 1; +} + +static __inline__ int pack_DAPF_to_DAPQ(register int start) +{ + register WORD DAPQ_tail; + register int protect = start, nbanks = 0; + LPDAQD DAQD; + + DAPQ_tail = isa_readw(dev.DAPQ + JQS_wTail); + while (DAPQ_tail != isa_readw(dev.DAPQ + JQS_wHead) || start) { + register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size); + register int n; + unsigned long flags; + + /* Write the data to the new tail */ + if (protect) { + /* Critical section: protect fifo in non-interrupt */ + spin_lock_irqsave(&dev.lock, flags); + if ((n = msnd_fifo_read( + &dev.DAPF, + (char *)(dev.base + bank_num * DAP_BUFF_SIZE), + DAP_BUFF_SIZE, 0)) < 0) { + spin_unlock_irqrestore(&dev.lock, flags); + return n; + } + spin_unlock_irqrestore(&dev.lock, flags); + } else { + if ((n = msnd_fifo_read( + &dev.DAPF, + (char *)(dev.base + bank_num * DAP_BUFF_SIZE), + DAP_BUFF_SIZE, 0)) < 0) { + return n; + } + } + if (!n) + break; + + if (start) + start = 0; + + /* Get our digital audio queue struct */ + DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF; + + /* Write size of this bank */ + isa_writew(n, DAQD + DAQDS_wSize); + ++nbanks; + + /* Then advance the tail */ + DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size); + isa_writew(DAPQ_tail, dev.DAPQ + JQS_wTail); + /* Tell the DSP to play the bank */ + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + } + return nbanks; +} + +static int dsp_read(char *buf, size_t len) +{ + int count = len; + + while (count > 0) { + int n; + unsigned long flags; + + /* Critical section: protect fifo in non-interrupt */ + spin_lock_irqsave(&dev.lock, flags); + if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) { + printk(KERN_WARNING LOGNAME ": FIFO read error\n"); + spin_unlock_irqrestore(&dev.lock, flags); + return n; + } + spin_unlock_irqrestore(&dev.lock, flags); + buf += n; + count -= n; + + if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) { + dev.last_recbank = -1; + if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0) + set_bit(F_READING, &dev.flags); + } + + if (dev.rec_ndelay) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + set_bit(F_READBLOCK, &dev.flags); + if (!interruptible_sleep_on_timeout( + &dev.readblock, + get_rec_delay_jiffies(DAR_BUFF_SIZE))) + clear_bit(F_READING, &dev.flags); + clear_bit(F_READBLOCK, &dev.flags); + if (signal_pending(current)) + return -EINTR; + } + } + + return len - count; +} + +static int dsp_write(const char *buf, size_t len) +{ + int count = len; + + while (count > 0) { + int n; + unsigned long flags; + + /* Critical section: protect fifo in non-interrupt */ + spin_lock_irqsave(&dev.lock, flags); + if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) { + printk(KERN_WARNING LOGNAME ": FIFO write error\n"); + spin_unlock_irqrestore(&dev.lock, flags); + return n; + } + spin_unlock_irqrestore(&dev.lock, flags); + buf += n; + count -= n; + + if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { + dev.last_playbank = -1; + if (pack_DAPF_to_DAPQ(1) > 0) + set_bit(F_WRITING, &dev.flags); + } + + if (dev.play_ndelay) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + set_bit(F_WRITEBLOCK, &dev.flags); + interruptible_sleep_on_timeout( + &dev.writeblock, + get_play_delay_jiffies(DAP_BUFF_SIZE)); + clear_bit(F_WRITEBLOCK, &dev.flags); + if (signal_pending(current)) + return -EINTR; + } + } + + return len - count; +} + +static ssize_t dev_read(struct file *file, char *buf, size_t count, loff_t *off) +{ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); + if (minor == dev.dsp_minor) + return dsp_read(buf, count); + else + return -EINVAL; +} + +static ssize_t dev_write(struct file *file, const char *buf, size_t count, loff_t *off) +{ + int minor = MINOR(file->f_dentry->d_inode->i_rdev); + if (minor == dev.dsp_minor) + return dsp_write(buf, count); + else + return -EINVAL; +} + +static __inline__ void eval_dsp_msg(register WORD wMessage) +{ + switch (HIBYTE(wMessage)) { + case HIMT_PLAY_DONE: + if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags)) + break; + dev.last_playbank = LOBYTE(wMessage); + + if (pack_DAPF_to_DAPQ(0) <= 0) { + if (!test_bit(F_WRITEBLOCK, &dev.flags)) { + if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags)) + wake_up_interruptible(&dev.writeflush); + } + clear_bit(F_WRITING, &dev.flags); + } + + if (test_bit(F_WRITEBLOCK, &dev.flags)) + wake_up_interruptible(&dev.writeblock); + break; + + case HIMT_RECORD_DONE: + if (dev.last_recbank == LOBYTE(wMessage)) + break; + dev.last_recbank = LOBYTE(wMessage); + + pack_DARQ_to_DARF(dev.last_recbank); + + if (test_bit(F_READBLOCK, &dev.flags)) + wake_up_interruptible(&dev.readblock); + break; + + case HIMT_DSP: + switch (LOBYTE(wMessage)) { +#ifndef MSND_CLASSIC + case HIDSP_PLAY_UNDER: +#endif + case HIDSP_INT_PLAY_UNDER: +/* printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */ + clear_bit(F_WRITING, &dev.flags); + break; + + case HIDSP_INT_RECORD_OVER: +/* printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */ + clear_bit(F_READING, &dev.flags); + break; + + default: +/* printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n", + LOBYTE(wMessage), LOBYTE(wMessage)); */ + break; + } + break; + + case HIMT_MIDI_IN_UCHAR: + if (dev.midi_in_interrupt) + (*dev.midi_in_interrupt)(&dev); + break; + + default: +/* printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */ + break; + } +} + +static void intr(int irq, void *dev_id, struct pt_regs *regs) +{ + /* Send ack to DSP */ + inb(dev.io + HP_RXL); + + /* Evaluate queued DSP messages */ + while (isa_readw(dev.DSPQ + JQS_wTail) != isa_readw(dev.DSPQ + JQS_wHead)) { + register WORD wTmp; + + eval_dsp_msg(isa_readw(dev.pwDSPQData + 2*isa_readw(dev.DSPQ + JQS_wHead))); + + if ((wTmp = isa_readw(dev.DSPQ + JQS_wHead) + 1) > isa_readw(dev.DSPQ + JQS_wSize)) + isa_writew(0, dev.DSPQ + JQS_wHead); + else + isa_writew(wTmp, dev.DSPQ + JQS_wHead); + } +} + +static struct file_operations dev_fileops = { + owner: THIS_MODULE, + read: dev_read, + write: dev_write, + ioctl: dev_ioctl, + open: dev_open, + release: dev_release, +}; + +static int reset_dsp(void) +{ + int timeout = 100; + + outb(HPDSPRESET_ON, dev.io + HP_DSPR); + mdelay(1); +#ifndef MSND_CLASSIC + dev.info = inb(dev.io + HP_INFO); +#endif + outb(HPDSPRESET_OFF, dev.io + HP_DSPR); + mdelay(1); + while (timeout-- > 0) { + if (inb(dev.io + HP_CVR) == HP_CVR_DEF) + return 0; + mdelay(1); + } + printk(KERN_ERR LOGNAME ": Cannot reset DSP\n"); + + return -EIO; +} + +static int __init probe_multisound(void) +{ +#ifndef MSND_CLASSIC + char *xv, *rev = NULL; + char *pin = "Pinnacle", *fiji = "Fiji"; + char *pinfiji = "Pinnacle/Fiji"; +#endif + + if (check_region(dev.io, dev.numio)) { + printk(KERN_ERR LOGNAME ": I/O port conflict\n"); + return -ENODEV; + } + request_region(dev.io, dev.numio, "probing"); + + if (reset_dsp() < 0) { + release_region(dev.io, dev.numio); + return -ENODEV; + } + +#ifdef MSND_CLASSIC + dev.name = "Classic/Tahiti/Monterey"; + printk(KERN_INFO LOGNAME ": %s, " +#else + switch (dev.info >> 4) { + case 0xf: xv = "<= 1.15"; break; + case 0x1: xv = "1.18/1.2"; break; + case 0x2: xv = "1.3"; break; + case 0x3: xv = "1.4"; break; + default: xv = "unknown"; break; + } + + switch (dev.info & 0x7) { + case 0x0: rev = "I"; dev.name = pin; break; + case 0x1: rev = "F"; dev.name = pin; break; + case 0x2: rev = "G"; dev.name = pin; break; + case 0x3: rev = "H"; dev.name = pin; break; + case 0x4: rev = "E"; dev.name = fiji; break; + case 0x5: rev = "C"; dev.name = fiji; break; + case 0x6: rev = "D"; dev.name = fiji; break; + case 0x7: + rev = "A-B (Fiji) or A-E (Pinnacle)"; + dev.name = pinfiji; + break; + } + printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, " +#endif /* MSND_CLASSIC */ + "I/O 0x%x-0x%x, IRQ %d, memory mapped to 0x%lX-0x%lX\n", + dev.name, +#ifndef MSND_CLASSIC + rev, xv, +#endif + dev.io, dev.io + dev.numio - 1, + dev.irq, + dev.base, dev.base + 0x7fff); + + release_region(dev.io, dev.numio); + return 0; +} + +static int init_sma(void) +{ + static int initted; + WORD mastVolLeft, mastVolRight; + unsigned long flags; + +#ifdef MSND_CLASSIC + outb(dev.memid, dev.io + HP_MEMM); +#endif + outb(HPBLKSEL_0, dev.io + HP_BLKS); + if (initted) { + mastVolLeft = isa_readw(dev.SMA + SMA_wCurrMastVolLeft); + mastVolRight = isa_readw(dev.SMA + SMA_wCurrMastVolRight); + } else + mastVolLeft = mastVolRight = 0; + isa_memset_io(dev.base, 0, 0x8000); + + /* Critical section: bank 1 access */ + spin_lock_irqsave(&dev.lock, flags); + outb(HPBLKSEL_1, dev.io + HP_BLKS); + isa_memset_io(dev.base, 0, 0x8000); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + spin_unlock_irqrestore(&dev.lock, flags); + + dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF); + dev.pwMODQData = (dev.base + MODQ_DATA_BUFF); + dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF); + + /* Motorola 56k shared memory base */ + dev.SMA = dev.base + SMA_STRUCT_START; + + /* Digital audio play queue */ + dev.DAPQ = dev.base + DAPQ_OFFSET; + msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE); + + /* Digital audio record queue */ + dev.DARQ = dev.base + DARQ_OFFSET; + msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); + + /* MIDI out queue */ + dev.MODQ = dev.base + MODQ_OFFSET; + msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE); + + /* MIDI in queue */ + dev.MIDQ = dev.base + MIDQ_OFFSET; + msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE); + + /* DSP -> host message queue */ + dev.DSPQ = dev.base + DSPQ_OFFSET; + msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE); + + /* Setup some DSP values */ +#ifndef MSND_CLASSIC + isa_writew(1, dev.SMA + SMA_wCurrPlayFormat); + isa_writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize); + isa_writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels); + isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate); +#endif + isa_writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD); + isa_writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft); + isa_writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight); +#ifndef MSND_CLASSIC + isa_writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch); + isa_writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate); +#endif + isa_writew(0x303, dev.SMA + SMA_wCurrInputTagBits); + + initted = 1; + + return 0; +} + +static int __init calibrate_adc(WORD srate) +{ + isa_writew(srate, dev.SMA + SMA_wCalFreqAtoD); + if (dev.calibrate_signal == 0) + isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags) + | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags); + else + isa_writew(isa_readw(dev.SMA + SMA_wCurrHostStatusFlags) + & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags); + if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && + chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 3); + return 0; + } + printk(KERN_WARNING LOGNAME ": ADC calibration failed\n"); + + return -EIO; +} + +static int upload_dsp_code(void) +{ + outb(HPBLKSEL_0, dev.io + HP_BLKS); +#ifndef HAVE_DSPCODEH + INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE); + if (!INITCODE) { + printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE); + return -EBUSY; + } + + PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE); + if (!PERMCODE) { + printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE); + vfree(INITCODE); + return -EBUSY; + } +#endif + isa_memcpy_toio(dev.base, PERMCODE, PERMCODESIZE); + if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) { + printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n"); + return -ENODEV; + } +#ifdef HAVE_DSPCODEH + printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n"); +#else + printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n"); +#endif + +#ifndef HAVE_DSPCODEH + vfree(INITCODE); + vfree(PERMCODE); +#endif + + return 0; +} + +#ifdef MSND_CLASSIC +static void reset_proteus(void) +{ + outb(HPPRORESET_ON, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET); + outb(HPPRORESET_OFF, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET_DONE); +} +#endif + +static int initialize(void) +{ + int err, timeout; + +#ifdef MSND_CLASSIC + outb(HPWAITSTATE_0, dev.io + HP_WAIT); + outb(HPBITMODE_16, dev.io + HP_BITM); + + reset_proteus(); +#endif + if ((err = init_sma()) < 0) { + printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n"); + return err; + } + + if ((err = reset_dsp()) < 0) + return err; + + if ((err = upload_dsp_code()) < 0) { + printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n"); + return err; + } + + timeout = 200; + while (isa_readw(dev.base)) { + mdelay(1); + if (!timeout--) { + printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n"); + return -EIO; + } + } + + mixer_setup(); + + return 0; +} + +static int dsp_full_reset(void) +{ + int rv; + + if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10) + return 0; + + set_bit(F_RESETTING, &dev.flags); + printk(KERN_INFO LOGNAME ": DSP reset\n"); + dsp_halt(NULL); /* Unconditionally halt */ + if ((rv = initialize())) + printk(KERN_WARNING LOGNAME ": DSP reset failed\n"); + force_recsrc(dev.recsrc); + dsp_open(NULL); + clear_bit(F_RESETTING, &dev.flags); + + return rv; +} + +static int __init attach_multisound(void) +{ + int err; + + if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) { + printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq); + return err; + } + request_region(dev.io, dev.numio, dev.name); + + if ((err = dsp_full_reset()) < 0) { + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + } + + if ((err = msnd_register(&dev)) < 0) { + printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n"); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + } + + if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) { + printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n"); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return dev.dsp_minor; + } + + if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) { + printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n"); + unregister_sound_mixer(dev.mixer_minor); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return dev.mixer_minor; + } + + dev.ext_midi_dev = dev.hdr_midi_dev = -1; + + disable_irq(dev.irq); + calibrate_adc(dev.play_sample_rate); +#ifndef MSND_CLASSIC + force_recsrc(SOUND_MASK_IMIX); +#endif + + return 0; +} + +static void __exit unload_multisound(void) +{ + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + unregister_sound_mixer(dev.mixer_minor); + unregister_sound_dsp(dev.dsp_minor); + msnd_unregister(&dev); +} + +#ifndef MSND_CLASSIC + +/* Pinnacle/Fiji Logical Device Configuration */ + +static int __init msnd_write_cfg(int cfg, int reg, int value) +{ + outb(reg, cfg); + outb(value, cfg + 1); + if (value != inb(cfg + 1)) { + printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n"); + return -EIO; + } + return 0; +} + +static int __init msnd_write_cfg_io0(int cfg, int num, WORD io) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_io1(int cfg, int num, WORD io) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_mem(int cfg, int num, int mem) +{ + WORD wmem; + + mem >>= 8; + mem &= 0xfff; + wmem = (WORD)mem; + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) + return -EIO; + if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) + return -EIO; + if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) + return -EIO; + return 0; +} + +static int __init msnd_activate_logical(int cfg, int num) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) + return -EIO; + return 0; +} + +static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) +{ + if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) + return -EIO; + if (msnd_write_cfg_io0(cfg, num, io0)) + return -EIO; + if (msnd_write_cfg_io1(cfg, num, io1)) + return -EIO; + if (msnd_write_cfg_irq(cfg, num, irq)) + return -EIO; + if (msnd_write_cfg_mem(cfg, num, mem)) + return -EIO; + if (msnd_activate_logical(cfg, num)) + return -EIO; + return 0; +} + +typedef struct msnd_pinnacle_cfg_device { + WORD io0, io1, irq; + int mem; +} msnd_pinnacle_cfg_t[4]; + +static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device) +{ + int i; + + /* Reset devices if told to */ + if (reset) { + printk(KERN_INFO LOGNAME ": Resetting all devices\n"); + for (i = 0; i < 4; ++i) + if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0)) + return -EIO; + } + + /* Configure specified devices */ + for (i = 0; i < 4; ++i) { + + switch (i) { + case 0: /* DSP */ + if (!(device[i].io0 && device[i].irq && device[i].mem)) + continue; + break; + case 1: /* MPU */ + if (!(device[i].io0 && device[i].irq)) + continue; + printk(KERN_INFO LOGNAME + ": Configuring MPU to I/O 0x%x IRQ %d\n", + device[i].io0, device[i].irq); + break; + case 2: /* IDE */ + if (!(device[i].io0 && device[i].io1 && device[i].irq)) + continue; + printk(KERN_INFO LOGNAME + ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n", + device[i].io0, device[i].io1, device[i].irq); + break; + case 3: /* Joystick */ + if (!(device[i].io0)) + continue; + printk(KERN_INFO LOGNAME + ": Configuring joystick to I/O 0x%x\n", + device[i].io0); + break; + } + + /* Configure the device */ + if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem)) + return -EIO; + } + + return 0; +} +#endif + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath "); +MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM (io, "i"); +MODULE_PARM (irq, "i"); +MODULE_PARM (mem, "i"); +MODULE_PARM (write_ndelay, "i"); +MODULE_PARM (fifosize, "i"); +MODULE_PARM (calibrate_signal, "i"); +#ifndef MSND_CLASSIC +MODULE_PARM (digital, "i"); +MODULE_PARM (cfg, "i"); +MODULE_PARM (reset, "i"); +MODULE_PARM (mpu_io, "i"); +MODULE_PARM (mpu_irq, "i"); +MODULE_PARM (ide_io0, "i"); +MODULE_PARM (ide_io1, "i"); +MODULE_PARM (ide_irq, "i"); +MODULE_PARM (joystick_io, "i"); +#endif + +static int io __initdata = -1; +static int irq __initdata = -1; +static int mem __initdata = -1; +static int write_ndelay __initdata = -1; + +#ifndef MSND_CLASSIC +/* Pinnacle/Fiji non-PnP Config Port */ +static int cfg __initdata = -1; + +/* Extra Peripheral Configuration */ +static int reset __initdata = 0; +static int mpu_io __initdata = 0; +static int mpu_irq __initdata = 0; +static int ide_io0 __initdata = 0; +static int ide_io1 __initdata = 0; +static int ide_irq __initdata = 0; +static int joystick_io __initdata = 0; + +/* If we have the digital daugherboard... */ +static int digital __initdata = 0; +#endif + +static int fifosize __initdata = DEFFIFOSIZE; +static int calibrate_signal __initdata = 0; + +#else /* not a module */ + +static int write_ndelay __initdata = -1; + +#ifdef MSND_CLASSIC +static int io __initdata = CONFIG_MSNDCLAS_IO; +static int irq __initdata = CONFIG_MSNDCLAS_IRQ; +static int mem __initdata = CONFIG_MSNDCLAS_MEM; +#else /* Pinnacle/Fiji */ + +static int io __initdata = CONFIG_MSNDPIN_IO; +static int irq __initdata = CONFIG_MSNDPIN_IRQ; +static int mem __initdata = CONFIG_MSNDPIN_MEM; + +/* Pinnacle/Fiji non-PnP Config Port */ +#ifdef CONFIG_MSNDPIN_NONPNP +# ifndef CONFIG_MSNDPIN_CFG +# define CONFIG_MSNDPIN_CFG 0x250 +# endif +#else +# ifdef CONFIG_MSNDPIN_CFG +# undef CONFIG_MSNDPIN_CFG +# endif +# define CONFIG_MSNDPIN_CFG -1 +#endif +static int cfg __initdata = CONFIG_MSNDPIN_CFG; +/* If not a module, we don't need to bother with reset=1 */ +static int reset; + +/* Extra Peripheral Configuration (Default: Disable) */ +#ifndef CONFIG_MSNDPIN_MPU_IO +# define CONFIG_MSNDPIN_MPU_IO 0 +#endif +static int mpu_io __initdata = CONFIG_MSNDPIN_MPU_IO; + +#ifndef CONFIG_MSNDPIN_MPU_IRQ +# define CONFIG_MSNDPIN_MPU_IRQ 0 +#endif +static int mpu_irq __initdata = CONFIG_MSNDPIN_MPU_IRQ; + +#ifndef CONFIG_MSNDPIN_IDE_IO0 +# define CONFIG_MSNDPIN_IDE_IO0 0 +#endif +static int ide_io0 __initdata = CONFIG_MSNDPIN_IDE_IO0; + +#ifndef CONFIG_MSNDPIN_IDE_IO1 +# define CONFIG_MSNDPIN_IDE_IO1 0 +#endif +static int ide_io1 __initdata = CONFIG_MSNDPIN_IDE_IO1; + +#ifndef CONFIG_MSNDPIN_IDE_IRQ +# define CONFIG_MSNDPIN_IDE_IRQ 0 +#endif +static int ide_irq __initdata = CONFIG_MSNDPIN_IDE_IRQ; + +#ifndef CONFIG_MSNDPIN_JOYSTICK_IO +# define CONFIG_MSNDPIN_JOYSTICK_IO 0 +#endif +static int joystick_io __initdata = CONFIG_MSNDPIN_JOYSTICK_IO; + +/* Have SPDIF (Digital) Daughterboard */ +#ifndef CONFIG_MSNDPIN_DIGITAL +# define CONFIG_MSNDPIN_DIGITAL 0 +#endif +static int digital __initdata = CONFIG_MSNDPIN_DIGITAL; + +#endif /* MSND_CLASSIC */ + +#ifndef CONFIG_MSND_FIFOSIZE +# define CONFIG_MSND_FIFOSIZE DEFFIFOSIZE +#endif +static int fifosize __initdata = CONFIG_MSND_FIFOSIZE; + +#ifndef CONFIG_MSND_CALSIGNAL +# define CONFIG_MSND_CALSIGNAL 0 +#endif +static int +calibrate_signal __initdata = CONFIG_MSND_CALSIGNAL; +#endif /* MODULE */ + + +static int __init msnd_init(void) +{ + int err; +#ifndef MSND_CLASSIC + static msnd_pinnacle_cfg_t pinnacle_devs; +#endif /* MSND_CLASSIC */ + + printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version " + VERSION ", Copyright (C) 1998 Andrew Veliath\n"); + + if (io == -1 || irq == -1 || mem == -1) + printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n"); + +#ifdef MSND_CLASSIC + if (io == -1 || + !(io == 0x290 || + io == 0x260 || + io == 0x250 || + io == 0x240 || + io == 0x230 || + io == 0x220 || + io == 0x210 || + io == 0x3e0)) { + printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n"); + return -EINVAL; + } +#else + if (io == -1 || + io < 0x100 || + io > 0x3e0 || + (io % 0x10) != 0) { + printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n"); + return -EINVAL; + } +#endif /* MSND_CLASSIC */ + + if (irq == -1 || + !(irq == 5 || + irq == 7 || + irq == 9 || + irq == 10 || + irq == 11 || + irq == 12)) { + printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n"); + return -EINVAL; + } + + if (mem == -1 || + !(mem == 0xb0000 || + mem == 0xc8000 || + mem == 0xd0000 || + mem == 0xd8000 || + mem == 0xe0000 || + mem == 0xe8000)) { + printk(KERN_ERR LOGNAME ": \"mem\" - must be set to " + "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); + return -EINVAL; + } + +#ifdef MSND_CLASSIC + switch (irq) { + case 5: dev.irqid = HPIRQ_5; break; + case 7: dev.irqid = HPIRQ_7; break; + case 9: dev.irqid = HPIRQ_9; break; + case 10: dev.irqid = HPIRQ_10; break; + case 11: dev.irqid = HPIRQ_11; break; + case 12: dev.irqid = HPIRQ_12; break; + } + + switch (mem) { + case 0xb0000: dev.memid = HPMEM_B000; break; + case 0xc8000: dev.memid = HPMEM_C800; break; + case 0xd0000: dev.memid = HPMEM_D000; break; + case 0xd8000: dev.memid = HPMEM_D800; break; + case 0xe0000: dev.memid = HPMEM_E000; break; + case 0xe8000: dev.memid = HPMEM_E800; break; + } +#else + if (cfg == -1) { + printk(KERN_INFO LOGNAME ": Assuming PnP mode\n"); + } else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) { + printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n"); + return -EINVAL; + } else { + printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg); + + /* DSP */ + pinnacle_devs[0].io0 = io; + pinnacle_devs[0].irq = irq; + pinnacle_devs[0].mem = mem; + + /* The following are Pinnacle specific */ + + /* MPU */ + pinnacle_devs[1].io0 = mpu_io; + pinnacle_devs[1].irq = mpu_irq; + + /* IDE */ + pinnacle_devs[2].io0 = ide_io0; + pinnacle_devs[2].io1 = ide_io1; + pinnacle_devs[2].irq = ide_irq; + + /* Joystick */ + pinnacle_devs[3].io0 = joystick_io; + + if (check_region(cfg, 2)) { + printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg); + return -EIO; + } + + request_region(cfg, 2, "Pinnacle/Fiji Config"); + if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) { + printk(KERN_ERR LOGNAME ": Device configuration error\n"); + release_region(cfg, 2); + return -EIO; + } + release_region(cfg, 2); + } +#endif /* MSND_CLASSIC */ + + if (fifosize < 16) + fifosize = 16; + + if (fifosize > 1024) + fifosize = 1024; + + set_default_audio_parameters(); +#ifdef MSND_CLASSIC + dev.type = msndClassic; +#else + dev.type = msndPinnacle; +#endif + dev.io = io; + dev.numio = DSP_NUMIO; + dev.irq = irq; + dev.base = mem; + dev.fifosize = fifosize * 1024; + dev.calibrate_signal = calibrate_signal ? 1 : 0; + dev.recsrc = 0; + dev.dspq_data_buff = DSPQ_DATA_BUFF; + dev.dspq_buff_size = DSPQ_BUFF_SIZE; + if (write_ndelay == -1) + write_ndelay = CONFIG_MSND_WRITE_NDELAY; + if (write_ndelay) + clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); + else + set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); +#ifndef MSND_CLASSIC + if (digital) + set_bit(F_HAVEDIGITAL, &dev.flags); +#endif + init_waitqueue_head(&dev.writeblock); + init_waitqueue_head(&dev.readblock); + init_waitqueue_head(&dev.writeflush); + msnd_fifo_init(&dev.DAPF); + msnd_fifo_init(&dev.DARF); + spin_lock_init(&dev.lock); + printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize); + if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) { + printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n"); + return err; + } + + if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) { + printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n"); + msnd_fifo_free(&dev.DAPF); + return err; + } + + if ((err = probe_multisound()) < 0) { + printk(KERN_ERR LOGNAME ": Probe failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + } + + if ((err = attach_multisound()) < 0) { + printk(KERN_ERR LOGNAME ": Attach failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + } + + return 0; +} + +static void __exit msdn_cleanup(void) +{ + unload_multisound(); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); +} + +module_init(msnd_init); +module_exit(msdn_cleanup); diff -Nru linux/sound/oss/msnd_pinnacle.h linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.h --- linux/sound/oss/msnd_pinnacle.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/msnd_pinnacle.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,249 @@ +/********************************************************************* + * + * msnd_pinnacle.h + * + * Turtle Beach MultiSound Sound Card Driver for Linux + * + * Some parts of this header file were derived from the Turtle Beach + * MultiSound Driver Development Kit. + * + * Copyright (C) 1998 Andrew Veliath + * Copyright (C) 1993 Turtle Beach Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: msnd_pinnacle.h,v 1.11 1999/03/21 17:36:09 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_PINNACLE_H +#define __MSND_PINNACLE_H + +#include + +#define DSP_NUMIO 0x08 + +#define IREG_LOGDEVICE 0x07 +#define IREG_ACTIVATE 0x30 +#define LD_ACTIVATE 0x01 +#define LD_DISACTIVATE 0x00 +#define IREG_EECONTROL 0x3F +#define IREG_MEMBASEHI 0x40 +#define IREG_MEMBASELO 0x41 +#define IREG_MEMCONTROL 0x42 +#define IREG_MEMRANGEHI 0x43 +#define IREG_MEMRANGELO 0x44 +#define MEMTYPE_8BIT 0x00 +#define MEMTYPE_16BIT 0x02 +#define MEMTYPE_RANGE 0x00 +#define MEMTYPE_HIADDR 0x01 +#define IREG_IO0_BASEHI 0x60 +#define IREG_IO0_BASELO 0x61 +#define IREG_IO1_BASEHI 0x62 +#define IREG_IO1_BASELO 0x63 +#define IREG_IRQ_NUMBER 0x70 +#define IREG_IRQ_TYPE 0x71 +#define IRQTYPE_HIGH 0x02 +#define IRQTYPE_LOW 0x00 +#define IRQTYPE_LEVEL 0x01 +#define IRQTYPE_EDGE 0x00 + +#define HP_DSPR 0x04 +#define HP_BLKS 0x04 + +#define HPDSPRESET_OFF 2 +#define HPDSPRESET_ON 0 + +#define HPBLKSEL_0 2 +#define HPBLKSEL_1 3 + +#define HIMT_DAT_OFF 0x03 + +#define HIDSP_PLAY_UNDER 0x00 +#define HIDSP_INT_PLAY_UNDER 0x01 +#define HIDSP_SSI_TX_UNDER 0x02 +#define HIDSP_RECQ_OVERFLOW 0x08 +#define HIDSP_INT_RECORD_OVER 0x09 +#define HIDSP_SSI_RX_OVERFLOW 0x0a + +#define HIDSP_MIDI_IN_OVER 0x10 + +#define HIDSP_MIDI_FRAME_ERR 0x11 +#define HIDSP_MIDI_PARITY_ERR 0x12 +#define HIDSP_MIDI_OVERRUN_ERR 0x13 + +#define HIDSP_INPUT_CLIPPING 0x20 +#define HIDSP_MIX_CLIPPING 0x30 +#define HIDSP_DAT_IN_OFF 0x21 + +#define HDEXAR_SET_ANA_IN 0 +#define HDEXAR_CLEAR_PEAKS 1 +#define HDEXAR_IN_SET_POTS 2 +#define HDEXAR_AUX_SET_POTS 3 +#define HDEXAR_CAL_A_TO_D 4 +#define HDEXAR_RD_EXT_DSP_BITS 5 + +#define HDEXAR_SET_SYNTH_IN 4 +#define HDEXAR_READ_DAT_IN 5 +#define HDEXAR_MIC_SET_POTS 6 +#define HDEXAR_SET_DAT_IN 7 + +#define HDEXAR_SET_SYNTH_48 8 +#define HDEXAR_SET_SYNTH_44 9 + +#define TIME_PRO_RESET_DONE 0x028A +#define TIME_PRO_SYSEX 0x001E +#define TIME_PRO_RESET 0x0032 + +#define AGND 0x01 +#define SIGNAL 0x02 + +#define EXT_DSP_BIT_DCAL 0x0001 +#define EXT_DSP_BIT_MIDI_CON 0x0002 + +#define BUFFSIZE 0x8000 +#define HOSTQ_SIZE 0x40 + +#define SRAM_CNTL_START 0x7F00 +#define SMA_STRUCT_START 0x7F40 + +#define DAP_BUFF_SIZE 0x2400 +#define DAR_BUFF_SIZE 0x2000 + +#define DAPQ_STRUCT_SIZE 0x10 +#define DARQ_STRUCT_SIZE 0x10 +#define DAPQ_BUFF_SIZE (3 * 0x10) +#define DARQ_BUFF_SIZE (3 * 0x10) +#define MODQ_BUFF_SIZE 0x400 +#define MIDQ_BUFF_SIZE 0x800 +#define DSPQ_BUFF_SIZE 0x5A0 + +#define DAPQ_DATA_BUFF 0x6C00 +#define DARQ_DATA_BUFF 0x6C30 +#define MODQ_DATA_BUFF 0x6C60 +#define MIDQ_DATA_BUFF 0x7060 +#define DSPQ_DATA_BUFF 0x7860 + +#define DAPQ_OFFSET SRAM_CNTL_START +#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) +#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) +#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) +#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) + +#define MOP_WAVEHDR 0 +#define MOP_EXTOUT 1 +#define MOP_HWINIT 0xfe +#define MOP_NONE 0xff +#define MOP_MAX 1 + +#define MIP_EXTIN 0 +#define MIP_WAVEHDR 1 +#define MIP_HWINIT 0xfe +#define MIP_MAX 1 + +/* Pinnacle/Fiji SMA Common Data */ +#define SMA_wCurrPlayBytes 0x0000 +#define SMA_wCurrRecordBytes 0x0002 +#define SMA_wCurrPlayVolLeft 0x0004 +#define SMA_wCurrPlayVolRight 0x0006 +#define SMA_wCurrInVolLeft 0x0008 +#define SMA_wCurrInVolRight 0x000a +#define SMA_wCurrMHdrVolLeft 0x000c +#define SMA_wCurrMHdrVolRight 0x000e +#define SMA_dwCurrPlayPitch 0x0010 +#define SMA_dwCurrPlayRate 0x0014 +#define SMA_wCurrMIDIIOPatch 0x0018 +#define SMA_wCurrPlayFormat 0x001a +#define SMA_wCurrPlaySampleSize 0x001c +#define SMA_wCurrPlayChannels 0x001e +#define SMA_wCurrPlaySampleRate 0x0020 +#define SMA_wCurrRecordFormat 0x0022 +#define SMA_wCurrRecordSampleSize 0x0024 +#define SMA_wCurrRecordChannels 0x0026 +#define SMA_wCurrRecordSampleRate 0x0028 +#define SMA_wCurrDSPStatusFlags 0x002a +#define SMA_wCurrHostStatusFlags 0x002c +#define SMA_wCurrInputTagBits 0x002e +#define SMA_wCurrLeftPeak 0x0030 +#define SMA_wCurrRightPeak 0x0032 +#define SMA_bMicPotPosLeft 0x0034 +#define SMA_bMicPotPosRight 0x0035 +#define SMA_bMicPotMaxLeft 0x0036 +#define SMA_bMicPotMaxRight 0x0037 +#define SMA_bInPotPosLeft 0x0038 +#define SMA_bInPotPosRight 0x0039 +#define SMA_bAuxPotPosLeft 0x003a +#define SMA_bAuxPotPosRight 0x003b +#define SMA_bInPotMaxLeft 0x003c +#define SMA_bInPotMaxRight 0x003d +#define SMA_bAuxPotMaxLeft 0x003e +#define SMA_bAuxPotMaxRight 0x003f +#define SMA_bInPotMaxMethod 0x0040 +#define SMA_bAuxPotMaxMethod 0x0041 +#define SMA_wCurrMastVolLeft 0x0042 +#define SMA_wCurrMastVolRight 0x0044 +#define SMA_wCalFreqAtoD 0x0046 +#define SMA_wCurrAuxVolLeft 0x0048 +#define SMA_wCurrAuxVolRight 0x004a +#define SMA_wCurrPlay1VolLeft 0x004c +#define SMA_wCurrPlay1VolRight 0x004e +#define SMA_wCurrPlay2VolLeft 0x0050 +#define SMA_wCurrPlay2VolRight 0x0052 +#define SMA_wCurrPlay3VolLeft 0x0054 +#define SMA_wCurrPlay3VolRight 0x0056 +#define SMA_wCurrPlay4VolLeft 0x0058 +#define SMA_wCurrPlay4VolRight 0x005a +#define SMA_wCurrPlay1PeakLeft 0x005c +#define SMA_wCurrPlay1PeakRight 0x005e +#define SMA_wCurrPlay2PeakLeft 0x0060 +#define SMA_wCurrPlay2PeakRight 0x0062 +#define SMA_wCurrPlay3PeakLeft 0x0064 +#define SMA_wCurrPlay3PeakRight 0x0066 +#define SMA_wCurrPlay4PeakLeft 0x0068 +#define SMA_wCurrPlay4PeakRight 0x006a +#define SMA_wCurrPlayPeakLeft 0x006c +#define SMA_wCurrPlayPeakRight 0x006e +#define SMA_wCurrDATSR 0x0070 +#define SMA_wCurrDATRXCHNL 0x0072 +#define SMA_wCurrDATTXCHNL 0x0074 +#define SMA_wCurrDATRXRate 0x0076 +#define SMA_dwDSPPlayCount 0x0078 +#define SMA__size 0x007c + +#ifdef HAVE_DSPCODEH +# include "pndsperm.c" +# include "pndspini.c" +# define PERMCODE pndsperm +# define INITCODE pndspini +# define PERMCODESIZE sizeof(pndsperm) +# define INITCODESIZE sizeof(pndspini) +#else +# ifndef CONFIG_MSNDPIN_INIT_FILE +# define CONFIG_MSNDPIN_INIT_FILE \ + "/etc/sound/pndspini.bin" +# endif +# ifndef CONFIG_MSNDPIN_PERM_FILE +# define CONFIG_MSNDPIN_PERM_FILE \ + "/etc/sound/pndsperm.bin" +# endif +# define PERMCODEFILE CONFIG_MSNDPIN_PERM_FILE +# define INITCODEFILE CONFIG_MSNDPIN_INIT_FILE +# define PERMCODE dspini +# define INITCODE permini +# define PERMCODESIZE sizeof_dspini +# define INITCODESIZE sizeof_permini +#endif +#define LONGNAME "MultiSound (Pinnacle/Fiji)" + +#endif /* __MSND_PINNACLE_H */ diff -Nru linux/sound/oss/nec_vrc5477.c linux-2.4.19-pre5-mjc/sound/oss/nec_vrc5477.c --- linux/sound/oss/nec_vrc5477.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/nec_vrc5477.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,2039 @@ +/*********************************************************************** + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * drivers/sound/nec_vrc5477.c + * AC97 sound dirver for NEC Vrc5477 chip (an integrated, + * multi-function controller chip for MIPS CPUs) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + *********************************************************************** + */ + +/* + * This code is derived from ite8172.c, which is written by Steve Longerbeam. + * + * Features: + * Currently we only support the following capabilities: + * . mono output to PCM L/R (line out). + * . stereo output to PCM L/R (line out). + * . mono input from PCM L (line in). + * . stereo output from PCM (line in). + * . sampling rate at 48k or variable sampling rate + * . support /dev/dsp, /dev/mixer devices, standard OSS devices. + * . only support 16-bit PCM format (hardware limit, no software + * translation) + * . support duplex, but no trigger or realtime. + * + * Specifically the following are not supported: + * . app-set frag size. + * . mmap'ed buffer access + */ + +/* + * Original comments from ite8172.c file. + */ + +/* + * + * Notes: + * + * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are + * taken, slightly modified or not at all, from the ES1371 driver, + * so refer to the credits in es1371.c for those. The rest of the + * code (probe, open, read, write, the ISR, etc.) is new. + * 2. The following support is untested: + * * Memory mapping the audio buffers, and the ioctl controls that go + * with it. + * * S/PDIF output. + * 3. The following is not supported: + * * I2S input. + * * legacy audio mode. + * 4. Support for volume button interrupts is implemented but doesn't + * work yet. + * + * Revision history + * 02.08.2001 0.1 Initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef VRC5477_AC97_VERBOSE_DEBUG + +/* one must turn on CONFIG_LL_DEBUG before VERBOSE_DEBUG is turned */ +#if defined(VRC5477_AC97_VERBOSE_DEBUG) +#if !defined(CONFIG_LL_DEBUG) +#error "You must turn CONFIG_LL_DEBUG" +#endif +#endif + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) +static u16 inTicket=0; /* check sync between intr & write */ +static u16 outTicket=0; +#endif + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define VRC5477_INT_CLR 0x0 +#define VRC5477_INT_STATUS 0x0 +#define VRC5477_CODEC_WR 0x4 +#define VRC5477_CODEC_RD 0x8 +#define VRC5477_CTRL 0x18 +#define VRC5477_ACLINK_CTRL 0x1c +#define VRC5477_INT_MASK 0x24 + +#define VRC5477_DAC1_CTRL 0x30 +#define VRC5477_DAC1L 0x34 +#define VRC5477_DAC1_BADDR 0x38 +#define VRC5477_DAC2_CTRL 0x3c +#define VRC5477_DAC2L 0x40 +#define VRC5477_DAC2_BADDR 0x44 +#define VRC5477_DAC3_CTRL 0x48 +#define VRC5477_DAC3L 0x4c +#define VRC5477_DAC3_BADDR 0x50 + +#define VRC5477_ADC1_CTRL 0x54 +#define VRC5477_ADC1L 0x58 +#define VRC5477_ADC1_BADDR 0x5c +#define VRC5477_ADC2_CTRL 0x60 +#define VRC5477_ADC2L 0x64 +#define VRC5477_ADC2_BADDR 0x68 +#define VRC5477_ADC3_CTRL 0x6c +#define VRC5477_ADC3L 0x70 +#define VRC5477_ADC3_BADDR 0x74 + +#define VRC5477_CODEC_WR_RWC (1 << 23) + +#define VRC5477_CODEC_RD_RRDYA (1 << 31) +#define VRC5477_CODEC_RD_RRDYD (1 << 30) + +#define VRC5477_ACLINK_CTRL_RST_ON (1 << 15) +#define VRC5477_ACLINK_CTRL_RST_TIME 0x7f +#define VRC5477_ACLINK_CTRL_SYNC_ON (1 << 30) +#define VRC5477_ACLINK_CTRL_CK_STOP_ON (1 << 31) + +#define VRC5477_CTRL_DAC2ENB (1 << 15) +#define VRC5477_CTRL_ADC2ENB (1 << 14) +#define VRC5477_CTRL_DAC1ENB (1 << 13) +#define VRC5477_CTRL_ADC1ENB (1 << 12) + +#define VRC5477_INT_MASK_NMASK (1 << 31) +#define VRC5477_INT_MASK_DAC1END (1 << 5) +#define VRC5477_INT_MASK_DAC2END (1 << 4) +#define VRC5477_INT_MASK_DAC3END (1 << 3) +#define VRC5477_INT_MASK_ADC1END (1 << 2) +#define VRC5477_INT_MASK_ADC2END (1 << 1) +#define VRC5477_INT_MASK_ADC3END (1 << 0) + +#define VRC5477_DMA_ACTIVATION (1 << 31) +#define VRC5477_DMA_WIP (1 << 30) + + +#define VRC5477_AC97_MODULE_NAME "NEC_Vrc5477_audio" +#define PFX VRC5477_AC97_MODULE_NAME ": " + +/* --------------------------------------------------------------------- */ + +struct vrc5477_ac97_state { + /* list of vrc5477_ac97 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + + /* hardware resources */ + unsigned long io; + unsigned int irq; + +#ifdef CONFIG_LL_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; +#endif /* CONFIG_LL_DEBUG */ + + struct ac97_codec codec; + + unsigned dacChannels, adcChannels; + unsigned short dacRate, adcRate; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *lbuf, *rbuf; + dma_addr_t lbufDma, rbufDma; + unsigned bufOrder; + unsigned numFrag; + unsigned fragShift; + unsigned fragSize; /* redundant */ + unsigned fragTotalSize; /* = numFrag * fragSize(real) */ + unsigned nextIn; + unsigned nextOut; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* OSS stuff */ + unsigned stopped:1; + unsigned ready:1; + } dma_dac, dma_adc; + + #define WORK_BUF_SIZE 2048 + struct { + u16 lchannel; + u16 rchannel; + } workBuf[WORK_BUF_SIZE/4]; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)codec->private_data; + unsigned long flags; + u32 result; + + spin_lock_irqsave(&s->lock, flags); + + /* wait until we can access codec registers */ + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and "read" command to codec */ + addr = addr & 0x7f; + outl((addr << 16) | VRC5477_CODEC_WR_RWC, s->io + VRC5477_CODEC_WR); + + /* get the return result */ + udelay(100); /* workaround hardware bug */ + while ( (result = inl(s->io + VRC5477_CODEC_RD)) & + (VRC5477_CODEC_RD_RRDYA | VRC5477_CODEC_RD_RRDYD) ) { + /* we get either addr or data, or both */ + if (result & VRC5477_CODEC_RD_RRDYA) { + MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); + } + if (result & VRC5477_CODEC_RD_RRDYD) { + break; + } + } + + spin_unlock_irqrestore(&s->lock, flags); + + return result & 0xffff;; +} + + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)codec->private_data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + /* wait until we can access codec registers */ + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and value to codec */ + outl((addr << 16) | data, s->io + VRC5477_CODEC_WR); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void waitcodec(struct ac97_codec *codec) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)codec->private_data; + + /* wait until we can access codec registers */ + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); +} + + +/* --------------------------------------------------------------------- */ + +static void vrc5477_ac97_delay(int msec) +{ + unsigned long tmo; + signed long tmo2; + + if (in_interrupt()) + return; + + tmo = jiffies + (msec*HZ)/1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } +} + + +static void set_adc_rate(struct vrc5477_ac97_state *s, unsigned rate) +{ + wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, rate); + s->adcRate = rate; +} + + +static void set_dac_rate(struct vrc5477_ac97_state *s, unsigned rate) +{ + wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, rate); + s->dacRate = rate; +} + + +/* --------------------------------------------------------------------- */ + +extern inline void +stop_dac(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* deactivate the dma */ + outl(0, s->io + VRC5477_DAC1_CTRL); + outl(0, s->io + VRC5477_DAC2_CTRL); + + /* wait for DAM completely stop */ + while (inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); + while (inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); + + /* disable dac slots in aclink */ + temp = inl(s->io + VRC5477_CTRL); + temp &= ~ (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* disable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp &= ~ (VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END); + outl (temp, s->io + VRC5477_INT_MASK); + + /* clear pending ones */ + outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, + s->io + VRC5477_INT_CLR); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + u32 dmaLength; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (!db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* we should have some data to do the DMA trasnfer */ + MIPS_ASSERT(db->count >= db->fragSize); + + /* clear pending fales interrupts */ + outl(VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END, + s->io + VRC5477_INT_CLR); + + /* enable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp |= VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; + outl(temp, s->io + VRC5477_INT_MASK); + + /* setup dma base addr */ + outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC1_BADDR); + if (s->dacChannels == 1) { + outl(db->lbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); + } else { + outl(db->rbufDma + db->nextOut, s->io + VRC5477_DAC2_BADDR); + } + + /* set dma length, in the unit of 0x10 bytes */ + dmaLength = db->fragSize >> 4; + outl(dmaLength, s->io + VRC5477_DAC1L); + outl(dmaLength, s->io + VRC5477_DAC2L); + + /* activate dma */ + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC1_CTRL); + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_DAC2_CTRL); + + /* enable dac slots - we should hear the music now! */ + temp = inl(s->io + VRC5477_CTRL); + temp |= (VRC5477_CTRL_DAC1ENB | VRC5477_CTRL_DAC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* it is time to setup next dma transfer */ + MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); + MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); + + temp = db->nextOut + db->fragSize; + if (temp >= db->fragTotalSize) { + MIPS_ASSERT(temp == db->fragTotalSize); + temp = 0; + } + + outl(db->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); + if (s->dacChannels == 1) { + outl(db->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } else { + outl(db->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } + + db->stopped = 0; + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + outTicket = *(u16*)(db->lbuf+db->nextOut); + if (db->count > db->fragSize) { + MIPS_ASSERT((u16)(outTicket+1) == *(u16*)(db->lbuf+temp)); + } +#endif + + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_adc(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* deactivate the dma */ + outl(0, s->io + VRC5477_ADC1_CTRL); + outl(0, s->io + VRC5477_ADC2_CTRL); + + /* disable adc slots in aclink */ + temp = inl(s->io + VRC5477_CTRL); + temp &= ~ (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* disable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp &= ~ (VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END); + outl (temp, s->io + VRC5477_INT_MASK); + + /* clear pending ones */ + outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, + s->io + VRC5477_INT_CLR); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct vrc5477_ac97_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + u32 dmaLength; + u32 temp; + + spin_lock_irqsave(&s->lock, flags); + + if (!db->stopped) { + spin_unlock_irqrestore(&s->lock, flags); + return; + } + + /* we should at least have some free space in the buffer */ + MIPS_ASSERT(db->count < db->fragTotalSize - db->fragSize * 2); + + /* clear pending ones */ + outl(VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END, + s->io + VRC5477_INT_CLR); + + /* enable interrupts */ + temp = inl(s->io + VRC5477_INT_MASK); + temp |= VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; + outl(temp, s->io + VRC5477_INT_MASK); + + /* setup dma base addr */ + outl(db->lbufDma + db->nextIn, s->io + VRC5477_ADC1_BADDR); + outl(db->rbufDma + db->nextIn, s->io + VRC5477_ADC2_BADDR); + + /* setup dma length */ + dmaLength = db->fragSize >> 4; + outl(dmaLength, s->io + VRC5477_ADC1L); + outl(dmaLength, s->io + VRC5477_ADC2L); + + /* activate dma */ + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC1_CTRL); + outl(VRC5477_DMA_ACTIVATION, s->io + VRC5477_ADC2_CTRL); + + /* enable adc slots */ + temp = inl(s->io + VRC5477_CTRL); + temp |= (VRC5477_CTRL_ADC1ENB | VRC5477_CTRL_ADC2ENB); + outl (temp, s->io + VRC5477_CTRL); + + /* it is time to setup next dma transfer */ + temp = db->nextIn + db->fragSize; + if (temp >= db->fragTotalSize) { + MIPS_ASSERT(temp == db->fragTotalSize); + temp = 0; + } + outl(db->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); + outl(db->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +extern inline void dealloc_dmabuf(struct vrc5477_ac97_state *s, + struct dmabuf *db) +{ + if (db->lbuf) { + MIPS_ASSERT(db->rbuf); + pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, + db->lbuf, db->lbufDma); + pci_free_consistent(s->dev, PAGE_SIZE << db->bufOrder, + db->rbuf, db->rbufDma); + db->lbuf = db->rbuf = NULL; + } + db->nextIn = db->nextOut = 0; + db->ready = 0; +} + +static int prog_dmabuf(struct vrc5477_ac97_state *s, + struct dmabuf *db, + unsigned rate) +{ + int order; + unsigned bufsize; + + if (!db->lbuf) { + MIPS_ASSERT(!db->rbuf); + + db->ready = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; + order--) { + db->lbuf = pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->lbufDma); + db->rbuf = pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->rbufDma); + if (db->lbuf && db->rbuf) break; + if (db->lbuf) { + MIPS_ASSERT(!db->rbuf); + pci_free_consistent(s->dev, + PAGE_SIZE << order, + db->lbuf, + db->lbufDma); + } + } + if (!db->lbuf) { + MIPS_ASSERT(!db->rbuf); + return -ENOMEM; + } + + db->bufOrder = order; + } + + db->count = 0; + db->nextIn = db->nextOut = 0; + + bufsize = PAGE_SIZE << db->bufOrder; + db->fragShift = ld2(rate * 2 / 100); + if (db->fragShift < 4) db->fragShift = 4; + + db->numFrag = bufsize >> db->fragShift; + while (db->numFrag < 4 && db->fragShift > 4) { + db->fragShift--; + db->numFrag = bufsize >> db->fragShift; + } + db->fragSize = 1 << db->fragShift; + db->fragTotalSize = db->numFrag << db->fragShift; + memset(db->lbuf, 0, db->fragTotalSize); + memset(db->rbuf, 0, db->fragTotalSize); + + db->ready = 1; + + return 0; +} + +extern inline int prog_dmabuf_adc(struct vrc5477_ac97_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcRate); +} + +extern inline int prog_dmabuf_dac(struct vrc5477_ac97_state *s) +{ + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac, s->dacRate); +} + + +/* --------------------------------------------------------------------- */ +/* hold spinlock for the following! */ + +static inline void vrc5477_ac97_adc_interrupt(struct vrc5477_ac97_state *s) +{ + struct dmabuf* adc = &s->dma_adc; + unsigned temp; + + /* we need two frags avaiable because one is already being used + * and the other will be used when next interrupt happens. + */ + if (adc->count >= adc->fragTotalSize - adc->fragSize) { + stop_adc(s); + adc->error++; + printk(KERN_INFO PFX "adc overrun\n"); + return; + } + + /* set the base addr for next DMA transfer */ + temp = adc->nextIn + 2*adc->fragSize; + if (temp >= adc->fragTotalSize) { + MIPS_ASSERT( (temp == adc->fragTotalSize) || + (temp == adc->fragTotalSize + adc->fragSize) ); + temp -= adc->fragTotalSize; + } + outl(adc->lbufDma + temp, s->io + VRC5477_ADC1_BADDR); + outl(adc->rbufDma + temp, s->io + VRC5477_ADC2_BADDR); + + /* adjust nextIn */ + adc->nextIn += adc->fragSize; + if (adc->nextIn >= adc->fragTotalSize) { + MIPS_ASSERT(adc->nextIn == adc->fragTotalSize); + adc->nextIn = 0; + } + + /* adjust count */ + adc->count += adc->fragSize; + + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) { + wake_up_interruptible(&adc->wait); + } +} + +static inline void vrc5477_ac97_dac_interrupt(struct vrc5477_ac97_state *s) +{ + struct dmabuf* dac = &s->dma_dac; + unsigned temp; + + /* next DMA transfer should already started */ + MIPS_ASSERT(inl(s->io + VRC5477_DAC1_CTRL) & VRC5477_DMA_WIP); + MIPS_ASSERT(inl(s->io + VRC5477_DAC2_CTRL) & VRC5477_DMA_WIP); + + /* let us set for next next DMA transfer */ + temp = dac->nextOut + dac->fragSize*2; + if (temp >= dac->fragTotalSize) { + MIPS_ASSERT( (temp == dac->fragTotalSize) || + (temp == dac->fragTotalSize + dac->fragSize) ); + temp -= dac->fragTotalSize; + } + outl(dac->lbufDma + temp, s->io + VRC5477_DAC1_BADDR); + if (s->dacChannels == 1) { + outl(dac->lbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } else { + outl(dac->rbufDma + temp, s->io + VRC5477_DAC2_BADDR); + } + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + if (*(u16*)(dac->lbuf + dac->nextOut) != outTicket) { + printk("assert fail: - %d vs %d\n", + *(u16*)(dac->lbuf + dac->nextOut), + outTicket); + MIPS_ASSERT(1 == 0); + } +#endif + + /* adjust nextOut pointer */ + dac->nextOut += dac->fragSize; + if (dac->nextOut >= dac->fragTotalSize) { + MIPS_ASSERT(dac->nextOut == dac->fragTotalSize); + dac->nextOut = 0; + } + + /* adjust count */ + dac->count -= dac->fragSize; + if (dac->count <=0 ) { + MIPS_ASSERT(dac->count == 0); + MIPS_ASSERT(dac->nextIn == dac->nextOut); + /* buffer under run */ + stop_dac(s); + } + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + if (dac->count) { + outTicket ++; + MIPS_ASSERT(*(u16*)(dac->lbuf + dac->nextOut) == outTicket); + } +#endif + + /* we cannot have both under run and someone is waiting on us */ + MIPS_ASSERT(! (waitqueue_active(&dac->wait) && (dac->count <= 0)) ); + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up_interruptible(&dac->wait); +} + +static void vrc5477_ac97_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)dev_id; + u32 irqStatus; + u32 adcInterrupts, dacInterrupts; + + spin_lock(&s->lock); + + /* get irqStatus and clear the detected ones */ + irqStatus = inl(s->io + VRC5477_INT_STATUS); + outl(irqStatus, s->io + VRC5477_INT_CLR); + + /* let us see what we get */ + dacInterrupts = VRC5477_INT_MASK_DAC1END | VRC5477_INT_MASK_DAC2END; + adcInterrupts = VRC5477_INT_MASK_ADC1END | VRC5477_INT_MASK_ADC2END; + if (irqStatus & dacInterrupts) { + /* we should get both interrupts, but just in case ... */ + if (irqStatus & VRC5477_INT_MASK_DAC1END) { + vrc5477_ac97_dac_interrupt(s); + } + if ( (irqStatus & dacInterrupts) != dacInterrupts ) { + printk(KERN_WARNING "vrc5477_ac97 : dac interrupts not in sync!!!\n"); + stop_dac(s); + start_dac(s); + } + } else if (irqStatus & adcInterrupts) { + /* we should get both interrupts, but just in case ... */ + if(irqStatus & VRC5477_INT_MASK_ADC1END) { + vrc5477_ac97_adc_interrupt(s); + } + if ( (irqStatus & adcInterrupts) != adcInterrupts ) { + printk(KERN_WARNING "vrc5477_ac97 : adc interrupts not in sync!!!\n"); + stop_adc(s); + start_adc(s); + } + } + + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static int vrc5477_ac97_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct vrc5477_ac97_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct vrc5477_ac97_state, devs); + if (s->codec.dev_mixer == minor) + break; + } + file->private_data = s; + return 0; +} + +static int vrc5477_ac97_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + + +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, + unsigned long arg) +{ + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int vrc5477_ac97_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)file->private_data; + struct ac97_codec *codec = &s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations vrc5477_ac97_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: vrc5477_ac97_ioctl_mixdev, + open: vrc5477_ac97_open_mixdev, + release: vrc5477_ac97_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct vrc5477_ac97_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (!s->dma_dac.ready) + return 0; + + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) + return -EBUSY; + tmo = 1000 * count / s->dacRate / 2; + vrc5477_ac97_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int inline +copy_two_channel_adc_to_user(struct vrc5477_ac97_state *s, + char *buffer, + int copyCount) +{ + struct dmabuf *db = &s->dma_adc; + int bufStart = db->nextOut; + for (; copyCount > 0; ) { + int i; + int count = copyCount; + if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2; + for (i=0; i< count/2; i++) { + s->workBuf[i].lchannel = + *(u16*)(db->lbuf + bufStart + i*2); + s->workBuf[i].rchannel = + *(u16*)(db->rbuf + bufStart + i*2); + } + if (copy_to_user(buffer, s->workBuf, count*2)) { + return -1; + } + + copyCount -= count; + bufStart += count; + MIPS_ASSERT(bufStart <= db->fragTotalSize); + buffer += count *2; + } + return 0; +} + +/* return the total bytes that is copied */ +static int inline +copy_adc_to_user(struct vrc5477_ac97_state *s, + char * buffer, + size_t count, + int avail) +{ + struct dmabuf *db = &s->dma_adc; + int copyCount=0; + int copyFragCount=0; + int totalCopyCount = 0; + int totalCopyFragCount = 0; + unsigned long flags; + + /* adjust count to signel channel byte count */ + count >>= s->adcChannels - 1; + + /* we may have to "copy" twice as ring buffer wraps around */ + for (; (avail > 0) && (count > 0); ) { + /* determine max possible copy count for single channel */ + copyCount = count; + if (copyCount > avail) { + copyCount = avail; + } + if (copyCount + db->nextOut > db->fragTotalSize) { + copyCount = db->fragTotalSize - db->nextOut; + MIPS_ASSERT((copyCount % db->fragSize) == 0); + } + + copyFragCount = (copyCount-1) >> db->fragShift; + copyFragCount = (copyFragCount+1) << db->fragShift; + MIPS_ASSERT(copyFragCount >= copyCount); + + /* we copy differently based on adc channels */ + if (s->adcChannels == 1) { + if (copy_to_user(buffer, + db->lbuf + db->nextOut, + copyCount)) + return -1; + } else { + /* *sigh* we have to mix two streams into one */ + if (copy_two_channel_adc_to_user(s, buffer, copyCount)) + return -1; + } + + count -= copyCount; + totalCopyCount += copyCount; + avail -= copyFragCount; + totalCopyFragCount += copyFragCount; + + buffer += copyCount << (s->adcChannels-1); + + db->nextOut += copyFragCount; + if (db->nextOut >= db->fragTotalSize) { + MIPS_ASSERT(db->nextOut == db->fragTotalSize); + db->nextOut = 0; + } + + MIPS_ASSERT((copyFragCount % db->fragSize) == 0); + MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount)); + } + + spin_lock_irqsave(&s->lock, flags); + db->count -= totalCopyFragCount; + spin_unlock_irqrestore(&s->lock, flags); + + return totalCopyCount << (s->adcChannels-1); +} + +static ssize_t +vrc5477_ac97_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + ssize_t ret = 0; + unsigned long flags; + int copyCount; + size_t avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + MIPS_ASSERT(db->ready); + + while (count > 0) { + // wait for samples in capture buffer + do { + spin_lock_irqsave(&s->lock, flags); + if (db->stopped) + start_adc(s); + avail = db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + MIPS_ASSERT( (avail % db->fragSize) == 0); + copyCount = copy_adc_to_user(s, buffer, count, avail); + if (copyCount <=0 ) { + if (!ret) ret = -EFAULT; + return ret; + } + + count -= copyCount; + buffer += copyCount; + ret += copyCount; + } // while (count > 0) + + return ret; +} + +static int inline +copy_two_channel_dac_from_user(struct vrc5477_ac97_state *s, + const char *buffer, + int copyCount) +{ + struct dmabuf *db = &s->dma_dac; + int bufStart = db->nextIn; + + MIPS_ASSERT(db->ready); + + for (; copyCount > 0; ) { + int i; + int count = copyCount; + if (count > WORK_BUF_SIZE/2) count = WORK_BUF_SIZE/2; + if (copy_from_user(s->workBuf, buffer, count*2)) { + return -1; + } + for (i=0; i< count/2; i++) { + *(u16*)(db->lbuf + bufStart + i*2) = + s->workBuf[i].lchannel; + *(u16*)(db->rbuf + bufStart + i*2) = + s->workBuf[i].rchannel; + } + + copyCount -= count; + bufStart += count; + MIPS_ASSERT(bufStart <= db->fragTotalSize); + buffer += count *2; + } + return 0; + +} + +/* return the total bytes that is copied */ +static int inline +copy_dac_from_user(struct vrc5477_ac97_state *s, + const char *buffer, + size_t count, + int avail) +{ + struct dmabuf *db = &s->dma_dac; + int copyCount=0; + int copyFragCount=0; + int totalCopyCount = 0; + int totalCopyFragCount = 0; + unsigned long flags; +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + int i; +#endif + + /* adjust count to signel channel byte count */ + count >>= s->dacChannels - 1; + + /* we may have to "copy" twice as ring buffer wraps around */ + for (; (avail > 0) && (count > 0); ) { + /* determine max possible copy count for single channel */ + copyCount = count; + if (copyCount > avail) { + copyCount = avail; + } + if (copyCount + db->nextIn > db->fragTotalSize) { + copyCount = db->fragTotalSize - db->nextIn; + MIPS_ASSERT((copyCount % db->fragSize) == 0); + MIPS_ASSERT(copyCount > 0); + } + + copyFragCount = (copyCount-1) >> db->fragShift; + copyFragCount = (copyFragCount+1) << db->fragShift; + MIPS_ASSERT(copyFragCount >= copyCount); + + /* we copy differently based on the number channels */ + if (s->dacChannels == 1) { + if (copy_from_user(db->lbuf + db->nextIn, + buffer, + copyCount)) + return -1; + /* fill gaps with 0 */ + memset(db->lbuf + db->nextIn + copyCount, + 0, + copyFragCount - copyCount); + } else { + /* we have demux the stream into two separate ones */ + if (copy_two_channel_dac_from_user(s, buffer, copyCount)) + return -1; + /* fill gaps with 0 */ + memset(db->lbuf + db->nextIn + copyCount, + 0, + copyFragCount - copyCount); + memset(db->rbuf + db->nextIn + copyCount, + 0, + copyFragCount - copyCount); + } + +#if defined(VRC5477_AC97_VERBOSE_DEBUG) + for (i=0; i< copyFragCount; i+= db->fragSize) { + *(u16*)(db->lbuf + db->nextIn + i) = inTicket ++; + } +#endif + + count -= copyCount; + totalCopyCount =+ copyCount; + avail -= copyFragCount; + totalCopyFragCount += copyFragCount; + + buffer += copyCount << (s->dacChannels - 1); + + db->nextIn += copyFragCount; + if (db->nextIn >= db->fragTotalSize) { + MIPS_ASSERT(db->nextIn == db->fragTotalSize); + db->nextIn = 0; + } + + MIPS_ASSERT((copyFragCount % db->fragSize) == 0); + MIPS_ASSERT( (count == 0) || (copyCount == copyFragCount)); + } + + spin_lock_irqsave(&s->lock, flags); + db->count += totalCopyFragCount; + if (db->stopped) { + start_dac(s); + } + + /* nextIn should not be equal to nextOut unless we are full */ + MIPS_ASSERT( ( (db->count == db->fragTotalSize) && + (db->nextIn == db->nextOut) ) || + ( (db->count < db->fragTotalSize) && + (db->nextIn != db->nextOut) ) ); + + spin_unlock_irqrestore(&s->lock, flags); + + return totalCopyCount << (s->dacChannels-1); + +} + +static ssize_t vrc5477_ac97_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + ssize_t ret; + unsigned long flags; + int copyCount, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = db->fragTotalSize - db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + MIPS_ASSERT( (avail % db->fragSize) == 0); + copyCount = copy_dac_from_user(s, buffer, count, avail); + if (copyCount < 0) { + if (!ret) ret = -EFAULT; + return ret; + } + + count -= copyCount; + buffer += copyCount; + ret += copyCount; + } // while (count > 0) + + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int vrc5477_ac97_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragSize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if ((signed)s->dma_dac.fragTotalSize >= + s->dma_dac.count + (signed)s->dma_dac.fragSize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#ifdef CONFIG_LL_DEBUG +static struct ioctl_str_t { + unsigned int cmd; + const char* str; +} ioctl_str[] = { + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} +}; +#endif + +static int vrc5477_ac97_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + int count; + int val, ret; + +#ifdef CONFIG_LL_DEBUG + for (count=0; countf_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user((file->f_mode & FMODE_READ) ? + s->adcRate : s->dacRate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val) + s->adcChannels = 2; + else + s->adcChannels = 1; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val) + s->dacChannels = 2; + else + s->dacChannels = 1; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if ( (val != 1) && (val != 2)) val = 2; + + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dacChannels = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dacChannels = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (val != AFMT_S16_LE) return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + val = AFMT_S16_LE; + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + case SNDCTL_DSP_SETTRIGGER: + /* NO trigger */ + return -EINVAL; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragSize << (s->dacChannels-1); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + abinfo.bytes = (s->dma_dac.fragTotalSize - count) << + (s->dacChannels-1); + abinfo.fragstotal = s->dma_dac.numFrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragShift >> + (s->dacChannels-1); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragSize << (s->adcChannels-1); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count << (s->adcChannels-1); + abinfo.fragstotal = s->dma_adc.numFrag; + abinfo.fragments = (abinfo.bytes >> s->dma_adc.fragShift) >> + (s->adcChannels-1); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + /* we cannot get DMA ptr */ + return -EINVAL; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragSize << (s->dacChannels-1), (int *)arg); + else + return put_user(s->dma_adc.fragSize << (s->adcChannels-1), (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + /* we ignore fragment size request */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + /* what is this for? [jsun] */ + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->adcRate : s->dacRate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->adcChannels, (int *)arg); + else + return put_user(s->dacChannels ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user(16, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + + return mixdev_ioctl(&s->codec, cmd, arg); +} + + +static int vrc5477_ac97_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct vrc5477_ac97_state *s; + int ret=0; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct vrc5477_ac97_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + file->private_data = s; + + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + /* set default settings */ + set_adc_rate(s, 48000); + s->adcChannels = 2; + + ret = prog_dmabuf_adc(s); + if (ret) goto bailout; + } + if (file->f_mode & FMODE_WRITE) { + /* set default settings */ + set_dac_rate(s, 48000); + s->dacChannels = 2; + + ret = prog_dmabuf_dac(s); + if (ret) goto bailout; + } + + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + bailout: + spin_unlock_irqrestore(&s->lock, flags); + + up(&s->open_sem); + return ret; +} + +static int vrc5477_ac97_release(struct inode *inode, struct file *file) +{ + struct vrc5477_ac97_state *s = + (struct vrc5477_ac97_state *)file->private_data; + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations vrc5477_ac97_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: vrc5477_ac97_read, + write: vrc5477_ac97_write, + poll: vrc5477_ac97_poll, + ioctl: vrc5477_ac97_ioctl, + // mmap: vrc5477_ac97_mmap, + open: vrc5477_ac97_open, + release: vrc5477_ac97_release, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef CONFIG_LL_DEBUG + +struct { + const char *regname; + unsigned regaddr; +} vrc5477_ac97_regs[] = { + {"VRC5477_INT_STATUS", VRC5477_INT_STATUS}, + {"VRC5477_CODEC_WR", VRC5477_CODEC_WR}, + {"VRC5477_CODEC_RD", VRC5477_CODEC_RD}, + {"VRC5477_CTRL", VRC5477_CTRL}, + {"VRC5477_ACLINK_CTRL", VRC5477_ACLINK_CTRL}, + {"VRC5477_INT_MASK", VRC5477_INT_MASK}, + {"VRC5477_DAC1_CTRL", VRC5477_DAC1_CTRL}, + {"VRC5477_DAC1L", VRC5477_DAC1L}, + {"VRC5477_DAC1_BADDR", VRC5477_DAC1_BADDR}, + {"VRC5477_DAC2_CTRL", VRC5477_DAC2_CTRL}, + {"VRC5477_DAC2L", VRC5477_DAC2L}, + {"VRC5477_DAC2_BADDR", VRC5477_DAC2_BADDR}, + {"VRC5477_DAC3_CTRL", VRC5477_DAC3_CTRL}, + {"VRC5477_DAC3L", VRC5477_DAC3L}, + {"VRC5477_DAC3_BADDR", VRC5477_DAC3_BADDR}, + {"VRC5477_ADC1_CTRL", VRC5477_ADC1_CTRL}, + {"VRC5477_ADC1L", VRC5477_ADC1L}, + {"VRC5477_ADC1_BADDR", VRC5477_ADC1_BADDR}, + {"VRC5477_ADC2_CTRL", VRC5477_ADC2_CTRL}, + {"VRC5477_ADC2L", VRC5477_ADC2L}, + {"VRC5477_ADC2_BADDR", VRC5477_ADC2_BADDR}, + {"VRC5477_ADC3_CTRL", VRC5477_ADC3_CTRL}, + {"VRC5477_ADC3L", VRC5477_ADC3L}, + {"VRC5477_ADC3_BADDR", VRC5477_ADC3_BADDR}, + {NULL, 0x0} +}; + +static int proc_vrc5477_ac97_dump (char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct vrc5477_ac97_state *s; + int cnt, len = 0; + + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct vrc5477_ac97_state, devs); + + /* print out header */ + len += sprintf(buf + len, "\n\t\tVrc5477 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf (buf + len, "NEC Vrc5477 Audio Controller registers\n"); + len += sprintf (buf + len, "---------------------------------\n"); + for (cnt=0; vrc5477_ac97_regs[cnt].regname != NULL; cnt++) { + len+= sprintf (buf + len, "%-20s = %08x\n", + vrc5477_ac97_regs[cnt].regname, + inl(s->io + vrc5477_ac97_regs[cnt].regaddr)); + } + + /* print out driver state */ + len += sprintf (buf + len, "NEC Vrc5477 Audio driver states\n"); + len += sprintf (buf + len, "---------------------------------\n"); + len += sprintf (buf + len, "dacChannels = %d\n", s->dacChannels); + len += sprintf (buf + len, "adcChannels = %d\n", s->adcChannels); + len += sprintf (buf + len, "dacRate = %d\n", s->dacRate); + len += sprintf (buf + len, "adcRate = %d\n", s->adcRate); + + len += sprintf (buf + len, "dma_dac is %s ready\n", + s->dma_dac.ready? "" : "not"); + if (s->dma_dac.ready) { + len += sprintf (buf + len, "dma_dac is %s stopped.\n", + s->dma_dac.stopped? "" : "not"); + len += sprintf (buf + len, "dma_dac.fragSize = %x\n", + s->dma_dac.fragSize); + len += sprintf (buf + len, "dma_dac.fragShift = %x\n", + s->dma_dac.fragShift); + len += sprintf (buf + len, "dma_dac.numFrag = %x\n", + s->dma_dac.numFrag); + len += sprintf (buf + len, "dma_dac.fragTotalSize = %x\n", + s->dma_dac.fragTotalSize); + len += sprintf (buf + len, "dma_dac.nextIn = %x\n", + s->dma_dac.nextIn); + len += sprintf (buf + len, "dma_dac.nextOut = %x\n", + s->dma_dac.nextOut); + len += sprintf (buf + len, "dma_dac.count = %x\n", + s->dma_dac.count); + } + + len += sprintf (buf + len, "dma_adc is %s ready\n", + s->dma_adc.ready? "" : "not"); + if (s->dma_adc.ready) { + len += sprintf (buf + len, "dma_adc is %s stopped.\n", + s->dma_adc.stopped? "" : "not"); + len += sprintf (buf + len, "dma_adc.fragSize = %x\n", + s->dma_adc.fragSize); + len += sprintf (buf + len, "dma_adc.fragShift = %x\n", + s->dma_adc.fragShift); + len += sprintf (buf + len, "dma_adc.numFrag = %x\n", + s->dma_adc.numFrag); + len += sprintf (buf + len, "dma_adc.fragTotalSize = %x\n", + s->dma_adc.fragTotalSize); + len += sprintf (buf + len, "dma_adc.nextIn = %x\n", + s->dma_adc.nextIn); + len += sprintf (buf + len, "dma_adc.nextOut = %x\n", + s->dma_adc.nextOut); + len += sprintf (buf + len, "dma_adc.count = %x\n", + s->dma_adc.count); + } + + /* print out CODEC state */ + len += sprintf (buf + len, "\nAC97 CODEC registers\n"); + len += sprintf (buf + len, "----------------------\n"); + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, rdcodec(&s->codec, cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* CONFIG_LL_DEBUG */ + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static unsigned int devindex = 0; + +MODULE_AUTHOR("Monta Vista Software, jsun@mvista.com or jsun@junsun.net"); +MODULE_DESCRIPTION("NEC Vrc5477 audio (AC97) Driver"); +MODULE_LICENSE("GPL"); + +/* --------------------------------------------------------------------- */ +extern void jsun_scan_pci_bus(void); +extern void vrc5477_show_pci_regs(void); +extern void vrc5477_show_pdar_regs(void); + +/* -------------------------------------------------------- */ +#define AC97_BASE 0xbb000000 +#define myinl(x) *(volatile u32*)(AC97_BASE + (x)) +#define myoutl(x,y) *(volatile u32*)(AC97_BASE + (y)) = (x) + +u16 myrdcodec(u8 addr) +{ + u32 result; + + /* wait until we can access codec registers */ + // while (inl(VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and "read" command to codec */ + addr = addr & 0x7f; + myoutl((addr << 16) | VRC5477_CODEC_WR_RWC, VRC5477_CODEC_WR); + + /* get the return result */ + udelay(100); /* workaround hardware bug */ + // dump_memory(0xbb000000, 48); + while ( ((result=myinl(VRC5477_CODEC_RD)) & 0xc0000000) != 0xc0000000); + MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); + return result & 0xffff; +} + +void mywrcodec(u8 addr, u16 data) +{ + /* wait until we can access codec registers */ + while (myinl(VRC5477_CODEC_WR) & 0x80000000); + + /* write the address and value to codec */ + myoutl((addr << 16) | data, VRC5477_CODEC_WR); + +} + + +void jsun_ac97_test(struct vrc5477_ac97_state *s) +{ + int i; + + /* reset codec */ + /* + wrcodec(&s->codec, 0, 0); + while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); + */ + mywrcodec(0, 0); + while (myinl(VRC5477_CODEC_WR) & 0x80000000); + + for (i=0; i< 0x40; i+=4) { + MIPS_ASSERT(inl(s->io+i) == myinl(i)); + } + + printk("codec registers : "); + for (i=0; i<= 0x3a; i+=2) { + if ( (i%0x10) == 0) { + printk("\n%02x\t", i); + } + // printk("%04x\t", rdcodec(&s->codec, i)); + printk("%04x\t", myrdcodec(i)); + } + printk("\n\n"); + printk("codec registers : "); + for (i=0; i<= 0x3a; i+=2) { + if ( (i%0x10) == 0) { + printk("\n%02x\t", i); + } + printk("%04x\t", rdcodec(&s->codec, i)); + } + printk("\n\n"); +} + +static int __devinit vrc5477_ac97_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + struct vrc5477_ac97_state *s; + char proc_str[80]; + + MIPS_DEBUG(printk("vrc5477_ac97_probe() invoked\n")); + + if (pcidev->irq == 0) + return -1; + + if (!(s = kmalloc(sizeof(struct vrc5477_ac97_state), GFP_KERNEL))) { + printk(KERN_ERR PFX "alloc of device struct failed\n"); + return -1; + } + memset(s, 0, sizeof(struct vrc5477_ac97_state)); + + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + + s->codec.private_data = s; + s->codec.id = 0; + s->codec.codec_read = rdcodec; + s->codec.codec_write = wrcodec; + s->codec.codec_wait = waitcodec; + + /* setting some other default values such as + * adcChannels, adcRate is done in open() so that + * no persistent state across file opens. + */ + + if (!request_region(s->io, pci_resource_len(pcidev,0), + VRC5477_AC97_MODULE_NAME)) { + printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n", + s->io, s->io + pci_resource_len(pcidev,0)-1); + goto err_region; + } + if (request_irq(s->irq, vrc5477_ac97_interrupt, SA_INTERRUPT, + VRC5477_AC97_MODULE_NAME, s)) { + printk(KERN_ERR PFX "irq %u in use\n", s->irq); + goto err_irq; + } + + printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq); + + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&vrc5477_ac97_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec.dev_mixer = + register_sound_mixer(&vrc5477_ac97_mixer_fops, -1)) < 0) + goto err_dev2; + +#ifdef CONFIG_LL_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(VRC5477_AC97_MODULE_NAME, 0, NULL, + proc_vrc5477_ac97_dump, NULL); +#endif /* CONFIG_LL_DEBUG */ + + /* enable pci io and bus mastering */ + if (pci_enable_device(pcidev)) + goto err_dev3; + pci_set_master(pcidev); + +/* +jsun_scan_pci_bus(); +vrc5477_show_pci_regs(); +vrc5477_show_pdar_regs(); +*/ + + /* cold reset the AC97 */ + outl(VRC5477_ACLINK_CTRL_RST_ON | VRC5477_ACLINK_CTRL_RST_TIME, + s->io + VRC5477_ACLINK_CTRL); + while (inl(s->io + VRC5477_ACLINK_CTRL) & VRC5477_ACLINK_CTRL_RST_ON); + +/* +jsun_ac97_test(s); +*/ + + /* codec init */ + if (!ac97_probe_codec(&s->codec)) + goto err_dev3; + +#ifdef CONFIG_LL_DEBUG + sprintf(proc_str, "driver/%s/%d/ac97", + VRC5477_AC97_MODULE_NAME, s->codec.id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, &s->codec); + /* TODO : why this proc file does not show up? */ +#endif + + /* let us get the default volumne louder */ + wrcodec(&s->codec, 0x2, 0); + wrcodec(&s->codec, 0x18, 0x0707); + /* mute line in loopback to line out */ + wrcodec(&s->codec, 0x10, 0x8000); + + /* by default we select line in the input */ + wrcodec(&s->codec, 0x1a, 0x0404); + /* pick middle value for record gain */ + // wrcodec(&s->codec, 0x1c, 0x0707); + wrcodec(&s->codec, 0x1c, 0x0f0f); + wrcodec(&s->codec, 0x1e, 0x07); + + /* enable the master interrupt but disable all others */ + outl(VRC5477_INT_MASK_NMASK, s->io + VRC5477_INT_MASK); + + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev3: + unregister_sound_mixer(s->codec.dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR PFX "cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, pci_resource_len(pcidev,0)); + err_region: + kfree(s); + return -1; +} + +static void __devinit vrc5477_ac97_remove(struct pci_dev *dev) +{ + struct vrc5477_ac97_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); +#ifdef CONFIG_LL_DEBUG + if (s->ps) + remove_proc_entry(VRC5477_AC97_MODULE_NAME, NULL); +#endif /* CONFIG_LL_DEBUG */ + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, pci_resource_len(dev,0)); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec.dev_mixer); + kfree(s); + pci_set_drvdata(dev, NULL); +} + + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00A6 +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_VRC5477_AC97, + PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver vrc5477_ac97_driver = { + name: VRC5477_AC97_MODULE_NAME, + id_table: id_table, + probe: vrc5477_ac97_probe, + remove: vrc5477_ac97_remove +}; + +static int __init init_vrc5477_ac97(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk("Vrc5477 AC97 driver: version v0.1 time " __TIME__ " " __DATE__ " by Jun Sun\n"); + return pci_module_init(&vrc5477_ac97_driver); +} + +static void __exit cleanup_vrc5477_ac97(void) +{ + printk(KERN_INFO PFX "unloading\n"); + pci_unregister_driver(&vrc5477_ac97_driver); +} + +module_init(init_vrc5477_ac97); +module_exit(cleanup_vrc5477_ac97); + diff -Nru linux/sound/oss/nm256.h linux-2.4.19-pre5-mjc/sound/oss/nm256.h --- linux/sound/oss/nm256.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/nm256.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,290 @@ +#ifndef _NM256_H_ +#define _NM256_H_ + +#include "ac97.h" + +/* The revisions that we currently handle. */ +enum nm256rev { + REV_NM256AV, REV_NM256ZX +}; + +/* Per-card structure. */ +struct nm256_info +{ + /* Magic number used to verify that this struct is valid. */ +#define NM_MAGIC_SIG 0x55aa00ff + int magsig; + + /* Revision number */ + enum nm256rev rev; + + struct ac97_hwint mdev; + + /* Our audio device numbers. */ + int dev[2]; + + /* The # of times each device has been opened. (Should only be + 0 or 1). */ + int opencnt[2]; + + /* We use two devices, because we can do simultaneous play and record. + This keeps track of which device is being used for what purpose; + these are the actual device numbers. */ + int dev_for_play; + int dev_for_record; + + /* The mixer device. */ + int mixer_oss_dev; + + /* + * Can only be opened once for each operation. These aren't set + * until an actual I/O operation is performed; this allows one + * device to be open for read/write without inhibiting I/O to + * the other device. + */ + int is_open_play; + int is_open_record; + + /* Non-zero if we're currently playing a sample. */ + int playing; + /* Ditto for recording a sample. */ + int recording; + + /* The two memory ports. */ + struct nm256_ports { + /* Physical address of the port. */ + u32 physaddr; + /* Our mapped-in pointer. */ + char *ptr; + /* PTR's offset within the physical port. */ + u32 start_offset; + /* And the offset of the end of the buffer. */ + u32 end_offset; + } port[2]; + + /* The following are offsets within memory port 1. */ + u32 coeffBuf; + u32 allCoeffBuf; + + /* Record and playback buffers. */ + u32 abuf1, abuf2; + + /* Offset of the AC97 mixer in memory port 2. */ + u32 mixer; + + /* Offset of the mixer status register in memory port 2. */ + u32 mixer_status_offset; + + /* Non-zero if we have written initial values to the mixer. */ + u8 mixer_values_init; + + /* + * Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means + * it's ready. + */ + u16 mixer_status_mask; + + /* The sizes of the playback and record ring buffers. */ + u32 playbackBufferSize; + u32 recordBufferSize; + + /* Are the coefficient values in the memory cache current? */ + u8 coeffsCurrent; + + /* For writes, the amount we last wrote. */ + u32 requested_amt; + /* The start of the block currently playing. */ + u32 curPlayPos; + + /* The amount of data we were requested to record. */ + u32 requestedRecAmt; + /* The offset of the currently-recording block. */ + u32 curRecPos; + /* The destination buffer. */ + char *recBuf; + + /* Our IRQ number. */ + int irq; + + /* A flag indicating how many times we've grabbed the IRQ. */ + int has_irq; + + /* The card interrupt service routine. */ + void (*introutine) (int, void *, struct pt_regs *); + + /* Current audio config, cached. */ + struct sinfo { + u32 samplerate; + u8 bits; + u8 stereo; + } sinfo[2]; /* goes with each device */ + + /* The cards are stored in a chain; this is the next card. */ + struct nm256_info *next_card; +}; + +/* Debug flag--bigger numbers mean more output. */ +extern int nm256_debug; + +/* The BIOS signature. */ +#define NM_SIGNATURE 0x4e4d0000 +/* Signature mask. */ +#define NM_SIG_MASK 0xffff0000 + +/* Size of the second memory area. */ +#define NM_PORT2_SIZE 4096 + +/* The base offset of the mixer in the second memory area. */ +#define NM_MIXER_OFFSET 0x600 + +/* The maximum size of a coefficient entry. */ +#define NM_MAX_COEFFICIENT 0x5000 + +/* The interrupt register. */ +#define NM_INT_REG 0xa04 +/* And its bits. */ +#define NM_PLAYBACK_INT 0x40 +#define NM_RECORD_INT 0x100 +#define NM_MISC_INT_1 0x4000 +#define NM_MISC_INT_2 0x1 +#define NM_ACK_INT(CARD, X) nm256_writePort16((CARD), 2, NM_INT_REG, (X) << 1) + +/* The AV's "mixer ready" status bit and location. */ +#define NM_MIXER_STATUS_OFFSET 0xa04 +#define NM_MIXER_READY_MASK 0x0800 +#define NM_MIXER_PRESENCE 0xa06 +#define NM_PRESENCE_MASK 0x0050 +#define NM_PRESENCE_VALUE 0x0040 + +/* + * For the ZX. It uses the same interrupt register, but it holds 32 + * bits instead of 16. + */ +#define NM2_PLAYBACK_INT 0x10000 +#define NM2_RECORD_INT 0x80000 +#define NM2_MISC_INT_1 0x8 +#define NM2_MISC_INT_2 0x2 +#define NM2_ACK_INT(CARD, X) nm256_writePort32((CARD), 2, NM_INT_REG, (X)) + +/* The ZX's "mixer ready" status bit and location. */ +#define NM2_MIXER_STATUS_OFFSET 0xa06 +#define NM2_MIXER_READY_MASK 0x0800 + +/* The playback registers start from here. */ +#define NM_PLAYBACK_REG_OFFSET 0x0 +/* The record registers start from here. */ +#define NM_RECORD_REG_OFFSET 0x200 + +/* The rate register is located 2 bytes from the start of the register area. */ +#define NM_RATE_REG_OFFSET 2 + +/* Mono/stereo flag, number of bits on playback, and rate mask. */ +#define NM_RATE_STEREO 1 +#define NM_RATE_BITS_16 2 +#define NM_RATE_MASK 0xf0 + +/* Playback enable register. */ +#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) +#define NM_PLAYBACK_ENABLE_FLAG 1 +#define NM_PLAYBACK_ONESHOT 2 +#define NM_PLAYBACK_FREERUN 4 + +/* Mutes the audio output. */ +#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) +#define NM_AUDIO_MUTE_LEFT 0x8000 +#define NM_AUDIO_MUTE_RIGHT 0x0080 + +/* Recording enable register. */ +#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) +#define NM_RECORD_ENABLE_FLAG 1 +#define NM_RECORD_FREERUN 2 + +#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) +#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) +#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) +#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) + +#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) +#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) +#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) +#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) + +/* A few trivial routines to make it easier to work with the registers + on the chip. */ + +/* This is a common code portion used to fix up the port offsets. */ +#define NM_FIX_PORT \ + if (port < 1 || port > 2 || card == NULL) \ + return -1; \ +\ + if (offset < card->port[port - 1].start_offset \ + || offset >= card->port[port - 1].end_offset) { \ + printk (KERN_ERR "Bad access: port %d, offset 0x%x\n", port, offset); \ + return -1; \ + } \ + offset -= card->port[port - 1].start_offset; + +#define DEFwritePortX(X, func) \ +static inline int nm256_writePort##X (struct nm256_info *card,\ + int port, int offset, int value)\ +{\ + u##X *addr;\ +\ + if (nm256_debug > 1)\ + printk (KERN_DEBUG "Writing 0x%x to %d:0x%x\n", value, port, offset);\ +\ + NM_FIX_PORT;\ +\ + addr = (u##X *)(card->port[port - 1].ptr + offset);\ + func (value, addr);\ + return 0;\ +} + +DEFwritePortX (8, writeb) +DEFwritePortX (16, writew) +DEFwritePortX (32, writel) + +#define DEFreadPortX(X, func) \ +static inline u##X nm256_readPort##X (struct nm256_info *card,\ + int port, int offset)\ +{\ + u##X *addr;\ +\ + NM_FIX_PORT\ +\ + addr = (u##X *)(card->port[port - 1].ptr + offset);\ + return func(addr);\ +} + +DEFreadPortX (8, readb) +DEFreadPortX (16, readw) +DEFreadPortX (32, readl) + +static inline int +nm256_writeBuffer8 (struct nm256_info *card, u8 *src, int port, int offset, + int amt) +{ + NM_FIX_PORT; + memcpy_toio (card->port[port - 1].ptr + offset, src, amt); + return 0; +} + +static inline int +nm256_readBuffer8 (struct nm256_info *card, u8 *dst, int port, int offset, + int amt) +{ + NM_FIX_PORT; + memcpy_fromio (dst, card->port[port - 1].ptr + offset, amt); + return 0; +} + +/* Returns a non-zero value if we should use the coefficient cache. */ +extern int nm256_cachedCoefficients (struct nm256_info *card); + +#endif + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru linux/sound/oss/nm256_audio.c linux-2.4.19-pre5-mjc/sound/oss/nm256_audio.c --- linux/sound/oss/nm256_audio.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/nm256_audio.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1690 @@ +/* + * Audio driver for the NeoMagic 256AV and 256ZX chipsets in native + * mode, with AC97 mixer support. + * + * Overall design and parts of this code stolen from vidc_*.c and + * skeleton.c. + * + * Yeah, there are a lot of magic constants in here. You tell ME what + * they are. I just get this stuff psychically, remember? + * + * This driver was written by someone who wishes to remain anonymous. + * It is in the public domain, so share and enjoy. Try to make a profit + * off of it; go on, I dare you. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added some __init + * 19-04-2001 Marcus Meissner + * Ported to 2.4 PCI API. + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "nm256.h" +#include "nm256_coeff.h" + +int nm256_debug; +static int force_load; + +/* + * The size of the playback reserve. When the playback buffer has less + * than NM256_PLAY_WMARK_SIZE bytes to output, we request a new + * buffer. + */ +#define NM256_PLAY_WMARK_SIZE 512 + +static struct audio_driver nm256_audio_driver; + +static int nm256_grabInterrupt (struct nm256_info *card); +static int nm256_releaseInterrupt (struct nm256_info *card); +static void nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy); +static void nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy); +static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data); + +/* These belong in linux/pci.h. */ +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 + +/* List of cards. */ +static struct nm256_info *nmcard_list; + +/* Release the mapped-in memory for CARD. */ +static void +nm256_release_ports (struct nm256_info *card) +{ + int x; + + for (x = 0; x < 2; x++) { + if (card->port[x].ptr != NULL) { + iounmap (card->port[x].ptr); + card->port[x].ptr = NULL; + } + } +} + +/* + * Map in the memory ports for CARD, if they aren't already mapped in + * and have been configured. If successful, a zero value is returned; + * otherwise any previously mapped-in areas are released and a non-zero + * value is returned. + * + * This is invoked twice, once for each port. Ideally it would only be + * called once, but we now need to map in the second port in order to + * check how much memory the card has on the 256ZX. + */ +static int +nm256_remap_ports (struct nm256_info *card) +{ + int x; + + for (x = 0; x < 2; x++) { + if (card->port[x].ptr == NULL && card->port[x].end_offset > 0) { + u32 physaddr + = card->port[x].physaddr + card->port[x].start_offset; + u32 size + = card->port[x].end_offset - card->port[x].start_offset; + + card->port[x].ptr = ioremap_nocache (physaddr, size); + + if (card->port[x].ptr == NULL) { + printk (KERN_ERR "NM256: Unable to remap port %d\n", x + 1); + nm256_release_ports (card); + return -1; + } + } + } + return 0; +} + +/* Locate the card in our list. */ +static struct nm256_info * +nm256_find_card (int dev) +{ + struct nm256_info *card; + + for (card = nmcard_list; card != NULL; card = card->next_card) + if (card->dev[0] == dev || card->dev[1] == dev) + return card; + + return NULL; +} + +/* + * Ditto, but find the card struct corresponding to the mixer device DEV + * instead. + */ +static struct nm256_info * +nm256_find_card_for_mixer (int dev) +{ + struct nm256_info *card; + + for (card = nmcard_list; card != NULL; card = card->next_card) + if (card->mixer_oss_dev == dev) + return card; + + return NULL; +} + +static int usecache; +static int buffertop; + +/* Check to see if we're using the bank of cached coefficients. */ +int +nm256_cachedCoefficients (struct nm256_info *card) +{ + return usecache; +} + +/* The actual rates supported by the card. */ +static int samplerates[9] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 +}; + +/* + * Set the card samplerate, word size and stereo mode to correspond to + * the settings in the CARD struct for the specified device in DEV. + * We keep two separate sets of information, one for each device; the + * hardware is not actually configured until a read or write is + * attempted. + */ + +int +nm256_setInfo (int dev, struct nm256_info *card) +{ + int x; + int w; + int targetrate; + + if (card->dev[0] == dev) + w = 0; + else if (card->dev[1] == dev) + w = 1; + else + return -ENODEV; + + targetrate = card->sinfo[w].samplerate; + + if ((card->sinfo[w].bits != 8 && card->sinfo[w].bits != 16) + || targetrate < samplerates[0] + || targetrate > samplerates[7]) + return -EINVAL; + + for (x = 0; x < 8; x++) + if (targetrate < ((samplerates[x] + samplerates[x + 1]) / 2)) + break; + + if (x < 8) { + u8 ratebits = ((x << 4) & NM_RATE_MASK); + if (card->sinfo[w].bits == 16) + ratebits |= NM_RATE_BITS_16; + if (card->sinfo[w].stereo) + ratebits |= NM_RATE_STEREO; + + card->sinfo[w].samplerate = samplerates[x]; + + + if (card->dev_for_play == dev && card->playing) { + if (nm256_debug) + printk (KERN_DEBUG "Setting play ratebits to 0x%x\n", + ratebits); + nm256_loadCoefficient (card, 0, x); + nm256_writePort8 (card, 2, + NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + } + + if (card->dev_for_record == dev && card->recording) { + if (nm256_debug) + printk (KERN_DEBUG "Setting record ratebits to 0x%x\n", + ratebits); + nm256_loadCoefficient (card, 1, x); + nm256_writePort8 (card, 2, + NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + } + return 0; + } + else + return -EINVAL; +} + +/* Start the play process going. */ +static void +startPlay (struct nm256_info *card) +{ + if (! card->playing) { + card->playing = 1; + if (nm256_grabInterrupt (card) == 0) { + nm256_setInfo (card->dev_for_play, card); + + /* Enable playback engine and interrupts. */ + nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, + NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); + + /* Enable both channels. */ + nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, 0x0); + } + } +} + +/* + * Request one chunk of AMT bytes from the recording device. When the + * operation is complete, the data will be copied into BUFFER and the + * function DMAbuf_inputintr will be invoked. + */ + +static void +nm256_startRecording (struct nm256_info *card, char *buffer, u32 amt) +{ + u32 endpos; + int enableEngine = 0; + u32 ringsize = card->recordBufferSize; + unsigned long flags; + + if (amt > (ringsize / 2)) { + /* + * Of course this won't actually work right, because the + * caller is going to assume we will give what we got asked + * for. + */ + printk (KERN_ERR "NM256: Read request too large: %d\n", amt); + amt = ringsize / 2; + } + + if (amt < 8) { + printk (KERN_ERR "NM256: Read request too small; %d\n", amt); + return; + } + + save_flags (flags); + cli (); + /* + * If we're not currently recording, set up the start and end registers + * for the recording engine. + */ + if (! card->recording) { + card->recording = 1; + if (nm256_grabInterrupt (card) == 0) { + card->curRecPos = 0; + nm256_setInfo (card->dev_for_record, card); + nm256_writePort32 (card, 2, NM_RBUFFER_START, card->abuf2); + nm256_writePort32 (card, 2, NM_RBUFFER_END, + card->abuf2 + ringsize); + + nm256_writePort32 (card, 2, NM_RBUFFER_CURRP, + card->abuf2 + card->curRecPos); + enableEngine = 1; + } + else { + /* Not sure what else to do here. */ + restore_flags (flags); + return; + } + } + + /* + * If we happen to go past the end of the buffer a bit (due to a + * delayed interrupt) it's OK. So might as well set the watermark + * right at the end of the data we want. + */ + endpos = card->abuf2 + ((card->curRecPos + amt) % ringsize); + + card->recBuf = buffer; + card->requestedRecAmt = amt; + nm256_writePort32 (card, 2, NM_RBUFFER_WMARK, endpos); + /* Enable recording engine and interrupts. */ + if (enableEngine) + nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, + NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); + + restore_flags (flags); +} + +/* Stop the play engine. */ +static void +stopPlay (struct nm256_info *card) +{ + /* Shut off sound from both channels. */ + nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, + NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); + /* Disable play engine. */ + nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, 0); + if (card->playing) { + nm256_releaseInterrupt (card); + + /* Reset the relevant state bits. */ + card->playing = 0; + card->curPlayPos = 0; + } +} + +/* Stop recording. */ +static void +stopRecord (struct nm256_info *card) +{ + /* Disable recording engine. */ + nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, 0); + + if (card->recording) { + nm256_releaseInterrupt (card); + + card->recording = 0; + card->curRecPos = 0; + } +} + +/* + * Ring buffers, man. That's where the hip-hop, wild-n-wooly action's at. + * 1972? (Well, I suppose it was cheep-n-easy to implement.) + * + * Write AMT bytes of BUFFER to the playback ring buffer, and start the + * playback engine running. It will only accept up to 1/2 of the total + * size of the ring buffer. No check is made that we're about to overwrite + * the currently-playing sample. + */ + +static void +nm256_write_block (struct nm256_info *card, char *buffer, u32 amt) +{ + u32 ringsize = card->playbackBufferSize; + u32 endstop; + unsigned long flags; + + if (amt > (ringsize / 2)) { + printk (KERN_ERR "NM256: Write request too large: %d\n", amt); + amt = (ringsize / 2); + } + + if (amt < NM256_PLAY_WMARK_SIZE) { + printk (KERN_ERR "NM256: Write request too small: %d\n", amt); + return; + } + + card->curPlayPos %= ringsize; + + card->requested_amt = amt; + + save_flags (flags); + cli (); + + if ((card->curPlayPos + amt) >= ringsize) { + u32 rem = ringsize - card->curPlayPos; + + nm256_writeBuffer8 (card, buffer, 1, + card->abuf1 + card->curPlayPos, + rem); + if (amt > rem) + nm256_writeBuffer8 (card, buffer + rem, 1, card->abuf1, + amt - rem); + } + else + nm256_writeBuffer8 (card, buffer, 1, + card->abuf1 + card->curPlayPos, + amt); + + /* + * Setup the start-n-stop-n-limit registers, and start that engine + * goin'. + * + * Normally we just let it wrap around to avoid the click-click + * action scene. + */ + if (! card->playing) { + /* The PBUFFER_END register in this case points to one sample + before the end of the buffer. */ + int w = (card->dev_for_play == card->dev[0] ? 0 : 1); + int sampsize = (card->sinfo[w].bits == 16 ? 2 : 1); + + if (card->sinfo[w].stereo) + sampsize *= 2; + + /* Need to set the not-normally-changing-registers up. */ + nm256_writePort32 (card, 2, NM_PBUFFER_START, + card->abuf1 + card->curPlayPos); + nm256_writePort32 (card, 2, NM_PBUFFER_END, + card->abuf1 + ringsize - sampsize); + nm256_writePort32 (card, 2, NM_PBUFFER_CURRP, + card->abuf1 + card->curPlayPos); + } + endstop = (card->curPlayPos + amt - NM256_PLAY_WMARK_SIZE) % ringsize; + nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); + + if (! card->playing) + startPlay (card); + + restore_flags (flags); +} + +/* We just got a card playback interrupt; process it. */ +static void +nm256_get_new_block (struct nm256_info *card) +{ + /* Check to see how much got played so far. */ + u32 amt = nm256_readPort32 (card, 2, NM_PBUFFER_CURRP) - card->abuf1; + + if (amt >= card->playbackBufferSize) { + printk (KERN_ERR "NM256: Sound playback pointer invalid!\n"); + amt = 0; + } + + if (amt < card->curPlayPos) + amt = (card->playbackBufferSize - card->curPlayPos) + amt; + else + amt -= card->curPlayPos; + + if (card->requested_amt > (amt + NM256_PLAY_WMARK_SIZE)) { + u32 endstop = + card->curPlayPos + card->requested_amt - NM256_PLAY_WMARK_SIZE; + nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop); + } + else { + card->curPlayPos += card->requested_amt; + /* Get a new block to write. This will eventually invoke + nm256_write_block () or stopPlay (). */ + DMAbuf_outputintr (card->dev_for_play, 1); + } +} + +/* Ultra cheez-whiz. But I'm too lazy to grep headers. */ +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/* + * Read the last-recorded block from the ring buffer, copy it into the + * saved buffer pointer, and invoke DMAuf_inputintr() with the recording + * device. + */ + +static void +nm256_read_block (struct nm256_info *card) +{ + /* Grab the current position of the recording pointer. */ + u32 currptr = nm256_readPort32 (card, 2, NM_RBUFFER_CURRP) - card->abuf2; + u32 amtToRead = card->requestedRecAmt; + u32 ringsize = card->recordBufferSize; + + if (currptr >= card->recordBufferSize) { + printk (KERN_ERR "NM256: Sound buffer record pointer invalid!\n"); + currptr = 0; + } + + /* + * This test is probably redundant; we shouldn't be here unless + * it's true. + */ + if (card->recording) { + /* If we wrapped around, copy everything from the start of our + recording buffer to the end of the buffer. */ + if (currptr < card->curRecPos) { + u32 amt = MIN (ringsize - card->curRecPos, amtToRead); + + nm256_readBuffer8 (card, card->recBuf, 1, + card->abuf2 + card->curRecPos, + amt); + amtToRead -= amt; + card->curRecPos += amt; + card->recBuf += amt; + if (card->curRecPos == ringsize) + card->curRecPos = 0; + } + + if ((card->curRecPos < currptr) && (amtToRead > 0)) { + u32 amt = MIN (currptr - card->curRecPos, amtToRead); + nm256_readBuffer8 (card, card->recBuf, 1, + card->abuf2 + card->curRecPos, amt); + card->curRecPos = ((card->curRecPos + amt) % ringsize); + } + card->recBuf = NULL; + card->requestedRecAmt = 0; + DMAbuf_inputintr (card->dev_for_record); + } +} +#undef MIN + +/* + * Initialize the hardware. + */ +static void +nm256_initHw (struct nm256_info *card) +{ + /* Reset everything. */ + nm256_writePort8 (card, 2, 0x0, 0x11); + nm256_writePort16 (card, 2, 0x214, 0); + + stopRecord (card); + stopPlay (card); +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * + * I don't like the cut-n-paste job here either between the two routines, + * but there are sufficient differences between the two interrupt handlers + * that parameterizing it isn't all that great either. (Could use a macro, + * I suppose...yucky bleah.) + */ + +static void +nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct nm256_info *card = (struct nm256_info *)dev_id; + u16 status; + static int badintrcount = 0; + + if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) { + printk (KERN_ERR "NM256: Bad card pointer\n"); + return; + } + + status = nm256_readPort16 (card, 2, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + if (badintrcount++ > 1000) { + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with IRQ 9s. + */ + + if (card->playing) + stopPlay (card); + if (card->recording) + stopRecord (card); + badintrcount = 0; + } + return; + } + + badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + if (status & NM_PLAYBACK_INT) { + status &= ~NM_PLAYBACK_INT; + NM_ACK_INT (card, NM_PLAYBACK_INT); + + if (card->playing) + nm256_get_new_block (card); + } + + if (status & NM_RECORD_INT) { + status &= ~NM_RECORD_INT; + NM_ACK_INT (card, NM_RECORD_INT); + + if (card->recording) + nm256_read_block (card); + } + + if (status & NM_MISC_INT_1) { + u8 cbyte; + + status &= ~NM_MISC_INT_1; + printk (KERN_ERR "NM256: Got misc interrupt #1\n"); + NM_ACK_INT (card, NM_MISC_INT_1); + nm256_writePort16 (card, 2, NM_INT_REG, 0x8000); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte | 2); + } + + if (status & NM_MISC_INT_2) { + u8 cbyte; + + status &= ~NM_MISC_INT_2; + printk (KERN_ERR "NM256: Got misc interrupt #2\n"); + NM_ACK_INT (card, NM_MISC_INT_2); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM_ACK_INT (card, status); + } +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * This handler is for the 256ZX, and is very similar to the non-ZX + * routine. + */ + +static void +nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct nm256_info *card = (struct nm256_info *)dev_id; + u32 status; + static int badintrcount = 0; + + if ((card == NULL) || (card->magsig != NM_MAGIC_SIG)) { + printk (KERN_ERR "NM256: Bad card pointer\n"); + return; + } + + status = nm256_readPort32 (card, 2, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + if (badintrcount++ > 1000) { + printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n"); + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with + * IRQ 9s. + */ + + if (card->playing) + stopPlay (card); + if (card->recording) + stopRecord (card); + badintrcount = 0; + } + return; + } + + badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + if (status & NM2_PLAYBACK_INT) { + status &= ~NM2_PLAYBACK_INT; + NM2_ACK_INT (card, NM2_PLAYBACK_INT); + + if (card->playing) + nm256_get_new_block (card); + } + + if (status & NM2_RECORD_INT) { + status &= ~NM2_RECORD_INT; + NM2_ACK_INT (card, NM2_RECORD_INT); + + if (card->recording) + nm256_read_block (card); + } + + if (status & NM2_MISC_INT_1) { + u8 cbyte; + + status &= ~NM2_MISC_INT_1; + printk (KERN_ERR "NM256: Got misc interrupt #1\n"); + NM2_ACK_INT (card, NM2_MISC_INT_1); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte | 2); + } + + if (status & NM2_MISC_INT_2) { + u8 cbyte; + + status &= ~NM2_MISC_INT_2; + printk (KERN_ERR "NM256: Got misc interrupt #2\n"); + NM2_ACK_INT (card, NM2_MISC_INT_2); + cbyte = nm256_readPort8 (card, 2, 0x400); + nm256_writePort8 (card, 2, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM2_ACK_INT (card, status); + } +} + +/* + * Request our interrupt. + */ +static int +nm256_grabInterrupt (struct nm256_info *card) +{ + if (card->has_irq++ == 0) { + if (request_irq (card->irq, card->introutine, SA_SHIRQ, + "NM256_audio", card) < 0) { + printk (KERN_ERR "NM256: can't obtain IRQ %d\n", card->irq); + return -1; + } + } + return 0; +} + +/* + * Release our interrupt. + */ +static int +nm256_releaseInterrupt (struct nm256_info *card) +{ + if (card->has_irq <= 0) { + printk (KERN_ERR "nm256: too many calls to releaseInterrupt\n"); + return -1; + } + card->has_irq--; + if (card->has_irq == 0) { + free_irq (card->irq, card); + } + return 0; +} + +/* + * Waits for the mixer to become ready to be written; returns a zero value + * if it timed out. + */ + +static int +nm256_isReady (struct ac97_hwint *dev) +{ + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + int t2 = 10; + u32 testaddr; + u16 testb; + int done = 0; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in isReady!\n"); + return 0; + } + + testaddr = card->mixer_status_offset; + testb = card->mixer_status_mask; + + /* + * Loop around waiting for the mixer to become ready. + */ + while (! done && t2-- > 0) { + if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0) + done = 1; + else + udelay (100); + } + return done; +} + +/* + * Return the contents of the AC97 mixer register REG. Returns a positive + * value if successful, or a negative error code. + */ +static int +nm256_readAC97Reg (struct ac97_hwint *dev, u8 reg) +{ + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in readAC97Reg!\n"); + return -EINVAL; + } + + if (reg < 128) { + int res; + + nm256_isReady (dev); + res = nm256_readPort16 (card, 2, card->mixer + reg); + /* Magic delay. Bleah yucky. */ + udelay (1000); + return res; + } + else + return -EINVAL; +} + +/* + * Writes VALUE to AC97 mixer register REG. Returns 0 if successful, or + * a negative error code. + */ +static int +nm256_writeAC97Reg (struct ac97_hwint *dev, u8 reg, u16 value) +{ + unsigned long flags; + int tries = 2; + int done = 0; + u32 base; + + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in writeAC97Reg!\n"); + return -EINVAL; + } + + base = card->mixer; + + save_flags (flags); + cli (); + + nm256_isReady (dev); + + /* Wait for the write to take, too. */ + while ((tries-- > 0) && !done) { + nm256_writePort16 (card, 2, base + reg, value); + if (nm256_isReady (dev)) { + done = 1; + break; + } + + } + + restore_flags (flags); + udelay (1000); + + return ! done; +} + +/* + * Initial register values to be written to the AC97 mixer. + * While most of these are identical to the reset values, we do this + * so that we have most of the register contents cached--this avoids + * reading from the mixer directly (which seems to be problematic, + * probably due to ignorance). + */ +struct initialValues +{ + unsigned short port; + unsigned short value; +}; + +static struct initialValues nm256_ac97_initial_values[] = +{ + { AC97_MASTER_VOL_STEREO, 0x8000 }, + { AC97_HEADPHONE_VOL, 0x8000 }, + { AC97_MASTER_VOL_MONO, 0x0000 }, + { AC97_PCBEEP_VOL, 0x0000 }, + { AC97_PHONE_VOL, 0x0008 }, + { AC97_MIC_VOL, 0x8000 }, + { AC97_LINEIN_VOL, 0x8808 }, + { AC97_CD_VOL, 0x8808 }, + { AC97_VIDEO_VOL, 0x8808 }, + { AC97_AUX_VOL, 0x8808 }, + { AC97_PCMOUT_VOL, 0x0808 }, + { AC97_RECORD_SELECT, 0x0000 }, + { AC97_RECORD_GAIN, 0x0B0B }, + { AC97_GENERAL_PURPOSE, 0x0000 }, + { 0xffff, 0xffff } +}; + +/* Initialize the AC97 into a known state. */ +static int +nm256_resetAC97 (struct ac97_hwint *dev) +{ + struct nm256_info *card = (struct nm256_info *)dev->driver_private; + int x; + + if (card->magsig != NM_MAGIC_SIG) { + printk (KERN_ERR "NM256: Bad magic signature in resetAC97!\n"); + return -EINVAL; + } + + /* Reset the mixer. 'Tis magic! */ + nm256_writePort8 (card, 2, 0x6c0, 1); + nm256_writePort8 (card, 2, 0x6cc, 0x87); + nm256_writePort8 (card, 2, 0x6cc, 0x80); + nm256_writePort8 (card, 2, 0x6cc, 0x0); + + if (! card->mixer_values_init) { + for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) { + ac97_put_register (dev, + nm256_ac97_initial_values[x].port, + nm256_ac97_initial_values[x].value); + card->mixer_values_init = 1; + } + } + + return 0; +} + +/* + * We don't do anything particularly special here; it just passes the + * mixer ioctl to the AC97 driver. + */ +static int +nm256_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + struct nm256_info *card = nm256_find_card_for_mixer (dev); + if (card != NULL) + return ac97_mixer_ioctl (&(card->mdev), cmd, arg); + else + return -ENODEV; +} + +static struct mixer_operations nm256_mixer_operations = { + owner: THIS_MODULE, + id: "NeoMagic", + name: "NM256AC97Mixer", + ioctl: nm256_default_mixer_ioctl +}; + +/* + * Default settings for the OSS mixer. These are set last, after the + * mixer is initialized. + * + * I "love" C sometimes. Got braces? + */ +static struct ac97_mixer_value_list mixer_defaults[] = { + { SOUND_MIXER_VOLUME, { { 85, 85 } } }, + { SOUND_MIXER_SPEAKER, { { 100 } } }, + { SOUND_MIXER_PCM, { { 65, 65 } } }, + { SOUND_MIXER_CD, { { 65, 65 } } }, + { -1, { { 0, 0 } } } +}; + + +/* Installs the AC97 mixer into CARD. */ +static int __init +nm256_install_mixer (struct nm256_info *card) +{ + int mixer; + + card->mdev.reset_device = nm256_resetAC97; + card->mdev.read_reg = nm256_readAC97Reg; + card->mdev.write_reg = nm256_writeAC97Reg; + card->mdev.driver_private = (void *)card; + + if (ac97_init (&(card->mdev))) + return -1; + + mixer = sound_alloc_mixerdev(); + if (num_mixers >= MAX_MIXER_DEV) { + printk ("NM256 mixer: Unable to alloc mixerdev\n"); + return -1; + } + + mixer_devs[mixer] = &nm256_mixer_operations; + card->mixer_oss_dev = mixer; + + /* Some reasonable default values. */ + ac97_set_values (&(card->mdev), mixer_defaults); + + printk(KERN_INFO "Initialized AC97 mixer\n"); + return 0; +} + +/* Perform a full reset on the hardware; this is invoked when an APM + resume event occurs. */ +static void +nm256_full_reset (struct nm256_info *card) +{ + nm256_initHw (card); + ac97_reset (&(card->mdev)); +} + +/* + * See if the signature left by the NM256 BIOS is intact; if so, we use + * the associated address as the end of our audio buffer in the video + * RAM. + */ + +static void __init +nm256_peek_for_sig (struct nm256_info *card) +{ + u32 port1offset + = card->port[0].physaddr + card->port[0].end_offset - 0x0400; + /* The signature is located 1K below the end of video RAM. */ + char *temp = ioremap_nocache (port1offset, 16); + /* Default buffer end is 5120 bytes below the top of RAM. */ + u32 default_value = card->port[0].end_offset - 0x1400; + u32 sig; + + /* Install the default value first, so we don't have to repeatedly + do it if there is a problem. */ + card->port[0].end_offset = default_value; + + if (temp == NULL) { + printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n"); + return; + } + sig = readl (temp); + if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { + u32 pointer = readl (temp + 4); + + /* + * If it's obviously invalid, don't use it (the port already has a + * suitable default value set). + */ + if (pointer != 0xffffffff) + card->port[0].end_offset = pointer; + + printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n", + pointer); + } + + iounmap (temp); +} + +/* + * Install a driver for the PCI device referenced by PCIDEV. + * VERSTR is a human-readable version string. + */ + +static int __init +nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr) +{ + struct nm256_info *card; + struct pm_dev *pmdev; + int x; + + if (pci_enable_device(pcidev)) + return 0; + + card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL); + if (card == NULL) { + printk (KERN_ERR "NM256: out of memory!\n"); + return 0; + } + + card->magsig = NM_MAGIC_SIG; + card->playing = 0; + card->recording = 0; + card->rev = rev; + + /* Init the memory port info. */ + for (x = 0; x < 2; x++) { + card->port[x].physaddr = pci_resource_start (pcidev, x); + card->port[x].ptr = NULL; + card->port[x].start_offset = 0; + card->port[x].end_offset = 0; + } + + /* Port 2 is easy. */ + card->port[1].start_offset = 0; + card->port[1].end_offset = NM_PORT2_SIZE; + + /* Yuck. But we have to map in port 2 so we can check how much RAM the + card has. */ + if (nm256_remap_ports (card)) { + kfree (card); + return 0; + } + + /* + * The NM256 has two memory ports. The first port is nothing + * more than a chunk of video RAM, which is used as the I/O ring + * buffer. The second port has the actual juicy stuff (like the + * mixer and the playback engine control registers). + */ + + if (card->rev == REV_NM256AV) { + /* Ok, try to see if this is a non-AC97 version of the hardware. */ + int pval = nm256_readPort16 (card, 2, NM_MIXER_PRESENCE); + if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { + if (! force_load) { + printk (KERN_ERR "NM256: This doesn't look to me like the AC97-compatible version.\n"); + printk (KERN_ERR " You can force the driver to load by passing in the module\n"); + printk (KERN_ERR " parameter:\n"); + printk (KERN_ERR " force_ac97 = 1\n"); + printk (KERN_ERR "\n"); + printk (KERN_ERR " More likely, you should be using the appropriate SB-16 or\n"); + printk (KERN_ERR " CS4232 driver instead. (If your BIOS has settings for\n"); + printk (KERN_ERR " IRQ and/or DMA for the sound card, this is *not* the correct\n"); + printk (KERN_ERR " driver to use.)\n"); + nm256_release_ports (card); + kfree (card); + return 0; + } + else { + printk (KERN_INFO "NM256: Forcing driver load as per user request.\n"); + } + } + else { + /* printk (KERN_INFO "NM256: Congratulations. You're not running Eunice.\n")*/; + } + card->port[0].end_offset = 2560 * 1024; + card->introutine = nm256_interrupt; + card->mixer_status_offset = NM_MIXER_STATUS_OFFSET; + card->mixer_status_mask = NM_MIXER_READY_MASK; + } + else { + /* Not sure if there is any relevant detect for the ZX or not. */ + if (nm256_readPort8 (card, 2, 0xa0b) != 0) + card->port[0].end_offset = 6144 * 1024; + else + card->port[0].end_offset = 4096 * 1024; + + card->introutine = nm256_interrupt_zx; + card->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; + card->mixer_status_mask = NM2_MIXER_READY_MASK; + } + + if (buffertop >= 98304 && buffertop < card->port[0].end_offset) + card->port[0].end_offset = buffertop; + else + nm256_peek_for_sig (card); + + card->port[0].start_offset = card->port[0].end_offset - 98304; + + printk (KERN_INFO "NM256: Mapping port 1 from 0x%x - 0x%x\n", + card->port[0].start_offset, card->port[0].end_offset); + + if (nm256_remap_ports (card)) { + kfree (card); + return 0; + } + + /* See if we can get the interrupt. */ + + card->irq = pcidev->irq; + card->has_irq = 0; + + if (nm256_grabInterrupt (card) != 0) { + nm256_release_ports (card); + kfree (card); + return 0; + } + + nm256_releaseInterrupt (card); + + /* + * Init the board. + */ + + card->playbackBufferSize = 16384; + card->recordBufferSize = 16384; + + card->coeffBuf = card->port[0].end_offset - NM_MAX_COEFFICIENT; + card->abuf2 = card->coeffBuf - card->recordBufferSize; + card->abuf1 = card->abuf2 - card->playbackBufferSize; + card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4); + + /* Fixed setting. */ + card->mixer = NM_MIXER_OFFSET; + card->mixer_values_init = 0; + + card->is_open_play = 0; + card->is_open_record = 0; + + card->coeffsCurrent = 0; + + card->opencnt[0] = 0; card->opencnt[1] = 0; + + /* Reasonable default settings, but largely unnecessary. */ + for (x = 0; x < 2; x++) { + card->sinfo[x].bits = 8; + card->sinfo[x].stereo = 0; + card->sinfo[x].samplerate = 8000; + } + + nm256_initHw (card); + + for (x = 0; x < 2; x++) { + if ((card->dev[x] = + sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "NM256", &nm256_audio_driver, + sizeof(struct audio_driver), + DMA_NODMA, AFMT_U8 | AFMT_S16_LE, + NULL, -1, -1)) >= 0) { + /* 1K minimum buffer size. */ + audio_devs[card->dev[x]]->min_fragment = 10; + /* Maximum of 8K buffer size. */ + audio_devs[card->dev[x]]->max_fragment = 13; + } + else { + printk(KERN_ERR "NM256: Too many PCM devices available\n"); + nm256_release_ports (card); + kfree (card); + return 0; + } + } + + pci_set_drvdata(pcidev,card); + + /* Insert the card in the list. */ + card->next_card = nmcard_list; + nmcard_list = card; + + printk(KERN_INFO "Initialized NeoMagic %s audio in PCI native mode\n", + verstr); + + /* + * And our mixer. (We should allow support for other mixers, maybe.) + */ + + nm256_install_mixer (card); + + pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), handle_pm_event); + if (pmdev) + pmdev->data = card; + + return 1; +} + + +/* + * PM event handler, so the card is properly reinitialized after a power + * event. + */ +static int +handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct nm256_info *crd = (struct nm256_info*) dev->data; + if (crd) { + switch (rqst) { + case PM_SUSPEND: + break; + case PM_RESUME: + { + int playing = crd->playing; + nm256_full_reset (crd); + /* + * A little ugly, but that's ok; pretend the + * block we were playing is done. + */ + if (playing) + DMAbuf_outputintr (crd->dev_for_play, 1); + } + break; + } + } + return 0; +} + +static int __devinit +nm256_probe(struct pci_dev *pcidev,const struct pci_device_id *pciid) +{ + if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO) + return nm256_install(pcidev, REV_NM256AV, "256AV"); + if (pcidev->device == PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO) + return nm256_install(pcidev, REV_NM256ZX, "256ZX"); + return -1; /* should not come here ... */ +} + +static void __devinit +nm256_remove(struct pci_dev *pcidev) { + struct nm256_info *xcard = pci_get_drvdata(pcidev); + struct nm256_info *card,*next_card = NULL; + + for (card = nmcard_list; card != NULL; card = next_card) { + next_card = card->next_card; + if (card == xcard) { + stopPlay (card); + stopRecord (card); + if (card->has_irq) + free_irq (card->irq, card); + nm256_release_ports (card); + sound_unload_mixerdev (card->mixer_oss_dev); + sound_unload_audiodev (card->dev[0]); + sound_unload_audiodev (card->dev[1]); + kfree (card); + break; + } + } + if (nmcard_list == card) + nmcard_list = next_card; +} + +/* + * Open the device + * + * DEV - device + * MODE - mode to open device (logical OR of OPEN_READ and OPEN_WRITE) + * + * Called when opening the DMAbuf (dmabuf.c:259) + */ +static int +nm256_audio_open(int dev, int mode) +{ + struct nm256_info *card = nm256_find_card (dev); + int w; + + if (card == NULL) + return -ENODEV; + + if (card->dev[0] == dev) + w = 0; + else if (card->dev[1] == dev) + w = 1; + else + return -ENODEV; + + if (card->opencnt[w] > 0) + return -EBUSY; + + /* No bits set? Huh? */ + if (! ((mode & OPEN_READ) || (mode & OPEN_WRITE))) + return -EIO; + + /* + * If it's open for both read and write, and the card's currently + * being read or written to, then do the opposite of what has + * already been done. Otherwise, don't specify any mode until the + * user actually tries to do I/O. (Some programs open the device + * for both read and write, but only actually do reading or writing.) + */ + + if ((mode & OPEN_WRITE) && (mode & OPEN_READ)) { + if (card->is_open_play) + mode = OPEN_WRITE; + else if (card->is_open_record) + mode = OPEN_READ; + else mode = 0; + } + + if (mode & OPEN_WRITE) { + if (card->is_open_play == 0) { + card->dev_for_play = dev; + card->is_open_play = 1; + } + else + return -EBUSY; + } + + if (mode & OPEN_READ) { + if (card->is_open_record == 0) { + card->dev_for_record = dev; + card->is_open_record = 1; + } + else + return -EBUSY; + } + + card->opencnt[w]++; + return 0; +} + +/* + * Close the device + * + * DEV - device + * + * Called when closing the DMAbuf (dmabuf.c:477) + * after halt_xfer + */ +static void +nm256_audio_close(int dev) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + int w; + + if (card->dev[0] == dev) + w = 0; + else if (card->dev[1] == dev) + w = 1; + else + return; + + card->opencnt[w]--; + if (card->opencnt[w] <= 0) { + card->opencnt[w] = 0; + + if (card->dev_for_play == dev) { + stopPlay (card); + card->is_open_play = 0; + card->dev_for_play = -1; + } + + if (card->dev_for_record == dev) { + stopRecord (card); + card->is_open_record = 0; + card->dev_for_record = -1; + } + } + } +} + +/* Standard ioctl handler. */ +static int +nm256_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int ret; + u32 oldinfo; + int w; + + struct nm256_info *card = nm256_find_card (dev); + + if (card == NULL) + return -ENODEV; + + if (dev == card->dev[0]) + w = 0; + else + w = 1; + + /* + * The code here is messy. There are probably better ways to do + * it. (It should be possible to handle it the same way the AC97 mixer + * is done.) + */ + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + if (ret != 0) { + oldinfo = card->sinfo[w].samplerate; + card->sinfo[w].samplerate = ret; + ret = nm256_setInfo(dev, card); + if (ret != 0) + card->sinfo[w].samplerate = oldinfo; + } + if (ret == 0) + ret = card->sinfo[w].samplerate; + break; + + case SOUND_PCM_READ_RATE: + ret = card->sinfo[w].samplerate; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + card->sinfo[w].stereo = ret ? 1 : 0; + ret = nm256_setInfo (dev, card); + if (ret == 0) + ret = card->sinfo[w].stereo; + + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + if (ret < 1 || ret > 3) + ret = card->sinfo[w].stereo + 1; + else { + card->sinfo[w].stereo = ret - 1; + ret = nm256_setInfo (dev, card); + if (ret == 0) + ret = card->sinfo[w].stereo + 1; + } + break; + + case SOUND_PCM_READ_CHANNELS: + ret = card->sinfo[w].stereo + 1; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(ret, (int *) arg)) + return -EFAULT; + + if (ret != 0) { + oldinfo = card->sinfo[w].bits; + card->sinfo[w].bits = ret; + ret = nm256_setInfo (dev, card); + if (ret != 0) + card->sinfo[w].bits = oldinfo; + } + if (ret == 0) + ret = card->sinfo[w].bits; + break; + + case SOUND_PCM_READ_BITS: + ret = card->sinfo[w].bits; + break; + + default: + return -EINVAL; + } + return put_user(ret, (int *) arg); +} + +/* + * Given the sound device DEV and an associated physical buffer PHYSBUF, + * return a pointer to the actual buffer in kernel space. + * + * This routine should exist as part of the soundcore routines. + */ + +static char * +nm256_getDMAbuffer (int dev, unsigned long physbuf) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + char *dma_start = + (char *)(physbuf - (unsigned long)dmap->raw_buf_phys + + (unsigned long)dmap->raw_buf); + + return dma_start; +} + + +/* + * Output a block to sound device + * + * dev - device number + * buf - physical address of buffer + * total_count - total byte count in buffer + * intrflag - set if this has been called from an interrupt + * (via DMAbuf_outputintr) + * restart_dma - set if engine needs to be re-initialised + * + * Called when: + * 1. Starting output (dmabuf.c:1327) + * 2. (dmabuf.c:1504) + * 3. A new buffer needs to be sent to the device (dmabuf.c:1579) + */ +static void +nm256_audio_output_block(int dev, unsigned long physbuf, + int total_count, int intrflag) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + char *dma_buf = nm256_getDMAbuffer (dev, physbuf); + card->is_open_play = 1; + card->dev_for_play = dev; + nm256_write_block (card, dma_buf, total_count); + } +} + +/* Ditto, but do recording instead. */ +static void +nm256_audio_start_input(int dev, unsigned long physbuf, int count, + int intrflag) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + char *dma_buf = nm256_getDMAbuffer (dev, physbuf); + card->is_open_record = 1; + card->dev_for_record = dev; + nm256_startRecording (card, dma_buf, count); + } +} + +/* + * Prepare for inputting samples to DEV. + * Each requested buffer will be BSIZE byes long, with a total of + * BCOUNT buffers. + */ + +static int +nm256_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card == NULL) + return -ENODEV; + + if (card->is_open_record && card->dev_for_record != dev) + return -EBUSY; + + audio_devs[dev]->dmap_in->flags |= DMA_NODMA; + return 0; +} + +/* + * Prepare for outputting samples to `dev' + * + * Each buffer that will be passed will be `bsize' bytes long, + * with a total of `bcount' buffers. + * + * Called when: + * 1. A trigger enables audio output (dmabuf.c:978) + * 2. We get a write buffer without dma_mode setup (dmabuf.c:1152) + * 3. We restart a transfer (dmabuf.c:1324) + */ + +static int +nm256_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card == NULL) + return -ENODEV; + + if (card->is_open_play && card->dev_for_play != dev) + return -EBUSY; + + audio_devs[dev]->dmap_out->flags |= DMA_NODMA; + return 0; +} + +/* Stop the current operations associated with DEV. */ +static void +nm256_audio_reset(int dev) +{ + struct nm256_info *card = nm256_find_card (dev); + + if (card != NULL) { + if (card->dev_for_play == dev) + stopPlay (card); + if (card->dev_for_record == dev) + stopRecord (card); + } +} + +static int +nm256_audio_local_qlen(int dev) +{ + return 0; +} + +static struct audio_driver nm256_audio_driver = +{ + owner: THIS_MODULE, + open: nm256_audio_open, + close: nm256_audio_close, + output_block: nm256_audio_output_block, + start_input: nm256_audio_start_input, + ioctl: nm256_audio_ioctl, + prepare_for_input: nm256_audio_prepare_for_input, + prepare_for_output:nm256_audio_prepare_for_output, + halt_io: nm256_audio_reset, + local_qlen: nm256_audio_local_qlen, +}; + +static struct pci_device_id nm256_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0}, + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, nm256_pci_tbl); +MODULE_LICENSE("GPL"); + + +struct pci_driver nm256_pci_driver = { + name:"nm256_audio", + id_table:nm256_pci_tbl, + probe:nm256_probe, + remove:nm256_remove, +}; + +MODULE_PARM (usecache, "i"); +MODULE_PARM (buffertop, "i"); +MODULE_PARM (nm256_debug, "i"); +MODULE_PARM (force_load, "i"); + +static int __init do_init_nm256(void) +{ + printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.1p\n"); + return pci_module_init(&nm256_pci_driver); +} + +static void __exit cleanup_nm256 (void) +{ + pci_unregister_driver(&nm256_pci_driver); + pm_unregister_all (&handle_pm_event); +} + +module_init(do_init_nm256); +module_exit(cleanup_nm256); + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru linux/sound/oss/nm256_coeff.h linux-2.4.19-pre5-mjc/sound/oss/nm256_coeff.h --- linux/sound/oss/nm256_coeff.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/nm256_coeff.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,4697 @@ +#ifndef NM256_COEFF_H +#define NM256_COEFF_H + +#define NM_TOTAL_COEFF_COUNT 0x3158 + +static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { + 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21, + 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01, + 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, + 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, + 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF, + 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, + 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, + 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9, + 0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C, + 0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00, + 0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, + 0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, + 0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00, + 0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1, + 0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, + 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, + 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD, + 0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38, + 0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, + 0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, + 0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06, + 0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, + 0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, + 0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA, + 0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9, + 0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68, + 0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32, + 0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, + 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, + 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53, + 0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04, + 0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01, + 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20, + 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43, + 0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1, + 0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD, + 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8, + 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5, + 0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04, + 0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04, + 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D, + 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE, + 0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD, + 0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, + 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2, + 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD, + 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, + 0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D, + 0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01, + 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, + 0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, + 0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, + 0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66, + 0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF, + 0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, + 0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, + 0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00, + 0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF, + 0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, + 0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, + 0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC, + 0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98, + 0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, + 0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, + 0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05, + 0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A, + 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, + 0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, + 0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8, + 0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40, + 0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85, + 0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36, + 0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D, + 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39, + 0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48, + 0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01, + 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F, + 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F, + 0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1, + 0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE, + 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A, + 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A, + 0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03, + 0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, + 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, + 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01, + 0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC, + 0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, + 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2, + 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF, + 0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48, + 0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01, + 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11, + 0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF, + 0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, + 0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, + 0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF, + 0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C, + 0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, + 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, + 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC, + 0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1, + 0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, + 0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, + 0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04, + 0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, + 0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, + 0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, + 0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7, + 0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3, + 0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9, + 0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A, + 0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, + 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, + 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, + 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A, + 0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48, + 0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, + 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91, + 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8, + 0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1, + 0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF, + 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA, + 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD, + 0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD, + 0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06, + 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF, + 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94, + 0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC, + 0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, + 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4, + 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF, + 0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A, + 0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01, + 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, + 0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, + 0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, + 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B, + 0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF, + 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, + 0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, + 0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF, + 0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC, + 0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, + 0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, + 0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC, + 0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE, + 0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, + 0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, + 0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03, + 0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91, + 0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, + 0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, + 0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5, + 0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20, + 0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3, + 0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E, + 0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00, + 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47, + 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96, + 0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46, + 0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01, + 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74, + 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B, + 0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2, + 0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF, + 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D, + 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A, + 0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE, + 0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, + 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, + 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF, + 0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81, + 0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC, + 0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, + 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5, + 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2, + 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF, + 0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED, + 0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01, + 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, + 0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25, + 0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, + 0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, + 0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2, + 0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, + 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, + 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, + 0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA, + 0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, + 0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, + 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02, + 0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB, + 0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, + 0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, + 0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4, + 0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85, + 0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02, + 0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41, + 0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, + 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, + 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, + 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA, + 0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44, + 0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01, + 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5, + 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A, + 0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3, + 0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00, + 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A, + 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C, + 0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00, + 0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06, + 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D, + 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0, + 0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC, + 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, + 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8, + 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7, + 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64, + 0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE, + 0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, + 0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, + 0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, + 0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, + 0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD, + 0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, + 0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, + 0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE, + 0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54, + 0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, + 0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, + 0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, + 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC, + 0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63, + 0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, + 0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, + 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00, + 0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34, + 0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, + 0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, + 0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3, + 0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0, + 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36, + 0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44, + 0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, + 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA, + 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00, + 0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4, + 0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42, + 0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01, + 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E, + 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31, + 0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4, + 0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01, + 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86, + 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D, + 0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01, + 0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, + 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, + 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88, + 0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF, + 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA, + 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92, + 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA, + 0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE, + 0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF, + 0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5, + 0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, + 0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, + 0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01, + 0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C, + 0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, + 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, + 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC, + 0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8, + 0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, + 0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, + 0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, + 0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF, + 0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99, + 0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, + 0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, + 0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2, + 0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F, + 0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D, + 0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46, + 0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, + 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, + 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, + 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, + 0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F, + 0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01, + 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A, + 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF, + 0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5, + 0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02, + 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9, + 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8, + 0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03, + 0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07, + 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0, + 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0, + 0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC, + 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9, + 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD, + 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66, + 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36, + 0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF, + 0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, + 0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, + 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF, + 0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11, + 0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, + 0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, + 0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01, + 0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74, + 0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF, + 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, + 0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, + 0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC, + 0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A, + 0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, + 0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, + 0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, + 0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD, + 0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08, + 0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, + 0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, + 0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32, + 0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1, + 0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70, + 0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5, + 0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47, + 0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF, + 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26, + 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00, + 0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, + 0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B, + 0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01, + 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E, + 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0, + 0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7, + 0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD, + 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F, + 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66, + 0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04, + 0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, + 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, + 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, + 0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48, + 0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC, + 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93, + 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01, + 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34, + 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA, + 0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF, + 0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF, + 0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10, + 0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, + 0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, + 0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01, + 0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30, + 0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, + 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, + 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC, + 0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A, + 0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, + 0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, + 0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03, + 0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04, + 0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, + 0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, + 0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1, + 0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE, + 0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48, + 0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71, + 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, + 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, + 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, + 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB, + 0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37, + 0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01, + 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2, + 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70, + 0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8, + 0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD, + 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9, + 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0, + 0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05, + 0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, + 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06, + 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40, + 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, + 0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53, + 0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC, + 0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, + 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC, + 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D, + 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6, + 0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00, + 0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, + 0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, + 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF, + 0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06, + 0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, + 0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, + 0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01, + 0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57, + 0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, + 0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, + 0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD, + 0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B, + 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, + 0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, + 0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03, + 0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24, + 0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, + 0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, + 0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1, + 0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8, + 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, + 0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04, + 0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C, + 0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02, + 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00, + 0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, + 0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33, + 0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00, + 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, + 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B, + 0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA, + 0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD, + 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58, + 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14, + 0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06, + 0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, + 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, + 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0, + 0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC, + 0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA, + 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4, + 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA, + 0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00, + 0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00, + 0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06, + 0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, + 0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, + 0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, + 0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE, + 0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, + 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, + 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD, + 0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0, + 0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, + 0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, + 0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04, + 0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B, + 0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, + 0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, + 0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, + 0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2, + 0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE, + 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E, + 0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08, + 0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4, + 0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, + 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, + 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B, + 0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E, + 0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00, + 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E, + 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C, + 0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC, + 0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC, + 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6, + 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B, + 0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06, + 0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05, + 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC, + 0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD, + 0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8, + 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A, + 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2, + 0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01, + 0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, + 0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, + 0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00, + 0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20, + 0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, + 0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, + 0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, + 0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7, + 0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, + 0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, + 0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE, + 0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F, + 0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, + 0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, + 0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05, + 0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D, + 0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, + 0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, + 0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2, + 0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, + 0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C, + 0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B, + 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0, + 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF, + 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66, + 0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29, + 0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF, + 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E, + 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4, + 0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01, + 0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC, + 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB, + 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12, + 0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06, + 0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, + 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, + 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12, + 0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD, + 0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7, + 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD, + 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0, + 0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01, + 0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00, + 0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64, + 0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, + 0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, + 0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73, + 0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, + 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, + 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF, + 0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC, + 0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, + 0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, + 0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06, + 0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF, + 0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, + 0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, + 0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9, + 0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4, + 0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, + 0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11, + 0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63, + 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, + 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, + 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22, + 0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25, + 0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF, + 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20, + 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87, + 0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE, + 0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC, + 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3, + 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7, + 0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07, + 0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03, + 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97, + 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA, + 0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02, + 0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5, + 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A, + 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE, + 0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01, + 0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7, + 0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, + 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00, + 0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4, + 0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, + 0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, + 0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, + 0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60, + 0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, + 0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, + 0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF, + 0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D, + 0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, + 0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, + 0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06, + 0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15, + 0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, + 0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, + 0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7, + 0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6, + 0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0, + 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15, + 0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C, + 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF, + 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3, + 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF, + 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1, + 0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F, + 0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF, + 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8, + 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23, + 0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA, + 0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC, + 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB, + 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49, + 0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07, + 0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, + 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, + 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07, + 0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01, + 0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4, + 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E, + 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B, + 0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01, + 0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00, + 0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB, + 0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, + 0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, + 0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01, + 0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB, + 0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, + 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, + 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00, + 0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48, + 0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, + 0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, + 0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06, + 0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81, + 0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, + 0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, + 0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, + 0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8, + 0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4, + 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A, + 0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9, + 0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, + 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, + 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74, + 0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A, + 0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE, + 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B, + 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC, + 0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8, + 0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC, + 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F, + 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96, + 0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06, + 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00, + 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C, + 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56, + 0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00, + 0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3, + 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8, + 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, + 0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6, + 0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, + 0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, + 0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1, + 0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00, + 0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, + 0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, + 0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01, + 0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D, + 0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, + 0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, + 0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01, + 0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73, + 0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, + 0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, + 0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07, + 0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08, + 0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, + 0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD, + 0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA, + 0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F, + 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F, + 0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB, + 0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF, + 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89, + 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D, + 0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15, + 0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01, + 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6, + 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86, + 0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6, + 0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC, + 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0, + 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2, + 0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06, + 0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, + 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, + 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3, + 0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF, + 0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2, + 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35, + 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00, + 0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD, + 0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01, + 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, + 0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41, + 0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00, + 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, + 0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01, + 0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1, + 0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, + 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, + 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02, + 0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95, + 0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, + 0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, + 0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07, + 0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9, + 0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00, + 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, + 0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, + 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7, + 0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE, + 0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62, + 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25, + 0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2, + 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, + 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, + 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F, + 0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11, + 0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01, + 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26, + 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53, + 0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4, + 0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC, + 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D, + 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D, + 0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06, + 0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD, + 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12, + 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29, + 0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF, + 0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1, + 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75, + 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, + 0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0, + 0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, + 0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, + 0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, + 0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02, + 0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00, + 0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, + 0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01, + 0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C, + 0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, + 0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, + 0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD, + 0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64, + 0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, + 0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, + 0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06, + 0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65, + 0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, + 0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, + 0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, + 0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01, + 0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F, + 0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, + 0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29, + 0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80, + 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF, + 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E, + 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B, + 0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C, + 0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01, + 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB, + 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, + 0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2, + 0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC, + 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29, + 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D, + 0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05, + 0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, + 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, + 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3, + 0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE, + 0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1, + 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7, + 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF, + 0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF, + 0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, + 0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E, + 0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00, + 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, + 0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, + 0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01, + 0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7, + 0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, + 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, + 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD, + 0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC, + 0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, + 0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, + 0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06, + 0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E, + 0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, + 0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, + 0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC, + 0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68, + 0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55, + 0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E, + 0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63, + 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, + 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, + 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4, + 0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08, + 0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01, + 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C, + 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33, + 0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2, + 0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD, + 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06, + 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6, + 0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04, + 0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04, + 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10, + 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F, + 0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD, + 0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, + 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1, + 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA, + 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, + 0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0, + 0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01, + 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, + 0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, + 0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, + 0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7, + 0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00, + 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, + 0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, + 0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00, + 0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5, + 0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, + 0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, + 0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC, + 0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78, + 0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, + 0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, + 0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06, + 0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, + 0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, + 0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA, + 0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE, + 0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D, + 0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33, + 0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E, + 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00, + 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96, + 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E, + 0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04, + 0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01, + 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD, + 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF, + 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49, + 0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1, + 0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD, + 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8, + 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E, + 0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03, + 0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, + 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, + 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97, + 0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD, + 0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, + 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2, + 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF, + 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5, + 0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7, + 0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF, + 0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, + 0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, + 0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00, + 0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C, + 0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, + 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, + 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC, + 0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3, + 0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00, + 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, + 0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, + 0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, + 0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05, + 0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, + 0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, + 0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, + 0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, + 0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8, + 0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54, + 0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B, + 0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37, + 0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, + 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, + 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, + 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, + 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42, + 0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48, + 0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01, + 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0, + 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A, + 0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1, + 0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE, + 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43, + 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF, + 0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03, + 0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05, + 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B, + 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7, + 0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC, + 0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08, + 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3, + 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF, + 0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86, + 0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01, + 0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A, + 0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, + 0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, + 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E, + 0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF, + 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, + 0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, + 0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF, + 0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0, + 0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, + 0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, + 0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC, + 0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04, + 0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, + 0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, + 0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, + 0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04, + 0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63, + 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, + 0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, + 0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7, + 0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7, + 0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0, + 0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B, + 0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00, + 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC, + 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72, + 0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47, + 0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF, + 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56, + 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7, + 0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1, + 0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF, + 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD, + 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4, + 0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD, + 0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, + 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, + 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A, + 0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC, + 0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, + 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4, + 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF, + 0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D, + 0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01, + 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97, + 0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF, + 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, + 0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, + 0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF, + 0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45, + 0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, + 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, + 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC, + 0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08, + 0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, + 0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, + 0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03, + 0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D, + 0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, + 0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, + 0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5, + 0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33, + 0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB, + 0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F, + 0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, + 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, + 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, + 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D, + 0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46, + 0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5, + 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, + 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D, + 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30, + 0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2, + 0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00, + 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC, + 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0, + 0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF, + 0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06, + 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07, + 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87, + 0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC, + 0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5, + 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6, + 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE, + 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, + 0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4, + 0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01, + 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, + 0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, + 0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, + 0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60, + 0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, + 0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, + 0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE, + 0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE, + 0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, + 0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, + 0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, + 0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB, + 0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, + 0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, + 0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, + 0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01, + 0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9, + 0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, + 0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, + 0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4, + 0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96, + 0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B, + 0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42, + 0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00, + 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50, + 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF, + 0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44, + 0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, + 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1, + 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3, + 0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3, + 0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00, + 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35, + 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F, + 0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00, + 0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, + 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, + 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9, + 0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC, + 0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1, + 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8, + 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1, + 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F, + 0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE, + 0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF, + 0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC, + 0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF, + 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, + 0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, + 0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE, + 0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1, + 0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, + 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, + 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC, + 0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B, + 0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, + 0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, + 0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, + 0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00, + 0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45, + 0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, + 0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, + 0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3, + 0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF, + 0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F, + 0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44, + 0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, + 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, + 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, + 0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41, + 0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01, + 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C, + 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E, + 0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4, + 0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01, + 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60, + 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D, + 0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02, + 0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07, + 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE, + 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4, + 0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB, + 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB, + 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B, + 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, + 0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73, + 0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, + 0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, + 0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, + 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF, + 0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B, + 0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, + 0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, + 0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01, + 0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70, + 0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF, + 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, + 0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, + 0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC, + 0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7, + 0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, + 0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, + 0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE, + 0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC, + 0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, + 0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, + 0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2, + 0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B, + 0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76, + 0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46, + 0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF, + 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C, + 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00, + 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E, + 0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF, + 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01, + 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8, + 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00, + 0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5, + 0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE, + 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0, + 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3, + 0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03, + 0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, + 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, + 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF, + 0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5, + 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE, + 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E, + 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A, + 0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF, + 0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF, + 0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30, + 0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, + 0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, + 0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01, + 0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9, + 0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, + 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, + 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC, + 0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20, + 0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, + 0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, + 0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD, + 0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C, + 0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, + 0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, + 0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1, + 0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A, + 0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF, + 0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48, + 0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62, + 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, + 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, + 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, + 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A, + 0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01, + 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B, + 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4, + 0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7, + 0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD, + 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89, + 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B, + 0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04, + 0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06, + 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61, + 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B, + 0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC, + 0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F, + 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01, + 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B, + 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6, + 0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF, + 0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, + 0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, + 0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, + 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF, + 0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C, + 0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, + 0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, + 0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01, + 0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7, + 0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF, + 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, + 0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, + 0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC, + 0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28, + 0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, + 0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, + 0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03, + 0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC, + 0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, + 0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, + 0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, + 0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1, + 0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB, + 0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8, + 0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48, + 0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, + 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD, + 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00, + 0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36, + 0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00, + 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC, + 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96, + 0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8, + 0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD, + 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F, + 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF, + 0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05, + 0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, + 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, + 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB, + 0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC, + 0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB, + 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72, + 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7, + 0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00, + 0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF, + 0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23, + 0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, + 0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, + 0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01, + 0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2, + 0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF, + 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, + 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, + 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD, + 0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01, + 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, + 0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, + 0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04, + 0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD, + 0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, + 0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, + 0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, + 0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1, + 0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD, + 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, + 0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04, + 0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03, + 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, + 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, + 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, + 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD, + 0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32, + 0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00, + 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10, + 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73, + 0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA, + 0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC, + 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98, + 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B, + 0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06, + 0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05, + 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, + 0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C, + 0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD, + 0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA, + 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9, + 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD, + 0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00, + 0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, + 0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, + 0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, + 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00, + 0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25, + 0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, + 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, + 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, + 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, + 0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, + 0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, + 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, + 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, + 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26, + 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06, + 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, + 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, + 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84, + 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD, + 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, + 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, + 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, + 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12, + 0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, + 0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, + 0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, + 0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, + 0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, + 0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, + 0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, + 0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, + 0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, + 0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, + 0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, + 0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD, + 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70, + 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, + 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD, + 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7, + 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC, + 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC, + 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2, + 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC, + 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79, + 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47, + 0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, + 0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, + 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, + 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, + 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, + 0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, + 0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, + 0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, + 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, + 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, + 0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, + 0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD, + 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07, + 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00, + 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96, + 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07, + 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, + 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, + 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85, + 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC, + 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C, + 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, + 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, + 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, + 0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, + 0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, + 0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, + 0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, + 0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, + 0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, + 0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, + 0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, + 0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, + 0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, + 0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC, + 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83, + 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03, + 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45, + 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF, + 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C, + 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC, + 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86, + 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03, + 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14, + 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, + 0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, + 0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, + 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, + 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, + 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, + 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, + 0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, + 0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, + 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, + 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, + 0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, + 0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, + 0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC, + 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC, + 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38, + 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06, + 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, + 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, + 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7, + 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC, + 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, + 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, + 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, + 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, + 0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, + 0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, + 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, + 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, + 0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, + 0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, + 0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, + 0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, + 0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, + 0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, + 0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, + 0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, + 0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC, + 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C, + 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05, + 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A, + 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC, + 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, + 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9, + 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19, + 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, + 0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, + 0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, + 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, + 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, + 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, + 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, + 0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, + 0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, + 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, + 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, + 0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, + 0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC, + 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F, + 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15, + 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05, + 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, + 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, + 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00, + 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10, + 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, + 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, + 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, + 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, + 0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, + 0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, + 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, + 0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, + 0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, + 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, + 0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, + 0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, + 0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, + 0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, + 0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, + 0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, + 0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, + 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1, + 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06, + 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C, + 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00, + 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7, + 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC, + 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, + 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6, + 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56, + 0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, + 0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, + 0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, + 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, + 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, + 0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, + 0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, + 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, + 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, + 0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, + 0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, + 0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC, + 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F, + 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D, + 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04, + 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, + 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, + 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0, + 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC, + 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, + 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, + 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, + 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47, + 0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, + 0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, + 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, + 0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, + 0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, + 0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, + 0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, + 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, + 0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, + 0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, + 0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, + 0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, + 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC, + 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA, + 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07, + 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18, + 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, + 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B, + 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD, + 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3, + 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4, + 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, + 0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, + 0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, + 0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, + 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, + 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, + 0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, + 0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, + 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, + 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, + 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, + 0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, + 0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC, + 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21, + 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13, + 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02, + 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, + 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, + 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00, + 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94, + 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD, + 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, + 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, + 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, + 0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, + 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, + 0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, + 0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, + 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, + 0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, + 0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, + 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, + 0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, + 0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, + 0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, + 0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC, + 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27, + 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07, + 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E, + 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00, + 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05, + 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD, + 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, + 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1, + 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C, + 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, + 0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, + 0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, + 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, + 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, + 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, + 0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, + 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, + 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, + 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, + 0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, + 0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD, + 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF, + 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D, + 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF, + 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE, + 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, + 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, + 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47, + 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02, + 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, + 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, + 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, + 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, + 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, + 0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, + 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, + 0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, + 0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, + 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, + 0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, + 0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, + 0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, + 0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, + 0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, + 0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD, + 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, + 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06, + 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94, + 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01, + 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, + 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1, + 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE, + 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, + 0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, + 0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, + 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, + 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, + 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, + 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, + 0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, + 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, + 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, + 0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, + 0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, + 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE, + 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33, + 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A, + 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02, + 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, + 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6, + 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00, + 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, + 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, + 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, + 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, + 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, + 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, + 0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, + 0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, + 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, + 0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, + 0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, + 0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, + 0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, + 0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, + 0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF, + 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A, + 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04, + 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04, + 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48, + 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF, + 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E, + 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2, + 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5, + 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, + 0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, + 0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, + 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, + 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, + 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, + 0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, + 0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, + 0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, + 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, + 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, + 0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, + 0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, + 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00, + 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6, + 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC, + 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04, + 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08, + 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, + 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, + 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF, + 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6, + 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE, + 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF, + 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, + 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, + 0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, + 0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, + 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, + 0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, + 0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, + 0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, + 0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, + 0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, + 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, + 0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, + 0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, + 0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, + 0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00, + 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D, + 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02, + 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD, + 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C, + 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE, + 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED, + 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5, + 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7, + 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD, + 0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, + 0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, + 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, + 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, + 0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, + 0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, + 0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, + 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, + 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, + 0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, + 0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, + 0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01, + 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46, + 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27, + 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06, + 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, + 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, + 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47, + 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD, + 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9, + 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, + 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, + 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA, + 0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, + 0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, + 0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, + 0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, + 0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, + 0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, + 0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, + 0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, + 0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, + 0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, + 0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE, + 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13, + 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF, + 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D, + 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61, + 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD, + 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4, + 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA, + 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C, + 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B, + 0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, + 0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, + 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, + 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, + 0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, + 0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, + 0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, + 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, + 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, + 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, + 0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, + 0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD, + 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA, + 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00, + 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA, + 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07, + 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, + 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, + 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, + 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6, + 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC, + 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE, + 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, + 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, + 0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, + 0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, + 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, + 0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, + 0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, + 0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, + 0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, + 0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, + 0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, + 0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, + 0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, + 0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD, + 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D, + 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03, + 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD, + 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, + 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80, + 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC, + 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98, + 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00, + 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40, + 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, + 0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, + 0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, + 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, + 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, + 0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, + 0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, + 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, + 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, + 0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, + 0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, + 0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC, + 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3, + 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00, + 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1, + 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07, + 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, + 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, + 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF, + 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97, + 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC, + 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, + 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, + 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, + 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, + 0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, + 0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, + 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, + 0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, + 0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, + 0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, + 0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, + 0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, + 0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, + 0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, + 0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, + 0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, + 0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC, + 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15, + 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04, + 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44, + 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14, + 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC, + 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, + 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA, + 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8, + 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, + 0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, + 0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, + 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, + 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, + 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, + 0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, + 0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, + 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, + 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, + 0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, + 0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC, + 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E, + 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C, + 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06, + 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, + 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, + 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00, + 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD, + 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC, + 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, + 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, + 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, + 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, + 0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, + 0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, + 0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, + 0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, + 0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, + 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, + 0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, + 0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, + 0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, + 0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, + 0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, + 0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC, + 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38, + 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06, + 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9, + 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00, + 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55, + 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC, + 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, + 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7, + 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2, + 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, + 0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, + 0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, + 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, + 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, + 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, + 0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, + 0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, + 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, + 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, + 0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, + 0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, + 0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, + 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00, + 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83, + 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05, + 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, + 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, + 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00, + 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20, + 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC, + 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, + 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, + 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, + 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, + 0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, + 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, + 0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, + 0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, + 0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, + 0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, + 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, + 0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, + 0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, + 0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, + 0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, + 0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC, + 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96, + 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06, + 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E, + 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E, + 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC, + 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, + 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4, + 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77, + 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45, + 0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, + 0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, + 0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, + 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, + 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, + 0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, + 0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, + 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, + 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, + 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, + 0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, + 0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, + 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC, + 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7, + 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, + 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70, + 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03, + 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, + 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, + 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00, + 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C, + 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD, + 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, + 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, + 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B, + 0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, + 0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, + 0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, + 0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, + 0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, + 0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, + 0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, + 0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, + 0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, + 0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, + 0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC, + 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26, + 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, + 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, + 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, + 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, + 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, + 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, + 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, + 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, + 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B, + 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11, + 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00, + 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C, + 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, + 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, + 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, + 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, + 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, + 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, + 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, + 0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, + 0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, + 0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, + 0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, + 0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, + 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, + 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, + 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, + 0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1, + 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07, + 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF, + 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A, + 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, + 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, + 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, + 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, + 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, + 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, + 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, + 0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, + 0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, + 0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, + 0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, + 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, + 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, + 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, + 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, + 0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43, + 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48, + 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99, + 0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE, + 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7, + 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, + 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, + 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, + 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, + 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, + 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, + 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, + 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, + 0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, + 0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, + 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D, + 0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, + 0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, + 0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, + 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, + 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, + 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, + 0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E, + 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46, + 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01, + 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D, + 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF, + 0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, + 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, + 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, + 0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, + 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, + 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06, + 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, + 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, + 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, + 0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, + 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A, + 0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, + 0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, + 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00, + 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, + 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, + 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, + 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, + 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41, + 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01, + 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9, + 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF, + 0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, + 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, + 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, + 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, + 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F, + 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, + 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, + 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00, + 0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, + 0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, + 0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD, + 0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, + 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00, + 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, + 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, + 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A, + 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, + 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85, + 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF, + 0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, + 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, + 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, + 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, + 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, + 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A, + 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, + 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, + 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00, + 0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, + 0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, + 0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7, + 0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, + 0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, + 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF, + 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, + 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, + 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, + 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, + 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC, + 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32, + 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01, + 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A, + 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, + 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, + 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, + 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, + 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, + 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00, + 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, + 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, + 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00, + 0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, + 0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, + 0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC, + 0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, + 0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, + 0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, + 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, + 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, + 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, + 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, + 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59, + 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28, + 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, + 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, + 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, + 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, + 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, + 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, + 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF, + 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, + 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, + 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, + 0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, + 0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, + 0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, + 0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF, + 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, + 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, + 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, + 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, + 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, + 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E, + 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01, + 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B, + 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, + 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, + 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, + 0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, + 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, + 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, + 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, + 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, + 0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, + 0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, + 0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, + 0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, + 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, + 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, + 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, + 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, + 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7, + 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14, + 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00, + 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47, + 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, + 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, + 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, + 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, + 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, + 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, + 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, + 0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, + 0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, + 0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, + 0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, + 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, + 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, + 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, + 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12, + 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B, + 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF, + 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19, + 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, + 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, + 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, + 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, + 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, + 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, + 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, + 0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, + 0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, + 0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, + 0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, + 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, + 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, + 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, + 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, + 0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18, + 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48, + 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87, + 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE, + 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61, + 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, + 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, + 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, + 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, + 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, + 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, + 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, + 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, + 0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, + 0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32, + 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, + 0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, + 0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, + 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, + 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, + 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, + 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, + 0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C, + 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47, + 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3, + 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01, + 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73, + 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF, + 0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, + 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, + 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, + 0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, + 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, + 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04, + 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, + 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, + 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, + 0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, + 0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, + 0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, + 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00, + 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, + 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, + 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, + 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, + 0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5, + 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43, + 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE, + 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01, + 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1, + 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF, + 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, + 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, + 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, + 0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, + 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, + 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, + 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, + 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00, + 0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, + 0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, + 0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, + 0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00, + 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, + 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, + 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, + 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, + 0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, + 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D, + 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03, + 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, + 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02, + 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF, + 0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, + 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, + 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, + 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, + 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, + 0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06, + 0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16, + 0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF, + 0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88, + 0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02, + 0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC, + 0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A, + 0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18, + 0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF, + 0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3, + 0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00, + 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75, + 0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10, + 0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72, + 0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF, + 0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6, + 0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF, + 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C, + 0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10, + 0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD, + 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00, + 0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35, + 0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1, + 0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E, + 0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9, + 0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02, + 0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F, + 0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF, + 0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71, + 0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05, + 0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06, + 0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6, + 0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF, + 0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B, + 0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02, + 0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89, + 0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E, + 0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1, + 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, + 0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD, + 0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00, + 0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F, + 0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10, + 0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E, + 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF, + 0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA, + 0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF, + 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6, + 0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10, + 0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2, + 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00, + 0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3, + 0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50, + 0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E, + 0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2, + 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02, + 0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03, + 0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF, + 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB, + 0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06, + 0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00, + 0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05, + 0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73, + 0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, + 0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88, + 0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02, + 0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, + 0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E, + 0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9, + 0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF, + 0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E, + 0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00, + 0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C, + 0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10, + 0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C, + 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF, + 0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38, + 0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF, + 0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6, + 0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10, + 0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97, + 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00, + 0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D, + 0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF, + 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB, + 0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A, + 0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, + 0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02, + 0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99, + 0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF, + 0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45, + 0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06, + 0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04, + 0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4, + 0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF, + 0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00, + 0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03, + 0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, + 0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D, + 0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7, + 0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF, + 0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05, + 0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01, + 0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, + 0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10, + 0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D, + 0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF, + 0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C, + 0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF, + 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB, + 0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10, + 0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D, + 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00, + 0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3, + 0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, + 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C, + 0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B, + 0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01, + 0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F, + 0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF, + 0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF, + 0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07, + 0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6, + 0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09, + 0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D, + 0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73, + 0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03, + 0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8, + 0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D, + 0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA, + 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF, + 0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9, + 0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF, + 0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70, + 0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F, + 0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72, + 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF, + 0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6, + 0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF, + 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96, + 0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10, + 0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84, + 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF, + 0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52, + 0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, + 0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3, + 0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B, + 0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE, + 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01, + 0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5, + 0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, + 0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C, + 0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07, + 0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC, + 0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09, + 0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2, + 0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3, + 0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04, + 0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3, + 0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C, + 0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24, + 0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF, + 0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E, + 0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF, + 0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75, + 0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F, + 0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A, + 0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF, + 0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15, + 0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF, + 0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0, + 0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F, + 0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7, + 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF, + 0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC, + 0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF, + 0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F, + 0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C, + 0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC, + 0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04, + 0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E, + 0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, + 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B, + 0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08, + 0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2, + 0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08, + 0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E, + 0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50, + 0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04, + 0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE, + 0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C, + 0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53, + 0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF, + 0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88, + 0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF, + 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C, + 0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F, + 0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86, + 0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF, + 0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A, + 0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF, + 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF, + 0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F, + 0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF, + 0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E, + 0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF, + 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2, + 0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C, + 0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9, + 0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04, + 0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6, + 0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD, + 0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09, + 0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7, + 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07, + 0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91, + 0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, + 0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82, + 0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01, + 0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8, + 0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B, + 0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A, + 0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, + 0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65, + 0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF, + 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90, + 0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10, + 0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81, + 0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF, + 0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73, + 0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF, + 0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74, + 0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F, + 0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4, + 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF, + 0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8, + 0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF, + 0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B, + 0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D, + 0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, + 0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03, + 0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32, + 0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, + 0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72, + 0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09, + 0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, + 0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07, + 0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA, + 0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, + 0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14, + 0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01, + 0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, + 0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B, + 0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6, + 0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, + 0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37, + 0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00, + 0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81, + 0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10, + 0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79, + 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF, + 0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91, + 0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF, + 0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41, + 0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10, + 0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA, + 0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01, + 0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88, + 0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, + 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B, + 0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D, + 0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, + 0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03, + 0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1, + 0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF, + 0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22, + 0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04, + 0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD, + 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06, + 0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A, + 0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, + 0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F, + 0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02, + 0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, + 0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A, + 0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A, + 0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF, + 0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF, + 0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00, + 0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77, + 0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10, + 0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73, + 0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF, + 0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4, + 0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF, + 0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14, + 0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10, + 0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF, + 0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00, + 0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A, + 0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1, + 0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E, + 0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA, + 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02, + 0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53, + 0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF, + 0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D, + 0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05, + 0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06, + 0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF, + 0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF, + 0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23, + 0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02, + 0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87, + 0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E, + 0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D, + 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, + 0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB, + 0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00, + 0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70, + 0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10, + 0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E, + 0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF, + 0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB, + 0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF, + 0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC, + 0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10, + 0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4, + 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00, + 0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9, + 0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F, + 0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E, + 0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3, + 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02, + 0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7, + 0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF, + 0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7, + 0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06, + 0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05, + 0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B, + 0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, + 0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1, + 0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02, + 0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91, + 0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E, + 0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5, + 0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, + 0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D, + 0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00, + 0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D, + 0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10, + 0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C, + 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, + 0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27, + 0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF, + 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB, + 0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10, + 0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99, + 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00, + 0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34, + 0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, + 0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3, + 0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A, + 0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00, + 0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02, + 0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D, + 0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61, + 0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06, + 0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05, + 0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC, + 0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, + 0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A, + 0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03, + 0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, + 0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D, + 0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1, + 0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, + 0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16, + 0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00, + 0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C, + 0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10, + 0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D, + 0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, + 0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D, + 0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF, + 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0, + 0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10, + 0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F, + 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00, + 0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA, + 0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, + 0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43, + 0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A, + 0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01, + 0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13, + 0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, + 0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB, + 0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07, + 0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4, + 0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09, + 0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E, + 0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E, + 0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03, + 0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6, + 0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D, + 0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3, + 0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF, + 0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA, + 0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF, + 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, + 0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F, + 0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71, + 0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF, + 0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47, + 0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE, + 0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30, + 0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20, + 0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42, + 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF, + 0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC, + 0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00, + 0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F, + 0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09, + 0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB, + 0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2, + 0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73, + 0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03, + 0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE, + 0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC, + 0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49, + 0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, + 0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65, + 0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB, + 0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36, + 0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E, + 0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02, + 0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00, + 0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0, + 0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00, + 0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1, + 0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21, + 0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00, + 0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62, + 0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00, + 0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52, + 0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20, + 0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D, + 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF, + 0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2, + 0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78, + 0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A, + 0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB, + 0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4, + 0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58, + 0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04, + 0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB, + 0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F, + 0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56, + 0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB, + 0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D, + 0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D, + 0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9, + 0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00, + 0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A, + 0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00, + 0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB, + 0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21, + 0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00, + 0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F, + 0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00, + 0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71, + 0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20, + 0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58, + 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00, + 0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9, + 0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96, + 0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B, + 0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC, + 0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB, + 0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10, + 0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF, + 0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37, + 0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB, + 0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB, + 0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D, + 0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C, + 0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB, + 0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24, + 0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C, + 0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD, + 0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00, + 0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24, + 0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00, + 0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3, + 0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21, + 0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6, + 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00, + 0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A, + 0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00, + 0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C, + 0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21, + 0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64, + 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00, + 0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02, + 0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00, + 0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB, + 0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C, + 0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB, + 0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB, + 0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56, + 0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF, + 0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69, + 0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB, + 0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB, + 0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44, + 0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, + 0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49, + 0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB, + 0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, + 0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B, + 0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E, + 0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00, + 0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD, + 0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00, + 0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8, + 0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20, + 0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2, + 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00, + 0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF, + 0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00, + 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4, + 0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21, + 0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70, + 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00, + 0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A, + 0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00, + 0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4, + 0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D, + 0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA, + 0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB, + 0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8, + 0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF, + 0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4, + 0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB, + 0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB, + 0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53, + 0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B, + 0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB, + 0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16, + 0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A, + 0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D, + 0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00, + 0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26, + 0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF, + 0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB, + 0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20, + 0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD, + 0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00, + 0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5, + 0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9, + 0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21, + 0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D, + 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00, + 0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32, + 0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00, + 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12, + 0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E, + 0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9, + 0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB, + 0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05, + 0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF, + 0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA, + 0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC, + 0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, + 0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03, + 0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2, + 0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54, + 0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB, + 0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, + 0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09, + 0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A, + 0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00, + 0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90, + 0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF, + 0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC, + 0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20, + 0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7, + 0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00, + 0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4, + 0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00, + 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB, + 0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21, + 0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A, + 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00, + 0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47, + 0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00, + 0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43, + 0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F, + 0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8, + 0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB, + 0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E, + 0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF, + 0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B, + 0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC, + 0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8, + 0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02, + 0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65, + 0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66, + 0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB, + 0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A, + 0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08, + 0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4, + 0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00, + 0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB, + 0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF, + 0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA, + 0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F, + 0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1, + 0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00, + 0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8, + 0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00, + 0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5, + 0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17, + 0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00, + 0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59, + 0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00, + 0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77, + 0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10, + 0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7, + 0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB, + 0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2, + 0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF, + 0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98, + 0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC, + 0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7, + 0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01, + 0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29, + 0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80, + 0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB, + 0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05, + 0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07, + 0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC, + 0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00, + 0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38, + 0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF, + 0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6, + 0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F, + 0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9, + 0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00, + 0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91, + 0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00, + 0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30, + 0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18, + 0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00, + 0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66, + 0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00, + 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE, + 0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11, + 0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7, + 0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC, + 0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62, + 0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF, + 0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF, + 0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC, + 0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7, + 0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01, + 0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF, + 0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, + 0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F, + 0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD, + 0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, + 0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06, + 0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72, + 0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00, + 0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78, + 0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF, + 0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1, + 0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E, + 0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0, + 0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00, + 0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F, + 0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00, + 0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B, + 0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19, + 0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00, + 0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E, + 0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00, + 0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7, + 0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13, + 0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7, + 0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC, + 0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED, + 0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF, + 0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73, + 0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD, + 0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7, + 0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00, + 0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5, + 0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, + 0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87, + 0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD, + 0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05, + 0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38, + 0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, + 0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC, + 0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE, + 0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA, + 0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E, + 0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6, + 0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00, + 0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21, + 0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00, + 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5, + 0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A, + 0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05, + 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD, + 0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63, + 0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22, + 0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14, + 0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7, + 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC, + 0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84, + 0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF, + 0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1, + 0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD, + 0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7, + 0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF, + 0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E, + 0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, + 0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B, + 0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD, + 0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC, + 0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04, + 0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC, + 0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00, + 0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3, + 0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE, + 0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2, + 0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D, + 0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA, + 0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00, + 0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6, + 0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00, + 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD, + 0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B, + 0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09, + 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE, + 0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48, + 0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E, + 0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15, + 0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8, + 0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC, + 0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26, + 0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF, + 0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C, + 0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE, + 0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8, + 0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF, + 0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49, + 0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, + 0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69, + 0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC, + 0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B, + 0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16, + 0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD, + 0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, + 0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0, + 0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE, + 0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9, + 0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C, + 0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB, + 0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00, + 0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C, + 0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00, + 0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13, + 0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C, + 0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F, + 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE, + 0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36, + 0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A, + 0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16, + 0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9, + 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC, + 0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4, + 0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF, + 0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12, + 0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE, + 0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9, + 0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE, + 0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18, + 0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, + 0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54, + 0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC, + 0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E, + 0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15, + 0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC, + 0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04, + 0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE, + 0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE, + 0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B, + 0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB, + 0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00, + 0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14, + 0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00, + 0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47, + 0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C, + 0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15, + 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE, + 0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C, + 0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00, + 0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56, + 0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04, + 0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD, + 0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C, + 0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF, + 0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6, + 0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8, + 0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE, + 0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42, + 0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5, + 0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03, + 0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC, + 0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3, + 0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF, + 0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF, + 0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3, + 0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE, + 0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68, + 0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A, + 0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1, + 0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00, + 0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8, + 0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00, + 0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB, + 0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27, + 0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3, + 0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE, + 0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E, + 0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF, + 0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4, + 0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4, + 0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01, + 0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03, + 0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F, + 0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62, + 0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05, + 0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05, + 0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB, + 0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54, + 0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03, + 0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1, + 0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3, + 0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7, + 0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF, + 0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB, + 0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE, + 0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55, + 0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28, + 0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4, + 0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00, + 0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8, + 0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00, + 0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8, + 0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29, + 0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD, + 0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE, + 0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02, + 0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A, + 0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3, + 0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01, + 0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03, + 0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28, + 0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16, + 0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05, + 0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05, + 0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00, + 0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE, + 0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04, + 0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5, + 0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4, + 0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2, + 0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF, + 0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F, + 0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE, + 0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43, + 0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26, + 0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6, + 0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00, + 0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98, + 0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00, + 0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25, + 0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B, + 0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7, + 0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE, + 0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8, + 0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F, + 0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3, + 0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03, + 0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39, + 0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC, + 0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05, + 0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05, + 0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40, + 0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71, + 0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04, + 0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9, + 0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4, + 0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E, + 0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF, + 0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70, + 0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE, + 0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31, + 0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24, + 0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7, + 0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00, + 0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68, + 0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00, + 0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51, + 0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D, + 0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1, + 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE, + 0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0, + 0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF, + 0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4, + 0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3, + 0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02, + 0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41, + 0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00, + 0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84, + 0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06, + 0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9, + 0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8, + 0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D, + 0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04, + 0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5, + 0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D, + 0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF, + 0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42, + 0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD, + 0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20, + 0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22, + 0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07, + 0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00, + 0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24, + 0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00, + 0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C, + 0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F, + 0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB, + 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE, + 0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC, + 0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF, + 0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79, + 0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3, + 0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02, + 0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40, + 0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C, + 0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06, + 0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E, + 0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9, + 0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2, + 0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0, + 0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04, + 0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5, + 0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F, + 0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF, + 0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16, + 0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD, + 0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10, + 0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20, + 0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17, + 0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD, + 0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00, + 0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7, + 0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31, + 0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5, + 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF, + 0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C, + 0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF, + 0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F, + 0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2, + 0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02, + 0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35, + 0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3, + 0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06, + 0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F, + 0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA, + 0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2, + 0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A, + 0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05, + 0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, + 0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6, + 0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34, + 0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF, + 0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED, + 0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD, + 0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D, + 0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26, + 0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00, + 0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F, + 0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, + 0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1, + 0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32, + 0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0, + 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF, + 0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41, + 0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF, + 0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6, + 0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2, + 0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04, + 0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01, + 0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20, + 0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00, + 0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8, + 0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06, + 0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB, + 0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5, + 0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, + 0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1, + 0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC, + 0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6, + 0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D, + 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF, + 0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8, + 0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD, + 0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2, + 0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B, + 0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33, + 0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00, + 0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB, + 0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01, + 0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9, + 0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34, + 0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA, + 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF, + 0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B, + 0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF, + 0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F, + 0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2, + 0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05, + 0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01, + 0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02, + 0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A, + 0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06, + 0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC, + 0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE, + 0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, + 0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2, + 0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC, + 0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7, + 0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09, + 0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB, + 0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD, + 0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4, + 0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19, + 0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F, + 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00, + 0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C, + 0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01, + 0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20, + 0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36, + 0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5, + 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF, + 0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB, + 0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF, + 0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A, + 0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3, + 0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06, + 0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01, + 0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9, + 0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00, + 0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09, + 0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06, + 0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC, + 0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC, + 0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00, + 0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34, + 0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC, + 0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8, + 0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8, + 0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95, + 0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD, + 0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7, + 0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17, + 0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00, + 0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87, + 0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01, + 0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45, + 0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37, + 0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0, + 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF, + 0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61, + 0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF, + 0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8, + 0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3, + 0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06, + 0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00, + 0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7, + 0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3, + 0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06, + 0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD, + 0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F, + 0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87, + 0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC, + 0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96, + 0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08, + 0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D, + 0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89, + 0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD, + 0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB, + 0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15, + 0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF, + 0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5, + 0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01, + 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69, + 0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39, + 0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B, + 0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF, + 0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF, + 0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF, + 0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79, + 0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3, + 0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07, + 0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00, + 0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A, + 0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58, + 0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06, + 0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10, + 0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE, + 0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27, + 0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE, + 0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC, + 0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92, + 0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06, + 0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56, + 0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88, + 0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD, + 0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13, + 0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF, + 0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6, + 0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01, + 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A, + 0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A, + 0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97, + 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00, + 0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93, + 0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF, + 0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D, + 0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3, + 0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08, + 0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00, + 0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23, + 0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00, + 0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7, + 0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05, + 0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10, + 0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE, + 0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45, + 0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00, + 0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69, + 0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC, + 0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F, + 0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04, + 0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D, + 0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, + 0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94, + 0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC, + 0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6, + 0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11, + 0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF, + 0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB, + 0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01, + 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9, + 0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C, + 0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93, + 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00, + 0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F, + 0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF, + 0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4, + 0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4, + 0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09, + 0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF, + 0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1, + 0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00, + 0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90, + 0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05, + 0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F, + 0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF, + 0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67, + 0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00, + 0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8, + 0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD, + 0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C, + 0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02, + 0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41, + 0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF, + 0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A, + 0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01, + 0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD, + 0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F, + 0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63, + 0x01, 0x8D, 0xFF, 0x0F, 0x00 +}; + +static u16 +CoefficientSizes[] = { + /* Playback */ + 0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000, + /* Record */ + 0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000, +}; + +#ifndef JUST_DATA + +static u16 +nm256_getStartOffset (u8 which) +{ + u16 offset = 0; + + while (which-- > 0) + offset += CoefficientSizes[which]; + + return offset; +} + +static void +nm256_loadOneCoefficient (struct nm256_info *card, int devnum, u32 port, + u16 which) +{ + u32 coeffBuf = (which < 8) ? card->coeffBuf : card->allCoeffBuf; + u16 offset = nm256_getStartOffset (which); + u16 size = CoefficientSizes[which]; + + card->coeffsCurrent = 0; + + if (nm256_debug) + printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d, size %d, port 0x%x\n", + coeffBuf, coeffBuf + size - 1, which, size, port); + nm256_writeBuffer8 (card, coefficients + offset, 1, coeffBuf, size); + nm256_writePort32 (card, 2, port + 0, coeffBuf); + /* ??? Record seems to behave differently than playback. */ + if (devnum == 0) + size--; + nm256_writePort32 (card, 2, port + 4, coeffBuf + size); +} + +static void +nm256_loadAllCoefficients (struct nm256_info *card) +{ + nm256_writeBuffer8 (card, coefficients, 1, card->allCoeffBuf, + NM_TOTAL_COEFF_COUNT * 4); + card->coeffsCurrent = 1; +} + +void +nm256_loadCoefficient (struct nm256_info *card, int which, int number) +{ + static u16 addrs[3] = { 0x1c, 0x21c, 0x408 }; + /* The enable register for the specified engine. */ + u32 poffset = (which == 1 ? 0x200 : 1); + + if (nm256_readPort8 (card, 2, poffset) & 1) { + printk (KERN_ERR "NM256: Engine was enabled while loading coefficients!\n"); + return; + } + + /* The recording engine uses coefficient values 8-15. */ + if (which == 1) + number += 8; + + if (! nm256_cachedCoefficients (card)) + nm256_loadOneCoefficient (card, which, addrs[which], number); + else { + u32 base = card->allCoeffBuf; + u32 offset = nm256_getStartOffset (number); + u32 endOffset = offset + CoefficientSizes[number]; + + if (nm256_debug) + printk (KERN_DEBUG "loading coefficient %d at port 0x%x, offset %d (0x%x-0x%x)\n", + number, addrs[which], offset, base + offset, + base + endOffset - 1); + + if (! card->coeffsCurrent) + nm256_loadAllCoefficients (card); + + nm256_writePort32 (card, 2, addrs[which], base + offset); + nm256_writePort32 (card, 2, addrs[which] + 4, base + endOffset - 1); + } +} + +#endif /* JUST_DATA */ + +#endif + +/* + * Local variables: + * c-basic-offset: 4 + * End: + */ diff -Nru linux/sound/oss/opl3.c linux-2.4.19-pre5-mjc/sound/oss/opl3.c --- linux/sound/oss/opl3.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/opl3.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1253 @@ +/* + * sound/opl3.c + * + * A low level driver for Yamaha YM3812 and OPL-3 -chips + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Changes + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, fixed sound_mem allocs. + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo get rid of check_region, use request_region for + * OPL4, release it on exit, some cleanups. + * + * Status + * Believed to work. Badly needs rewriting a bit to support multiple + * OPL3 devices. + */ + +#include +#include +#include + +/* + * Major improvements to the FM handling 30AUG92 by Rob Hooft, + * hooft@chem.ruu.nl + */ + +#include "sound_config.h" + +#include "opl3.h" +#include "opl3_hw.h" + +#define MAX_VOICE 18 +#define OFFS_4OP 11 + +struct voice_info +{ + unsigned char keyon_byte; + long bender; + long bender_range; + unsigned long orig_freq; + unsigned long current_freq; + int volume; + int mode; + int panning; /* 0xffff means not set */ +}; + +typedef struct opl_devinfo +{ + int base; + int left_io, right_io; + int nr_voice; + int lv_map[MAX_VOICE]; + + struct voice_info voc[MAX_VOICE]; + struct voice_alloc_info *v_alloc; + struct channel_info *chn_info; + + struct sbi_instrument i_map[SBFM_MAXINSTR]; + struct sbi_instrument *act_i[MAX_VOICE]; + + struct synth_info fm_info; + + int busy; + int model; + unsigned char cmask; + + int is_opl4; + int *osp; +} opl_devinfo; + +static struct opl_devinfo *devc = NULL; + +static int detected_model; + +static int store_instr(int instr_no, struct sbi_instrument *instr); +static void freq_to_fnum(int freq, int *block, int *fnum); +static void opl3_command(int io_addr, unsigned int addr, unsigned int val); +static int opl3_kill_note(int dev, int voice, int note, int velocity); + +static void enter_4op_mode(void) +{ + int i; + static int v4op[MAX_VOICE] = { + 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17 + }; + + devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */ + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f); + + for (i = 0; i < 3; i++) + pv_map[i].voice_mode = 4; + for (i = 3; i < 6; i++) + pv_map[i].voice_mode = 0; + + for (i = 9; i < 12; i++) + pv_map[i].voice_mode = 4; + for (i = 12; i < 15; i++) + pv_map[i].voice_mode = 0; + + for (i = 0; i < 12; i++) + devc->lv_map[i] = v4op[i]; + devc->v_alloc->max_voice = devc->nr_voice = 12; +} + +static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + struct sbi_instrument ins; + + switch (cmd) { + case SNDCTL_FM_LOAD_INSTR: + printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); + if (copy_from_user(&ins, arg, sizeof(ins))) + return -EFAULT; + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { + printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); + return -EINVAL; + } + return store_instr(ins.channel, &ins); + + case SNDCTL_SYNTH_INFO: + devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; + if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + case SNDCTL_FM_4OP_ENABLE: + if (devc->model == 2) + enter_4op_mode(); + return 0; + + default: + return -EINVAL; + } +} + +int opl3_detect(int ioaddr, int *osp) +{ + /* + * This function returns 1 if the FM chip is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, signature; + int i; + + if (devc != NULL) + { + printk(KERN_ERR "opl3: Only one OPL3 supported.\n"); + return 0; + } + + devc = (struct opl_devinfo *)kmalloc(sizeof(*devc), GFP_KERNEL); + + if (devc == NULL) + { + printk(KERN_ERR "opl3: Can't allocate memory for the device control " + "structure \n "); + return 0; + } + + memset(devc, 0, sizeof(*devc)); + strcpy(devc->fm_info.name, "OPL2"); + + if (!request_region(ioaddr, 4, devc->fm_info.name)) { + printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr); + goto cleanup_devc; + } + + devc->osp = osp; + devc->base = ioaddr; + + /* Reset timers 1 and 2 */ + opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); + + /* Reset the IRQ of the FM chip */ + opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); + + signature = stat1 = inb(ioaddr); /* Status register */ + + if (signature != 0x00 && signature != 0x06 && signature != 0x02 && + signature != 0x0f) + { + MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature)); + goto cleanup_region; + } + + if (signature == 0x06) /* OPL2 */ + { + detected_model = 2; + } + else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */ + { + unsigned char tmp; + + detected_model = 3; + + /* + * Detect availability of OPL4 (_experimental_). Works probably + * only after a cold boot. In addition the OPL4 port + * of the chip may not be connected to the PC bus at all. + */ + + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00); + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE); + + if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */ + { + detected_model = 4; + } + + if (request_region(ioaddr - 8, 2, "OPL4")) /* OPL4 port was free */ + { + int tmp; + + outb((0x02), ioaddr - 8); /* Select OPL4 ID register */ + udelay(10); + tmp = inb(ioaddr - 7); /* Read it */ + udelay(10); + + if (tmp == 0x20) /* OPL4 should return 0x20 here */ + { + detected_model = 4; + outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */ + udelay(10); + outb((0x1B), ioaddr - 7); /* Write value */ + udelay(10); + } + else + { /* release OPL4 port */ + release_region(ioaddr - 8, 2); + detected_model = 3; + } + } + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0); + } + for (i = 0; i < 9; i++) + opl3_command(ioaddr, KEYON_BLOCK + i, 0); /* + * Note off + */ + + opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); + opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /* + * Melodic mode. + */ + return 1; +cleanup_region: + release_region(ioaddr, 4); +cleanup_devc: + kfree(devc); + devc = NULL; + return 0; +} + +static int opl3_kill_note (int devno, int voice, int note, int velocity) +{ + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return 0; + + devc->v_alloc->map[voice] = 0; + + map = &pv_map[devc->lv_map[voice]]; + DEB(printk("Kill note %d\n", voice)); + + if (map->voice_mode == 0) + return 0; + + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20); + devc->voc[voice].keyon_byte = 0; + devc->voc[voice].bender = 0; + devc->voc[voice].volume = 64; + devc->voc[voice].panning = 0xffff; /* Not set */ + devc->voc[voice].bender_range = 200; + devc->voc[voice].orig_freq = 0; + devc->voc[voice].current_freq = 0; + devc->voc[voice].mode = 0; + return 0; +} + +#define HIHAT 0 +#define CYMBAL 1 +#define TOMTOM 2 +#define SNARE 3 +#define BDRUM 4 +#define UNDEFINED TOMTOM +#define DEFAULT TOMTOM + +static int store_instr(int instr_no, struct sbi_instrument *instr) +{ + if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2)) + printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key); + memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr)); + return 0; +} + +static int opl3_set_instr (int dev, int voice, int instr_no) +{ + if (voice < 0 || voice >= devc->nr_voice) + return 0; + if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) + instr_no = 0; /* Acoustic piano (usually) */ + + devc->act_i[voice] = &devc->i_map[instr_no]; + return 0; +} + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (RH) + */ + +static char fm_volume_table[128] = +{ + -64, -48, -40, -35, -32, -29, -27, -26, + -24, -23, -21, -20, -19, -18, -18, -17, + -16, -15, -15, -14, -13, -13, -12, -12, + -11, -11, -10, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -6, -6, -6, + -5, -5, -5, -5, -4, -4, -4, -4, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8 +}; + +static void calc_vol(unsigned char *regbyte, int volume, int main_vol) +{ + int level = (~*regbyte & 0x3f); + + if (main_vol > 127) + main_vol = 127; + volume = (volume * main_vol) / 127; + + if (level) + level += fm_volume_table[volume]; + + if (level > 0x3f) + level = 0x3f; + if (level < 0) + level = 0; + + *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); +} + +static void set_voice_volume(int voice, int volume, int main_vol) +{ + unsigned char vol1, vol2, vol3, vol4; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + map = &pv_map[devc->lv_map[voice]]; + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (instr->channel < 0) + return; + + if (devc->voc[voice].mode == 0) + return; + + if (devc->voc[voice].mode == 2) + { + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + if ((instr->operators[10] & 0x01)) + { + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol2, volume, main_vol); + } + else + { + calc_vol(&vol2, volume, main_vol); + } + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); + } + else + { /* + * 4 OP voice + */ + int connection; + + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + vol3 = instr->operators[OFFS_4OP + 2]; + vol4 = instr->operators[OFFS_4OP + 3]; + + /* + * The connection method for 4 OP devc->voc is defined by the rightmost + * bits at the offsets 10 and 10+OFFS_4OP + */ + + connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + calc_vol(&vol4, volume, main_vol); + break; + + case 1: + calc_vol(&vol2, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + case 2: + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + case 3: + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol3, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + default: + ; + } + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4); + } +} + +static int opl3_start_note (int dev, int voice, int note, int volume) +{ + unsigned char data, fpc; + int block, fnum, freq, voice_mode, pan; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return 0; + + map = &pv_map[devc->lv_map[voice]]; + pan = devc->voc[voice].panning; + + if (map->voice_mode == 0) + return 0; + + if (note == 255) /* + * Just change the volume + */ + { + set_voice_volume(voice, volume, devc->voc[voice].volume); + return 0; + } + + /* + * Kill previous note before playing + */ + + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* + * Carrier + * volume to + * min + */ + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* + * Modulator + * volume to + */ + + if (map->voice_mode == 4) + { + opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff); + } + + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* + * Note + * off + */ + + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (instr->channel < 0) + { + printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice); + return 0; + } + + if (map->voice_mode == 2 && instr->key == OPL3_PATCH) + return 0; /* + * Cannot play + */ + + voice_mode = map->voice_mode; + + if (voice_mode == 4) + { + int voice_shift; + + voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3; + voice_shift += map->voice_num; + + if (instr->key != OPL3_PATCH) /* + * Just 2 OP patch + */ + { + voice_mode = 2; + devc->cmask &= ~(1 << voice_shift); + } + else + { + devc->cmask |= (1 << voice_shift); + } + + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); + } + + /* + * Set Sound Characteristics + */ + + opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); + opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); + + /* + * Set Attack/Decay + */ + + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); + + /* + * Set Sustain/Release + */ + + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); + + /* + * Set Wave Select + */ + + opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); + opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); + + /* + * Set Feedback/Connection + */ + + fpc = instr->operators[10]; + + if (pan != 0xffff) + { + fpc &= ~STEREO_BITS; + if (pan < -64) + fpc |= VOICE_TO_LEFT; + else + if (pan > 64) + fpc |= VOICE_TO_RIGHT; + else + fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT); + } + + if (!(fpc & 0x30)) + fpc |= 0x30; /* + * Ensure that at least one chn is enabled + */ + opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc); + + /* + * If the voice is a 4 OP one, initialize the operators 3 and 4 also + */ + + if (voice_mode == 4) + { + /* + * Set Sound Characteristics + */ + + opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); + opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); + + /* + * Set Attack/Decay + */ + + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); + + /* + * Set Sustain/Release + */ + + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); + + /* + * Set Wave Select + */ + + opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); + opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); + + /* + * Set Feedback/Connection + */ + + fpc = instr->operators[OFFS_4OP + 10]; + if (!(fpc & 0x30)) + fpc |= 0x30; /* + * Ensure that at least one chn is enabled + */ + opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); + } + + devc->voc[voice].mode = voice_mode; + set_voice_volume(voice, volume, devc->voc[voice].volume); + + freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); + devc->voc[voice].current_freq = freq; + + freq_to_fnum(freq, &block, &fnum); + + /* + * Play note + */ + + data = fnum & 0xff; /* + * Least significant bits of fnumber + */ + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + devc->voc[voice].keyon_byte = data; + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); + if (voice_mode == 4) + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); + + return 0; +} + +static void freq_to_fnum (int freq, int *block, int *fnum) +{ + int f, octave; + + /* + * Converts the note frequency to block and fnum values for the FM chip + */ + /* + * First try to compute the block -value (octave) where the note belongs + */ + + f = freq; + + octave = 5; + + if (f == 0) + octave = 0; + else if (f < 261) + { + while (f < 261) + { + octave--; + f <<= 1; + } + } + else if (f > 493) + { + while (f > 493) + { + octave++; + f >>= 1; + } + } + + if (octave > 7) + octave = 7; + + *fnum = freq * (1 << (20 - octave)) / 49716; + *block = octave; +} + +static void opl3_command (int io_addr, unsigned int addr, unsigned int val) +{ + int i; + + /* + * The original 2-OP synth requires a quite long delay after writing to a + * register. The OPL-3 survives with just two INBs + */ + + outb(((unsigned char) (addr & 0xff)), io_addr); + + if (devc->model != 2) + udelay(10); + else + for (i = 0; i < 2; i++) + inb(io_addr); + + outb(((unsigned char) (val & 0xff)), io_addr + 1); + + if (devc->model != 2) + udelay(30); + else + for (i = 0; i < 2; i++) + inb(io_addr); +} + +static void opl3_reset(int devno) +{ + int i; + + for (i = 0; i < 18; i++) + devc->lv_map[i] = i; + + for (i = 0; i < devc->nr_voice; i++) + { + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff); + + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff); + + if (pv_map[devc->lv_map[i]].voice_mode == 4) + { + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff); + + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff); + } + + opl3_kill_note(devno, i, 0, 64); + } + + if (devc->model == 2) + { + devc->v_alloc->max_voice = devc->nr_voice = 18; + + for (i = 0; i < 18; i++) + pv_map[i].voice_mode = 2; + + } +} + +static int opl3_open(int dev, int mode) +{ + int i; + + if (devc->busy) + return -EBUSY; + devc->busy = 1; + + devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; + devc->v_alloc->timestamp = 0; + + for (i = 0; i < 18; i++) + { + devc->v_alloc->map[i] = 0; + devc->v_alloc->alloc_times[i] = 0; + } + + devc->cmask = 0x00; /* + * Just 2 OP mode + */ + if (devc->model == 2) + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); + return 0; +} + +static void opl3_close(int dev) +{ + devc->busy = 0; + devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; + + devc->fm_info.nr_drums = 0; + devc->fm_info.perc_mode = 0; + + opl3_reset(dev); +} + +static void opl3_hw_control(int dev, unsigned char *event) +{ +} + +static int opl3_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct sbi_instrument ins; + + if (count = SBFM_MAXINSTR) + { + printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); + return -EINVAL; + } + ins.key = format; + + return store_instr(ins.channel, &ins); +} + +static void opl3_panning(int dev, int voice, int value) +{ + devc->voc[voice].panning = value; +} + +static void opl3_volume_method(int dev, int mode) +{ +} + +#define SET_VIBRATO(cell) { \ + tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ + if (pressure > 110) \ + tmp |= 0x40; /* Vibrato on */ \ + opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} + +static void opl3_aftertouch(int dev, int voice, int pressure) +{ + int tmp; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + map = &pv_map[devc->lv_map[voice]]; + + DEB(printk("Aftertouch %d\n", voice)); + + if (map->voice_mode == 0) + return; + + /* + * Adjust the amount of vibrato depending the pressure + */ + + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (devc->voc[voice].mode == 4) + { + int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + SET_VIBRATO(4); + break; + + case 1: + SET_VIBRATO(2); + SET_VIBRATO(4); + break; + + case 2: + SET_VIBRATO(1); + SET_VIBRATO(4); + break; + + case 3: + SET_VIBRATO(1); + SET_VIBRATO(3); + SET_VIBRATO(4); + break; + + } + /* + * Not implemented yet + */ + } + else + { + SET_VIBRATO(1); + + if ((instr->operators[10] & 0x01)) /* + * Additive synthesis + */ + SET_VIBRATO(2); + } +} + +#undef SET_VIBRATO + +static void bend_pitch(int dev, int voice, int value) +{ + unsigned char data; + int block, fnum, freq; + struct physical_voice_info *map; + + map = &pv_map[devc->lv_map[voice]]; + + if (map->voice_mode == 0) + return; + + devc->voc[voice].bender = value; + if (!value) + return; + if (!(devc->voc[voice].keyon_byte & 0x20)) + return; /* + * Not keyed on + */ + + freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); + devc->voc[voice].current_freq = freq; + + freq_to_fnum(freq, &block, &fnum); + + data = fnum & 0xff; /* + * Least significant bits of fnumber + */ + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + devc->voc[voice].keyon_byte = data; + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); +} + +static void opl3_controller (int dev, int voice, int ctrl_num, int value) +{ + if (voice < 0 || voice >= devc->nr_voice) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + bend_pitch(dev, voice, value); + break; + + case CTRL_PITCH_BENDER_RANGE: + devc->voc[voice].bender_range = value; + break; + + case CTL_MAIN_VOLUME: + devc->voc[voice].volume = value / 128; + break; + + case CTL_PAN: + devc->voc[voice].panning = (value * 2) - 128; + break; + } +} + +static void opl3_bender(int dev, int voice, int value) +{ + if (voice < 0 || voice >= devc->nr_voice) + return; + + bend_pitch(dev, voice, value - 8192); +} + +static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, best, first, avail, best_time = 0x7fffffff; + struct sbi_instrument *instr; + int is4op; + int instr_no; + + if (chn < 0 || chn > 15) + instr_no = 0; + else + instr_no = devc->chn_info[chn].pgm_num; + + instr = &devc->i_map[instr_no]; + if (instr->channel < 0 || /* Instrument not loaded */ + devc->nr_voice != 12) /* Not in 4 OP mode */ + is4op = 0; + else if (devc->nr_voice == 12) /* 4 OP mode */ + is4op = (instr->key == OPL3_PATCH); + else + is4op = 0; + + if (is4op) + { + first = p = 0; + avail = 6; + } + else + { + if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */ + first = p = 6; + else + first = p = 0; + avail = devc->nr_voice; + } + + /* + * Now try to find a free voice + */ + best = first; + + for (i = 0; i < avail; i++) + { + if (alloc->map[p] == 0) + { + return p; + } + if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */ + { + best_time = alloc->alloc_times[p]; + best = p; + } + p = (p + 1) % avail; + } + + /* + * Insert some kind of priority mechanism here. + */ + + if (best < 0) + best = 0; + if (best > devc->nr_voice) + best -= devc->nr_voice; + + return best; /* All devc->voc in use. Select the first one. */ +} + +static void opl3_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info = + &synth_devs[dev]->chn_info[chn]; + + opl3_set_instr(dev, voice, info->pgm_num); + + devc->voc[voice].bender = 0; + devc->voc[voice].bender_range = info->bender_range; + devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME]; + devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; +} + +static struct synth_operations opl3_operations = +{ + owner: THIS_MODULE, + id: "OPL", + info: NULL, + midi_dev: 0, + synth_type: SYNTH_TYPE_FM, + synth_subtype: FM_TYPE_ADLIB, + open: opl3_open, + close: opl3_close, + ioctl: opl3_ioctl, + kill_note: opl3_kill_note, + start_note: opl3_start_note, + set_instr: opl3_set_instr, + reset: opl3_reset, + hw_control: opl3_hw_control, + load_patch: opl3_load_patch, + aftertouch: opl3_aftertouch, + controller: opl3_controller, + panning: opl3_panning, + volume_method: opl3_volume_method, + bender: opl3_bender, + alloc_voice: opl3_alloc_voice, + setup_voice: opl3_setup_voice +}; + +int opl3_init(int ioaddr, int *osp, struct module *owner) +{ + int i; + int me; + + if (devc == NULL) + { + printk(KERN_ERR "opl3: Device control structure not initialized.\n"); + return -1; + } + + if ((me = sound_alloc_synthdev()) == -1) + { + printk(KERN_WARNING "opl3: Too many synthesizers\n"); + return -1; + } + + devc->nr_voice = 9; + + devc->fm_info.device = 0; + devc->fm_info.synth_type = SYNTH_TYPE_FM; + devc->fm_info.synth_subtype = FM_TYPE_ADLIB; + devc->fm_info.perc_mode = 0; + devc->fm_info.nr_voices = 9; + devc->fm_info.nr_drums = 0; + devc->fm_info.instr_bank_size = SBFM_MAXINSTR; + devc->fm_info.capabilities = 0; + devc->left_io = ioaddr; + devc->right_io = ioaddr + 2; + + if (detected_model <= 2) + devc->model = 1; + else + { + devc->model = 2; + if (detected_model == 4) + devc->is_opl4 = 1; + } + + opl3_operations.info = &devc->fm_info; + + synth_devs[me] = &opl3_operations; + + if (owner) + synth_devs[me]->owner = owner; + + sequencer_init(); + devc->v_alloc = &opl3_operations.alloc; + devc->chn_info = &opl3_operations.chn_info[0]; + + if (devc->model == 2) + { + if (devc->is_opl4) + strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM"); + else + strcpy(devc->fm_info.name, "Yamaha OPL3"); + + devc->v_alloc->max_voice = devc->nr_voice = 18; + devc->fm_info.nr_drums = 0; + devc->fm_info.synth_subtype = FM_TYPE_OPL3; + devc->fm_info.capabilities |= SYNTH_CAP_OPL3; + + for (i = 0; i < 18; i++) + { + if (pv_map[i].ioaddr == USE_LEFT) + pv_map[i].ioaddr = devc->left_io; + else + pv_map[i].ioaddr = devc->right_io; + } + opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE); + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00); + } + else + { + strcpy(devc->fm_info.name, "Yamaha OPL2"); + devc->v_alloc->max_voice = devc->nr_voice = 9; + devc->fm_info.nr_drums = 0; + + for (i = 0; i < 18; i++) + pv_map[i].ioaddr = devc->left_io; + }; + conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1); + + for (i = 0; i < SBFM_MAXINSTR; i++) + devc->i_map[i].channel = -1; + + return me; +} + +EXPORT_SYMBOL(opl3_init); +EXPORT_SYMBOL(opl3_detect); + +static int me; + +static int io = -1; + +MODULE_PARM(io, "i"); + +static int __init init_opl3 (void) +{ + printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n"); + + if (io != -1) /* User loading pure OPL3 module */ + { + if (!opl3_detect(io, NULL)) + { + return -ENODEV; + } + + me = opl3_init(io, NULL, THIS_MODULE); + } + + return 0; +} + +static void __exit cleanup_opl3(void) +{ + if (devc && io != -1) + { + if (devc->base) { + release_region(devc->base,4); + if (devc->is_opl4) + release_region(devc->base - 8, 2); + } + kfree(devc); + devc = NULL; + sound_unload_synthdev(me); + } +} + +module_init(init_opl3); +module_exit(cleanup_opl3); + +#ifndef MODULE +static int __init setup_opl3(char *str) +{ + /* io */ + int ints[2]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + + return 1; +} + +__setup("opl3=", setup_opl3); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/opl3.h linux-2.4.19-pre5-mjc/sound/oss/opl3.h --- linux/sound/oss/opl3.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/opl3.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,11 @@ +/* + * opl3.h + * + * Copyright: Christoph Hellwig + * + */ + +int opl3_detect (int ioaddr, int *osp); +int opl3_init(int ioaddr, int *osp, struct module *owner); + +void enable_opl3_mode(int left, int right, int both); diff -Nru linux/sound/oss/opl3_hw.h linux-2.4.19-pre5-mjc/sound/oss/opl3_hw.h --- linux/sound/oss/opl3_hw.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/opl3_hw.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,246 @@ +/* + * opl3_hw.h - Definitions of the OPL-3 registers + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exceptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + */ + +/* + * Register numbers for the global registers + */ + +#define TEST_REGISTER 0x01 +#define ENABLE_WAVE_SELECT 0x20 + +#define TIMER1_REGISTER 0x02 +#define TIMER2_REGISTER 0x03 +#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ +#define IRQ_RESET 0x80 +#define TIMER1_MASK 0x40 +#define TIMER2_MASK 0x20 +#define TIMER1_START 0x01 +#define TIMER2_START 0x02 + +#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ +#define RIGHT_4OP_0 0x01 +#define RIGHT_4OP_1 0x02 +#define RIGHT_4OP_2 0x04 +#define LEFT_4OP_0 0x08 +#define LEFT_4OP_1 0x10 +#define LEFT_4OP_2 0x20 + +#define OPL3_MODE_REGISTER 0x05 /* Right side */ +#define OPL3_ENABLE 0x01 +#define OPL4_ENABLE 0x02 + +#define KBD_SPLIT_REGISTER 0x08 /* Left side */ +#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define KEYBOARD_SPLIT 0x40 + +#define PERCOSSION_REGISTER 0xbd /* Left side only */ +#define TREMOLO_DEPTH 0x80 +#define VIBRATO_DEPTH 0x40 +#define PERCOSSION_ENABLE 0x20 +#define BASSDRUM_ON 0x10 +#define SNAREDRUM_ON 0x08 +#define TOMTOM_ON 0x04 +#define CYMBAL_ON 0x02 +#define HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ +#define AM_VIB 0x20 +#define TREMOLO_ON 0x80 +#define VIBRATO_ON 0x40 +#define SUSTAIN_ON 0x20 +#define KSR 0x10 /* Key scaling rate */ +#define MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define KSL_LEVEL 0x40 +#define KSL_MASK 0xc0 /* Envelope scaling bits */ +#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define ATTACK_DECAY 0x60 +#define ATTACK_MASK 0xf0 +#define DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define SUSTAIN_RELEASE 0x80 +#define SUSTAIN_MASK 0xf0 +#define RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define WAVE_SELECT 0xe0 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define KEYON_BLOCK 0xb0 +#define KEYON_BIT 0x20 +#define BLOCKNUM_MASK 0x1c +#define FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halves (gives 4 ways to connect the operators). + */ +#define FEEDBACK_CONNECTION 0xc0 +#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define STEREO_BITS 0x30 /* OPL-3 only */ +#define VOICE_TO_LEFT 0x10 +#define VOICE_TO_RIGHT 0x20 + +/* + * Definition table for the physical voices + */ + +struct physical_voice_info { + unsigned char voice_num; + unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ + unsigned short ioaddr; /* I/O port (left or right side) */ + unsigned char op[4]; /* Operator offsets */ + }; + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + +#define USE_LEFT 0 +#define USE_RIGHT 1 + +static struct physical_voice_info pv_map[18] = +{ +/* No Mode Side OP1 OP2 OP3 OP4 */ +/* --------------------------------------------------- */ + { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */ + { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */ + { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */ + + { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}}, + { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}}, + { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}} +}; +/* + * DMA buffer calls + */ diff -Nru linux/sound/oss/opl3sa.c linux-2.4.19-pre5-mjc/sound/oss/opl3sa.c --- linux/sound/oss/opl3sa.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/opl3sa.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,337 @@ +/* + * sound/opl3sa.c + * + * Low level driver for Yamaha YMF701B aka OPL3-SA chip + * + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox Modularisation + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo got rid of attach_uart401 + * + * FIXME: + * Check for install of mpu etc is wrong, should check result of the mss stuff + */ + +#include +#include + +#undef SB_OK + +#include "sound_config.h" + +#include "ad1848.h" +#include "mpu401.h" + +#ifdef SB_OK +#include "sb.h" +static int sb_initialized = 0; +#endif + +static int kilroy_was_here = 0; /* Don't detect twice */ +static int mpu_initialized = 0; + +static int *opl3sa_osp = NULL; + +static unsigned char opl3sa_read(int addr) +{ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + tmp = inb(0xf87); /* data */ + restore_flags(flags); + + return tmp; +} + +static void opl3sa_write(int addr, int data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + outb(((unsigned char) data), 0xf87); /* data */ + restore_flags(flags); +} + +static int __init opl3sa_detect(void) +{ + int tmp; + + if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04) + { + DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01))); + /* return 0; */ + } + + /* + * Check that the password feature has any effect + */ + + if (inb(0xf87) == tmp) + { + DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87))); + return 0; + } + tmp = (opl3sa_read(0x04) & 0xe0) >> 5; + + if (tmp != 0 && tmp != 1) + { + DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp)); + return 0; + } + DDB(printk("OPL3-SA mode %x detected\n", tmp)); + + opl3sa_write(0x01, 0x00); /* Disable MSS */ + opl3sa_write(0x02, 0x00); /* Disable SB */ + opl3sa_write(0x03, 0x00); /* Disable MPU */ + + return 1; +} + +/* + * Probe and attach routines for the Windows Sound System mode of + * OPL3-SA + */ + +static int __init probe_opl3sa_wss(struct address_info *hw_config) +{ + int ret; + unsigned char tmp = 0x24; /* WSS enable */ + + if (check_region(0xf86, 2)) /* Control port is busy */ + return 0; + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (OPL3-SA for example) + * return 0x00. + */ + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + opl3sa_osp = hw_config->osp; + + if (!opl3sa_detect()) + { + printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0xe80: + tmp |= 0x08; + break; + case 0xf40: + tmp |= 0x10; + break; + case 0x604: + tmp |= 0x18; + break; + default: + printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); + return 0; + } + + opl3sa_write(0x01, tmp); /* WSS setup register */ + kilroy_was_here = 1; + + ret = probe_ms_sound(hw_config); + if (ret) + request_region(0xf86, 2, "OPL3-SA"); + + return ret; +} + +static void __init attach_opl3sa_wss(struct address_info *hw_config) +{ + int nm = num_mixers; + + /* FIXME */ + attach_ms_sound(hw_config, THIS_MODULE); + if (num_mixers > nm) /* A mixer was installed */ + { + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } +} + + +static int __init probe_opl3sa_mpu(struct address_info *hw_config) +{ + unsigned char conf; + static signed char irq_bits[] = { + -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 + }; + + if (!kilroy_was_here) + return 0; /* OPL3-SA has not been detected earlier */ + + if (mpu_initialized) + { + DDB(printk("OPL3-SA: MPU mode already initialized\n")); + return 0; + } + if (hw_config->irq > 10) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + if (irq_bits[hw_config->irq] == -1) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + switch (hw_config->io_base) + { + case 0x330: + conf = 0x00; + break; + case 0x332: + conf = 0x20; + break; + case 0x334: + conf = 0x40; + break; + case 0x300: + conf = 0x60; + break; + default: + return 0; /* Invalid port */ + } + + conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ + conf |= irq_bits[hw_config->irq] << 2; + + opl3sa_write(0x03, conf); + + mpu_initialized = 1; + hw_config->name = "OPL3-SA (MPU401)"; + + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_opl3sa_wss(struct address_info *hw_config) +{ + int dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = hw_config->dma; + + release_region(0xf86, 2); + release_region(hw_config->io_base, 4); + + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); + sound_unload_audiodev(hw_config->slots[0]); +} + +static inline void __exit unload_opl3sa_mpu(struct address_info *hw_config) +{ + unload_uart401(hw_config); +} + +#ifdef SB_OK +static inline void __exit unload_opl3sa_sb(struct address_info *hw_config) +{ + sb_dsp_unload(hw_config); +} +#endif + +static int found_mpu; + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata mpu_io = -1; +static int __initdata mpu_irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(mpu_io,"i"); +MODULE_PARM(mpu_irq,"i"); + +static int __init init_opl3sa(void) +{ + if (io == -1 || irq == -1 || dma == -1) { + printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); + return -EINVAL; + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + if (probe_opl3sa_wss(&cfg) == 0) + return -ENODEV; + + found_mpu=probe_opl3sa_mpu(&cfg_mpu); + + attach_opl3sa_wss(&cfg); + return 0; +} + +static void __exit cleanup_opl3sa(void) +{ + if(found_mpu) + unload_opl3sa_mpu(&cfg_mpu); + unload_opl3sa_wss(&cfg); +} + +module_init(init_opl3sa); +module_exit(cleanup_opl3sa); + +#ifndef MODULE +static int __init setup_opl3sa(char *str) +{ + /* io, irq, dma, dma2, mpu_io, mpu_irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + + return 1; +} + +__setup("opl3sa=", setup_opl3sa); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/opl3sa2.c linux-2.4.19-pre5-mjc/sound/oss/opl3sa2.c --- linux/sound/oss/opl3sa2.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/opl3sa2.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1190 @@ +/* + * sound/opl3sa2.c + * + * A low level driver for Yamaha OPL3-SA2 and SA3 cards. + * NOTE: All traces of the name OPL3-SAx have now (December 2000) been + * removed from the driver code, as an email exchange with Yamaha + * provided the information that the YMF-719 is indeed just a + * re-badged 715. + * + * Copyright 1998-2001 Scott Murray + * + * Originally based on the CS4232 driver (in cs4232.c) by Hannu Savolainen + * and others. Now incorporates code/ideas from pss.c, also by Hannu + * Savolainen. Both of those files are distributed with the following + * license: + * + * "Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info." + * + * As such, in accordance with the above license, this file, opl3sa2.c, is + * distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 (June 1991). + * See the "COPYING" file distributed with this software for more information. + * + * Change History + * -------------- + * Scott Murray Original driver (Jun 14, 1998) + * Paul J.Y. Lahaie Changed probing / attach code order + * Scott Murray Added mixer support (Dec 03, 1998) + * Scott Murray Changed detection code to be more forgiving, + * added force option as last resort, + * fixed ioctl return values. (Dec 30, 1998) + * Scott Murray Simpler detection code should work all the time now + * (with thanks to Ben Hutchings for the heuristic), + * removed now unnecessary force option. (Jan 5, 1999) + * Christoph Hellwig Adapted to module_init/module_exit (Mar 4, 2000) + * Scott Murray Reworked SA2 versus SA3 mixer code, updated chipset + * version detection code (again!). (Dec 5, 2000) + * Scott Murray Adjusted master volume mixer scaling. (Dec 6, 2000) + * Scott Murray Based on a patch by Joel Yliluoma (aka Bisqwit), + * integrated wide mixer and adjusted mic, bass, treble + * scaling. (Dec 6, 2000) + * Scott Murray Based on a patch by Peter Englmaier, integrated + * ymode and loopback options. (Dec 6, 2000) + * Scott Murray Inspired by a patch by Peter Englmaier, and based on + * what ALSA does, added initialization code for the + * default DMA and IRQ settings. (Dec 6, 2000) + * Scott Murray Added some more checks to the card detection code, + * based on what ALSA does. (Dec 12, 2000) + * Scott Murray Inspired by similar patches from John Fremlin, + * Jim Radford, Mike Rolig, and Ingmar Steen, added 2.4 + * ISA PnP API support, mainly based on bits from + * sb_card.c and awe_wave.c. (Dec 12, 2000) + * Scott Murray Some small cleanups to the init code output. + * (Jan 7, 2001) + * Zwane Mwaikambo Added PM support. (Dec 4 2001) + * + */ + +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +#include "ad1848.h" +#include "mpu401.h" + +/* Useful control port indexes: */ +#define OPL3SA2_PM 0x01 +#define OPL3SA2_SYS_CTRL 0x02 +#define OPL3SA2_IRQ_CONFIG 0x03 +#define OPL3SA2_DMA_CONFIG 0x06 +#define OPL3SA2_MASTER_LEFT 0x07 +#define OPL3SA2_MASTER_RIGHT 0x08 +#define OPL3SA2_MIC 0x09 +#define OPL3SA2_MISC 0x0A + +#define OPL3SA3_WIDE 0x14 +#define OPL3SA3_BASS 0x15 +#define OPL3SA3_TREBLE 0x16 + +/* Useful constants: */ +#define DEFAULT_VOLUME 50 +#define DEFAULT_MIC 50 +#define DEFAULT_TIMBRE 0 + +/* Power saving modes */ +#define OPL3SA2_PM_MODE1 0x05 +#define OPL3SA2_PM_MODE2 0x04 +#define OPL3SA2_PM_MODE3 0x03 + +/* For checking against what the card returns: */ +#define VERSION_UNKNOWN 0 +#define VERSION_YMF711 1 +#define VERSION_YMF715 2 +#define VERSION_YMF715B 3 +#define VERSION_YMF715E 4 +/* also assuming that anything > 4 but <= 7 is a 715E */ + +/* Chipset type constants for use below */ +#define CHIPSET_UNKNOWN -1 +#define CHIPSET_OPL3SA2 0 +#define CHIPSET_OPL3SA3 1 + +#ifdef __ISAPNP__ +#define OPL3SA2_CARDS_MAX 4 +#else +#define OPL3SA2_CARDS_MAX 1 +#endif + +/* This should be pretty obvious */ +static int opl3sa2_cards_num; /* = 0 */ + +/* What's my version(s)? */ +static int chipset[OPL3SA2_CARDS_MAX] = { CHIPSET_UNKNOWN }; + +/* Oh well, let's just cache the name(s) */ +static char chipset_name[OPL3SA2_CARDS_MAX][12]; + +/* Where's my mixer(s)? */ +static int opl3sa2_mixer[OPL3SA2_CARDS_MAX] = { -1 }; + +/* Bag o' mixer data */ +typedef struct opl3sa2_mixerdata_tag { + unsigned short cfg_port; + unsigned short padding; + unsigned char reg; + unsigned int in_suspend; + struct pm_dev *pmdev; + unsigned int card; + unsigned int volume_l; + unsigned int volume_r; + unsigned int mic; + unsigned int bass_l; + unsigned int bass_r; + unsigned int treble_l; + unsigned int treble_r; + unsigned int wide_l; + unsigned int wide_r; +} opl3sa2_mixerdata; +static opl3sa2_mixerdata opl3sa2_data[OPL3SA2_CARDS_MAX]; + +static struct address_info cfg[OPL3SA2_CARDS_MAX]; +static struct address_info cfg_mss[OPL3SA2_CARDS_MAX]; +static struct address_info cfg_mpu[OPL3SA2_CARDS_MAX]; + +/* Our parameters */ +static int __initdata io = -1; +static int __initdata mss_io = -1; +static int __initdata mpu_io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata ymode = -1; +static int __initdata loopback = -1; + +#ifdef __ISAPNP__ +/* PnP specific parameters */ +static int __initdata isapnp = 1; +static int __initdata multiple = 1; + +/* PnP devices */ +struct pci_dev* opl3sa2_dev[OPL3SA2_CARDS_MAX]; + +/* Whether said devices have been activated */ +static int opl3sa2_activated[OPL3SA2_CARDS_MAX]; +#else +static int __initdata isapnp; /* = 0 */ +static int __initdata multiple; /* = 0 */ +#endif + +MODULE_DESCRIPTION("Module for OPL3-SA2 and SA3 sound cards (uses AD1848 MSS driver)."); +MODULE_AUTHOR("Scott Murray "); +MODULE_LICENSE("GPL"); + + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "Set I/O base of OPL3-SA2 or SA3 card (usually 0x370. Address must be even and must be from 0x100 to 0xFFE)"); + +MODULE_PARM(mss_io, "i"); +MODULE_PARM_DESC(mss_io, "Set MSS (audio) I/O base (0x530, 0xE80, or other. Address must end in 0 or 4 and must be from 0x530 to 0xF48)"); + +MODULE_PARM(mpu_io, "i"); +MODULE_PARM_DESC(mpu_io, "Set MIDI I/O base (0x330 or other. Address must be even and must be from 0x300 to 0x334)"); + +MODULE_PARM(irq, "i"); +MODULE_PARM_DESC(mss_irq, "Set MSS (audio) IRQ (5, 7, 9, 10, 11, 12)"); + +MODULE_PARM(dma, "i"); +MODULE_PARM_DESC(dma, "Set MSS (audio) first DMA channel (0, 1, 3)"); + +MODULE_PARM(dma2, "i"); +MODULE_PARM_DESC(dma2, "Set MSS (audio) second DMA channel (0, 1, 3)"); + +MODULE_PARM(ymode, "i"); +MODULE_PARM_DESC(ymode, "Set Yamaha 3D enhancement mode (0 = Desktop/Normal, 1 = Notebook PC (1), 2 = Notebook PC (2), 3 = Hi-Fi)"); + +MODULE_PARM(loopback, "i"); +MODULE_PARM_DESC(loopback, "Set A/D input source. Useful for echo cancellation (0 = Mic Rch (default), 1 = Mono output loopback)"); + +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0, ISA PnP support will be disabled"); + +MODULE_PARM(multiple, "i"); +MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); +#endif + + +/* + * Standard read and write functions +*/ + +static inline void opl3sa2_write(unsigned short port, + unsigned char index, + unsigned char data) +{ + outb_p(index, port); + outb(data, port + 1); +} + + +static inline void opl3sa2_read(unsigned short port, + unsigned char index, + unsigned char* data) +{ + outb_p(index, port); + *data = inb(port + 1); +} + + +/* + * All of the mixer functions... + */ + +static void opl3sa2_set_volume(opl3sa2_mixerdata* devc, int left, int right) +{ + static unsigned char scale[101] = { + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + }; + unsigned char vol; + + vol = scale[left]; + + /* If level is zero, turn on mute */ + if(!left) + vol |= 0x80; + + opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_LEFT, vol); + + vol = scale[right]; + + /* If level is zero, turn on mute */ + if(!right) + vol |= 0x80; + + opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_RIGHT, vol); +} + + +static void opl3sa2_set_mic(opl3sa2_mixerdata* devc, int level) +{ + unsigned char vol = 0x1F; + + if((level >= 0) && (level <= 100)) + vol = 0x1F - (unsigned char) (32 * level / 101); + + /* If level is zero, turn on mute */ + if(!level) + vol |= 0x80; + + opl3sa2_write(devc->cfg_port, OPL3SA2_MIC, vol); +} + + +static void opl3sa3_set_bass(opl3sa2_mixerdata* devc, int left, int right) +{ + unsigned char bass; + + bass = left ? ((unsigned char) (8 * left / 101)) : 0; + bass |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; + + opl3sa2_write(devc->cfg_port, OPL3SA3_BASS, bass); +} + + +static void opl3sa3_set_treble(opl3sa2_mixerdata* devc, int left, int right) +{ + unsigned char treble; + + treble = left ? ((unsigned char) (8 * left / 101)) : 0; + treble |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; + + opl3sa2_write(devc->cfg_port, OPL3SA3_TREBLE, treble); +} + + +static void opl3sa3_set_wide(opl3sa2_mixerdata* devc, int left, int right) +{ + unsigned char wide; + + wide = left ? ((unsigned char) (8 * left / 101)) : 0; + wide |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; + + opl3sa2_write(devc->cfg_port, OPL3SA3_WIDE, wide); +} + + +static void opl3sa2_mixer_reset(opl3sa2_mixerdata* devc, int card) +{ + if(devc) { + opl3sa2_set_volume(devc, DEFAULT_VOLUME, DEFAULT_VOLUME); + devc->volume_l = devc->volume_r = DEFAULT_VOLUME; + + opl3sa2_set_mic(devc, DEFAULT_MIC); + devc->mic = DEFAULT_MIC; + + if(chipset[card] == CHIPSET_OPL3SA3) { + opl3sa3_set_bass(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE); + devc->bass_l = devc->bass_r = DEFAULT_TIMBRE; + opl3sa3_set_treble(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE); + devc->treble_l = devc->treble_r = DEFAULT_TIMBRE; + } + } +} + + +static void opl3sa2_mixer_restore(opl3sa2_mixerdata* devc, int card) +{ + if (devc) { + opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r); + opl3sa2_set_mic(devc, devc->mic); + + if (chipset[card] == CHIPSET_OPL3SA3) { + opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r); + opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r); + } + } +} + + +static inline void arg_to_vol_mono(unsigned int vol, int* value) +{ + int left; + + left = vol & 0x00ff; + if (left > 100) + left = 100; + *value = left; +} + + +static inline void arg_to_vol_stereo(unsigned int vol, int* aleft, int* aright) +{ + arg_to_vol_mono(vol, aleft); + arg_to_vol_mono(vol >> 8, aright); +} + + +static inline int ret_vol_mono(int vol) +{ + return ((vol << 8) | vol); +} + + +static inline int ret_vol_stereo(int left, int right) +{ + return ((right << 8) | left); +} + + +static int opl3sa2_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int cmdf = cmd & 0xff; + + opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc; + + switch(cmdf) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_MIC: + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + case SOUND_MIXER_RECMASK: + case SOUND_MIXER_RECSRC: + case SOUND_MIXER_CAPS: + break; + + default: + return -EINVAL; + } + + if(((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if(_SIOC_DIR (cmd) & _SIOC_WRITE) { + switch (cmdf) { + case SOUND_MIXER_VOLUME: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->volume_l, &devc->volume_r); + opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r); + *(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r); + return 0; + + case SOUND_MIXER_MIC: + arg_to_vol_mono(*(unsigned int*)arg, &devc->mic); + opl3sa2_set_mic(devc, devc->mic); + *(int*)arg = ret_vol_mono(devc->mic); + return 0; + + default: + return -EINVAL; + } + } + else { + /* + * Return parameters + */ + switch (cmdf) { + case SOUND_MIXER_DEVMASK: + *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC); + return 0; + + case SOUND_MIXER_STEREODEVS: + *(int*)arg = SOUND_MASK_VOLUME; + return 0; + + case SOUND_MIXER_RECMASK: + /* No recording devices */ + return (*(int*)arg = 0); + + case SOUND_MIXER_CAPS: + *(int*)arg = SOUND_CAP_EXCL_INPUT; + return 0; + + case SOUND_MIXER_RECSRC: + /* No recording source */ + return (*(int*)arg = 0); + + case SOUND_MIXER_VOLUME: + *(int*)arg = ret_vol_stereo(devc->volume_l, devc->volume_r); + return 0; + + case SOUND_MIXER_MIC: + *(int*)arg = ret_vol_mono(devc->mic); + return 0; + + default: + return -EINVAL; + } + } +} +/* opl3sa2_mixer_ioctl end */ + + +static int opl3sa3_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int cmdf = cmd & 0xff; + + opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc; + + switch(cmdf) { + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + case SOUND_MIXER_DIGITAL1: + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + break; + + default: + return opl3sa2_mixer_ioctl(dev, cmd, arg); + } + + if(((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if(_SIOC_DIR (cmd) & _SIOC_WRITE) { + switch (cmdf) { + case SOUND_MIXER_BASS: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->bass_l, &devc->bass_r); + opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r); + *(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r); + return 0; + + case SOUND_MIXER_TREBLE: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->treble_l, &devc->treble_r); + opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r); + *(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r); + return 0; + + case SOUND_MIXER_DIGITAL1: + arg_to_vol_stereo(*(unsigned int*)arg, + &devc->wide_l, &devc->wide_r); + opl3sa3_set_wide(devc, devc->wide_l, devc->wide_r); + *(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r); + return 0; + + default: + return -EINVAL; + } + } + else + { + /* + * Return parameters + */ + switch (cmdf) { + case SOUND_MIXER_DEVMASK: + *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_MIC | + SOUND_MASK_BASS | SOUND_MASK_TREBLE | + SOUND_MASK_DIGITAL1); + return 0; + + case SOUND_MIXER_STEREODEVS: + *(int*)arg = (SOUND_MASK_VOLUME | SOUND_MASK_BASS | + SOUND_MASK_TREBLE | SOUND_MASK_DIGITAL1); + return 0; + + case SOUND_MIXER_BASS: + *(int*)arg = ret_vol_stereo(devc->bass_l, devc->bass_r); + return 0; + + case SOUND_MIXER_TREBLE: + *(int*)arg = ret_vol_stereo(devc->treble_l, devc->treble_r); + return 0; + + case SOUND_MIXER_DIGITAL1: + *(int*)arg = ret_vol_stereo(devc->wide_l, devc->wide_r); + return 0; + + default: + return -EINVAL; + } + } +} +/* opl3sa3_mixer_ioctl end */ + + +static struct mixer_operations opl3sa2_mixer_operations = +{ + owner: THIS_MODULE, + id: "OPL3-SA2", + name: "Yamaha OPL3-SA2", + ioctl: opl3sa2_mixer_ioctl +}; + +static struct mixer_operations opl3sa3_mixer_operations = +{ + owner: THIS_MODULE, + id: "OPL3-SA3", + name: "Yamaha OPL3-SA3", + ioctl: opl3sa3_mixer_ioctl +}; + +/* End of mixer-related stuff */ + + +/* + * Component probe, attach, unload functions + */ + +static inline int __init probe_opl3sa2_mpu(struct address_info* hw_config) +{ + return probe_mpu401(hw_config); +} + + +static inline void __init attach_opl3sa2_mpu(struct address_info* hw_config) +{ + attach_mpu401(hw_config, THIS_MODULE); +} + + +static inline void __exit unload_opl3sa2_mpu(struct address_info *hw_config) +{ + unload_mpu401(hw_config); +} + + +static inline int __init probe_opl3sa2_mss(struct address_info* hw_config) +{ + return probe_ms_sound(hw_config); +} + + +static void __init attach_opl3sa2_mss(struct address_info* hw_config) +{ + int initial_mixers; + + initial_mixers = num_mixers; + attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ + if(hw_config->slots[0] != -1) { + /* Did the MSS driver install? */ + if(num_mixers == (initial_mixers + 1)) { + /* The MSS mixer is installed, reroute mixers appropiately */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } + else { + printk(KERN_ERR "opl3sa2: MSS mixer not installed?\n"); + } + } +} + + +static inline void __exit unload_opl3sa2_mss(struct address_info* hw_config) +{ + unload_ms_sound(hw_config); +} + + +static int __init probe_opl3sa2(struct address_info* hw_config, int card) +{ + unsigned char misc; + unsigned char tmp; + unsigned char version; + char tag; + + /* + * Verify that the I/O port range is free. + */ + if(check_region(hw_config->io_base, 2)) { + printk(KERN_ERR "opl3sa2: Control I/O port %#x not free\n", + hw_config->io_base); + return 0; + } + + /* + * Check if writing to the read-only version bits of the miscellaneous + * register succeeds or not (it should not). + */ + opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc); + opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc ^ 0x07); + opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &tmp); + if(tmp != misc) { + printk(KERN_ERR "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n", + hw_config->io_base); + return 0; + } + + /* + * Check if the MIC register is accessible. + */ + opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp); + opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, 0x8a); + opl3sa2_read(hw_config->io_base, OPL3SA2_MIC, &tmp); + if((tmp & 0x9f) != 0x8a) { + printk(KERN_ERR + "opl3sa2: Control I/O port %#x is not a YMF7xx chipset!\n", + hw_config->io_base); + return 0; + } + opl3sa2_write(hw_config->io_base, OPL3SA2_MIC, tmp); + + /* + * Determine chipset type (SA2 or SA3) + * + * This is done by looking at the chipset version in the lower 3 bits + * of the miscellaneous register. + */ + version = misc & 0x07; + printk(KERN_DEBUG "opl3sa2: chipset version = %#x\n", version); + switch(version) { + case 0: + chipset[card] = CHIPSET_UNKNOWN; + tag = '?'; /* silence compiler warning */ + printk(KERN_ERR + "opl3sa2: Unknown Yamaha audio controller version\n"); + break; + + case VERSION_YMF711: + chipset[card] = CHIPSET_OPL3SA2; + tag = '2'; + printk(KERN_INFO "opl3sa2: Found OPL3-SA2 (YMF711)\n"); + break; + + case VERSION_YMF715: + chipset[card] = CHIPSET_OPL3SA3; + tag = '3'; + printk(KERN_INFO + "opl3sa2: Found OPL3-SA3 (YMF715 or YMF719)\n"); + break; + + case VERSION_YMF715B: + chipset[card] = CHIPSET_OPL3SA3; + tag = '3'; + printk(KERN_INFO + "opl3sa2: Found OPL3-SA3 (YMF715B or YMF719B)\n"); + break; + + case VERSION_YMF715E: + default: + chipset[card] = CHIPSET_OPL3SA3; + tag = '3'; + printk(KERN_INFO + "opl3sa2: Found OPL3-SA3 (YMF715E or YMF719E)\n"); + break; + } + + if(chipset[card] != CHIPSET_UNKNOWN) { + /* Generate a pretty name */ + sprintf(chipset_name[card], "OPL3-SA%c", tag); + return 1; + } + return 0; +} + + +static void __init attach_opl3sa2(struct address_info* hw_config, int card) +{ + request_region(hw_config->io_base, 2, chipset_name[card]); + + /* Initialize IRQ configuration to IRQ-B: -, IRQ-A: WSS+MPU+OPL3 */ + opl3sa2_write(hw_config->io_base, OPL3SA2_IRQ_CONFIG, 0x0d); + + /* Initialize DMA configuration */ + if(hw_config->dma2 == hw_config->dma) { + /* Want DMA configuration DMA-B: -, DMA-A: WSS-P+WSS-R */ + opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x03); + } + else { + /* Want DMA configuration DMA-B: WSS-R, DMA-A: WSS-P */ + opl3sa2_write(hw_config->io_base, OPL3SA2_DMA_CONFIG, 0x21); + } +} + + +static void __init attach_opl3sa2_mixer(struct address_info *hw_config, int card) +{ + struct mixer_operations* mixer_operations; + opl3sa2_mixerdata* devc; + + /* Install master mixer */ + if(chipset[card] == CHIPSET_OPL3SA3) { + mixer_operations = &opl3sa3_mixer_operations; + } + else { + mixer_operations = &opl3sa2_mixer_operations; + } + + if((devc = &opl3sa2_data[card])) { + devc->cfg_port = hw_config->io_base; + + opl3sa2_mixer[card] = sound_install_mixer(MIXER_DRIVER_VERSION, + mixer_operations->name, + mixer_operations, + sizeof(struct mixer_operations), + devc); + if(opl3sa2_mixer[card] < 0) { + printk(KERN_ERR "opl3sa2: Could not install %s master mixer\n", + mixer_operations->name); + } + else + opl3sa2_mixer_reset(devc, card); + } +} + + +static void __init opl3sa2_clear_slots(struct address_info* hw_config) +{ + int i; + + for(i = 0; i < 6; i++) { + hw_config->slots[i] = -1; + } +} + + +static void __init opl3sa2_set_ymode(struct address_info* hw_config, int ymode) +{ + /* + * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and + * it's supported. + * + * 0: Desktop (aka normal) 5-12 cm speakers + * 1: Notebook PC mode 1 3 cm speakers + * 2: Notebook PC mode 2 1.5 cm speakers + * 3: Hi-fi 16-38 cm speakers + */ + if(ymode >= 0 && ymode <= 3) { + unsigned char sys_ctrl; + + opl3sa2_read(hw_config->io_base, OPL3SA2_SYS_CTRL, &sys_ctrl); + sys_ctrl = (sys_ctrl & 0xcf) | ((ymode & 3) << 4); + opl3sa2_write(hw_config->io_base, OPL3SA2_SYS_CTRL, sys_ctrl); + } + else { + printk(KERN_ERR "opl3sa2: not setting ymode, it must be one of 0,1,2,3\n"); + } +} + + +static void __init opl3sa2_set_loopback(struct address_info* hw_config, int loopback) +{ + if(loopback >= 0 && loopback <= 1) { + unsigned char misc; + + opl3sa2_read(hw_config->io_base, OPL3SA2_MISC, &misc); + misc = (misc & 0xef) | ((loopback & 1) << 4); + opl3sa2_write(hw_config->io_base, OPL3SA2_MISC, misc); + } + else { + printk(KERN_ERR "opl3sa2: not setting loopback, it must be either 0 or 1\n"); + } +} + + +static void __exit unload_opl3sa2(struct address_info* hw_config, int card) +{ + /* Release control ports */ + release_region(hw_config->io_base, 2); + + /* Unload mixer */ + if(opl3sa2_mixer[card] >= 0) + sound_unload_mixerdev(opl3sa2_mixer[card]); +} + + +#ifdef __ISAPNP__ + +struct isapnp_device_id isapnp_opl3sa2_list[] __initdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), + 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_opl3sa2_list); + +static int __init opl3sa2_isapnp_probe(struct address_info* hw_cfg, + struct address_info* mss_cfg, + struct address_info* mpu_cfg, + int card) +{ + static struct pci_dev* dev; + int ret; + + /* Find and configure device */ + dev = isapnp_find_dev(NULL, + ISAPNP_VENDOR('Y','M','H'), + ISAPNP_FUNCTION(0x0021), + dev); + if(dev == NULL) { + return -ENODEV; + } + + /* + * If device is active, assume configured with /proc/isapnp + * and use anyway. Any other way to check this? + */ + ret = dev->prepare(dev); + if(ret && ret != -EBUSY) { + printk(KERN_ERR "opl3sa2: ISA PnP found device that could not be autoconfigured.\n"); + return -ENODEV; + } + if(ret == -EBUSY) { + opl3sa2_activated[card] = 1; + } + else { + if(dev->activate(dev) < 0) { + printk(KERN_WARNING "opl3sa2: ISA PnP activate failed\n"); + opl3sa2_activated[card] = 0; + return -ENODEV; + } + + printk(KERN_DEBUG + "opl3sa2: Activated ISA PnP card %d (active=%d)\n", + card, dev->active); + + } + + /* Our own config: */ + hw_cfg->io_base = dev->resource[4].start; + hw_cfg->irq = dev->irq_resource[0].start; + hw_cfg->dma = dev->dma_resource[0].start; + hw_cfg->dma2 = dev->dma_resource[1].start; + + /* The MSS config: */ + mss_cfg->io_base = dev->resource[1].start; + mss_cfg->irq = dev->irq_resource[0].start; + mss_cfg->dma = dev->dma_resource[0].start; + mss_cfg->dma2 = dev->dma_resource[1].start; + mss_cfg->card_subtype = 1; /* No IRQ or DMA setup */ + + mpu_cfg->io_base = dev->resource[3].start; + mpu_cfg->irq = dev->irq_resource[0].start; + mpu_cfg->dma = -1; + mpu_cfg->dma2 = -1; + mpu_cfg->always_detect = 1; /* It's there, so use shared IRQs */ + + /* Call me paranoid: */ + opl3sa2_clear_slots(hw_cfg); + opl3sa2_clear_slots(mss_cfg); + opl3sa2_clear_slots(mpu_cfg); + + opl3sa2_dev[card] = dev; + + return 0; +} +#endif /* __ISAPNP__ */ + +/* End of component functions */ + +/* Power Management support functions */ +static int opl3sa2_suspend(struct pm_dev *pdev, unsigned char pm_mode) +{ + unsigned long flags; + opl3sa2_mixerdata *p; + + if (!pdev) + return -EINVAL; + + save_flags(flags); + cli(); + + p = (opl3sa2_mixerdata *) pdev->data; + p->in_suspend = 1; + switch (pm_mode) { + case 1: + pm_mode = OPL3SA2_PM_MODE1; + break; + case 2: + pm_mode = OPL3SA2_PM_MODE2; + break; + case 3: + pm_mode = OPL3SA2_PM_MODE3; + break; + default: + pm_mode = OPL3SA2_PM_MODE3; + break; + } + + /* its supposed to automute before suspending, so we wont bother */ + opl3sa2_read(p->cfg_port, OPL3SA2_PM, &p->reg); + opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg | pm_mode); + + restore_flags(flags); + return 0; +} + +static int opl3sa2_resume(struct pm_dev *pdev) +{ + unsigned long flags; + opl3sa2_mixerdata *p; + + if (!pdev) + return -EINVAL; + + p = (opl3sa2_mixerdata *) pdev->data; + save_flags(flags); + cli(); + + /* I don't think this is necessary */ + opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->reg); + opl3sa2_mixer_restore(p, p->card); + p->in_suspend = 0; + + restore_flags(flags); + return 0; +} + +static int opl3sa2_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data) +{ + unsigned char mode = (unsigned char)data; + + switch (rqst) { + case PM_SUSPEND: + return opl3sa2_suspend(pdev, mode); + + case PM_RESUME: + return opl3sa2_resume(pdev); + } + return 0; +} + +/* + * Install OPL3-SA2 based card(s). + * + * Need to have ad1848 and mpu401 loaded ready. + */ +static int __init init_opl3sa2(void) +{ + int card; + int max; + + /* Sanitize isapnp and multiple settings */ + isapnp = isapnp != 0 ? 1 : 0; + multiple = multiple != 0 ? 1 : 0; + + max = (multiple && isapnp) ? OPL3SA2_CARDS_MAX : 1; + for(card = 0; card < max; card++, opl3sa2_cards_num++) { +#ifdef __ISAPNP__ + /* + * Please remember that even with __ISAPNP__ defined one + * should still be able to disable PNP support for this + * single driver! + */ + if(isapnp && opl3sa2_isapnp_probe(&cfg[card], + &cfg_mss[card], + &cfg_mpu[card], + card) < 0) { + if(!opl3sa2_cards_num) + printk(KERN_INFO "opl3sa2: No PnP cards found\n"); + if(io == -1) + break; + isapnp=0; + printk(KERN_INFO "opl3sa2: Search for a card at 0x%d.\n", io); + /* Fall through */ + } +#endif + /* If a user wants an I/O then assume they meant it */ + + if(!isapnp) { + if(io == -1 || irq == -1 || dma == -1 || + dma2 == -1 || mss_io == -1) { + printk(KERN_ERR + "opl3sa2: io, mss_io, irq, dma, and dma2 must be set\n"); + return -EINVAL; + } + + /* + * Our own config: + * (NOTE: IRQ and DMA aren't used, so they're set to + * give pretty output from conf_printf. :) + */ + cfg[card].io_base = io; + cfg[card].irq = irq; + cfg[card].dma = dma; + cfg[card].dma2 = dma2; + + /* The MSS config: */ + cfg_mss[card].io_base = mss_io; + cfg_mss[card].irq = irq; + cfg_mss[card].dma = dma; + cfg_mss[card].dma2 = dma2; + cfg_mss[card].card_subtype = 1; /* No IRQ or DMA setup */ + + cfg_mpu[card].io_base = mpu_io; + cfg_mpu[card].irq = irq; + cfg_mpu[card].dma = -1; + cfg_mpu[card].always_detect = 1; /* Use shared IRQs */ + + /* Call me paranoid: */ + opl3sa2_clear_slots(&cfg[card]); + opl3sa2_clear_slots(&cfg_mss[card]); + opl3sa2_clear_slots(&cfg_mpu[card]); + } + + if(!probe_opl3sa2(&cfg[card], card) || + !probe_opl3sa2_mss(&cfg_mss[card])) { + /* + * If one or more cards are already registered, don't + * return an error but print a warning. Note, this + * should never really happen unless the hardware or + * ISA PnP screwed up. + */ + if(opl3sa2_cards_num) { + printk(KERN_WARNING + "opl3sa2: There was a problem probing one " + " of the ISA PNP cards, continuing\n"); + opl3sa2_cards_num--; + continue; + } else + return -ENODEV; + } + + attach_opl3sa2(&cfg[card], card); + conf_printf(chipset_name[card], &cfg[card]); + attach_opl3sa2_mss(&cfg_mss[card]); + attach_opl3sa2_mixer(&cfg[card], card); + + opl3sa2_data[card].card = card; + /* register our power management capabilities */ + opl3sa2_data[card].pmdev = pm_register(PM_ISA_DEV, card, opl3sa2_pm_callback); + if (opl3sa2_data[card].pmdev) + opl3sa2_data[card].pmdev->data = &opl3sa2_data[card]; + + /* + * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and + * it's supported. + */ + if(ymode != -1) { + if(chipset[card] == CHIPSET_OPL3SA2) { + printk(KERN_ERR + "opl3sa2: ymode not supported on OPL3-SA2\n"); + } + else { + opl3sa2_set_ymode(&cfg[card], ymode); + } + } + + + /* Set A/D input to Mono loopback if asked to. */ + if(loopback != -1) { + opl3sa2_set_loopback(&cfg[card], loopback); + } + + /* Attach MPU if we've been asked to do so */ + if(cfg_mpu[card].io_base != -1) { + if(probe_opl3sa2_mpu(&cfg_mpu[card])) { + attach_opl3sa2_mpu(&cfg_mpu[card]); + } + } + } + + if(isapnp) { + printk(KERN_NOTICE "opl3sa2: %d PnP card(s) found.\n", opl3sa2_cards_num); + } + + return 0; +} + + +/* + * Uninstall OPL3-SA2 based card(s). + */ +static void __exit cleanup_opl3sa2(void) +{ + int card; + + for(card = 0; card < opl3sa2_cards_num; card++) { + if (opl3sa2_data[card].pmdev) + pm_unregister(opl3sa2_data[card].pmdev); + + if(cfg_mpu[card].slots[1] != -1) { + unload_opl3sa2_mpu(&cfg_mpu[card]); + } + unload_opl3sa2_mss(&cfg_mss[card]); + unload_opl3sa2(&cfg[card], card); + +#ifdef __ISAPNP__ + if(opl3sa2_activated[card] && opl3sa2_dev[card]) { + opl3sa2_dev[card]->deactivate(opl3sa2_dev[card]); + + printk(KERN_DEBUG + "opl3sa2: Deactivated ISA PnP card %d (active=%d)\n", + card, opl3sa2_dev[card]->active); + } +#endif + } +} + +module_init(init_opl3sa2); +module_exit(cleanup_opl3sa2); + +#ifndef MODULE +static int __init setup_opl3sa2(char *str) +{ + /* io, irq, dma, dma2,... */ +#ifdef __ISAPNP__ + int ints[11]; +#else + int ints[9]; +#endif + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mss_io = ints[5]; + mpu_io = ints[6]; + ymode = ints[7]; + loopback = ints[8]; +#ifdef __ISAPNP__ + isapnp = ints[9]; + multiple = ints[10]; +#endif + return 1; +} + +__setup("opl3sa2=", setup_opl3sa2); +#endif diff -Nru linux/sound/oss/os.h linux-2.4.19-pre5-mjc/sound/oss/os.h --- linux/sound/oss/os.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/os.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,57 @@ +#define ALLOW_SELECT +#undef NO_INLINE_ASM +#define SHORT_BANNERS +#define MANUAL_PNP +#undef DO_TIMINGS + +#include +#include + +#if LINUX_VERSION_CODE > 131328 +#define LINUX21X +#endif + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __alpha__ +#include +#endif +#include +#include +#include +#include +#endif + +#include +#include + +#define FALSE 0 +#define TRUE 1 + +extern int sound_alloc_dma(int chn, char *deviceID); +extern int sound_open_dma(int chn, char *deviceID); +extern void sound_free_dma(int chn); +extern void sound_close_dma(int chn); + +extern void reprogram_timer(void); + +#define USE_AUTOINIT_DMA + +extern caddr_t sound_mem_blocks[1024]; +extern int sound_nblocks; + +#undef PSEUDO_DMA_AUTOINIT +#define ALLOW_BUFFER_MAPPING + +extern struct file_operations oss_sound_fops; diff -Nru linux/sound/oss/pas2.h linux-2.4.19-pre5-mjc/sound/oss/pas2.h --- linux/sound/oss/pas2.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/pas2.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,23 @@ +/* + * pas2.h + * + * Copyright: Christoph Hellwig + * + */ + +/* From pas_card.c */ +int pas_set_intr(int mask); +int pas_remove_intr(int mask); +unsigned char pas_read(int ioaddr); +void pas_write(unsigned char data, int ioaddr); + +/* From pas_audio.c */ +void pas_pcm_interrupt(unsigned char status, int cause); +void pas_pcm_init(struct address_info *hw_config); + +/* From pas_mixer.c */ +int pas_init_mixer(void); + +/* From pas_midi.c */ +void pas_midi_init(void); +void pas_midi_interrupt(void); diff -Nru linux/sound/oss/pas2_card.c linux-2.4.19-pre5-mjc/sound/oss/pas2_card.c --- linux/sound/oss/pas2_card.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/pas2_card.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,464 @@ +/* + * sound/pas2_card.c + * + * Detection routine for the Pro Audio Spectrum cards. + */ + +#include +#include +#include +#include "sound_config.h" + +#include "pas2.h" +#include "sb.h" + +static unsigned char dma_bits[] = { + 4, 1, 2, 3, 0, 5, 6, 7 +}; + +static unsigned char irq_bits[] = { + 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 +}; + +static unsigned char sb_irq_bits[] = { + 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, + 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 +}; + +static unsigned char sb_dma_bits[] = { + 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 +}; + +/* + * The Address Translation code is used to convert I/O register addresses to + * be relative to the given base -register + */ + +int translate_code = 0; +static int pas_intr_mask = 0; +static int pas_irq = 0; +static int pas_sb_base = 0; +#ifndef CONFIG_PAS_JOYSTICK +static int joystick = 0; +#else +static int joystick = 1; +#endif +#ifdef SYMPHONY_PAS +static int symphony = 1; +#else +static int symphony = 0; +#endif +#ifdef BROKEN_BUS_CLOCK +static int broken_bus_clock = 1; +#else +static int broken_bus_clock = 0; +#endif + +static struct address_info cfg; +static struct address_info cfg2; + +char pas_model = 0; +static char *pas_model_names[] = { + "", + "Pro AudioSpectrum+", + "CDPC", + "Pro AudioSpectrum 16", + "Pro AudioSpectrum 16D" +}; + +/* + * pas_read() and pas_write() are equivalents of inb and outb + * These routines perform the I/O address translation required + * to support other than the default base address + */ + +extern void mix_write(unsigned char data, int ioaddr); + +unsigned char pas_read(int ioaddr) +{ + return inb(ioaddr + translate_code); +} + +void pas_write(unsigned char data, int ioaddr) +{ + outb((data), ioaddr + translate_code); +} + +/******************* Begin of the Interrupt Handler ********************/ + +void pasintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + int status; + + status = pas_read(0x0B89); + pas_write(status, 0x0B89); /* Clear interrupt */ + + if (status & 0x08) + { + pas_pcm_interrupt(status, 1); + status &= ~0x08; + } + if (status & 0x10) + { + pas_midi_interrupt(); + status &= ~0x10; + } +} + +int pas_set_intr(int mask) +{ + if (!mask) + return 0; + + pas_intr_mask |= mask; + + pas_write(pas_intr_mask, 0x0B8B); + return 0; +} + +int pas_remove_intr(int mask) +{ + if (!mask) + return 0; + + pas_intr_mask &= ~mask; + pas_write(pas_intr_mask, 0x0B8B); + + return 0; +} + +/******************* End of the Interrupt handler **********************/ + +/******************* Begin of the Initialization Code ******************/ + +static int __init config_pas_hw(struct address_info *hw_config) +{ + char ok = 1; + unsigned int_ptrs; /* scsi/sound interrupt pointers */ + + pas_irq = hw_config->irq; + + pas_write(0x00, 0x0B8B); + pas_write(0x36, 0x138B); + pas_write(0x36, 0x1388); + pas_write(0, 0x1388); + pas_write(0x74, 0x138B); + pas_write(0x74, 0x1389); + pas_write(0, 0x1389); + + pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A); + pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A); + pas_write(0x01 | 0x02 | 0x04 | 0x10 /* + * | + * 0x80 + */ , 0xB88); + + pas_write(0x80 + | joystick?0x40:0 + ,0xF388); + + if (pas_irq < 0 || pas_irq > 15) + { + printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); + hw_config->irq=-1; + ok = 0; + } + else + { + int_ptrs = pas_read(0xF38A); + int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq]; + pas_write(int_ptrs, 0xF38A); + if (!irq_bits[pas_irq]) + { + printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); + hw_config->irq=-1; + ok = 0; + } + else + { + if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) { + printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq); + hw_config->irq=-1; + ok = 0; + } + } + } + + if (hw_config->dma < 0 || hw_config->dma > 7) + { + printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); + hw_config->dma=-1; + ok = 0; + } + else + { + pas_write(dma_bits[hw_config->dma], 0xF389); + if (!dma_bits[hw_config->dma]) + { + printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); + hw_config->dma=-1; + ok = 0; + } + else + { + if (sound_alloc_dma(hw_config->dma, "PAS16")) + { + printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n"); + hw_config->dma=-1; + ok = 0; + } + } + } + + /* + * This fixes the timing problems of the PAS due to the Symphony chipset + * as per Media Vision. Only define this if your PAS doesn't work correctly. + */ + + if(symphony) + { + outb((0x05), 0xa8); + outb((0x60), 0xa9); + } + + if(broken_bus_clock) + pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388); + else + /* + * pas_write(0x01, 0x8388); + */ + pas_write(0x01 | 0x10 | 0x20, 0x8388); + + pas_write(0x18, 0x838A); /* ??? */ + pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */ + pas_write(8, 0xBF8A); + + mix_write(0x80 | 5, 0x078B); + mix_write(5, 0x078B); + +#if !defined(DISABLE_SB_EMULATION) + + { + struct address_info *sb_config; + + sb_config = &cfg2; + if (sb_config->io_base) + { + unsigned char irq_dma; + + /* + * Turn on Sound Blaster compatibility + * bit 1 = SB emulation + * bit 0 = MPU401 emulation (CDPC only :-( ) + */ + + pas_write(0x02, 0xF788); + + /* + * "Emulation address" + */ + + pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789); + pas_sb_base = sb_config->io_base; + + if (!sb_dma_bits[sb_config->dma]) + printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma); + + if (!sb_irq_bits[sb_config->irq]) + printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq); + + irq_dma = sb_dma_bits[sb_config->dma] | + sb_irq_bits[sb_config->irq]; + + pas_write(irq_dma, 0xFB8A); + } + else + pas_write(0x00, 0xF788); + } +#else + pas_write(0x00, 0xF788); +#endif + + if (!ok) + printk(KERN_WARNING "PAS16: Driver not enabled\n"); + + return ok; +} + +static int __init detect_pas_hw(struct address_info *hw_config) +{ + unsigned char board_id, foo; + + /* + * WARNING: Setting an option like W:1 or so that disables warm boot reset + * of the card will screw up this detect code something fierce. Adding code + * to handle this means possibly interfering with other cards on the bus if + * you have something on base port 0x388. SO be forewarned. + */ + + outb((0xBC), 0x9A01); /* Activate first board */ + outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */ + translate_code = hw_config->io_base - 0x388; + pas_write(1, 0xBF88); /* Select one wait states */ + + board_id = pas_read(0x0B8B); + + if (board_id == 0xff) + return 0; + + /* + * We probably have a PAS-series board, now check for a PAS16-series board + * by trying to change the board revision bits. PAS16-series hardware won't + * let you do this - the bits are read-only. + */ + + foo = board_id ^ 0xe0; + + pas_write(foo, 0x0B8B); + foo = pas_read(0x0B8B); + pas_write(board_id, 0x0B8B); + + if (board_id != foo) + return 0; + + pas_model = pas_read(0xFF88); + + return pas_model; +} + +static void __init attach_pas_card(struct address_info *hw_config) +{ + pas_irq = hw_config->irq; + + if (detect_pas_hw(hw_config)) + { + + if ((pas_model = pas_read(0xFF88))) + { + char temp[100]; + + sprintf(temp, + "%s rev %d", pas_model_names[(int) pas_model], + pas_read(0x2789)); + conf_printf(temp, hw_config); + } + if (config_pas_hw(hw_config)) + { + pas_pcm_init(hw_config); + +#if !defined(MODULE) && !defined(DISABLE_SB_EMULATION) + sb_dsp_disable_midi(pas_sb_base); /* No MIDI capability */ +#endif + + pas_midi_init(); + pas_init_mixer(); + } + } +} + +static inline int __init probe_pas(struct address_info *hw_config) +{ + return detect_pas_hw(hw_config); +} + +static void __exit unload_pas(struct address_info *hw_config) +{ + extern int pas_audiodev; + extern int pas2_mididev; + + if (hw_config->dma>0) + sound_free_dma(hw_config->dma); + if (hw_config->irq>0) + free_irq(hw_config->irq, hw_config); + + if(pas_audiodev!=-1) + sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev); + if(pas2_mididev!=-1) + sound_unload_mididev(pas2_mididev); + if(pas_audiodev!=-1) + sound_unload_audiodev(pas_audiodev); +} + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ + +static int __initdata sb_io = 0; +static int __initdata sb_irq = -1; +static int __initdata sb_dma = -1; +static int __initdata sb_dma16 = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma16,"i"); + +MODULE_PARM(sb_io,"i"); +MODULE_PARM(sb_irq,"i"); +MODULE_PARM(sb_dma,"i"); +MODULE_PARM(sb_dma16,"i"); + +MODULE_PARM(joystick,"i"); +MODULE_PARM(symphony,"i"); +MODULE_PARM(broken_bus_clock,"i"); + +MODULE_LICENSE("GPL"); + +static int __init init_pas2(void) +{ + printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + + cfg2.io_base = sb_io; + cfg2.irq = sb_irq; + cfg2.dma = sb_dma; + cfg2.dma2 = sb_dma16; + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); + return -EINVAL; + } + + if (!probe_pas(&cfg)) + return -ENODEV; + attach_pas_card(&cfg); + + return 0; +} + +static void __exit cleanup_pas2(void) +{ + unload_pas(&cfg); +} + +module_init(init_pas2); +module_exit(cleanup_pas2); + +#ifndef MODULE +static int __init setup_pas2(char *str) +{ + /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */ + int ints[9]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + + sb_io = ints[5]; + sb_irq = ints[6]; + sb_dma = ints[7]; + sb_dma16 = ints[8]; + + return 1; +} + +__setup("pas2=", setup_pas2); +#endif diff -Nru linux/sound/oss/pas2_midi.c linux-2.4.19-pre5-mjc/sound/oss/pas2_midi.c --- linux/sound/oss/pas2_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/pas2_midi.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,264 @@ +/* + * sound/pas2_midi.c + * + * The low level driver for the PAS Midi Interface. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer() + */ + +#include +#include "sound_config.h" + +#include "pas2.h" + +static int midi_busy = 0, input_opened = 0; +static int my_dev; + +int pas2_mididev=-1; + +static unsigned char tmp_queue[256]; +static volatile int qlen; +static volatile unsigned char qhead, qtail; + +static void (*midi_input_intr) (int dev, unsigned char data); + +static int pas_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + int err; + unsigned long flags; + unsigned char ctrl; + + + if (midi_busy) + return -EBUSY; + + /* + * Reset input and output FIFO pointers + */ + pas_write(0x20 | 0x40, + 0x178b); + + save_flags(flags); + cli(); + + if ((err = pas_set_intr(0x10)) < 0) + { + restore_flags(flags); + return err; + } + /* + * Enable input available and output FIFO empty interrupts + */ + + ctrl = 0; + input_opened = 0; + midi_input_intr = input; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + { + ctrl |= 0x04; /* Enable input */ + input_opened = 1; + } + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) + { + ctrl |= 0x08 | 0x10; /* Enable output */ + } + pas_write(ctrl, 0x178b); + + /* + * Acknowledge any pending interrupts + */ + + pas_write(0xff, 0x1B88); + + restore_flags(flags); + + midi_busy = 1; + qlen = qhead = qtail = 0; + return 0; +} + +static void pas_midi_close(int dev) +{ + + /* + * Reset FIFO pointers, disable intrs + */ + pas_write(0x20 | 0x40, 0x178b); + + pas_remove_intr(0x10); + midi_busy = 0; +} + +static int dump_to_midi(unsigned char midi_byte) +{ + int fifo_space, x; + + fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f; + + /* + * The MIDI FIFO space register and it's documentation is nonunderstandable. + * There seem to be no way to differentiate between buffer full and buffer + * empty situations. For this reason we don't never write the buffer + * completely full. In this way we can assume that 0 (or is it 15) + * means that the buffer is empty. + */ + + if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */ + return 0; /* Ask upper layers to retry after some time */ + + pas_write(midi_byte, 0x178A); + + return 1; +} + +static int pas_midi_out(int dev, unsigned char midi_byte) +{ + + unsigned long flags; + + /* + * Drain the local queue first + */ + + save_flags(flags); + cli(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + restore_flags(flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* Local queue full */ + + save_flags(flags); + cli(); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + restore_flags(flags); + + return 1; +} + +static int pas_midi_start_read(int dev) +{ + return 0; +} + +static int pas_midi_end_read(int dev) +{ + return 0; +} + +static void pas_midi_kick(int dev) +{ +} + +static int pas_buffer_status(int dev) +{ + return qlen; +} + +#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations pas_midi_operations = +{ + owner: THIS_MODULE, + info: {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, + converter: &std_midi_synth, + in_info: {0}, + open: pas_midi_open, + close: pas_midi_close, + outputc: pas_midi_out, + start_read: pas_midi_start_read, + end_read: pas_midi_end_read, + kick: pas_midi_kick, + buffer_status: pas_buffer_status, +}; + +void __init pas_midi_init(void) +{ + int dev = sound_alloc_mididev(); + + if (dev == -1) + { + printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n"); + return; + } + std_midi_synth.midi_dev = my_dev = dev; + midi_devs[dev] = &pas_midi_operations; + pas2_mididev = dev; + sequencer_init(); +} + +void pas_midi_interrupt(void) +{ + unsigned char stat; + int i, incount; + unsigned long flags; + + stat = pas_read(0x1B88); + + if (stat & 0x04) /* Input data available */ + { + incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */ + if (!incount) + incount = 16; + + for (i = 0; i < incount; i++) + if (input_opened) + { + midi_input_intr(my_dev, pas_read(0x178A)); + } else + pas_read(0x178A); /* Flush */ + } + if (stat & (0x08 | 0x10)) + { + save_flags(flags); + cli(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + restore_flags(flags); + } + if (stat & 0x40) + { + printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat); + } + pas_write(stat, 0x1B88); /* Acknowledge interrupts */ +} diff -Nru linux/sound/oss/pas2_mixer.c linux-2.4.19-pre5-mjc/sound/oss/pas2_mixer.c --- linux/sound/oss/pas2_mixer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/pas2_mixer.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,335 @@ + +/* + * sound/pas2_mixer.c + * + * Mixer routines for the Pro Audio Spectrum cards. + */ + +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer() + */ +#include +#include "sound_config.h" + +#include "pas2.h" + +#ifndef DEB +#define DEB(what) /* (what) */ +#endif + +extern int translate_code; +extern char pas_model; +extern int *pas_osp; +extern int pas_audiodev; + +static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */ +static int mode_control = 0; + +#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM) + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV) + +static int *levels; + +static int default_levels[32] = +{ + 0x3232, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x5050, /* FM */ + 0x4b4b, /* PCM */ + 0x3232, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x4b4b, /* Mic */ + 0x4b4b, /* CD */ + 0x6464, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x6464 /* Recording level */ +}; + +void +mix_write(unsigned char data, int ioaddr) +{ + /* + * The Revision D cards have a problem with their MVA508 interface. The + * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and + * MSBs out of the output byte and to do a 16-bit out to the mixer port - + * 1. We need to do this because it isn't timing problem but chip access + * sequence problem. + */ + + if (pas_model == 4) + { + outw(data | (data << 8), (ioaddr + translate_code) - 1); + outb((0x80), 0); + } else + pas_write(data, ioaddr); +} + +static int +mixer_output(int right_vol, int left_vol, int div, int bits, + int mixer) /* Input or output mixer */ +{ + int left = left_vol * div / 100; + int right = right_vol * div / 100; + + + if (bits & 0x10) + { + left |= mixer; + right |= mixer; + } + if (bits == 0x03 || bits == 0x04) + { + mix_write(0x80 | bits, 0x078B); + mix_write(left, 0x078B); + right_vol = left_vol; + } else + { + mix_write(0x80 | 0x20 | bits, 0x078B); + mix_write(left, 0x078B); + mix_write(0x80 | 0x40 | bits, 0x078B); + mix_write(right, 0x078B); + } + + return (left_vol | (right_vol << 8)); +} + +static void +set_mode(int new_mode) +{ + mix_write(0x80 | 0x05, 0x078B); + mix_write(new_mode, 0x078B); + + mode_control = new_mode; +} + +static int +pas_mixer_set(int whichDev, unsigned int level) +{ + int left, right, devmask, changed, i, mixer = 0; + + DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + if (whichDev < SOUND_MIXER_NRDEVICES) { + if ((1 << whichDev) & rec_devices) + mixer = 0x20; + else + mixer = 0x00; + } + + switch (whichDev) + { + case SOUND_MIXER_VOLUME: /* Master volume (0-63) */ + levels[whichDev] = mixer_output(right, left, 63, 0x01, 0); + break; + + /* + * Note! Bass and Treble are mono devices. Will use just the left + * channel. + */ + case SOUND_MIXER_BASS: /* Bass (0-12) */ + levels[whichDev] = mixer_output(right, left, 12, 0x03, 0); + break; + case SOUND_MIXER_TREBLE: /* Treble (0-12) */ + levels[whichDev] = mixer_output(right, left, 12, 0x04, 0); + break; + + case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer); + break; + case SOUND_MIXER_PCM: /* PAS PCM (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer); + break; + case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer); + break; + case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer); + break; + case SOUND_MIXER_LINE: /* External line (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer); + break; + case SOUND_MIXER_CD: /* CD (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer); + break; + case SOUND_MIXER_MIC: /* External microphone (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer); + break; + case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */ + levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01, + 0x00); + break; + case SOUND_MIXER_RECLEV: /* Recording level (0-15) */ + levels[whichDev] = mixer_output(right, left, 15, 0x02, 0); + break; + + + case SOUND_MIXER_RECSRC: + devmask = level & POSSIBLE_RECORDING_DEVICES; + + changed = devmask ^ rec_devices; + rec_devices = devmask; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (changed & (1 << i)) + { + pas_mixer_set(i, levels[i]); + } + return rec_devices; + break; + + default: + return -EINVAL; + } + + return (levels[whichDev]); +} + +/*****/ + +static void +pas_mixer_reset(void) +{ + int foo; + + DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n")); + + for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) + pas_mixer_set(foo, levels[foo]); + + set_mode(0x04 | 0x01); +} + +static int pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int level,v ; + + DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */ + if (get_user(level, (int *)arg)) + return -EFAULT; + if (level == -1) /* Return current settings */ + level = (mode_control & 0x04); + else { + mode_control &= ~0x04; + if (level) + mode_control |= 0x04; + set_mode(mode_control); + } + level = !!level; + return put_user(level, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */ + if (get_user(level, (int *)arg)) + return -EFAULT; + if (level == -1) { /* Return current settings */ + if (!(mode_control & 0x03)) + level = 0; + else + level = ((mode_control & 0x03) + 1) * 20; + } else { + int i = 0; + + level &= 0x7f; + if (level) + i = (level / 20) - 1; + mode_control &= ~0x03; + mode_control |= i & 0x03; + set_mode(mode_control); + if (i) + i = (i + 1) * 20; + level = i; + } + return put_user(level, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */ + if (get_user(level, (int *)arg)) + return -EFAULT; + if (level == -1) /* Return current settings */ + level = !(pas_read(0x0B8A) & 0x20); + else { + if (level) + pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A); + else + pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A); + + level = !(pas_read(0x0B8A) & 0x20); + } + return put_user(level, (int *)arg); + } + if (((cmd >> 8) & 0xff) == 'M') { + if (get_user(v, (int *)arg)) + return -EFAULT; + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + v = pas_mixer_set(cmd & 0xff, v); + } else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + v = rec_devices; + break; + + case SOUND_MIXER_STEREODEVS: + v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE); + break; + + case SOUND_MIXER_DEVMASK: + v = SUPPORTED_MIXER_DEVICES; + break; + + case SOUND_MIXER_RECMASK: + v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES; + break; + + case SOUND_MIXER_CAPS: + v = 0; /* No special capabilities */ + break; + + default: + v = levels[cmd & 0xff]; + break; + } + } + return put_user(v, (int *)arg); + } + return -EINVAL; +} + +static struct mixer_operations pas_mixer_operations = +{ + owner: THIS_MODULE, + id: "PAS16", + name: "Pro Audio Spectrum 16", + ioctl: pas_mixer_ioctl +}; + +int __init +pas_init_mixer(void) +{ + int d; + + levels = load_mixer_volumes("PAS16_1", default_levels, 1); + + pas_mixer_reset(); + + if ((d = sound_alloc_mixerdev()) != -1) + { + audio_devs[pas_audiodev]->mixer_dev = d; + mixer_devs[d] = &pas_mixer_operations; + } + return 1; +} diff -Nru linux/sound/oss/pas2_pcm.c linux-2.4.19-pre5-mjc/sound/oss/pas2_pcm.c --- linux/sound/oss/pas2_pcm.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/pas2_pcm.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,438 @@ +/* + * pas2_pcm.c Audio routines for PAS16 + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Alan Cox : Swatted a double allocation of device bug. Made a few + * more things module options. + * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init() + */ + +#include +#include "sound_config.h" + +#include "pas2.h" + +#ifndef DEB +#define DEB(WHAT) +#endif + +#define PAS_PCM_INTRBITS (0x08) +/* + * Sample buffer timer interrupt enable + */ + +#define PCM_NON 0 +#define PCM_DAC 1 +#define PCM_ADC 2 + +static unsigned long pcm_speed = 0; /* sampling rate */ +static unsigned char pcm_channels = 1; /* channels (1 or 2) */ +static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ +static unsigned char pcm_filter = 0; /* filter FLAG */ +static unsigned char pcm_mode = PCM_NON; +static unsigned long pcm_count = 0; +static unsigned short pcm_bitsok = 8; /* mask of OK bits */ +static int pcm_busy = 0; +int pas_audiodev = -1; +static int open_mode = 0; + +static int pcm_set_speed(int arg) +{ + int foo, tmp; + unsigned long flags; + + if (arg == 0) + return pcm_speed; + + if (arg > 44100) + arg = 44100; + if (arg < 5000) + arg = 5000; + + if (pcm_channels & 2) + { + foo = (596590 + (arg / 2)) / arg; + arg = (596590 + (foo / 2)) / foo; + } + else + { + foo = (1193180 + (arg / 2)) / arg; + arg = (1193180 + (foo / 2)) / foo; + } + + pcm_speed = arg; + + tmp = pas_read(0x0B8A); + + /* + * Set anti-aliasing filters according to sample rate. You really *NEED* + * to enable this feature for all normal recording unless you want to + * experiment with aliasing effects. + * These filters apply to the selected "recording" source. + * I (pfw) don't know the encoding of these 5 bits. The values shown + * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. + * + * I cleared bit 5 of these values, since that bit controls the master + * mute flag. (Olav Wölfelschneider) + * + */ +#if !defined NO_AUTO_FILTER_SET + tmp &= 0xe0; + if (pcm_speed >= 2 * 17897) + tmp |= 0x01; + else if (pcm_speed >= 2 * 15909) + tmp |= 0x02; + else if (pcm_speed >= 2 * 11931) + tmp |= 0x09; + else if (pcm_speed >= 2 * 8948) + tmp |= 0x11; + else if (pcm_speed >= 2 * 5965) + tmp |= 0x19; + else if (pcm_speed >= 2 * 2982) + tmp |= 0x04; + pcm_filter = tmp; +#endif + + save_flags(flags); + cli(); + + pas_write(tmp & ~(0x40 | 0x80), 0x0B8A); + pas_write(0x00 | 0x30 | 0x04, 0x138B); + pas_write(foo & 0xff, 0x1388); + pas_write((foo >> 8) & 0xff, 0x1388); + pas_write(tmp, 0x0B8A); + + restore_flags(flags); + + return pcm_speed; +} + +static int pcm_set_channels(int arg) +{ + + if ((arg != 1) && (arg != 2)) + return pcm_channels; + + if (arg != pcm_channels) + { + pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A); + + pcm_channels = arg; + pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ + } + return pcm_channels; +} + +static int pcm_set_bits(int arg) +{ + if (arg == 0) + return pcm_bits; + + if ((arg & pcm_bitsok) != arg) + return pcm_bits; + + if (arg != pcm_bits) + { + pas_write(pas_read(0x8389) ^ 0x04, 0x8389); + + pcm_bits = arg; + } + return pcm_bits; +} + +static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val, ret; + + DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_speed(val); + break; + + case SOUND_PCM_READ_RATE: + ret = pcm_speed; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_channels(val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_channels(val); + break; + + case SOUND_PCM_READ_CHANNELS: + ret = pcm_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + ret = pcm_set_bits(val); + break; + + case SOUND_PCM_READ_BITS: + ret = pcm_bits; + break; + + default: + return -EINVAL; + } + return put_user(ret, (int *)arg); +} + +static void pas_audio_reset(int dev) +{ + DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n")); + + pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */ +} + +static int pas_audio_open(int dev, int mode) +{ + int err; + unsigned long flags; + + DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode)); + + save_flags(flags); + cli(); + if (pcm_busy) + { + restore_flags(flags); + return -EBUSY; + } + pcm_busy = 1; + restore_flags(flags); + + if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0) + return err; + + + pcm_count = 0; + open_mode = mode; + + return 0; +} + +static void pas_audio_close(int dev) +{ + unsigned long flags; + + DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n")); + + save_flags(flags); + cli(); + + pas_audio_reset(dev); + pas_remove_intr(PAS_PCM_INTRBITS); + pcm_mode = PCM_NON; + + pcm_busy = 0; + restore_flags(flags); +} + +static void pas_audio_output_block(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags, cnt; + + DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmap_out->dma > 3) + cnt >>= 1; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; + + save_flags(flags); + cli(); + + pas_write(pas_read(0xF8A) & ~0x40, + 0xF8A); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + + if (count != pcm_count) + { + pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); + pas_write(0x40 | 0x30 | 0x04, 0x138B); + pas_write(count & 0xff, 0x1389); + pas_write((count >> 8) & 0xff, 0x1389); + pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); + + pcm_count = count; + } + pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); +#ifdef NO_TRIGGER + pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); +#endif + + pcm_mode = PCM_DAC; + + restore_flags(flags); +} + +static void pas_audio_start_input(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + int cnt; + + DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmap_out->dma > 3) + cnt >>= 1; + + if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; + + save_flags(flags); + cli(); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + + if (count != pcm_count) + { + pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); + pas_write(0x40 | 0x30 | 0x04, 0x138B); + pas_write(count & 0xff, 0x1389); + pas_write((count >> 8) & 0xff, 0x1389); + pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); + + pcm_count = count; + } + pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); +#ifdef NO_TRIGGER + pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); +#endif + + pcm_mode = PCM_ADC; + + restore_flags(flags); +} + +#ifndef NO_TRIGGER +static void pas_audio_trigger(int dev, int state) +{ + unsigned long flags; + + save_flags(flags); + cli(); + state &= open_mode; + + if (state & PCM_ENABLE_OUTPUT) + pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); + else if (state & PCM_ENABLE_INPUT) + pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); + else + pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); + + restore_flags(flags); +} +#endif + +static int pas_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + pas_audio_reset(dev); + return 0; +} + +static int pas_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + pas_audio_reset(dev); + return 0; +} + +static struct audio_driver pas_audio_driver = +{ + owner: THIS_MODULE, + open: pas_audio_open, + close: pas_audio_close, + output_block: pas_audio_output_block, + start_input: pas_audio_start_input, + ioctl: pas_audio_ioctl, + prepare_for_input: pas_audio_prepare_for_input, + prepare_for_output: pas_audio_prepare_for_output, + halt_io: pas_audio_reset, + trigger: pas_audio_trigger +}; + +void __init pas_pcm_init(struct address_info *hw_config) +{ + DEB(printk("pas2_pcm.c: long pas_pcm_init()\n")); + + pcm_bitsok = 8; + if (pas_read(0xEF8B) & 0x08) + pcm_bitsok |= 16; + + pcm_set_speed(DSP_DEFAULT_SPEED); + + if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "Pro Audio Spectrum", + &pas_audio_driver, + sizeof(struct audio_driver), + DMA_AUTOMODE, + AFMT_U8 | AFMT_S16_LE, + NULL, + hw_config->dma, + hw_config->dma)) < 0) + printk(KERN_WARNING "PAS16: Too many PCM devices available\n"); +} + +void pas_pcm_interrupt(unsigned char status, int cause) +{ + if (cause == 1) + { + /* + * Halt the PCM first. Otherwise we don't have time to start a new + * block before the PCM chip proceeds to the next sample + */ + + if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE)) + pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); + + switch (pcm_mode) + { + case PCM_DAC: + DMAbuf_outputintr(pas_audiodev, 1); + break; + + case PCM_ADC: + DMAbuf_inputintr(pas_audiodev); + break; + + default: + printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n"); + } + } +} diff -Nru linux/sound/oss/pss.c linux-2.4.19-pre5-mjc/sound/oss/pss.c --- linux/sound/oss/pss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/pss.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1238 @@ +/* + * sound/pss.c + * + * The low level driver for the Personal Sound System (ECHO ESC614). + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, clean up. + * + * 98-02-21: Vladimir Michl + * Added mixer device for Beethoven ADSP-16 (master volume, + * bass, treble, synth), only for speakers. + * Fixed bug in pss_write (exchange parameters) + * Fixed config port of SB + * Requested two regions for PSS (PSS mixer, PSS config) + * Modified pss_download_boot + * To probe_pss_mss added test for initialize AD1848 + * 98-05-28: Vladimir Michl + * Fixed computation of mixer volumes + * 04-05-1999: Anthony Barbachan + * Added code that allows the user to enable his cdrom and/or + * joystick through the module parameters pss_cdrom_port and + * pss_enable_joystick. pss_cdrom_port takes a port address as its + * argument. pss_enable_joystick takes either a 0 or a non-0 as its + * argument. + * 04-06-1999: Anthony Barbachan + * Separated some code into new functions for easier reuse. + * Cleaned up and streamlined new code. Added code to allow a user + * to only use this driver for enabling non-sound components + * through the new module parameter pss_no_sound (flag). Added + * code that would allow a user to decide whether the driver should + * reset the configured hardware settings for the PSS board through + * the module parameter pss_keep_settings (flag). This flag will + * allow a user to free up resources in use by this card if needbe, + * furthermore it allows him to use this driver to just enable the + * emulations and then be unloaded as it is no longer needed. Both + * new settings are only available to this driver if compiled as a + * module. The default settings of all new parameters are set to + * load the driver as it did in previous versions. + * 04-07-1999: Anthony Barbachan + * Added module parameter pss_firmware to allow the user to tell + * the driver where the fireware file is located. The default + * setting is the previous hardcoded setting "/etc/sound/pss_synth". + * 00-03-03: Christoph Hellwig + * Adapted to module_init/module_exit + * 11-10-2000: Bartlomiej Zolnierkiewicz + * Added __init to probe_pss(), attach_pss() and probe_pss_mpu() + * 02-Jan-2001: Chris Rankin + * Specify that this module owns the coprocessor + */ + + +#include +#include +#include + +#include "sound_config.h" +#include "sound_firmware.h" + +#include "ad1848.h" +#include "mpu401.h" + +/* + * PSS registers. + */ +#define REG(x) (devc->base+x) +#define PSS_DATA 0 +#define PSS_STATUS 2 +#define PSS_CONTROL 2 +#define PSS_ID 4 +#define PSS_IRQACK 4 +#define PSS_PIO 0x1a + +/* + * Config registers + */ +#define CONF_PSS 0x10 +#define CONF_WSS 0x12 +#define CONF_SB 0x14 +#define CONF_CDROM 0x16 +#define CONF_MIDI 0x18 + +/* + * Status bits. + */ +#define PSS_FLAG3 0x0800 +#define PSS_FLAG2 0x0400 +#define PSS_FLAG1 0x1000 +#define PSS_FLAG0 0x0800 +#define PSS_WRITE_EMPTY 0x8000 +#define PSS_READ_FULL 0x4000 + +/* + * WSS registers + */ +#define WSS_INDEX 4 +#define WSS_DATA 5 + +/* + * WSS status bits + */ +#define WSS_INITIALIZING 0x80 +#define WSS_AUTOCALIBRATION 0x20 + +#define NO_WSS_MIXER -1 + +#include "coproc.h" + +#include "pss_boot.h" + +/* If compiled into kernel, it enable or disable pss mixer */ +#ifdef CONFIG_PSS_MIXER +static unsigned char pss_mixer = 1; +#else +static unsigned char pss_mixer = 0; +#endif + + +typedef struct pss_mixerdata { + unsigned int volume_l; + unsigned int volume_r; + unsigned int bass; + unsigned int treble; + unsigned int synth; +} pss_mixerdata; + +typedef struct pss_confdata { + int base; + int irq; + int dma; + int *osp; + pss_mixerdata mixer; + int ad_mixer_dev; +} pss_confdata; + +static pss_confdata pss_data; +static pss_confdata *devc = &pss_data; + +static int pss_initialized = 0; +static int nonstandard_microcode = 0; +static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */ +static int pss_enable_joystick = 0;/* Parameter for enabling the joystick */ + +static void pss_write(pss_confdata *devc, int data) +{ + int i, limit; + + limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */ + /* + * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 5000000 && time_before(jiffies, limit); i++) + { + if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY) + { + outw(data, REG(PSS_DATA)); + return; + } + } + printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data); +} + +int __init probe_pss(struct address_info *hw_config) +{ + unsigned short id; + int irq, dma; + + devc->base = hw_config->io_base; + irq = devc->irq = hw_config->irq; + dma = devc->dma = hw_config->dma; + devc->osp = hw_config->osp; + + if (devc->base != 0x220 && devc->base != 0x240) + if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ + return 0; + + if (check_region(devc->base, 0x19 /*16*/)) { + printk(KERN_ERR "PSS: I/O port conflict\n"); + return 0; + } + id = inw(REG(PSS_ID)); + if ((id >> 8) != 'E') { + printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); + return 0; + } + return 1; +} + +static int set_irq(pss_confdata * devc, int dev, int irq) +{ + static unsigned short irq_bits[16] = + { + 0x0000, 0x0000, 0x0000, 0x0008, + 0x0000, 0x0010, 0x0000, 0x0018, + 0x0000, 0x0020, 0x0028, 0x0030, + 0x0038, 0x0000, 0x0000, 0x0000 + }; + + unsigned short tmp, bits; + + if (irq < 0 || irq > 15) + return 0; + + tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ + + if ((bits = irq_bits[irq]) == 0 && irq != 0) + { + printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq); + return 0; + } + outw(tmp | bits, REG(dev)); + return 1; +} + +static int set_io_base(pss_confdata * devc, int dev, int base) +{ + unsigned short tmp = inw(REG(dev)) & 0x003f; + unsigned short bits = (base & 0x0ffc) << 4; + + outw(bits | tmp, REG(dev)); + + return 1; +} + +static int set_dma(pss_confdata * devc, int dev, int dma) +{ + static unsigned short dma_bits[8] = + { + 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0006, 0x0007 + }; + + unsigned short tmp, bits; + + if (dma < 0 || dma > 7) + return 0; + + tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ + + if ((bits = dma_bits[dma]) == 0 && dma != 4) + { + printk(KERN_ERR "PSS: Invalid DMA %d\n", dma); + return 0; + } + outw(tmp | bits, REG(dev)); + return 1; +} + +static int pss_reset_dsp(pss_confdata * devc) +{ + unsigned long i, limit = jiffies + HZ/10; + + outw(0x2000, REG(PSS_CONTROL)); + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) + inw(REG(PSS_CONTROL)); + outw(0x0000, REG(PSS_CONTROL)); + return 1; +} + +static int pss_put_dspword(pss_confdata * devc, unsigned short word) +{ + int i, val; + + for (i = 0; i < 327680; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_WRITE_EMPTY) + { + outw(word, REG(PSS_DATA)); + return 1; + } + } + return 0; +} + +static int pss_get_dspword(pss_confdata * devc, unsigned short *word) +{ + int i, val; + + for (i = 0; i < 327680; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + { + *word = inw(REG(PSS_DATA)); + return 1; + } + } + return 0; +} + +static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags) +{ + int i, limit, val, count; + + if (flags & CPF_FIRST) + { +/*_____ Warn DSP software that a boot is coming */ + outw(0x00fe, REG(PSS_DATA)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && time_before(jiffies, limit); i++) + if (inw(REG(PSS_DATA)) == 0x5500) + break; + + outw(*block++, REG(PSS_DATA)); + pss_reset_dsp(devc); + } + count = 1; + while ((flags&CPF_LAST) || count= size && flags & CPF_LAST) + break; + else + { + printk("\n"); + printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size); + return 0; + } + } +/*_____ Send the next byte */ + if (count >= size) + { + /* If not data in block send 0xffff */ + outw (0xffff, REG (PSS_DATA)); + } + else + { + /*_____ Send the next byte */ + outw (*block++, REG (PSS_DATA)); + }; + count++; + } + + if (flags & CPF_LAST) + { +/*_____ Why */ + outw(0, REG(PSS_DATA)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit - jiffies >= 0); i++) + val = inw(REG(PSS_STATUS)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) + { + val = inw(REG(PSS_STATUS)); + if (val & 0x4000) + break; + } + + /* now read the version */ + for (i = 0; i < 32000; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + break; + } + if (i == 32000) + return 0; + + val = inw(REG(PSS_DATA)); + /* printk( "", val/16, val % 16); */ + } + return 1; +} + +/* Mixer */ +static void set_master_volume(pss_confdata *devc, int left, int right) +{ + static unsigned char log_scale[101] = { + 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, + 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff + }; + pss_write(devc, 0x0010); + pss_write(devc, log_scale[left] | 0x0000); + pss_write(devc, 0x0010); + pss_write(devc, log_scale[right] | 0x0100); +} + +static void set_synth_volume(pss_confdata *devc, int volume) +{ + int vol = ((0x8000*volume)/100L); + pss_write(devc, 0x0080); + pss_write(devc, vol); + pss_write(devc, 0x0081); + pss_write(devc, vol); +} + +static void set_bass(pss_confdata *devc, int level) +{ + int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0; + pss_write(devc, 0x0010); + pss_write(devc, vol | 0x0200); +}; + +static void set_treble(pss_confdata *devc, int level) +{ + int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0; + pss_write(devc, 0x0010); + pss_write(devc, vol | 0x0300); +}; + +static void pss_mixer_reset(pss_confdata *devc) +{ + set_master_volume(devc, 33, 33); + set_bass(devc, 50); + set_treble(devc, 50); + set_synth_volume(devc, 30); + pss_write (devc, 0x0010); + pss_write (devc, 0x0800 | 0xce); /* Stereo */ + + if(pss_mixer) + { + devc->mixer.volume_l = devc->mixer.volume_r = 33; + devc->mixer.bass = 50; + devc->mixer.treble = 50; + devc->mixer.synth = 30; + } +} + +static void arg_to_volume_mono(unsigned int volume, int *aleft) +{ + int left; + + left = volume & 0x00ff; + if (left > 100) + left = 100; + *aleft = left; +} + +static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright) +{ + arg_to_volume_mono(volume, aleft); + arg_to_volume_mono(volume >> 8, aright); +} + +static int ret_vol_mono(int left) +{ + return ((left << 8) | left); +} + +static int ret_vol_stereo(int left, int right) +{ + return ((right << 8) | left); +} + +static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, caddr_t arg) +{ + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg); + else + return -EINVAL; +} + +static int pss_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + pss_confdata *devc = mixer_devs[dev]->devc; + int cmdf = cmd & 0xff; + + if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) && + (cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) && + (cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) && + (cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) && + (cmdf != SOUND_MIXER_RECSRC)) + { + return call_ad_mixer(devc, cmd, arg); + } + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if (_SIOC_DIR (cmd) & _SIOC_WRITE) + { + switch (cmdf) + { + case SOUND_MIXER_RECSRC: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + { + if (*(int *)arg != 0) + return -EINVAL; + return 0; + } + case SOUND_MIXER_VOLUME: + arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l, + &devc->mixer.volume_r); + set_master_volume(devc, devc->mixer.volume_l, + devc->mixer.volume_r); + return ret_vol_stereo(devc->mixer.volume_l, + devc->mixer.volume_r); + + case SOUND_MIXER_BASS: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.bass); + set_bass(devc, devc->mixer.bass); + return ret_vol_mono(devc->mixer.bass); + + case SOUND_MIXER_TREBLE: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.treble); + set_treble(devc, devc->mixer.treble); + return ret_vol_mono(devc->mixer.treble); + + case SOUND_MIXER_SYNTH: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.synth); + set_synth_volume(devc, devc->mixer.synth); + return ret_vol_mono(devc->mixer.synth); + + default: + return -EINVAL; + } + } + else + { + /* + * Return parameters + */ + switch (cmdf) + { + + case SOUND_MIXER_DEVMASK: + if (call_ad_mixer(devc, cmd, arg) == -EINVAL) + *(int *)arg = 0; /* no mixer devices */ + return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH); + + case SOUND_MIXER_STEREODEVS: + if (call_ad_mixer(devc, cmd, arg) == -EINVAL) + *(int *)arg = 0; /* no stereo devices */ + return (*(int *)arg |= SOUND_MASK_VOLUME); + + case SOUND_MIXER_RECMASK: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = 0); /* no record devices */ + + case SOUND_MIXER_CAPS: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = SOUND_CAP_EXCL_INPUT); + + case SOUND_MIXER_RECSRC: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = 0); /* no record source */ + + case SOUND_MIXER_VOLUME: + return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r)); + + case SOUND_MIXER_BASS: + return (*(int *)arg = ret_vol_mono(devc->mixer.bass)); + + case SOUND_MIXER_TREBLE: + return (*(int *)arg = ret_vol_mono(devc->mixer.treble)); + + case SOUND_MIXER_SYNTH: + return (*(int *)arg = ret_vol_mono(devc->mixer.synth)); + default: + return -EINVAL; + } + } +} + +static struct mixer_operations pss_mixer_operations = +{ + owner: THIS_MODULE, + id: "SOUNDPORT", + name: "PSS-AD1848", + ioctl: pss_mixer_ioctl +}; + +void disable_all_emulations(void) +{ + outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */ + outw(0x0000, REG(CONF_WSS)); + outw(0x0000, REG(CONF_SB)); + outw(0x0000, REG(CONF_MIDI)); + outw(0x0000, REG(CONF_CDROM)); +} + +void configure_nonsound_components(void) +{ + /* Configure Joystick port */ + + if(pss_enable_joystick) + { + outw(0x0400, REG(CONF_PSS)); /* 0x0400 enables joystick */ + printk(KERN_INFO "PSS: joystick enabled.\n"); + } + else + { + printk(KERN_INFO "PSS: joystick port not enabled.\n"); + } + + /* Configure CDROM port */ + + if(pss_cdrom_port == -1) /* If cdrom port enablation wasn't requested */ + { + printk(KERN_INFO "PSS: CDROM port not enabled.\n"); + } + else if(check_region(pss_cdrom_port, 2)) + { + printk(KERN_ERR "PSS: CDROM I/O port conflict.\n"); + } + else if(!set_io_base(devc, CONF_CDROM, pss_cdrom_port)) + { + printk(KERN_ERR "PSS: CDROM I/O port could not be set.\n"); + } + else /* CDROM port successfully configured */ + { + printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port); + } +} + +void __init attach_pss(struct address_info *hw_config) +{ + unsigned short id; + char tmp[100]; + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma = hw_config->dma; + devc->osp = hw_config->osp; + devc->ad_mixer_dev = NO_WSS_MIXER; + + if (!probe_pss(hw_config)) + return; + + request_region(hw_config->io_base, 0x10, "PSS mixer, SB emulation"); + request_region(hw_config->io_base + 0x10, 0x9, "PSS config"); + + id = inw(REG(PSS_ID)) & 0x00ff; + + /* + * Disable all emulations. Will be enabled later (if required). + */ + + disable_all_emulations(); + +#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES + if (sound_alloc_dma(hw_config->dma, "PSS")) + { + printk("pss.c: Can't allocate DMA channel.\n"); + return; + } + if (!set_irq(devc, CONF_PSS, devc->irq)) + { + printk("PSS: IRQ allocation error.\n"); + return; + } + if (!set_dma(devc, CONF_PSS, devc->dma)) + { + printk(KERN_ERR "PSS: DMA allocation error\n"); + return; + } +#endif + + configure_nonsound_components(); + pss_initialized = 1; + sprintf(tmp, "ECHO-PSS Rev. %d", id); + conf_printf(tmp, hw_config); +} + +int __init probe_pss_mpu(struct address_info *hw_config) +{ + int timeout; + + if (!pss_initialized) + return 0; + + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "PSS: MPU I/O port conflict\n"); + return 0; + } + if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) + { + printk(KERN_ERR "PSS: MIDI base could not be set.\n"); + return 0; + } + if (!set_irq(devc, CONF_MIDI, hw_config->irq)) + { + printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n"); + return 0; + } + if (!pss_synthLen) + { + printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n"); + return 0; + } + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return 0; + } + + /* + * Finally wait until the DSP algorithm has initialized itself and + * deactivates receive interrupt. + */ + + for (timeout = 900000; timeout > 0; timeout--) + { + if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ + inb(hw_config->io_base); /* Discard it */ + else + break; /* No more input */ + } + + return probe_mpu401(hw_config); +} + +static int pss_coproc_open(void *dev_info, int sub_device) +{ + switch (sub_device) + { + case COPR_MIDI: + if (pss_synthLen == 0) + { + printk(KERN_ERR "PSS: MIDI synth microcode not available.\n"); + return -EIO; + } + if (nonstandard_microcode) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return -EIO; + } + nonstandard_microcode = 0; + break; + + default: + break; + } + return 0; +} + +static void pss_coproc_close(void *dev_info, int sub_device) +{ + return; +} + +static void pss_coproc_reset(void *dev_info) +{ + if (pss_synthLen) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + } + nonstandard_microcode = 0; +} + +static int download_boot_block(void *dev_info, copr_buffer * buf) +{ + if (buf->len <= 0 || buf->len > sizeof(buf->data)) + return -EINVAL; + + if (!pss_download_boot(devc, buf->data, buf->len, buf->flags)) + { + printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n"); + return -EIO; + } + nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */ + return 0; +} + +static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) +{ + copr_buffer *buf; + copr_msg *mbuf; + copr_debug_buf dbuf; + unsigned short tmp; + unsigned long flags; + unsigned short *data; + int i, err; + /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ + + switch (cmd) + { + case SNDCTL_COPR_RESET: + pss_coproc_reset(dev_info); + return 0; + + case SNDCTL_COPR_LOAD: + buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); + if (buf == NULL) + return -ENOSPC; + if (copy_from_user(buf, arg, sizeof(copr_buffer))) { + vfree(buf); + return -EFAULT; + } + err = download_boot_block(dev_info, buf); + vfree(buf); + return err; + + case SNDCTL_COPR_SENDMSG: + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + if (copy_from_user(mbuf, arg, sizeof(copr_msg))) { + vfree(mbuf); + return -EFAULT; + } + data = (unsigned short *)(mbuf->data); + save_flags(flags); + cli(); + for (i = 0; i < mbuf->len; i++) { + if (!pss_put_dspword(devc, *data++)) { + restore_flags(flags); + mbuf->len = i; /* feed back number of WORDs sent */ + err = copy_to_user(arg, mbuf, sizeof(copr_msg)); + vfree(mbuf); + return err ? -EFAULT : -EIO; + } + } + restore_flags(flags); + vfree(mbuf); + return 0; + + case SNDCTL_COPR_RCVMSG: + err = 0; + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + data = (unsigned short *)mbuf->data; + save_flags(flags); + cli(); + for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) { + mbuf->len = i; /* feed back number of WORDs read */ + if (!pss_get_dspword(devc, data++)) { + if (i == 0) + err = -EIO; + break; + } + } + restore_flags(flags); + if (copy_to_user(arg, mbuf, sizeof(copr_msg))) + err = -EFAULT; + vfree(mbuf); + return err; + + case SNDCTL_COPR_RDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d0)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp; + restore_flags(flags); + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; + + case SNDCTL_COPR_WDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d1)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + return 0; + + case SNDCTL_COPR_WCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d3)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0x00ff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + return 0; + + case SNDCTL_COPR_RCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d2)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp << 8; + if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 |= tmp & 0x00ff; + restore_flags(flags); + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } + return -EINVAL; +} + +static coproc_operations pss_coproc_operations = +{ + "ADSP-2115", + THIS_MODULE, + pss_coproc_open, + pss_coproc_close, + pss_coproc_ioctl, + pss_coproc_reset, + &pss_data +}; + +static void __init attach_pss_mpu(struct address_info *hw_config) +{ + attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */ + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; +} + +static int __init probe_pss_mss(struct address_info *hw_config) +{ + volatile int timeout; + + if (!pss_initialized) + return 0; + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); + return 0; + } + if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) + { + printk("PSS: WSS base not settable.\n"); + return 0; + } + if (!set_irq(devc, CONF_WSS, hw_config->irq)) + { + printk("PSS: WSS IRQ allocation error.\n"); + return 0; + } + if (!set_dma(devc, CONF_WSS, hw_config->dma)) + { + printk(KERN_ERR "PSS: WSS DMA allocation error\n"); + return 0; + } + /* + * For some reason the card returns 0xff in the WSS status register + * immediately after boot. Probably MIDI+SB emulation algorithm + * downloaded to the ADSP2115 spends some time initializing the card. + * Let's try to wait until it finishes this task. + */ + for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) & + WSS_INITIALIZING); timeout++) + ; + + outb((0x0b), hw_config->io_base + WSS_INDEX); /* Required by some cards */ + + for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) && + (timeout < 100000); timeout++) + ; + + return probe_ms_sound(hw_config); +} + +static void __init attach_pss_mss(struct address_info *hw_config) +{ + int my_mix = -999; /* gcc shut up */ + + devc->ad_mixer_dev = NO_WSS_MIXER; + if (pss_mixer) + { + if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION, + "PSS-SPEAKERS and AD1848 (through MSS audio codec)", + &pss_mixer_operations, + sizeof (struct mixer_operations), + devc)) < 0) + { + printk(KERN_ERR "Could not install PSS mixer\n"); + return; + } + } + pss_mixer_reset(devc); + attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ + + if (hw_config->slots[0] != -1) + { + /* The MSS driver installed itself */ + audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations; + if (pss_mixer && (num_mixers == (my_mix + 2))) + { + /* The MSS mixer installed */ + devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev; + } + } +} + +static inline void __exit unload_pss(struct address_info *hw_config) +{ + release_region(hw_config->io_base, 0x10); + release_region(hw_config->io_base+0x10, 0x9); +} + +static inline void __exit unload_pss_mpu(struct address_info *hw_config) +{ + unload_mpu401(hw_config); +} + +static inline void __exit unload_pss_mss(struct address_info *hw_config) +{ + unload_ms_sound(hw_config); +} + + +static struct address_info cfg; +static struct address_info cfg2; +static struct address_info cfg_mpu; + +static int pss_io __initdata = -1; +static int mss_io __initdata = -1; +static int mss_irq __initdata = -1; +static int mss_dma __initdata = -1; +static int mpu_io __initdata = -1; +static int mpu_irq __initdata = -1; +static int pss_no_sound __initdata = 0; /* Just configure non-sound components */ +static int pss_keep_settings = 1; /* Keep hardware settings at module exit */ +static char *pss_firmware = "/etc/sound/pss_synth"; + +MODULE_PARM(pss_io, "i"); +MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)"); +MODULE_PARM(mss_io, "i"); +MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)"); +MODULE_PARM(mss_irq, "i"); +MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)"); +MODULE_PARM(mss_dma, "i"); +MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)"); +MODULE_PARM(pss_cdrom_port, "i"); +MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)"); +MODULE_PARM(pss_enable_joystick, "i"); +MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)"); +MODULE_PARM(pss_no_sound, "i"); +MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)"); +MODULE_PARM(pss_keep_settings, "i"); +MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)"); +MODULE_PARM(pss_firmware, "s"); +MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)"); +MODULE_PARM(pss_mixer, "b"); +MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards."); +MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl"); +MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).\n"); +MODULE_LICENSE("GPL"); + + +static int fw_load = 0; +static int pssmpu = 0, pssmss = 0; + +/* + * Load a PSS sound card module + */ + +static int __init init_pss(void) +{ + + if(pss_no_sound) /* If configuring only nonsound components */ + { + cfg.io_base = pss_io; + if(!probe_pss(&cfg)) + return -ENODEV; + printk(KERN_INFO "ECHO-PSS Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff); + printk(KERN_INFO "PSS: loading in no sound mode.\n"); + disable_all_emulations(); + configure_nonsound_components(); + return 0; + } + + cfg.io_base = pss_io; + + cfg2.io_base = mss_io; + cfg2.irq = mss_irq; + cfg2.dma = mss_dma; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) { + printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n"); + return -EINVAL; + } + + if (!pss_synth) { + fw_load = 1; + pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth); + } + if (!probe_pss(&cfg)) + return -ENODEV; + attach_pss(&cfg); + /* + * Attach stuff + */ + if (probe_pss_mpu(&cfg_mpu)) { + pssmpu = 1; + attach_pss_mpu(&cfg_mpu); + } + if (probe_pss_mss(&cfg2)) { + pssmss = 1; + attach_pss_mss(&cfg2); + } + + return 0; +} + +static void __exit cleanup_pss(void) +{ + if(!pss_no_sound) + { + if(fw_load && pss_synth) + vfree(pss_synth); + if(pssmss) + unload_pss_mss(&cfg2); + if(pssmpu) + unload_pss_mpu(&cfg_mpu); + unload_pss(&cfg); + } + + if(!pss_keep_settings) /* Keep hardware settings if asked */ + { + disable_all_emulations(); + printk(KERN_INFO "Resetting PSS sound card configurations.\n"); + } +} + +module_init(init_pss); +module_exit(cleanup_pss); + +#ifndef MODULE +static int __init setup_pss(char *str) +{ + /* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + pss_io = ints[1]; + mss_io = ints[2]; + mss_irq = ints[3]; + mss_dma = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + + return 1; +} + +__setup("pss=", setup_pss); +#endif diff -Nru linux/sound/oss/rme96xx.c linux-2.4.19-pre5-mjc/sound/oss/rme96xx.c --- linux/sound/oss/rme96xx.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/rme96xx.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1565 @@ +/* (C) 2000 Guenter Geiger + with copy/pastes from the driver of Winfried Ritsch + based on es1370.c + + + + * 10 Jan 2001: 0.1 initial version + * 19 Jan 2001: 0.2 fixed bug in select() + * 27 Apr 2001: 0.3 more than one card usable + * 11 May 2001: 0.4 fixed for SMP, included into kernel source tree + * 17 May 2001: 0.5 draining code didn't work on new cards + * 18 May 2001: 0.6 remove synchronize_irq() call + +TODO: + - test more than one card --- done + - check for pci IOREGION (see es1370) in rme96xx_probe ?? + - error detection + - mmap interface + - mixer mmap interface + - mixer ioctl + - get rid of noise upon first open (why ??) + - allow multiple open(at least for read) + - allow multiple open for non overlapping regions + - recheck the multiple devices part (offsets of different devices, etc) + - do decent draining in _release --- done + - SMP support +*/ + +#ifndef RMEVERSION +#define RMEVERSION "0.6" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rme96xx.h" + +#define NR_DEVICE 2 + +static int devices = 1; +MODULE_PARM(devices, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(devices, "number of dsp devices allocated by the driver"); + + +MODULE_AUTHOR("Guenter Geiger, geiger@debian.org"); +MODULE_DESCRIPTION("RME9652/36 \"Hammerfall\" Driver"); +MODULE_LICENSE("GPL"); + + +#ifdef DEBUG +#define DBG(x) printk("RME_DEBUG:");x +#define COMM(x) printk("RME_COMM: " x "\n"); +#else +#define DBG(x) while (0) {} +#define COMM(x) +#endif + +/*-------------------------------------------------------------------------- + Preporcessor Macros and Definitions + --------------------------------------------------------------------------*/ + +#define RME96xx_MAGIC 0x6473 + +/* Registers-Space in offsets from base address with 16MByte size */ + +#define RME96xx_IO_EXTENT 16l*1024l*1024l +#define RME96xx_CHANNELS_PER_CARD 26 + +/* Write - Register */ + +/* 0,4,8,12,16,20,24,28 ... hardware init (erasing fifo-pointer intern) */ +#define RME96xx_num_of_init_regs 8 + +#define RME96xx_init_buffer (0/4) +#define RME96xx_play_buffer (32/4) /* pointer to 26x64kBit RAM from mainboard */ +#define RME96xx_rec_buffer (36/4) /* pointer to 26x64kBit RAM from mainboard */ +#define RME96xx_control_register (64/4) /* exact meaning see below */ +#define RME96xx_irq_clear (96/4) /* irq acknowledge */ +#define RME96xx_time_code (100/4) /* if used with alesis adat */ +#define RME96xx_thru_base (128/4) /* 132...228 Thru for 26 channels */ +#define RME96xx_thru_channels RME96xx_CHANNELS_PER_CARD + +/* Read Register */ + +#define RME96xx_status_register 0 /* meaning see below */ + + + +/* Status Register: */ +/* ------------------------------------------------------------------------ */ +#define RME96xx_IRQ 0x0000001 /* IRQ is High if not reset by RMExx_irq_clear */ +#define RME96xx_lock_2 0x0000002 /* ADAT 3-PLL: 1=locked, 0=unlocked */ +#define RME96xx_lock_1 0x0000004 /* ADAT 2-PLL: 1=locked, 0=unlocked */ +#define RME96xx_lock_0 0x0000008 /* ADAT 1-PLL: 1=locked, 0=unlocked */ + +#define RME96xx_fs48 0x0000010 /* sample rate 0 ...44.1/88.2, 1 ... 48/96 Khz */ +#define RME96xx_wsel_rd 0x0000020 /* if Word-Clock is used and valid then 1 */ +#define RME96xx_buf_pos1 0x0000040 /* Bit 6..15 : Position of buffer-pointer in 64Bytes-blocks */ +#define RME96xx_buf_pos2 0x0000080 /* resolution +/- 1 64Byte/block (since 64Bytes bursts) */ + +#define RME96xx_buf_pos3 0x0000100 /* 10 bits = 1024 values */ +#define RME96xx_buf_pos4 0x0000200 /* if we mask off the first 6 bits, we can take the status */ +#define RME96xx_buf_pos5 0x0000400 /* register as sample counter in the hardware buffer */ +#define RME96xx_buf_pos6 0x0000800 + +#define RME96xx_buf_pos7 0x0001000 +#define RME96xx_buf_pos8 0x0002000 +#define RME96xx_buf_pos9 0x0004000 +#define RME96xx_buf_pos10 0x0008000 + +#define RME96xx_sync_2 0x0010000 /* if ADAT-IN3 synced to system clock */ +#define RME96xx_sync_1 0x0020000 /* if ADAT-IN2 synced to system clock */ +#define RME96xx_sync_0 0x0040000 /* if ADAT-IN1 synced to system clock */ +#define RME96xx_DS_rd 0x0080000 /* 1=Double Speed, 0=Normal Speed */ + +#define RME96xx_tc_busy 0x0100000 /* 1=time-code copy in progress (960ms) */ +#define RME96xx_tc_out 0x0200000 /* time-code out bit */ +#define RME96xx_F_0 0x0400000 /* 000=64kHz, 100=88.2kHz, 011=96kHz */ +#define RME96xx_F_1 0x0800000 /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ + +#define RME96xx_F_2 0x1000000 /* od external Crystal Chip if ERF=1*/ +#define RME96xx_ERF 0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/ +#define RME96xx_buffer_id 0x4000000 /* toggles by each interrupt on rec/play */ +#define RME96xx_tc_valid 0x8000000 /* 1 = a signal is detected on time-code input */ + +/* Status Register Fields */ + +#define RME96xx_lock (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2) +#define RME96xx_buf_pos 0x000FFC0 +#define RME96xx_sync (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2) +#define RME96xx_F (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2) + + + +/* Control-Register: */ +/*--------------------------------------------------------------------------------*/ + +#define RME96xx_start_bit 0x0001 /* start record/play */ +#define RME96xx_latency0 0x0002 /* Bit 0 - Buffer size or latency */ +#define RME96xx_latency1 0x0004 /* Bit 1 - Buffer size or latency */ +#define RME96xx_latency2 0x0008 /* Bit 2 - Buffer size or latency */ + +#define RME96xx_Master 0x0010 /* Clock Mode Master=1,Slave/Auto=0 */ +#define RME96xx_IE 0x0020 /* Interupt Enable */ +#define RME96xx_freq 0x0040 /* samplerate 0=44.1/88.2, 1=48/96 kHz*/ + + +#define RME96xx_DS 0x0100 /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ +#define RME96xx_PRO 0x0200 /* spdif 0=consumer, 1=professional Mode*/ +#define RME96xx_EMP 0x0400 /* spdif Emphasis 0=None, 1=ON */ +#define RME96xx_Dolby 0x0800 /* spdif Non-audio bit 1=set, 0=unset */ + +#define RME96xx_opt_out 0x1000 /* Use 1st optical OUT as SPDIF: 1=yes,0=no */ +#define RME96xx_wsel 0x2000 /* use Wordclock as sync (overwrites master)*/ +#define RME96xx_inp_0 0x4000 /* SPDIF-IN: 00=optical (ADAT1), */ +#define RME96xx_inp_1 0x8000 /* 01=koaxial (Cinch), 10=Internal CDROM*/ + +#define RME96xx_SyncRef0 0x10000 /* preferred sync-source in autosync */ +#define RME96xx_SyncRef1 0x20000 /* 00=ADAT1,01=ADAT2,10=ADAT3,11=SPDIF */ + + +#define RME96xx_ctrl_init (RME96xx_latency0 |\ + RME96xx_Master |\ + RME96xx_inp_1) + + + +/* Control register fields and shortcuts */ + +#define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2) +#define RME96xx_inp (RME96xx_inp_0|RME96xx_inp_1) +#define RME96xx_SyncRef (RME96xx_SyncRef0|RME96xx_SyncRef1) +/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit0 */ + +#define RME96xx_SET_LATENCY(x) (((x)&0x7)<<1) +#define RME96xx_GET_LATENCY(x) (((x)>>1)&0x7) +#define RME96xx_SET_inp(x) (((x)&0x3)<<14) +#define RME96xx_GET_inp(x) (((x)>>14)&0x3) +#define RME96xx_SET_SyncRef(x) (((x)&0x3)<<17) +#define RME96xx_GET_SyncRef(x) (((x)>>17)&0x3) + + +/* buffer sizes */ +#define RME96xx_BYTES_PER_SAMPLE 4 /* sizeof(u32) */ +#define RME_16K 16*1024 + +#define RME96xx_DMA_MAX_SAMPLES (RME_16K) +#define RME96xx_DMA_MAX_SIZE (RME_16K * RME96xx_BYTES_PER_SAMPLE) +#define RME96xx_DMA_MAX_SIZE_ALL (RME96xx_DMA_MAX_SIZE * RME96xx_CHANNELS_PER_CARD) + +#define RME96xx_NUM_OF_FRAGMENTS 2 +#define RME96xx_FRAGMENT_MAX_SIZE (RME96xx_DMA_MAX_SIZE/2) +#define RME96xx_FRAGMENT_MAX_SAMPLES (RME96xx_DMA_MAX_SAMPLES/2) +#define RME96xx_MAX_LATENCY 7 /* 16k samples */ + + +#define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */ + +#define RME_MESS "rme96xx:" +/*------------------------------------------------------------------------ + Types, struct and function declarations + ------------------------------------------------------------------------*/ + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT RME_MESS" invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != RME96xx_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + + +static struct file_operations rme96xx_audio_fops; +static struct file_operations rme96xx_mixer_fops; +static int numcards; + +typedef int32_t raw_sample_t; + +typedef struct _rme96xx_info { + + /* hardware settings */ + int magic; + struct pci_dev * pcidev; /* pci_dev structure */ + unsigned long *iobase; + unsigned int irq; + + /* list of rme96xx devices */ + struct list_head devs; + + spinlock_t lock; + + u32 *recbuf; /* memory for rec buffer */ + u32 *playbuf; /* memory for play buffer */ + + u32 control_register; + + u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ + + int open_count; + + + int rate; + int latency; + unsigned int fragsize; + int started; + + int hwptr; /* can be negativ because of pci burst offset */ + unsigned int hwbufid; /* set by interrupt, buffer which is written/read now */ + + struct dmabuf { + + unsigned int format; + int formatshift; + int inchannels; /* number of channels for device */ + int outchannels; /* number of channels for device */ + int mono; /* if true, we play mono on 2 channels */ + int inoffset; /* which channel is considered the first one */ + int outoffset; + + /* state */ + int opened; /* open() made */ + int started; /* first write/read */ + int mmapped; /* mmap */ + int open_mode; + + struct _rme96xx_info *s; + + /* pointer to read/write position in buffer */ + unsigned readptr; + unsigned writeptr; + + unsigned error; /* over/underruns cleared on sync again */ + + /* waiting and locking */ + wait_queue_head_t wait; + struct semaphore open_sem; + wait_queue_head_t open_wait; + + } dma[RME96xx_MAX_DEVS]; + + int dspnum[RME96xx_MAX_DEVS]; /* register with sound subsystem */ + int mixer; /* register with sound subsystem */ +} rme96xx_info; + + +/* fiddling with the card (first level hardware control) */ + +inline void rme96xx_set_ctrl(rme96xx_info* s,int mask) +{ + + s->control_register|=mask; + writel(s->control_register,s->iobase + RME96xx_control_register); + +} + +inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask) +{ + + s->control_register&=(~mask); + writel(s->control_register,s->iobase + RME96xx_control_register); + +} + + + +/* the hwbuf in the status register seems to have some jitter, to get rid of + it, we first only let the numbers grow, to be on the secure side we + subtract a certain amount RME96xx_BURSTBYTES from the resulting number */ + +/* the function returns the hardware pointer in bytes */ +#define RME96xx_BURSTBYTES -64 /* bytes by which hwptr could be off */ + +inline int rme96xx_gethwptr(rme96xx_info* s,int exact) +{ + long flags; + if (exact) { + unsigned int hwp; +/* the hwptr seems to be rather unreliable :(, so we don't use it */ + spin_lock_irqsave(&s->lock,flags); + + hwp = readl(s->iobase + RME96xx_status_register) & 0xffc0; + s->hwptr = (hwp < s->hwptr) ? s->hwptr : hwp; +// s->hwptr = hwp; + + spin_unlock_irqrestore(&s->lock,flags); + return (s->hwptr+RME96xx_BURSTBYTES) & ((s->fragsize<<1)-1); + } + return (s->hwbufid ? s->fragsize : 0); +} + +inline void rme96xx_setlatency(rme96xx_info* s,int l) +{ + s->latency = l; + s->fragsize = 1<<(8+l); + rme96xx_unset_ctrl(s,RME96xx_latency); + rme96xx_set_ctrl(s,RME96xx_SET_LATENCY(l)); +} + + +static void rme96xx_clearbufs(struct dmabuf* dma) +{ + int i,j; + unsigned long flags; + + /* clear dmabufs */ + for(i=0;ioutchannels + dma->mono;j++) + memset(&dma->s->playbuf[(dma->outoffset + j)*RME96xx_DMA_MAX_SAMPLES], + 0, RME96xx_DMA_MAX_SIZE); + } + spin_lock_irqsave(&dma->s->lock,flags); + dma->writeptr = 0; + dma->readptr = 0; + spin_unlock_irqrestore(&dma->s->lock,flags); +} + +static int rme96xx_startcard(rme96xx_info *s,int stop) +{ + int i; + long flags; + + COMM ("startcard"); + if(s->control_register & RME96xx_IE){ + /* disable interrupt first */ + + rme96xx_unset_ctrl( s,RME96xx_start_bit ); + udelay(10); + rme96xx_unset_ctrl( s,RME96xx_IE); + spin_lock_irqsave(&s->lock,flags); /* timing is critical */ + s->started = 0; + spin_unlock_irqrestore(&s->lock,flags); + if (stop) { + COMM("Sound card stopped"); + return 1; + } + } + COMM ("interupt disabled"); + /* first initialize all pointers on card */ + for(i=0;iiobase + i); + udelay(10); /* ?? */ + } + COMM ("regs cleaned"); + + spin_lock_irqsave(&s->lock,flags); /* timing is critical */ + udelay(10); + s->started = 1; + s->hwptr = 0; + spin_unlock_irqrestore(&s->lock,flags); + + rme96xx_set_ctrl( s, RME96xx_IE | RME96xx_start_bit); + + + COMM("Sound card started"); + + return 1; +} + + +inline int rme96xx_getospace(struct dmabuf * dma, unsigned int hwp) +{ + int cnt; + int swptr; + unsigned long flags; + + spin_lock_irqsave(&dma->s->lock,flags); + swptr = dma->writeptr; + cnt = (hwp - swptr); + + if (cnt < 0) { + cnt = ((dma->s->fragsize<<1) - swptr); + } + spin_unlock_irqrestore(&dma->s->lock,flags); + return cnt; +} + +inline int rme96xx_getispace(struct dmabuf * dma, unsigned int hwp) +{ + int cnt; + int swptr; + unsigned long flags; + + spin_lock_irqsave(&dma->s->lock,flags); + swptr = dma->readptr; + cnt = (hwp - swptr); + + if (cnt < 0) { + cnt = ((dma->s->fragsize<<1) - swptr); + } + spin_unlock_irqrestore(&dma->s->lock,flags); + return cnt; +} + + +inline int rme96xx_copyfromuser(struct dmabuf* dma,const char* buffer,int count,int hop) +{ + int swptr = dma->writeptr; + switch (dma->format) { + case AFMT_S32_BLOCKED: + { + char* buf = (char*)buffer; + int cnt = count/dma->outchannels; + int i; + for (i=0;i < dma->outchannels;i++) { + char* hwbuf =(char*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=swptr; + + if (copy_from_user(hwbuf,buf, cnt)) + return -1; + buf+=hop; + } + swptr+=cnt; + break; + } + case AFMT_S16_LE: + { + int i,j; + int cnt = count/dma->outchannels; + for (i=0;i < dma->outchannels + dma->mono;i++) { + short* sbuf = (short*)buffer + i*(!dma->mono); + short* hwbuf =(short*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=(swptr>>1); + for (j=0;j<(cnt>>1);j++) { + hwbuf++; /* skip the low 16 bits */ + __get_user(*hwbuf++,sbuf++); + sbuf+=(dma->outchannels-1); + } + } + swptr += (cnt<<1); + break; + } + default: + printk(RME_MESS" unsupported format\n"); + return -1; + } /* switch */ + + swptr&=((dma->s->fragsize<<1) -1); + dma->writeptr = swptr; + + return 0; +} + +/* The count argument is the number of bytes */ +inline int rme96xx_copytouser(struct dmabuf* dma,const char* buffer,int count,int hop) +{ + int swptr = dma->readptr; + switch (dma->format) { + case AFMT_S32_BLOCKED: + { + char* buf = (char*)buffer; + int cnt = count/dma->inchannels; + int i; + + for (i=0;i < dma->inchannels;i++) { + char* hwbuf =(char*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=swptr; + + if (copy_to_user(buf,hwbuf,cnt)) + return -1; + buf+=hop; + } + swptr+=cnt; + break; + } + case AFMT_S16_LE: + { + int i,j; + int cnt = count/dma->inchannels; + for (i=0;i < dma->inchannels;i++) { + short* sbuf = (short*)buffer + i; + short* hwbuf =(short*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=(swptr>>1); + for (j=0;j<(cnt>>1);j++) { + hwbuf++; + __put_user(*hwbuf++,sbuf++); + sbuf+=(dma->inchannels-1); + } + } + swptr += (cnt<<1); + break; + } + default: + printk(RME_MESS" unsupported format\n"); + return -1; + } /* switch */ + + swptr&=((dma->s->fragsize<<1) -1); + dma->readptr = swptr; + return 0; +} + + +static void rme96xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int i; + rme96xx_info *s = (rme96xx_info *)dev_id; + struct dmabuf *db; + u32 status; + unsigned long flags; + + status = readl(s->iobase + RME96xx_status_register); + if (!(status & RME96xx_IRQ)) { + return; + } + + spin_lock_irqsave(&s->lock,flags); + writel(0,s->iobase + RME96xx_irq_clear); + + s->hwbufid = (status & RME96xx_buffer_id)>>26; + if ((status & 0xffc0) <= 256) s->hwptr = 0; + for(i=0;idma[i]); + if(db->started > 0) + wake_up(&(db->wait)); + } + spin_unlock_irqrestore(&s->lock,flags); +} + + + +/*---------------------------------------------------------------------------- + PCI detection and module initialization stuff + ----------------------------------------------------------------------------*/ + +void* busmaster_malloc(int size) { + int pg; /* 2 s exponent of memory size */ + char *buf; + + DBG(printk("kernel malloc pages ..\n")); + + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + + buf = (char *) __get_free_pages(GFP_KERNEL | GFP_DMA, pg); + + if (buf) { + struct page* page, *last_page; + + page = virt_to_page(buf); + last_page = virt_to_page(buf + (1 << pg)); + DBG(printk("setting reserved bit\n")); + while (page < last_page) { + SetPageReserved(page); + page++; + } + return buf; + } + DBG(printk("allocated %ld",(long)buf)); + return NULL; +} + +void busmaster_free(void* ptr,int size) { + int pg; + struct page* page, *last_page; + + if (ptr == NULL) + return; + + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + + page = virt_to_page(ptr); + last_page = page + (1 << pg); + while (page < last_page) { + ClearPageReserved(page); + page++; + } + DBG(printk("freeing pages\n")); + free_pages((unsigned long) ptr, pg); + DBG(printk("done\n")); +} + +/* initialize those parts of the info structure which are not pci detectable resources */ + +static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,int ooffset) { + + init_MUTEX(&dma->open_sem); + init_waitqueue_head(&dma->open_wait); + init_waitqueue_head(&dma->wait); + dma->s = s; + dma->error = 0; + + dma->format = AFMT_S32_BLOCKED; + dma->formatshift = 0; + dma->inchannels = dma->outchannels = 1; + dma->inoffset = ioffset; + dma->outoffset = ooffset; + + dma->opened=0; + dma->started=0; + dma->mmapped=0; + dma->open_mode=0; + dma->mono=0; + + rme96xx_clearbufs(dma); + return 0; +} + + +int rme96xx_init(rme96xx_info* s) +{ + int i; + DBG(printk(__FUNCTION__"\n")); + numcards++; + + s->magic = RME96xx_MAGIC; + + spin_lock_init(&s->lock); + + COMM ("setup busmaster memory") + s->recbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); + s->playbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); + + if (!s->recbuf || !s->playbuf) { + printk(KERN_ERR RME_MESS" Unable to allocate busmaster memory\n"); + return -ENODEV; + } + + COMM ("setting rec and playbuffers") + + writel((u32) virt_to_bus(s->recbuf),s->iobase + RME96xx_rec_buffer); + writel((u32) virt_to_bus(s->playbuf),s->iobase + RME96xx_play_buffer); + + COMM ("initializing control register") + rme96xx_unset_ctrl(s,0xffffffff); + rme96xx_set_ctrl(s,RME96xx_ctrl_init); + + + COMM ("setup devices") + for (i=0;i < devices;i++) { + struct dmabuf * dma = &s->dma[i]; + rme96xx_dmabuf_init(s,dma,2*i,2*i); + } + + s->started = 0; + rme96xx_setlatency(s,7); + + printk(KERN_INFO RME_MESS" card %d initialized\n",numcards); + return 0; +} + + +/* open uses this to figure out which device was opened .. this seems to be + unnecessary complex */ + +static LIST_HEAD(devs); + +static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + int i; + rme96xx_info *s; + + DBG(printk(__FUNCTION__"\n")); + + if (pcidev->irq == 0) + return -1; + if (!pci_dma_supported(pcidev, 0xffffffff)) { + printk(KERN_WARNING RME_MESS" architecture does not support 32bit PCI busmaster DMA\n"); + return -1; + } + if (!(s = kmalloc(sizeof(rme96xx_info), GFP_KERNEL))) { + printk(KERN_WARNING RME_MESS" out of memory\n"); + return -1; + } + memset(s, 0, sizeof(rme96xx_info)); + + s->pcidev = pcidev; + s->iobase = ioremap(pci_resource_start(pcidev, 0),RME96xx_IO_EXTENT); + s->irq = pcidev->irq; + + DBG(printk("remapped iobase: %lx irq %d\n",(long)s->iobase,s->irq)); + + if (pci_enable_device(pcidev)) + goto err_irq; + if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "es1370", s)) { + printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); + goto err_irq; + } + + /* initialize the card */ + + i = 0; + if (rme96xx_init(s) < 0) { + printk(KERN_ERR RME_MESS" initialization failed\n"); + goto err_devices; + } + for (i=0;idspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0) + goto err_devices; + } + + if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0) + goto err_devices; + + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; /* ????? */ + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + + DBG(printk("initialization successful\n")); + return 0; + + /* error handler */ + err_devices: + while (i--) + unregister_sound_dsp(s->dspnum[i]); + free_irq(s->irq,s); + err_irq: + kfree(s); + return -1; +} + + +static void __devinit rme96xx_remove(struct pci_dev *dev) +{ + int i; + rme96xx_info *s = pci_get_drvdata(dev); + + if (!s) { + printk(KERN_ERR"device structure not valid\n"); + return ; + } + + if (s->started) rme96xx_startcard(s,0); + + i = devices; + while (i) { + i--; + unregister_sound_dsp(s->dspnum[i]); + } + + unregister_sound_mixer(s->mixer); +/* synchronize_irq(); This call got lost somehow ? */ + free_irq(s->irq,s); + busmaster_free(s->recbuf,RME96xx_DMA_MAX_SIZE_ALL); + busmaster_free(s->playbuf,RME96xx_DMA_MAX_SIZE_ALL); + kfree(s); + pci_set_drvdata(dev, NULL); +} + + +#ifndef PCI_VENDOR_ID_RME +#define PCI_VENDOR_ID_RME 0x10ee +#endif +#ifndef PCI_DEVICE_ID_RME9652 +#define PCI_DEVICE_ID_RME9652 0x3fc4 +#endif +#ifndef PCI_ANY_ID +#define PCI_ANY_ID 0 +#endif + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_RME, PCI_DEVICE_ID_RME9652, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver rme96xx_driver = { + name: "rme96xx", + id_table: id_table, + probe: rme96xx_probe, + remove: rme96xx_remove +}; + +static int __init init_rme96xx(void) +{ + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices); + numcards = 0; + return pci_module_init(&rme96xx_driver); +} + +static void __exit cleanup_rme96xx(void) +{ + printk(KERN_INFO RME_MESS" unloading\n"); + pci_unregister_driver(&rme96xx_driver); +} + +module_init(init_rme96xx); +module_exit(cleanup_rme96xx); + + + + + +/*-------------------------------------------------------------------------- + Implementation of file operations +---------------------------------------------------------------------------*/ + +#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED) + + +static int rme96xx_ioctl(struct inode *in, struct file *file, + unsigned int cmd, unsigned long arg) +{ + + + struct dmabuf * dma = (struct dmabuf *)file->private_data; + rme96xx_info *s = dma->s; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val = 0; + + VALIDATE_STATE(s); + + DBG(printk("ioctl %ud\n",cmd)); + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: +#if 0 + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); +#endif + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: +// rme96xx_clearbufs(dma); + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { +/* generally it's not a problem if we change the speed + if (dma->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; +*/ + spin_lock_irqsave(&s->lock, flags); + + switch (val) { + case 44100: + case 88200: + rme96xx_unset_ctrl(s,RME96xx_freq); + break; + case 48000: + case 96000: + rme96xx_set_ctrl(s,RME96xx_freq); + break; + default: + rme96xx_unset_ctrl(s,RME96xx_freq); + val = 44100; + } + if (val > 50000) + rme96xx_set_ctrl(s,RME96xx_DS); + else + rme96xx_unset_ctrl(s,RME96xx_DS); + s->rate = val; + spin_unlock_irqrestore(&s->lock, flags); + } + DBG(printk("speed set to %d\n",val)); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_STEREO: /* this plays a mono file on two channels */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (!val) { + DBG(printk("setting to mono\n")); + dma->mono=1; + dma->inchannels = 1; + dma->outchannels = 1; + } + else { + DBG(printk("setting to stereo\n")); + dma->mono = 0; + dma->inchannels = 2; + dma->outchannels = 2; + } + return 0; + case SNDCTL_DSP_CHANNELS: + /* remember to check for resonable offset/channel pairs here */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) { + if (val > 0 && (dma->outoffset + val) <= RME96xx_CHANNELS_PER_CARD) + dma->outchannels = val; + else + dma->outchannels = val = 2; + DBG(printk("setting to outchannels %d\n",val)); + } + if (file->f_mode & FMODE_READ) { + if (val > 0 && (dma->inoffset + val) <= RME96xx_CHANNELS_PER_CARD) + dma->inchannels = val; + else + dma->inchannels = val = 2; + DBG(printk("setting to inchannels %d\n",val)); + } + + dma->mono=0; + + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(RME96xx_FMT, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + DBG(printk("setting to format %x\n",val)); + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (val & RME96xx_FMT) + dma->format = val; + switch (dma->format) { + case AFMT_S16_LE: + dma->formatshift=1; + break; + case AFMT_S32_BLOCKED: + dma->formatshift=0; + break; + } + } + return put_user(dma->format, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; +#if 0 + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; +#endif + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; +#if 0 + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + start_dac2(s); + } else + stop_dac2(s); + } +#endif + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + + + count = rme96xx_getospace(dma,val); + if (!s->started) count = s->fragsize*2; + abinfo.fragsize =(s->fragsize*dma->outchannels)>>dma->formatshift; + abinfo.bytes = (count*dma->outchannels)>>dma->formatshift; + abinfo.fragstotal = 2; + abinfo.fragments = (count > s->fragsize); + + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + + count = rme96xx_getispace(dma,val); + + abinfo.fragsize = (s->fragsize*dma->inchannels)>>dma->formatshift; + abinfo.bytes = (count*dma->inchannels)>>dma->formatshift;; + abinfo.fragstotal = 2; + abinfo.fragments = count > s->fragsize; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: /* What shold this exactly do ? , + ATM it is just abinfo.bytes */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + count = val - dma->readptr; + if (count < 0) + count += s->fragsize<<1; + + return put_user(count, (int *)arg); + + +/* check out how to use mmaped mode (can only be blocked !!!) */ + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + val = rme96xx_gethwptr(dma->s,0); + spin_lock_irqsave(&s->lock,flags); + cinfo.bytes = s->fragsize<<1;; + count = val - dma->readptr; + if (count < 0) + count += s->fragsize<<1; + + cinfo.blocks = (count > s->fragsize); + cinfo.ptr = val; + if (dma->mmapped) + dma->readptr &= s->fragsize<<1; + spin_unlock_irqrestore(&s->lock,flags); + + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + val = rme96xx_gethwptr(dma->s,0); + spin_lock_irqsave(&s->lock,flags); + cinfo.bytes = s->fragsize<<1;; + count = val - dma->writeptr; + if (count < 0) + count += s->fragsize<<1; + + cinfo.blocks = (count > s->fragsize); + cinfo.ptr = val; + if (dma->mmapped) + dma->writeptr &= s->fragsize<<1; + spin_unlock_irqrestore(&s->lock,flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETBLKSIZE: + return put_user(s->fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + val&=0xffff; + val -= 7; + if (val < 0) val = 0; + if (val > 7) val = 7; + rme96xx_setlatency(s,val); + return 0; + + case SNDCTL_DSP_SUBDIVIDE: +#if 0 + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; +#endif + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user(dma->outchannels, (int *)arg); + + case SOUND_PCM_READ_BITS: + switch (dma->format) { + case AFMT_S32_BLOCKED: + val = 32; + break; + case AFMT_S16_LE: + val = 16; + break; + } + return put_user(val, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + + + return -ENODEV; +} + + + +static int rme96xx_open(struct inode *in, struct file *f) +{ + int minor = minor(in->i_rdev); + struct list_head *list; + int devnum = ((minor-3)/16) % devices; /* default = 0 */ + rme96xx_info *s; + struct dmabuf* dma; + DECLARE_WAITQUEUE(wait, current); + + DBG(printk("device num %d open\n",devnum)); + +/* ??? */ + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, rme96xx_info, devs); + if (!((s->dspnum[devnum] ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); +/* ??? */ + + dma = &s->dma[devnum]; + f->private_data = dma; + /* wait for device to become free */ + down(&s->dma[devnum].open_sem); + while (dma->open_mode & f->f_mode) { + if (f->f_flags & O_NONBLOCK) { + up(&dma->open_sem); + return -EBUSY; + } + add_wait_queue(&dma->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&dma->open_sem); + schedule(); + remove_wait_queue(&dma->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&dma->open_sem); + } + + COMM ("hardware open") + + if (!s->dma[devnum].opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset); + + s->dma[devnum].open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE)); + s->dma[devnum].opened = 1; + up(&s->dma[devnum].open_sem); + + DBG(printk("device num %d open finished\n",devnum)); + return 0; +} + +static int rme96xx_release(struct inode *in, struct file *file) +{ + struct dmabuf * dma = (struct dmabuf*) file->private_data; + int hwp; + DBG(printk(__FUNCTION__"\n")); + + COMM ("draining") + if (dma->open_mode & FMODE_WRITE) { +#if 0 /* Why doesn't this work with some cards ?? */ + hwp = rme96xx_gethwptr(dma->s,0); + while (rme96xx_getospace(dma,hwp)) { + interruptible_sleep_on(&(dma->wait)); + hwp = rme96xx_gethwptr(dma->s,0); + } +#endif + rme96xx_clearbufs(dma); + } + + dma->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + if (!(dma->open_mode & (FMODE_READ|FMODE_WRITE))) { + dma->opened = 0; + if (dma->s->started) rme96xx_startcard(dma->s,1); + } + + wake_up(&dma->open_wait); + up(&dma->open_sem); + + return 0; +} + + +static ssize_t rme96xx_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + ssize_t ret = 0; + int cnt; /* number of bytes from "buffer" that will/can be used */ + int hop = count/dma->outchannels; + int hwp; + int exact = (file->f_flags & O_NONBLOCK); + + + if(dma == NULL || (dma->s) == NULL) + return -ENXIO; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (dma->mmapped || !dma->opened) + return -ENXIO; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + if (! (dma->open_mode & FMODE_WRITE)) + return -ENXIO; + + if (!dma->s->started) rme96xx_startcard(dma->s,exact); + hwp = rme96xx_gethwptr(dma->s,0); + + if(!(dma->started)){ + COMM ("first write") + + dma->readptr = hwp; + dma->writeptr = hwp; + dma->started = 1; + COMM ("first write done") + } + + while (count > 0) { + cnt = rme96xx_getospace(dma,hwp); + cnt>>=dma->formatshift; + cnt*=dma->outchannels; + if (cnt > count) + cnt = count; + + if (cnt != 0) { + if (rme96xx_copyfromuser(dma,buffer,cnt,hop)) + return ret ? ret : -EFAULT; + count -= cnt; + buffer += cnt; + ret += cnt; + if (count == 0) return ret; + } + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + + if ((hwp - dma->writeptr) <= 0) { + interruptible_sleep_on(&(dma->wait)); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + } + + hwp = rme96xx_gethwptr(dma->s,exact); + + }; /* count > 0 */ + + return ret; +} + +static ssize_t rme96xx_read(struct file *file, char *buffer,size_t count, loff_t *ppos) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + ssize_t ret = 0; + int cnt; + int hop = count/dma->inchannels; + int hwp; + int exact = (file->f_flags & O_NONBLOCK); + + + if(dma == NULL || (dma->s) == NULL) + return -ENXIO; + + if (ppos != &file->f_pos) + return -ESPIPE; + + if (dma->mmapped || !dma->opened) + return -ENXIO; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + if (! (dma->open_mode & FMODE_READ)) + return -ENXIO; + + if (count > ((dma->s->fragsize*dma->inchannels)>>dma->formatshift)) + return -EFAULT; + + if (!dma->s->started) rme96xx_startcard(dma->s,exact); + hwp = rme96xx_gethwptr(dma->s,0); + + if(!(dma->started)){ + COMM ("first read") + + dma->writeptr = hwp; + dma->readptr = hwp; + dma->started = 1; + } + + while (count > 0) { + cnt = rme96xx_getispace(dma,hwp); + cnt>>=dma->formatshift; + cnt*=dma->inchannels; + + if (cnt > count) + cnt = count; + + if (cnt != 0) { + + if (rme96xx_copytouser(dma,buffer,cnt,hop)) + return ret ? ret : -EFAULT; + + count -= cnt; + buffer += cnt; + ret += cnt; + if (count == 0) return ret; + } + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + + if ((hwp - dma->readptr) <= 0) { + interruptible_sleep_on(&(dma->wait)); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + } + hwp = rme96xx_gethwptr(dma->s,exact); + + }; /* count > 0 */ + + return ret; +} + +static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) { + struct dmabuf *dma = (struct dmabuf *)file->private_data; + rme96xx_info* s = dma->s; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + + if (vma->vm_pgoff != 0) { + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + if (size > RME96xx_DMA_MAX_SIZE) { + unlock_kernel(); + return -EINVAL; + } + + + if (vma->vm_flags & VM_WRITE) { + if (!s->started) rme96xx_startcard(s,1); + + if (remap_page_range(vma->vm_start, virt_to_phys(s->playbuf + dma->outoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + } + else if (vma->vm_flags & VM_READ) { + if (!s->started) rme96xx_startcard(s,1); + if (remap_page_range(vma->vm_start, virt_to_phys(s->playbuf + dma->inoffset*RME96xx_DMA_MAX_SIZE), size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + } else { + unlock_kernel(); + return -EINVAL; + } + + +/* this is the mapping */ + + dma->mmapped = 1; + unlock_kernel(); + return 0; +} + +static unsigned int rme96xx_poll(struct file *file, struct poll_table_struct *wait) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + rme96xx_info* s = dma->s; + unsigned int mask = 0; + unsigned int hwp,cnt; + + DBG(printk("rme96xx poll_wait ...\n")); + VALIDATE_STATE(s); + + if (!s->started) { + mask |= POLLOUT | POLLWRNORM; + } + poll_wait(file, &dma->wait, wait); + + hwp = rme96xx_gethwptr(dma->s,0); + + DBG(printk("rme96xx poll: ..cnt %d > %d\n",cnt,s->fragsize)); + + cnt = rme96xx_getispace(dma,hwp); + + if (file->f_mode & FMODE_READ) + if (cnt > 0) + mask |= POLLIN | POLLRDNORM; + + + + cnt = rme96xx_getospace(dma,hwp); + + if (file->f_mode & FMODE_WRITE) + if (cnt > 0) + mask |= POLLOUT | POLLWRNORM; + + +// printk("rme96xx poll_wait ...%d > %d\n",rme96xx_getospace(dma,hwp),rme96xx_getispace(dma,hwp)); + + return mask; +} + + +static struct file_operations rme96xx_audio_fops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + owner: THIS_MODULE, +#endif + read: rme96xx_read, + write: rme96xx_write, + poll: rme96xx_poll, + ioctl: rme96xx_ioctl, + mmap: rm96xx_mmap, + open: rme96xx_open, + release: rme96xx_release +}; + +static int rme96xx_mixer_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct list_head *list; + rme96xx_info *s; + + COMM ("mixer open"); + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, rme96xx_info, devs); + if (s->mixer== minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + + COMM ("mixer opened") + return 0; +} + +static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + rme96xx_info *s = (rme96xx_info *)file->private_data; + u32 status; + + status = readl(s->iobase + RME96xx_status_register); + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + rme_mixer mixer; + copy_from_user(&mixer,(void*)arg,sizeof(mixer)); + + if (file->f_mode & FMODE_WRITE) { + s->dma[mixer.devnr].outoffset = mixer.o_offset; + s->dma[mixer.devnr].inoffset = mixer.i_offset; + } + + mixer.o_offset = s->dma[mixer.devnr].outoffset; + mixer.i_offset = s->dma[mixer.devnr].inoffset; + + return copy_to_user((void *)arg, &mixer, sizeof(mixer)) ? -EFAULT : 0; + } + if (cmd == SOUND_MIXER_PRIVATE2) { + return put_user(status, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE3) { + u32 control; + copy_from_user(&control,(void*)arg,sizeof(control)); + if (file->f_mode & FMODE_WRITE) { + s->control_register = control; + writel(control,s->iobase + RME96xx_control_register); + } + + return put_user(s->control_register, (int *)arg); + } + return -1; +} + + + +static int rme96xx_mixer_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static /*const*/ struct file_operations rme96xx_mixer_fops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + owner: THIS_MODULE, +#endif + ioctl: rme96xx_mixer_ioctl, + open: rme96xx_mixer_open, + release: rme96xx_mixer_release, +}; diff -Nru linux/sound/oss/rme96xx.h linux-2.4.19-pre5-mjc/sound/oss/rme96xx.h --- linux/sound/oss/rme96xx.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/rme96xx.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,61 @@ +/* (C) 2000 Guenter Geiger + with copy/pastes from the driver of Winfried Ritsch +*/ + + +#ifndef AFMT_S32_BLOCKED +#define AFMT_S32_BLOCKED 0x0000400 +#endif + +#ifndef AFMT_S16_BLOCKED +#define AFMT_S16_BLOCKED 0x0000800 +#endif + + +typedef struct rme_status { + unsigned int irq:1; /* high or low */ + unsigned int lockmask:3; /* ADAT1, ADAT2, ADAT3 */ + unsigned int sr48:1; /* current sample rate */ + unsigned int wclock:1; /* wordclock used ? */ + unsigned int bufpoint:10; + + unsigned int syncmask:3; /* ADAT1, ADAT2, ADAT3 */ + unsigned int doublespeed:1; + unsigned int tc_busy:1; + unsigned int tc_out:1; + unsigned int crystalrate:3; + unsigned int spdif_error:1; + unsigned int bufid:1; + unsigned int tc_valid:1; +} rme_status_t; + + +typedef struct rme_control { + unsigned int start:1; + unsigned int latency:3; + + unsigned int master:1; + unsigned int ie:1; + unsigned int sr48:1; + unsigned int spare:1; + + unsigned int doublespeed:1; + unsigned int pro:1; + unsigned int emphasis:1; + unsigned int dolby:1; + + unsigned int opt_out:1; + unsigned int wordclock:1; + unsigned int spdif_in:2; + + unsigned int sync_ref:2; +} rme_ctrl_t; + + +typedef struct _rme_mixer { + int i_offset; + int o_offset; + int devnr; + int spare[8]; +} rme_mixer; + diff -Nru linux/sound/oss/sb.h linux-2.4.19-pre5-mjc/sound/oss/sb.h --- linux/sound/oss/sb.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb.h Mon Apr 8 22:31:22 2002 @@ -0,0 +1,187 @@ +#define DSP_RESET (devc->base + 0x6) +#define DSP_READ (devc->base + 0xA) +#define DSP_WRITE (devc->base + 0xC) +#define DSP_COMMAND (devc->base + 0xC) +#define DSP_STATUS (devc->base + 0xC) +#define DSP_DATA_AVAIL (devc->base + 0xE) +#define DSP_DATA_AVL16 (devc->base + 0xF) +#define MIXER_ADDR (devc->base + 0x4) +#define MIXER_DATA (devc->base + 0x5) +#define OPL3_LEFT (devc->base + 0x0) +#define OPL3_RIGHT (devc->base + 0x2) +#define OPL3_BOTH (devc->base + 0x8) +/* DSP Commands */ + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 +#define DSP_CMD_DMAON 0xD0 +#define DSP_CMD_DMAOFF 0xD4 + +#define IMODE_NONE 0 +#define IMODE_OUTPUT PCM_ENABLE_OUTPUT +#define IMODE_INPUT PCM_ENABLE_INPUT +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +#define NORMAL_MIDI 0 +#define UART_MIDI 1 + + +/* + * Device models + */ +#define MDL_NONE 0 +#define MDL_SB1 1 /* SB1.0 or 1.5 */ +#define MDL_SB2 2 /* SB2.0 */ +#define MDL_SB201 3 /* SB2.01 */ +#define MDL_SBPRO 4 /* SB Pro */ +#define MDL_SB16 5 /* SB16/32/AWE */ +#define MDL_SBPNP 6 /* SB16/32/AWE PnP */ +#define MDL_JAZZ 10 /* Media Vision Jazz16 */ +#define MDL_SMW 11 /* Logitech SoundMan Wave (Jazz16) */ +#define MDL_ESS 12 /* ESS ES688 and ES1688 */ +#define MDL_AZTECH 13 /* Aztech Sound Galaxy family */ +#define MDL_ES1868MIDI 14 /* MIDI port of ESS1868 */ +#define MDL_AEDSP 15 /* Audio Excel DSP 16 */ +#define MDL_ESSPCI 16 /* ESS PCI card */ +#define MDL_YMPCI 17 /* Yamaha PCI sb in emulation */ + +#define SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */ + /* register assignment */ +#define SUBMDL_ALS100 43 /* ALS-100 allows sampling rates of up */ + /* to 48kHz */ + +/* + * Config flags + */ +#define SB_NO_MIDI 0x00000001 +#define SB_NO_MIXER 0x00000002 +#define SB_NO_AUDIO 0x00000004 +#define SB_NO_RECORDING 0x00000008 /* No audio recording */ +#define SB_MIDI_ONLY (SB_NO_AUDIO|SB_NO_MIXER) +#define SB_PCI_IRQ 0x00000010 /* PCI shared IRQ */ + +struct mixer_def { + unsigned int regno: 8; + unsigned int bitoffs:4; + unsigned int nbits:4; +}; + +typedef struct mixer_def mixer_tab[32][2]; +typedef struct mixer_def mixer_ent; + +struct sb_module_options +{ + int esstype; /* ESS chip type */ + int acer; /* Do acer notebook init? */ + int sm_games; /* Logitech soundman games? */ +}; + +typedef struct sb_devc { + int dev; + + /* Hardware parameters */ + int *osp; + int minor, major; + int type; + int model, submodel; + int caps; +# define SBCAP_STEREO 0x00000001 +# define SBCAP_16BITS 0x00000002 + + /* Hardware resources */ + int base; + int irq; + int dma8, dma16; + + int pcibase; /* For ESS Maestro etc */ + + /* State variables */ + int opened; + /* new audio fields for full duplex support */ + int fullduplex; + int duplex; + int speed, bits, channels; + volatile int irq_ok; + volatile int intr_active, irq_mode; + /* duplicate audio fields for full duplex support */ + volatile int intr_active_16, irq_mode_16; + + /* Mixer fields */ + int *levels; + mixer_tab *iomap; + int mixer_caps, recmask, outmask, supported_devices; + int supported_rec_devices, supported_out_devices; + int my_mixerdev; + int sbmixnum; + + /* Audio fields */ + unsigned long trg_buf; + int trigger_bits; + int trg_bytes; + int trg_intrflag; + int trg_restart; + /* duplicate audio fields for full duplex support */ + unsigned long trg_buf_16; + int trigger_bits_16; + int trg_bytes_16; + int trg_intrflag_16; + int trg_restart_16; + + unsigned char tconst; + + /* MIDI fields */ + int my_mididev; + int input_opened; + int midi_broken; + void (*midi_input_intr) (int dev, unsigned char data); + void *midi_irq_cookie; /* IRQ cookie for the midi */ + + spinlock_t lock; + + struct sb_module_options sbmo; /* Module options */ + + } sb_devc; + +/* + * PCI card types + */ + +#define SB_PCI_ESSMAESTRO 1 /* ESS Maestro Legacy */ +#define SB_PCI_YAMAHA 2 /* Yamaha Legacy */ + +/* + * Functions + */ + +int sb_dsp_command (sb_devc *devc, unsigned char val); +int sb_dsp_get_byte(sb_devc * devc); +int sb_dsp_reset (sb_devc *devc); +void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value); +unsigned int sb_getmixer (sb_devc *devc, unsigned int port); +int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo); +int sb_dsp_init (struct address_info *hw_config, struct module *owner); +void sb_dsp_unload(struct address_info *hw_config, int sbmpu); +int sb_mixer_init(sb_devc *devc, struct module *owner); +void sb_mixer_unload(sb_devc *devc); +void sb_mixer_set_stereo (sb_devc *devc, int mode); +void smw_mixer_init(sb_devc *devc); +void sb_dsp_midi_init (sb_devc *devc, struct module *owner); +void sb_audio_init (sb_devc *devc, char *name, struct module *owner); +void sb_midi_interrupt (sb_devc *devc); +void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); +int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right); + +int sb_audio_open(int dev, int mode); +void sb_audio_close(int dev); + +extern sb_devc *last_sb; + +/* From sb_common.c */ +void sb_dsp_disable_midi(int port); +void sb_dsp_disable_recording(int port); +int probe_sbmpu (struct address_info *hw_config, struct module *owner); +void unload_sbmpu (struct address_info *hw_config); + +void unload_sb16(struct address_info *hw_info); +void unload_sb16midi(struct address_info *hw_info); diff -Nru linux/sound/oss/sb_audio.c linux-2.4.19-pre5-mjc/sound/oss/sb_audio.c --- linux/sound/oss/sb_audio.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_audio.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1093 @@ +/* + * sound/sb_audio.c + * + * Audio routines for Sound Blaster compatible cards. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes + * Alan Cox : Formatting and clean ups + * + * Status + * Mostly working. Weird uart bug causing irq storms + * + * Daniel J. Rodriksson: Changes to make sb16 work full duplex. + * Maybe other 16 bit cards in this code could behave + * the same. + * Chris Rankin: Use spinlocks instead of CLI/STI + */ + +#include + +#include "sound_config.h" + +#include "sb_mixer.h" +#include "sb.h" + +#include "sb_ess.h" + +int sb_audio_open(int dev, int mode) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + { + printk(KERN_ERR "Sound Blaster: incomplete initialization.\n"); + return -ENXIO; + } + if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) + { + if (mode == OPEN_READ) + return -EPERM; + } + spin_lock_irqsave(&devc->lock, flags); + if (devc->opened) + { + spin_unlock_irqrestore(&devc->lock, flags); + return -EBUSY; + } + if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) + { + if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit")) + { + spin_unlock_irqrestore(&devc->lock, flags); + return -EBUSY; + } + } + devc->opened = mode; + spin_unlock_irqrestore(&devc->lock, flags); + + devc->irq_mode = IMODE_NONE; + devc->irq_mode_16 = IMODE_NONE; + devc->fullduplex = devc->duplex && + ((mode & OPEN_READ) && (mode & OPEN_WRITE)); + sb_dsp_reset(devc); + + /* At first glance this check isn't enough, some ESS chips might not + * have a RECLEV. However if they don't common_mixer_set will refuse + * cause devc->iomap has no register mapping for RECLEV + */ + if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV); + + /* The ALS007 seems to require that the DSP be removed from the output */ + /* in order for recording to be activated properly. This is done by */ + /* setting the appropriate bits of the output control register 4ch to */ + /* zero. This code assumes that the output control registers are not */ + /* used anywhere else and therefore the DSP bits are *always* ON for */ + /* output and OFF for sampling. */ + + if (devc->submodel == SUBMDL_ALS007) + { + if (mode & OPEN_READ) + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9); + else + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); + } + return 0; +} + +void sb_audio_close(int dev) +{ + sb_devc *devc = audio_devs[dev]->devc; + + /* fix things if mmap turned off fullduplex */ + if(devc->duplex + && !devc->fullduplex + && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE)) + { + struct dma_buffparms *dmap_temp; + dmap_temp = audio_devs[dev]->dmap_out; + audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in; + audio_devs[dev]->dmap_in = dmap_temp; + } + audio_devs[dev]->dmap_out->dma = devc->dma8; + audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ? + devc->dma16 : devc->dma8; + + if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) + sound_close_dma(devc->dma16); + + /* For ALS007, turn DSP output back on if closing the device for read */ + + if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ)) + { + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); + } + devc->opened = 0; +} + +static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes, + int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex || devc->bits == AFMT_S16_LE) + { + devc->trg_buf = buf; + devc->trg_bytes = nr_bytes; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_OUTPUT; + } + else + { + devc->trg_buf_16 = buf; + devc->trg_bytes_16 = nr_bytes; + devc->trg_intrflag_16 = intrflag; + devc->irq_mode_16 = IMODE_OUTPUT; + } +} + +static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex || devc->bits != AFMT_S16_LE) + { + devc->trg_buf = buf; + devc->trg_bytes = count; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_INPUT; + } + else + { + devc->trg_buf_16 = buf; + devc->trg_bytes_16 = count; + devc->trg_intrflag_16 = intrflag; + devc->irq_mode_16 = IMODE_INPUT; + } +} + +/* + * SB1.x compatible routines + */ + +static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + } + else + printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + devc->intr_active = 1; +} + +static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_INPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + } + else + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + + devc->intr_active = 1; +} + +static void sb1_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + bits &= devc->irq_mode; + + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + devc->trigger_bits = bits; +} + +static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKOFF); + spin_unlock_irqrestore(&devc->lock, flags); + + devc->trigger_bits = 0; + return 0; +} + +static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKON); + spin_unlock_irqrestore(&devc->lock, flags); + devc->trigger_bits = 0; + return 0; +} + +static int sb1_audio_set_speed(int dev, int speed) +{ + int max_speed = 23000; + sb_devc *devc = audio_devs[dev]->devc; + int tmp; + + if (devc->opened & OPEN_READ) + max_speed = 13000; + + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + + if (speed > max_speed) + speed = max_speed; + + devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + tmp = 256 - devc->tconst; + speed = (1000000 + tmp / 2) / tmp; + + devc->speed = speed; + } + return devc->speed; +} + +static short sb1_audio_set_channels(int dev, short channels) +{ + sb_devc *devc = audio_devs[dev]->devc; + return devc->channels = 1; +} + +static unsigned int sb1_audio_set_bits(int dev, unsigned int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + return devc->bits = 8; +} + +static void sb1_audio_halt_xfer(int dev) +{ + unsigned long flags; + sb_devc *devc = audio_devs[dev]->devc; + + spin_lock_irqsave(&devc->lock, flags); + sb_dsp_reset(devc); + spin_unlock_irqrestore(&devc->lock, flags); +} + +/* + * SB 2.0 and SB 2.01 compatible routines + */ + +static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes, + int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + unsigned char cmd; + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + + if (devc->speed * devc->channels <= 23000) + cmd = 0x1c; /* 8 bit PCM output */ + else + cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ + + if (!sb_dsp_command(devc, cmd)) + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); + } + else + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + devc->intr_active = 1; +} + +static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + unsigned char cmd; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_INPUT; + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + + if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000)) + cmd = 0x2c; /* 8 bit PCM input */ + else + cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */ + + if (!sb_dsp_command(devc, cmd)) + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + } + else + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + spin_unlock_irqrestore(&devc->lock, flags); + devc->intr_active = 1; +} + +static void sb20_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + bits &= devc->irq_mode; + + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + devc->trigger_bits = bits; +} + +/* + * SB2.01 specific speed setup + */ + +static int sb201_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + int tmp; + int s = speed * devc->channels; + + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (devc->opened & OPEN_READ && speed > 15000) + speed = 15000; + devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; + tmp = 256 - devc->tconst; + speed = ((1000000 + tmp / 2) / tmp) / devc->channels; + + devc->speed = speed; + } + return devc->speed; +} + +/* + * SB Pro specific routines + */ + +static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount) +{ /* For SB Pro and Jazz16 */ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + unsigned char bits = 0; + + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = + devc->bits == 16 ? devc->dma16 : devc->dma8; + + if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) + if (devc->bits == AFMT_S16_LE) + bits = 0x04; /* 16 bit mode */ + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKOFF); + if (devc->channels == 1) + sb_dsp_command(devc, 0xa0 | bits); /* Mono input */ + else + sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */ + spin_unlock_irqrestore(&devc->lock, flags); + + devc->trigger_bits = 0; + return 0; +} + +static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount) +{ /* For SB Pro and Jazz16 */ + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + unsigned char tmp; + unsigned char bits = 0; + + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8; + if (devc->model == MDL_SBPRO) + sb_mixer_set_stereo(devc, devc->channels == 2); + + spin_lock_irqsave(&devc->lock, flags); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKON); + + if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) + { + if (devc->bits == AFMT_S16_LE) + bits = 0x04; /* 16 bit mode */ + + if (devc->channels == 1) + sb_dsp_command(devc, 0xa0 | bits); /* Mono output */ + else + sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */ + } + else + { + tmp = sb_getmixer(devc, 0x0e); + if (devc->channels == 1) + tmp &= ~0x02; + else + tmp |= 0x02; + sb_setmixer(devc, 0x0e, tmp); + } + spin_unlock_irqrestore(&devc->lock, flags); + devc->trigger_bits = 0; + return 0; +} + +static int sbpro_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (devc->channels > 1 && speed > 22050) + speed = 22050; + sb201_audio_set_speed(dev, speed); + } + return devc->speed; +} + +static short sbpro_audio_set_channels(int dev, short channels) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (channels == 1 || channels == 2) + { + if (channels != devc->channels) + { + devc->channels = channels; + if (devc->model == MDL_SBPRO && devc->channels == 2) + sbpro_audio_set_speed(dev, devc->speed); + } + } + return devc->channels; +} + +static int jazz16_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (speed > 0) + { + int tmp; + int s = speed * devc->channels; + + if (speed < 5000) + speed = 5000; + if (speed > 44100) + speed = 44100; + + devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; + + tmp = 256 - devc->tconst; + speed = ((1000000 + tmp / 2) / tmp) / devc->channels; + + devc->speed = speed; + } + return devc->speed; +} + +/* + * SB16 specific routines + */ + +static int sb16_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + int max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100; + + if (speed > 0) + { + if (speed < 5000) /* which of these */ + speed = 4000; /* is correct ??? */ + + if (speed > max_speed) + speed = max_speed; + + devc->speed = speed; + } + return devc->speed; +} + +static unsigned int sb16_audio_set_bits(int dev, unsigned int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (bits != 0) + { + if (bits == AFMT_U8 || bits == AFMT_S16_LE) + devc->bits = bits; + else + devc->bits = AFMT_U8; + } + + return devc->bits; +} + +static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex) + { + audio_devs[dev]->dmap_out->dma = + audio_devs[dev]->dmap_in->dma = + devc->bits == AFMT_S16_LE ? + devc->dma16 : devc->dma8; + } + else if (devc->bits == AFMT_S16_LE) + { + audio_devs[dev]->dmap_out->dma = devc->dma8; + audio_devs[dev]->dmap_in->dma = devc->dma16; + } + else + { + audio_devs[dev]->dmap_out->dma = devc->dma16; + audio_devs[dev]->dmap_in->dma = devc->dma8; + } + + devc->trigger_bits = 0; + return 0; +} + +static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex) + { + audio_devs[dev]->dmap_out->dma = + audio_devs[dev]->dmap_in->dma = + devc->bits == AFMT_S16_LE ? + devc->dma16 : devc->dma8; + } + else if (devc->bits == AFMT_S16_LE) + { + audio_devs[dev]->dmap_out->dma = devc->dma8; + audio_devs[dev]->dmap_in->dma = devc->dma16; + } + else + { + audio_devs[dev]->dmap_out->dma = devc->dma16; + audio_devs[dev]->dmap_in->dma = devc->dma8; + } + + devc->trigger_bits = 0; + return 0; +} + +static void sb16_audio_output_block(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags, cnt; + sb_devc *devc = audio_devs[dev]->devc; + unsigned long bits; + + if (!devc->fullduplex || devc->bits == AFMT_S16_LE) + { + devc->irq_mode = IMODE_OUTPUT; + devc->intr_active = 1; + } + else + { + devc->irq_mode_16 = IMODE_OUTPUT; + devc->intr_active_16 = 1; + } + + /* save value */ + spin_lock_irqsave(&devc->lock, flags); + bits = devc->bits; + if (devc->fullduplex) + devc->bits = (devc->bits == AFMT_S16_LE) ? + AFMT_U8 : AFMT_S16_LE; + spin_unlock_irqrestore(&devc->lock, flags); + + cnt = count; + if (devc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; + + spin_lock_irqsave(&devc->lock, flags); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + sb_dsp_command(devc, 0x41); + sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); + sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); + + sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); + sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + + (devc->bits == AFMT_S16_LE ? 0x10 : 0))); + sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); + sb_dsp_command(devc, (unsigned char) (cnt >> 8)); + + /* restore real value after all programming */ + devc->bits = bits; + spin_unlock_irqrestore(&devc->lock, flags); +} + + +/* + * This fails on the Cyrix MediaGX. If you don't have the DMA enabled + * before the first sample arrives it locks up. However even if you + * do enable the DMA in time you just get DMA timeouts and missing + * interrupts and stuff, so for now I've not bothered fixing this either. + */ + +static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag) +{ + unsigned long flags, cnt; + sb_devc *devc = audio_devs[dev]->devc; + + if (!devc->fullduplex || devc->bits != AFMT_S16_LE) + { + devc->irq_mode = IMODE_INPUT; + devc->intr_active = 1; + } + else + { + devc->irq_mode_16 = IMODE_INPUT; + devc->intr_active_16 = 1; + } + + cnt = count; + if (devc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; + + spin_lock_irqsave(&devc->lock, flags); + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + sb_dsp_command(devc, 0x42); + sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); + sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); + + sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce)); + sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + + (devc->bits == AFMT_S16_LE ? 0x10 : 0))); + sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); + sb_dsp_command(devc, (unsigned char) (cnt >> 8)); + + spin_unlock_irqrestore(&devc->lock, flags); +} + +static void sb16_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + int bits_16 = bits & devc->irq_mode_16; + bits &= devc->irq_mode; + + if (!bits && !bits_16) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + if (bits) + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb16_audio_start_input(dev, + devc->trg_buf, + devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb16_audio_output_block(dev, + devc->trg_buf, + devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + if (bits_16) + { + switch (devc->irq_mode_16) + { + case IMODE_INPUT: + sb16_audio_start_input(dev, + devc->trg_buf_16, + devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + + case IMODE_OUTPUT: + sb16_audio_output_block(dev, + devc->trg_buf_16, + devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + } + } + } + + devc->trigger_bits = bits | bits_16; +} + +static unsigned char lbuf8[2048]; +static signed short *lbuf16 = (signed short *)lbuf8; +#define LBUFCOPYSIZE 1024 +static void +sb16_copy_from_user(int dev, + char *localbuf, int localoffs, + const char *userbuf, int useroffs, + int max_in, int max_out, + int *used, int *returned, + int len) +{ + sb_devc *devc = audio_devs[dev]->devc; + int i, c, p, locallen; + unsigned char *buf8; + signed short *buf16; + + /* if not duplex no conversion */ + if (!devc->fullduplex) + { + copy_from_user (localbuf + localoffs, userbuf + useroffs, len); + *used = len; + *returned = len; + } + else if (devc->bits == AFMT_S16_LE) + { + /* 16 -> 8 */ + /* max_in >> 1, max number of samples in ( 16 bits ) */ + /* max_out, max number of samples out ( 8 bits ) */ + /* len, number of samples that will be taken ( 16 bits )*/ + /* c, count of samples remaining in buffer ( 16 bits )*/ + /* p, count of samples already processed ( 16 bits )*/ + len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1); + c = len; + p = 0; + buf8 = (unsigned char *)(localbuf + localoffs); + while (c) + { + locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); + /* << 1 in order to get 16 bit samples */ + copy_from_user (lbuf16, + userbuf+useroffs + (p << 1), + locallen << 1); + for (i = 0; i < locallen; i++) + { + buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80; + } + c -= locallen; p += locallen; + } + /* used = ( samples * 16 bits size ) */ + *used = len << 1; + /* returned = ( samples * 8 bits size ) */ + *returned = len; + } + else + { + /* 8 -> 16 */ + /* max_in, max number of samples in ( 8 bits ) */ + /* max_out >> 1, max number of samples out ( 16 bits ) */ + /* len, number of samples that will be taken ( 8 bits )*/ + /* c, count of samples remaining in buffer ( 8 bits )*/ + /* p, count of samples already processed ( 8 bits )*/ + len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in; + c = len; + p = 0; + buf16 = (signed short *)(localbuf + localoffs); + while (c) + { + locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); + copy_from_user (lbuf8, + userbuf+useroffs + p, + locallen); + for (i = 0; i < locallen; i++) + { + buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8; + } + c -= locallen; p += locallen; + } + /* used = ( samples * 8 bits size ) */ + *used = len; + /* returned = ( samples * 16 bits size ) */ + *returned = len << 1; + } +} + +static void +sb16_audio_mmap(int dev) +{ + sb_devc *devc = audio_devs[dev]->devc; + devc->fullduplex = 0; +} + +static struct audio_driver sb1_audio_driver = /* SB1.x */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb1_audio_prepare_for_input, + prepare_for_output: sb1_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb1_audio_trigger, + set_speed: sb1_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sb1_audio_set_channels +}; + +static struct audio_driver sb20_audio_driver = /* SB2.0 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb1_audio_prepare_for_input, + prepare_for_output: sb1_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: sb1_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sb1_audio_set_channels +}; + +static struct audio_driver sb201_audio_driver = /* SB2.01 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb1_audio_prepare_for_input, + prepare_for_output: sb1_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: sb201_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sb1_audio_set_channels +}; + +static struct audio_driver sbpro_audio_driver = /* SB Pro */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sbpro_audio_prepare_for_input, + prepare_for_output: sbpro_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: sbpro_audio_set_speed, + set_bits: sb1_audio_set_bits, + set_channels: sbpro_audio_set_channels +}; + +static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sbpro_audio_prepare_for_input, + prepare_for_output: sbpro_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + trigger: sb20_audio_trigger, + set_speed: jazz16_audio_set_speed, + set_bits: sb16_audio_set_bits, + set_channels: sbpro_audio_set_channels +}; + +static struct audio_driver sb16_audio_driver = /* SB16 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: sb_set_output_parms, + start_input: sb_set_input_parms, + prepare_for_input: sb16_audio_prepare_for_input, + prepare_for_output: sb16_audio_prepare_for_output, + halt_io: sb1_audio_halt_xfer, + copy_user: sb16_copy_from_user, + trigger: sb16_audio_trigger, + set_speed: sb16_audio_set_speed, + set_bits: sb16_audio_set_bits, + set_channels: sbpro_audio_set_channels, + mmap: sb16_audio_mmap +}; + +void sb_audio_init(sb_devc * devc, char *name, struct module *owner) +{ + int audio_flags = 0; + int format_mask = AFMT_U8; + + struct audio_driver *driver = &sb1_audio_driver; + + switch (devc->model) + { + case MDL_SB1: /* SB1.0 or SB 1.5 */ + DDB(printk("Will use standard SB1.x driver\n")); + audio_flags = DMA_HARDSTOP; + break; + + case MDL_SB2: + DDB(printk("Will use SB2.0 driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sb20_audio_driver; + break; + + case MDL_SB201: + DDB(printk("Will use SB2.01 (high speed) driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sb201_audio_driver; + break; + + case MDL_JAZZ: + case MDL_SMW: + DDB(printk("Will use Jazz16 driver\n")); + audio_flags = DMA_AUTOMODE; + format_mask |= AFMT_S16_LE; + driver = &jazz16_audio_driver; + break; + + case MDL_ESS: + DDB(printk("Will use ESS ES688/1688 driver\n")); + driver = ess_audio_init (devc, &audio_flags, &format_mask); + break; + + case MDL_SB16: + DDB(printk("Will use SB16 driver\n")); + audio_flags = DMA_AUTOMODE; + format_mask |= AFMT_S16_LE; + if (devc->dma8 != devc->dma16 && devc->dma16 != -1) + { + audio_flags |= DMA_DUPLEX; + devc->duplex = 1; + } + driver = &sb16_audio_driver; + break; + + default: + DDB(printk("Will use SB Pro driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sbpro_audio_driver; + } + + if (owner) + driver->owner = owner; + + if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + name,driver, sizeof(struct audio_driver), + audio_flags, format_mask, devc, + devc->dma8, + devc->duplex ? devc->dma16 : devc->dma8)) < 0) + { + printk(KERN_ERR "Sound Blaster: unable to install audio.\n"); + return; + } + audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev; + audio_devs[devc->dev]->min_fragment = 5; +} diff -Nru linux/sound/oss/sb_card.c linux-2.4.19-pre5-mjc/sound/oss/sb_card.c --- linux/sound/oss/sb_card.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_card.c Mon Apr 8 22:31:22 2002 @@ -0,0 +1,1035 @@ +/* + * sound/sb_card.c + * + * Detection routine for the Sound Blaster cards. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * 26-11-1999 Patched to compile without ISA PnP support in the + * kernel - Daniel Stone (tamriel@ductape.net) + * + * 06-01-2000 Refined and bugfixed ISA PnP support, added + * CMI 8330 support - Alessandro Zummo + * + * 18-01-2000 Separated sb_card and sb_common + * Jeff Garzik + * + * 04-02-2000 Added Soundblaster AWE 64 PnP support, isapnpjump + * Alessandro Zummo + * + * 11-02-2000 Added Soundblaster AWE 32 PnP support, refined PnP code + * Alessandro Zummo + * + * 13-02-2000 Hopefully fixed awe/sb16 related bugs, code cleanup + * Alessandro Zummo + * + * 13-03-2000 Added some more cards, thanks to Torsten Werner. + * Removed joystick and wavetable code, there are better places for them. + * Code cleanup plus some fixes. + * Alessandro Zummo + * + * 26-03-2000 Fixed acer, esstype and sm_games module options. + * Alessandro Zummo + * + * 12-04-2000 ISAPnP cleanup, reorg, fixes, and multiple card support. + * Thanks to Gaël Quéri and Alessandro Zummo for testing and fixes. + * Paul E. Laufer + * + * 06-05-2000 added another card. Daniel M. Newman + * + * 25-05-2000 Added Creative SB AWE64 Gold (CTL00B2). + * Pål-Kristian Engstad + * + * 12-08-2000 Added Creative SB32 PnP (CTL009F). + * Kasatenko Ivan Alex. + * + * 21-09-2000 Got rid of attach_sbmpu + * Arnaldo Carvalho de Melo + * + * 28-10-2000 Added pnplegacy support + * Daniel Church + * + * 01-10-2001 Added a new flavor of Creative SB AWE64 PnP (CTL00E9). + * Jerome Cornet + */ + +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#include "sb_mixer.h" +#include "sb.h" + +#ifdef __ISAPNP__ +#define SB_CARDS_MAX 5 +#else +#define SB_CARDS_MAX 1 +#endif + +static int sbmpu[SB_CARDS_MAX] = {0}; +static int sb_cards_num = 0; + +extern void *smw_free; + +/* + * Note DMA2 of -1 has the right meaning in the SB16 driver as well + * as here. It will cause either an error if it is needed or a fallback + * to the 8bit channel. + */ + +static int __initdata mpu_io = 0; +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata type = 0; /* Can set this to a specific card type */ +static int __initdata esstype = 0; /* ESS chip type */ +static int __initdata acer = 0; /* Do acer notebook init? */ +static int __initdata sm_games = 0; /* Logitech soundman games? */ + +static void __init attach_sb_card(struct address_info *hw_config) +{ + if(!sb_dsp_init(hw_config, THIS_MODULE)) + hw_config->slots[0] = -1; +} + +static int __init probe_sb(struct address_info *hw_config) +{ + struct sb_module_options sbmo; + + if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1) + { + printk(KERN_ERR "sb: I/O, IRQ, and DMA are mandatory\n"); + return -EINVAL; + } + +#ifdef CONFIG_MCA + /* MCA code added by ZP Gu (zpg@castle.net) */ + if (MCA_bus) { /* no multiple REPLY card probing */ + int slot; + u8 pos2, pos3, pos4; + + slot = mca_find_adapter( 0x5138, 0 ); + if( slot == MCA_NOTFOUND ) + { + slot = mca_find_adapter( 0x5137, 0 ); + + if (slot != MCA_NOTFOUND) + mca_set_adapter_name( slot, "REPLY SB16 & SCSI Adapter" ); + } + else + { + mca_set_adapter_name( slot, "REPLY SB16 Adapter" ); + } + + if (slot != MCA_NOTFOUND) + { + mca_mark_as_used(slot); + pos2 = mca_read_stored_pos( slot, 2 ); + pos3 = mca_read_stored_pos( slot, 3 ); + pos4 = mca_read_stored_pos( slot, 4 ); + + if (pos2 & 0x4) + { + /* enabled? */ + static unsigned short irq[] = { 0, 5, 7, 10 }; + /* + static unsigned short midiaddr[] = {0, 0x330, 0, 0x300 }; + */ + + hw_config->io_base = 0x220 + 0x20 * (pos2 >> 6); + hw_config->irq = irq[(pos4 >> 5) & 0x3]; + hw_config->dma = pos3 & 0xf; + /* Reply ADF wrong on High DMA, pos[1] should start w/ 00 */ + hw_config->dma2 = (pos3 >> 4) & 0x3; + if (hw_config->dma2 == 0) + hw_config->dma2 = hw_config->dma; + else + hw_config->dma2 += 4; + /* + hw_config->driver_use_2 = midiaddr[(pos2 >> 3) & 0x3]; + */ + + printk(KERN_INFO "sb: Reply MCA SB at slot=%d \ +iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", + slot+1, + hw_config->io_base, hw_config->irq, + hw_config->dma, hw_config->dma2); + } + else + { + printk (KERN_INFO "sb: Reply SB Base I/O address disabled\n"); + } + } + } +#endif + + /* Setup extra module options */ + + sbmo.acer = acer; + sbmo.sm_games = sm_games; + sbmo.esstype = esstype; + + return sb_dsp_detect(hw_config, 0, 0, &sbmo); +} + +static void __exit unload_sb(struct address_info *hw_config, int card) +{ + if(hw_config->slots[0]!=-1) + sb_dsp_unload(hw_config, sbmpu[card]); +} + +static struct address_info cfg[SB_CARDS_MAX]; +static struct address_info cfg_mpu[SB_CARDS_MAX]; + +struct pci_dev *sb_dev[SB_CARDS_MAX] = {NULL}, + *mpu_dev[SB_CARDS_MAX] = {NULL}, + *opl_dev[SB_CARDS_MAX] = {NULL}; + + +#ifdef __ISAPNP__ +static int isapnp = 1; +static int isapnpjump = 0; +static int multiple = 1; +static int pnplegacy = 0; +static int reverse = 0; +static int uart401 = 0; + +static int audio_activated[SB_CARDS_MAX] = {0}; +static int mpu_activated[SB_CARDS_MAX] = {0}; +static int opl_activated[SB_CARDS_MAX] = {0}; +#else +static int isapnp = 0; +static int multiple = 0; +static int pnplegacy = 0; +#endif + +MODULE_DESCRIPTION("Soundblaster driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(dma, "i"); +MODULE_PARM(dma16, "i"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(type, "i"); +MODULE_PARM(sm_games, "i"); +MODULE_PARM(esstype, "i"); +MODULE_PARM(acer, "i"); + +#ifdef __ISAPNP__ +MODULE_PARM(isapnp, "i"); +MODULE_PARM(isapnpjump, "i"); +MODULE_PARM(multiple, "i"); +MODULE_PARM(pnplegacy, "i"); +MODULE_PARM(reverse, "i"); +MODULE_PARM(uart401, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); +MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); +MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); +MODULE_PARM_DESC(pnplegacy, "When set to 1, will search for a legacy SB card along with any PnP cards."); +MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); +MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable the mpu on some clones"); +#endif + +MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); +MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); +MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); +MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); +MODULE_PARM_DESC(mpu_io, "Mpu base address"); +MODULE_PARM_DESC(type, "You can set this to specific card type"); +MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games"); +MODULE_PARM_DESC(esstype, "ESS chip type"); +MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks"); + +#ifdef __ISAPNP__ + +/* Please add new entries at the end of the table */ +static struct { + char *name; + unsigned short card_vendor, card_device, + audio_vendor, audio_function, + mpu_vendor, mpu_function, + opl_vendor, opl_function; + short dma, dma2, mpu_io, mpu_irq; /* see sb_init() */ +} sb_isapnp_list[] __initdata = { + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16S", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16C", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16CL", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16X", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Creative SB32 PnP", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64 Gold", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64 Gold", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0,0,0, + 0,1,1,-1}, + {"ESS 1688", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1869 PnP AudioDrive", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1869", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1878", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), + 0,0,0,0, + 0,1,2,-1}, + {"ESS 1879", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), + 0,0,0,0, + 0,1,2,-1}, + {"CMI 8330 SoundPRO", + ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + 0,1,0,-1}, + {"Diamond DT0197H", + ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 0,-1,0,0}, + {"ALS007", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 0,-1,0,0}, + {"ALS100", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"ALS110", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"ALS120", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"ALS200", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"RTL3000", + ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {0} +}; + +static struct isapnp_device_id id_table[] __devinitdata = { + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0025), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0028), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002a), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002c), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00ed), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0086), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009F), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00B2), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E9), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0968), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), 0 }, + + { ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), 0 }, + + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), 0 }, + + { ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + + { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), 0 }, + + { ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, id_table); + +static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) +{ + int err; + + /* Device already active? Let's use it */ + if(dev->active) + return(dev); + + if((err = dev->activate(dev)) < 0) { + printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); + + dev->deactivate(dev); + + return(NULL); + } + return(dev); +} + +static struct pci_dev *sb_init(struct pci_bus *bus, struct address_info *hw_config, struct address_info *mpu_config, int slot, int card) +{ + + /* Configure Audio device */ + if((sb_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].audio_vendor, sb_isapnp_list[slot].audio_function, NULL))) + { + int ret; + ret = sb_dev[card]->prepare(sb_dev[card]); + /* If device is active, assume configured with /proc/isapnp + * and use anyway. Some other way to check this? */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: ISAPnP found device that could not be autoconfigured.\n"); + return(NULL); + } + if(ret == -EBUSY) + audio_activated[card] = 1; + + if((sb_dev[card] = activate_dev(sb_isapnp_list[slot].name, "sb", sb_dev[card]))) + { + hw_config->io_base = sb_dev[card]->resource[0].start; + hw_config->irq = sb_dev[card]->irq_resource[0].start; + hw_config->dma = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma].start; + if(sb_isapnp_list[slot].dma2 != -1) + hw_config->dma2 = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma2].start; + else + hw_config->dma2 = -1; + } else + return(NULL); + } else + return(NULL); + + /* Cards with separate OPL3 device (ALS, CMI, etc.) + * This is just to activate the device so the OPL module can use it */ + if(sb_isapnp_list[slot].opl_vendor || sb_isapnp_list[slot].opl_function) { + if((opl_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].opl_vendor, sb_isapnp_list[slot].opl_function, NULL))) { + int ret = opl_dev[card]->prepare(opl_dev[card]); + /* If device is active, assume configured with + * /proc/isapnp and use anyway */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: OPL device could not be autoconfigured.\n"); + return(sb_dev[card]); + } + if(ret == -EBUSY) + opl_activated[card] = 1; + + /* Some have irq and dma for opl. the opl3 driver wont + * use 'em so don't configure 'em and hope it works -PEL */ + opl_dev[card]->irq_resource[0].flags = 0; + opl_dev[card]->dma_resource[0].flags = 0; + + opl_dev[card] = activate_dev(sb_isapnp_list[slot].name, "opl3", opl_dev[card]); + } else + printk(KERN_ERR "sb: %s isapnp panic: opl3 device not found\n", sb_isapnp_list[slot].name); + } + + /* Cards with MPU as part of Audio device (CTL and ESS) */ + if(!sb_isapnp_list[slot].mpu_vendor) { + mpu_config->io_base = sb_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; + return(sb_dev[card]); + } + + /* Cards with separate MPU device (ALS, CMI, etc.) */ + if(!uart401) + return(sb_dev[card]); + if((mpu_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].mpu_vendor, sb_isapnp_list[slot].mpu_function, NULL))) + { + int ret = mpu_dev[card]->prepare(mpu_dev[card]); + /* If device is active, assume configured with /proc/isapnp + * and use anyway */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: MPU device could not be autoconfigured.\n"); + return(sb_dev[card]); + } + if(ret == -EBUSY) + mpu_activated[card] = 1; + + /* Some cards ask for irq but don't need them - azummo */ + if(sb_isapnp_list[slot].mpu_irq == -1) + mpu_dev[card]->irq_resource[0].flags = 0; + + if((mpu_dev[card] = activate_dev(sb_isapnp_list[slot].name, "mpu", mpu_dev[card]))) { + mpu_config->io_base = mpu_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; + if(sb_isapnp_list[slot].mpu_irq != -1) + mpu_config->irq = mpu_dev[card]->irq_resource[sb_isapnp_list[slot].mpu_irq].start; + } + } + else + printk(KERN_ERR "sb: %s isapnp panic: mpu not found\n", sb_isapnp_list[slot].name); + + return(sb_dev[card]); +} + +static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, int slot, int card) +{ + char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; + + printk(KERN_INFO "sb: %s detected\n", busname); + + /* Initialize this baby. */ + + if(sb_init(bus, hw_config, mpu_config, slot, card)) { + /* We got it. */ + + printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", + busname, + hw_config->io_base, hw_config->irq, hw_config->dma, + hw_config->dma2); + return 1; + } + else + printk(KERN_INFO "sb: Failed to initialize %s\n", busname); + + return 0; +} + +static int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config, int card) +{ + static int first = 1; + int i; + + /* Count entries in sb_isapnp_list */ + for (i = 0; sb_isapnp_list[i].card_vendor != 0; i++); + i--; + + /* Check and adjust isapnpjump */ + if( isapnpjump < 0 || isapnpjump > i) { + isapnpjump = reverse ? i : 0; + printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); + } + + if(!first || !reverse) + i = isapnpjump; + first = 0; + while(sb_isapnp_list[i].card_vendor != 0) { + static struct pci_bus *bus = NULL; + + while ((bus = isapnp_find_card( + sb_isapnp_list[i].card_vendor, + sb_isapnp_list[i].card_device, + bus))) { + + if(sb_isapnp_init(hw_config, mpu_config, bus, i, card)) { + isapnpjump = i; /* start next search from here */ + return 0; + } + } + i += reverse ? -1 : 1; + } + + return -ENODEV; +} +#endif + +static int __init init_sb(void) +{ + int card, max = (multiple && isapnp) ? SB_CARDS_MAX : 1; + + printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + for(card = 0; card < max; card++, sb_cards_num++) { +#ifdef __ISAPNP__ + /* Please remember that even with __ISAPNP__ defined one + * should still be able to disable PNP support for this + * single driver! */ + if((!pnplegacy||card>0) && isapnp && (sb_isapnp_probe(&cfg[card], &cfg_mpu[card], card) < 0) ) { + if(!sb_cards_num) { + /* Found no ISAPnP cards, so check for a non-pnp + * card and set the detection loop for 1 cycle + */ + printk(KERN_NOTICE "sb: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + max = 1; + } else + /* found all the ISAPnP cards so exit the + * detection loop. */ + break; + } +#endif + + if(!isapnp || (pnplegacy&&card==0)) { + cfg[card].io_base = io; + cfg[card].irq = irq; + cfg[card].dma = dma; + cfg[card].dma2 = dma16; + } + + cfg[card].card_subtype = type; + + if (!probe_sb(&cfg[card])) { + /* if one or more cards already registered, don't + * return an error but print a warning. Note, this + * should never really happen unless the hardware + * or ISAPnP screwed up. */ + if (sb_cards_num) { + printk(KERN_WARNING "sb.c: There was a " \ + "problem probing one of your SoundBlaster " \ + "ISAPnP soundcards. Continuing.\n"); + card--; + sb_cards_num--; + continue; + } else if(pnplegacy && isapnp) { + printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ + "found. Continuing with PnP detection.\n"); + pnplegacy=0; + card--; + continue; + } else + return -ENODEV; + } + attach_sb_card(&cfg[card]); + + if(cfg[card].slots[0]==-1) { + if(card==0 && pnplegacy && isapnp) { + printk(KERN_NOTICE "sb: No legacy SoundBlaster cards " \ + "found. Continuing with PnP detection.\n"); + pnplegacy=0; + card--; + continue; + } else + return -ENODEV; + } + + if (!isapnp||(pnplegacy&&card==0)) + cfg_mpu[card].io_base = mpu_io; + if (probe_sbmpu(&cfg_mpu[card], THIS_MODULE)) + sbmpu[card] = 1; + } + + if(isapnp) + printk(KERN_NOTICE "sb: %d Soundblaster PnP card(s) found.\n", sb_cards_num); + + return 0; +} + +static void __exit cleanup_sb(void) +{ + int i; + + if (smw_free) { + vfree(smw_free); + smw_free = NULL; + } + + for(i = 0; i < sb_cards_num; i++) { + unload_sb(&cfg[i], i); + if (sbmpu[i]) + unload_sbmpu(&cfg_mpu[i]); + +#ifdef __ISAPNP__ + if(!audio_activated[i] && sb_dev[i]) + sb_dev[i]->deactivate(sb_dev[i]); + if(!mpu_activated[i] && mpu_dev[i]) + mpu_dev[i]->deactivate(mpu_dev[i]); + if(!opl_activated[i] && opl_dev[i]) + opl_dev[i]->deactivate(opl_dev[i]); +#endif + } +} + +module_init(init_sb); +module_exit(cleanup_sb); + +#ifndef MODULE +static int __init setup_sb(char *str) +{ + /* io, irq, dma, dma2 - just the basics */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + + return 1; +} +__setup("sb=", setup_sb); +#endif diff -Nru linux/sound/oss/sb_common.c linux-2.4.19-pre5-mjc/sound/oss/sb_common.c --- linux/sound/oss/sb_common.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_common.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1295 @@ +/* + * sound/sb_common.c + * + * Common routines for Sound Blaster compatible cards. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts + * for full duplex support ( only sb16 by now ) + * Rolf Fokkens: Added (BETA?) support for ES1887 chips. + * (fokkensr@vertis.nl) Which means: You can adjust the recording levels. + * + * 2000/01/18 - separated sb_card and sb_common - + * Jeff Garzik + * + * 2000/09/18 - got rid of attach_uart401 + * Arnaldo Carvalho de Melo + * + * 2001/01/26 - replaced CLI/STI with spinlocks + * Chris Rankin + */ + +#include +#include +#include +#include +#include + +#include "sound_config.h" +#include "sound_firmware.h" + +#include "mpu401.h" + +#include "sb_mixer.h" +#include "sb.h" +#include "sb_ess.h" + +/* + * global module flag + */ + +int sb_be_quiet; + +static sb_devc *detected_devc; /* For communication from probe to init */ +static sb_devc *last_devc; /* For MPU401 initialization */ + +static unsigned char jazz_irq_bits[] = { + 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6 +}; + +static unsigned char jazz_dma_bits[] = { + 0, 1, 0, 2, 0, 3, 0, 4 +}; + +void *smw_free; + +/* + * Jazz16 chipset specific control variables + */ + +static int jazz16_base; /* Not detected */ +static unsigned char jazz16_bits; /* I/O relocation bits */ +static spinlock_t jazz16_lock = SPIN_LOCK_UNLOCKED; + +/* + * Logitech Soundman Wave specific initialization code + */ + +#ifdef SMW_MIDI0001_INCLUDED +#include "smw-midi0001.h" +#else +static unsigned char *smw_ucode; +static int smw_ucodeLen; + +#endif + +sb_devc *last_sb; /* Last sb loaded */ + +int sb_dsp_command(sb_devc * devc, unsigned char val) +{ + int i; + unsigned long limit; + + limit = jiffies + HZ / 10; /* Timeout */ + + /* + * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 500000 && (limit-jiffies)>0; i++) + { + if ((inb(DSP_STATUS) & 0x80) == 0) + { + outb((val), DSP_COMMAND); + return 1; + } + } + printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val); + return 0; +} + +int sb_dsp_get_byte(sb_devc * devc) +{ + int i; + + for (i = 1000; i; i--) + { + if (inb(DSP_DATA_AVAIL) & 0x80) + return inb(DSP_READ); + } + return 0xffff; +} + +static void sb_intr (sb_devc *devc) +{ + int status; + unsigned char src = 0xff; + + if (devc->model == MDL_SB16) + { + src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */ + + if (src & 4) /* MPU401 interrupt */ + if(devc->midi_irq_cookie) + uart401intr(devc->irq, devc->midi_irq_cookie, NULL); + + if (!(src & 3)) + return; /* Not a DSP interrupt */ + } + if (devc->intr_active && (!devc->fullduplex || (src & 0x01))) + { + switch (devc->irq_mode) + { + case IMODE_OUTPUT: + DMAbuf_outputintr(devc->dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr(devc->dev); + break; + + case IMODE_INIT: + break; + + case IMODE_MIDI: + sb_midi_interrupt(devc); + break; + + default: + /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */ + ; + } + } + else if (devc->intr_active_16 && (src & 0x02)) + { + switch (devc->irq_mode_16) + { + case IMODE_OUTPUT: + DMAbuf_outputintr(devc->dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr(devc->dev); + break; + + case IMODE_INIT: + break; + + default: + /* printk(KERN_WARN "Sound Blaster: Unexpected interrupt\n"); */ + ; + } + } + /* + * Acknowledge interrupts + */ + + if (src & 0x01) + status = inb(DSP_DATA_AVAIL); + + if (devc->model == MDL_SB16 && src & 0x02) + status = inb(DSP_DATA_AVL16); +} + +static void pci_intr(sb_devc *devc) +{ + int src = inb(devc->pcibase+0x1A); + src&=3; + if(src) + sb_intr(devc); +} + +static void sbintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + sb_devc *devc = dev_id; + + devc->irq_ok = 1; + + switch (devc->model) { + case MDL_ESSPCI: + pci_intr (devc); + break; + + case MDL_ESS: + ess_intr (devc); + break; + default: + sb_intr (devc); + break; + } +} + +int sb_dsp_reset(sb_devc * devc) +{ + int loopc; + + DEB(printk("Entered sb_dsp_reset()\n")); + + if (devc->model == MDL_ESS) return ess_dsp_reset (devc); + + /* This is only for non-ESS chips */ + + outb(1, DSP_RESET); + + udelay(10); + outb(0, DSP_RESET); + udelay(30); + + for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); + + if (inb(DSP_READ) != 0xAA) + { + DDB(printk("sb: No response to RESET\n")); + return 0; /* Sorry */ + } + + DEB(printk("sb_dsp_reset() OK\n")); + + return 1; +} + +static void dsp_get_vers(sb_devc * devc) +{ + int i; + + unsigned long flags; + + DDB(printk("Entered dsp_get_vers()\n")); + spin_lock_irqsave(&devc->lock, flags); + devc->major = devc->minor = 0; + sb_dsp_command(devc, 0xe1); /* Get version */ + + for (i = 100000; i; i--) + { + if (inb(DSP_DATA_AVAIL) & 0x80) + { + if (devc->major == 0) + devc->major = inb(DSP_READ); + else + { + devc->minor = inb(DSP_READ); + break; + } + } + } + spin_unlock_irqrestore(&devc->lock, flags); + DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor)); +} + +static int sb16_set_dma_hw(sb_devc * devc) +{ + int bits; + + if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3) + { + printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8); + return 0; + } + bits = (1 << devc->dma8); + + if (devc->dma16 >= 5 && devc->dma16 <= 7) + bits |= (1 << devc->dma16); + + sb_setmixer(devc, DMA_NR, bits); + return 1; +} + +static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config) +{ + /* + * This routine initializes new MIDI port setup register of SB Vibra (CT2502). + */ + unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; + + switch (hw_config->io_base) + { + case 0x300: + sb_setmixer(devc, 0x84, bits | 0x04); + break; + + case 0x330: + sb_setmixer(devc, 0x84, bits | 0x00); + break; + + default: + sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ + printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base); + } +} + +static int sb16_set_irq_hw(sb_devc * devc, int level) +{ + int ival; + + switch (level) + { + case 5: + ival = 2; + break; + case 7: + ival = 4; + break; + case 9: + ival = 1; + break; + case 10: + ival = 8; + break; + default: + printk(KERN_ERR "SB16: Invalid IRQ%d\n", level); + return 0; + } + sb_setmixer(devc, IRQ_NR, ival); + return 1; +} + +static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config) +{ + unsigned char bits = 0; + unsigned long flags; + + if (jazz16_base != 0 && jazz16_base != hw_config->io_base) + return; + + switch (hw_config->io_base) + { + case 0x220: + bits = 1; + break; + case 0x240: + bits = 2; + break; + case 0x260: + bits = 3; + break; + default: + return; + } + bits = jazz16_bits = bits << 5; + jazz16_base = hw_config->io_base; + + /* + * Magic wake up sequence by writing to 0x201 (aka Joystick port) + */ + spin_lock_irqsave(&jazz16_lock, flags); + outb((0xAF), 0x201); + outb((0x50), 0x201); + outb((bits), 0x201); + spin_unlock_irqrestore(&jazz16_lock, flags); +} + +static int init_Jazz16(sb_devc * devc, struct address_info *hw_config) +{ + char name[100]; + /* + * First try to check that the card has Jazz16 chip. It identifies itself + * by returning 0x12 as response to DSP command 0xfa. + */ + + if (!sb_dsp_command(devc, 0xfa)) + return 0; + + if (sb_dsp_get_byte(devc) != 0x12) + return 0; + + /* + * OK so far. Now configure the IRQ and DMA channel used by the card. + */ + if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0) + { + printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq); + return 0; + } + if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0) + { + printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma); + return 0; + } + if (hw_config->dma2 < 0) + { + printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n"); + return 0; + } + if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0) + { + printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2); + return 0; + } + devc->dma16 = hw_config->dma2; + + if (!sb_dsp_command(devc, 0xfb)) + return 0; + + if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] | + (jazz_dma_bits[hw_config->dma2] << 4))) + return 0; + + if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq])) + return 0; + + /* + * Now we have configured a standard Jazz16 device. + */ + devc->model = MDL_JAZZ; + strcpy(name, "Jazz16"); + + hw_config->name = "Jazz16"; + devc->caps |= SB_NO_MIDI; + return 1; +} + +static void relocate_ess1688(sb_devc * devc) +{ + unsigned char bits; + + switch (devc->base) + { + case 0x220: + bits = 0x04; + break; + case 0x230: + bits = 0x05; + break; + case 0x240: + bits = 0x06; + break; + case 0x250: + bits = 0x07; + break; + default: + return; /* Wrong port */ + } + + DDB(printk("Doing ESS1688 address selection\n")); + + /* + * ES1688 supports two alternative ways for software address config. + * First try the so called Read-Sequence-Key method. + */ + + /* Reset the sequence logic */ + inb(0x229); + inb(0x229); + inb(0x229); + + /* Perform the read sequence */ + inb(0x22b); + inb(0x229); + inb(0x22b); + inb(0x229); + inb(0x229); + inb(0x22b); + inb(0x229); + + /* Select the base address by reading from it. Then probe using the port. */ + inb(devc->base); + if (sb_dsp_reset(devc)) /* Bingo */ + return; + +#if 0 /* This causes system lockups (Nokia 386/25 at least) */ + /* + * The last resort is the system control register method. + */ + + outb((0x00), 0xfb); /* 0xFB is the unlock register */ + outb((0x00), 0xe0); /* Select index 0 */ + outb((bits), 0xe1); /* Write the config bits */ + outb((0x00), 0xf9); /* 0xFB is the lock register */ +#endif +} + +int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo) +{ + sb_devc sb_info; + sb_devc *devc = &sb_info; + + memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */ + + /* Copy module options in place */ + if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options)); + + sb_info.my_mididev = -1; + sb_info.my_mixerdev = -1; + sb_info.dev = -1; + + /* + * Initialize variables + */ + + DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base)); + if (check_region(hw_config->io_base, 16)) + { +#ifdef MODULE + printk(KERN_INFO "sb: I/O region in use.\n"); +#endif + return 0; + } + + devc->lock = SPIN_LOCK_UNLOCKED; + devc->type = hw_config->card_subtype; + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma8 = hw_config->dma; + + devc->dma16 = -1; + devc->pcibase = pciio; + + if(pci == SB_PCI_ESSMAESTRO) + { + devc->model = MDL_ESSPCI; + devc->caps |= SB_PCI_IRQ; + hw_config->driver_use_1 |= SB_PCI_IRQ; + hw_config->card_subtype = MDL_ESSPCI; + } + + if(pci == SB_PCI_YAMAHA) + { + devc->model = MDL_YMPCI; + devc->caps |= SB_PCI_IRQ; + hw_config->driver_use_1 |= SB_PCI_IRQ; + hw_config->card_subtype = MDL_YMPCI; + + printk("Yamaha PCI mode.\n"); + } + + if (devc->sbmo.acer) + { + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x00); + spin_unlock_irqrestore(&devc->lock, flags); + } + /* + * Detect the device + */ + + if (sb_dsp_reset(devc)) + dsp_get_vers(devc); + else + devc->major = 0; + + if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW) + if (devc->major == 0 || (devc->major == 3 && devc->minor == 1)) + relocate_Jazz16(devc, hw_config); + + if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0)) + relocate_ess1688(devc); + + if (!sb_dsp_reset(devc)) + { + DDB(printk("SB reset failed\n")); +#ifdef MODULE + printk(KERN_INFO "sb: dsp reset failed.\n"); +#endif + return 0; + } + if (devc->major == 0) + dsp_get_vers(devc); + + if (devc->major == 3 && devc->minor == 1) + { + if (devc->type == MDL_AZTECH) /* SG Washington? */ + { + if (sb_dsp_command(devc, 0x09)) + if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */ + { + int i; + + /* Have some delay */ + for (i = 0; i < 10000; i++) + inb(DSP_DATA_AVAIL); + devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */ + devc->model = MDL_AZTECH; + } + } + } + + if(devc->type == MDL_ESSPCI) + devc->model = MDL_ESSPCI; + + if(devc->type == MDL_YMPCI) + { + printk("YMPCI selected\n"); + devc->model = MDL_YMPCI; + } + + /* + * Save device information for sb_dsp_init() + */ + + + detected_devc = (sb_devc *)kmalloc(sizeof(sb_devc), GFP_KERNEL); + if (detected_devc == NULL) + { + printk(KERN_ERR "sb: Can't allocate memory for device information\n"); + return 0; + } + memcpy(detected_devc, devc, sizeof(sb_devc)); + MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base)); + return 1; +} + +int sb_dsp_init(struct address_info *hw_config, struct module *owner) +{ + sb_devc *devc; + char name[100]; + extern int sb_be_quiet; + int mixer22, mixer30; + +/* + * Check if we had detected a SB device earlier + */ + DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base)); + name[0] = 0; + + if (detected_devc == NULL) + { + MDB(printk("No detected device\n")); + return 0; + } + devc = detected_devc; + detected_devc = NULL; + + if (devc->base != hw_config->io_base) + { + DDB(printk("I/O port mismatch\n")); + return 0; + } + /* + * Now continue initialization of the device + */ + + devc->caps = hw_config->driver_use_1; + + if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0) + { /* IRQ setup */ + + /* + * ESS PCI cards do shared PCI IRQ stuff. Since they + * will get shared PCI irq lines we must cope. + */ + + int i=(devc->caps&SB_PCI_IRQ)?SA_SHIRQ:0; + + if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0) + { + printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); + return 0; + } + devc->irq_ok = 0; + + if (devc->major == 4) + if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ + { + free_irq(devc->irq, devc); + return 0; + } + if ((devc->type == 0 || devc->type == MDL_ESS) && + devc->major == 3 && devc->minor == 1) + { /* Handle various chipsets which claim they are SB Pro compatible */ + if ((devc->type != 0 && devc->type != MDL_ESS) || + !ess_init(devc, hw_config)) + { + if ((devc->type != 0 && devc->type != MDL_JAZZ && + devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config)) + { + DDB(printk("This is a genuine SB Pro\n")); + } + } + } + if (devc->major == 4 && devc->minor <= 11 ) /* Won't work */ + devc->irq_ok = 1; + else + { + int n; + + for (n = 0; n < 3 && devc->irq_ok == 0; n++) + { + if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */ + { + int i; + + for (i = 0; !devc->irq_ok && i < 10000; i++); + } + } + if (!devc->irq_ok) + printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq); + else + { + DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq)); + } + } + } /* IRQ setup */ + request_region(hw_config->io_base, 16, "soundblaster"); + + last_sb = devc; + + switch (devc->major) + { + case 1: /* SB 1.0 or 1.5 */ + devc->model = hw_config->card_subtype = MDL_SB1; + break; + + case 2: /* SB 2.x */ + if (devc->minor == 0) + devc->model = hw_config->card_subtype = MDL_SB2; + else + devc->model = hw_config->card_subtype = MDL_SB201; + break; + + case 3: /* SB Pro and most clones */ + switch (devc->model) { + case 0: + devc->model = hw_config->card_subtype = MDL_SBPRO; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster Pro (8 BIT ONLY)"; + break; + case MDL_ESS: + ess_dsp_init(devc, hw_config); + break; + } + break; + + case 4: + devc->model = hw_config->card_subtype = MDL_SB16; + /* + * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0 + * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas + * a "standard" SB16 doesn't have a register at 0x4c. ALS100 actively + * updates register 0x22 whenever 0x30 changes, as per the SB16 spec. + * Since ALS007 doesn't, this can be used to differentiate the 2 cards. + */ + if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) + { + mixer30 = sb_getmixer(devc,0x30); + sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f); + sb_setmixer(devc,0x30,0xff); + /* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */ + /* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10. */ + if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) + { + devc->submodel = SUBMDL_ALS100; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16 (ALS-100)"; + } + else + { + sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */ + sb_setmixer(devc,0x4c,0x1f); + sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */ + devc->submodel = SUBMDL_ALS007; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16 (ALS-007)"; + } + sb_setmixer(devc,0x30,mixer30); + } + else if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16"; + + if (hw_config->dma2 == -1) + devc->dma16 = devc->dma8; + else if (hw_config->dma2 < 5 || hw_config->dma2 > 7) + { + printk(KERN_WARNING "SB16: Bad or missing 16 bit DMA channel\n"); + devc->dma16 = devc->dma8; + } + else + devc->dma16 = hw_config->dma2; + + if(!sb16_set_dma_hw(devc)) { + free_irq(devc->irq, devc); + release_region(hw_config->io_base, 16); + return 0; + } + + devc->caps |= SB_NO_MIDI; + } + + if (!(devc->caps & SB_NO_MIXER)) + if (devc->major == 3 || devc->major == 4) + sb_mixer_init(devc, owner); + + if (!(devc->caps & SB_NO_MIDI)) + sb_dsp_midi_init(devc, owner); + + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)"; + + sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor); + conf_printf(name, hw_config); + + /* + * Assuming that a sound card is Sound Blaster (compatible) is the most common + * configuration error and the mother of all problems. Usually sound cards + * emulate SB Pro but in addition they have a 16 bit native mode which should be + * used in Unix. See Readme.cards for more information about configuring OSS/Free + * properly. + */ + if (devc->model <= MDL_SBPRO) + { + if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */ + { + printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n"); + printk(KERN_INFO "In many cases there is another way to configure OSS so that\n"); + printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n"); + printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n"); + } + else if (!sb_be_quiet && devc->model == MDL_SBPRO) + { + printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor); + printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n"); + printk(KERN_INFO "is incorrectly configured.\n"); + } + } + hw_config->card_subtype = devc->model; + hw_config->slots[0]=devc->dev; + last_devc = devc; /* For SB MPU detection */ + + if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0) + { + if (sound_alloc_dma(devc->dma8, "SoundBlaster8")) + { + printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8); + } + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + { + if (sound_alloc_dma(devc->dma16, "SoundBlaster16")) + printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16); + } + sb_audio_init(devc, name, owner); + hw_config->slots[0]=devc->dev; + } + else + { + MDB(printk("Sound Blaster: no audio devices found.\n")); + } + return 1; +} + +void sb_dsp_disable_midi(int io_base) +{ +} + +void sb_dsp_disable_recording(int io_base) +{ +} + +/* if (sbmpu) below we allow mpu401 to manage the midi devs + otherwise we have to unload them. (Andrzej Krzysztofowicz) */ + +void sb_dsp_unload(struct address_info *hw_config, int sbmpu) +{ + sb_devc *devc; + + devc = audio_devs[hw_config->slots[0]]->devc; + + if (devc && devc->base == hw_config->io_base) + { + if ((devc->model & MDL_ESS) && devc->pcibase) + release_region(devc->pcibase, 8); + + release_region(devc->base, 16); + + if (!(devc->caps & SB_NO_AUDIO)) + { + sound_free_dma(devc->dma8); + if (devc->dma16 >= 0) + sound_free_dma(devc->dma16); + } + if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI)) + { + if (devc->irq > 0) + free_irq(devc->irq, devc); + + sb_mixer_unload(devc); + /* We don't have to do this bit any more the UART401 is its own + master -- Krzysztof Halasa */ + /* But we have to do it, if UART401 is not detected */ + if (!sbmpu) + sound_unload_mididev(devc->my_mididev); + sound_unload_audiodev(devc->dev); + } + kfree(devc); + } + else + release_region(hw_config->io_base, 16); + if(detected_devc) + kfree(detected_devc); +} + +/* + * Mixer access routines + * + * ES1887 modifications: some mixer registers reside in the + * range above 0xa0. These must be accessed in another way. + */ + +void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value) +{ + unsigned long flags; + + if (devc->model == MDL_ESS) return ess_setmixer (devc, port, value); + + spin_lock_irqsave(&devc->lock, flags); + + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + udelay(20); + outb(((unsigned char) (value & 0xff)), MIXER_DATA); + udelay(20); + + spin_unlock_irqrestore(&devc->lock, flags); +} + +unsigned int sb_getmixer(sb_devc * devc, unsigned int port) +{ + unsigned int val; + unsigned long flags; + + if (devc->model == MDL_ESS) return ess_getmixer (devc, port); + + spin_lock_irqsave(&devc->lock, flags); + + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + udelay(20); + val = inb(MIXER_DATA); + udelay(20); + + spin_unlock_irqrestore(&devc->lock, flags); + + return val; +} + +void sb_chgmixer + (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) +{ + int value; + + value = sb_getmixer(devc, reg); + value = (value & ~mask) | (val & mask); + sb_setmixer(devc, reg, value); +} + +/* + * MPU401 MIDI initialization. + */ + +static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ + + outb((addr & 0xff), base + 1); /* Low address bits */ + outb((addr >> 8), base + 2); /* High address bits */ + outb((val), base); /* Data */ + + spin_unlock_irqrestore(&jazz16_lock, flags); +} + +static unsigned char smw_getmem(sb_devc * devc, int base, int addr) +{ + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ + + outb((addr & 0xff), base + 1); /* Low address bits */ + outb((addr >> 8), base + 2); /* High address bits */ + val = inb(base); /* Data */ + + spin_unlock_irqrestore(&jazz16_lock, flags); + return val; +} + +static int smw_midi_init(sb_devc * devc, struct address_info *hw_config) +{ + int mpu_base = hw_config->io_base; + int mp_base = mpu_base + 4; /* Microcontroller base */ + int i; + unsigned char control; + + + /* + * Reset the microcontroller so that the RAM can be accessed + */ + + control = inb(mpu_base + 7); + outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */ + outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */ + + mdelay(3); /* Wait at least 1ms */ + + outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */ + + /* + * Detect microcontroller by probing the 8k RAM area + */ + smw_putmem(devc, mp_base, 0, 0x00); + smw_putmem(devc, mp_base, 1, 0xff); + udelay(10); + + if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff) + { + DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1))); + return 0; /* No RAM */ + } + /* + * There is RAM so assume it's really a SM Wave + */ + + devc->model = MDL_SMW; + smw_mixer_init(devc); + +#ifdef MODULE + if (!smw_ucode) + { + smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode); + smw_free = smw_ucode; + } +#endif + if (smw_ucodeLen > 0) + { + if (smw_ucodeLen != 8192) + { + printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n"); + return 1; + } + /* + * Download microcode + */ + + for (i = 0; i < 8192; i++) + smw_putmem(devc, mp_base, i, smw_ucode[i]); + + /* + * Verify microcode + */ + + for (i = 0; i < 8192; i++) + if (smw_getmem(devc, mp_base, i) != smw_ucode[i]) + { + printk(KERN_ERR "SM Wave: Microcode verification failed\n"); + return 0; + } + } + control = 0; +#ifdef SMW_SCSI_IRQ + /* + * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt + * is disabled by default. + * + * FIXME - make this a module option + * + * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10. + */ + { + static unsigned char scsi_irq_bits[] = { + 0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0 + }; + control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; + } +#endif + +#ifdef SMW_OPL4_ENABLE + /* + * Make the OPL4 chip visible on the PC bus at 0x380. + * + * There is no need to enable this feature since this driver + * doesn't support OPL4 yet. Also there is no RAM in SM Wave so + * enabling OPL4 is pretty useless. + */ + control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ + /* control |= 0x20; Uncomment this if you want to use IRQ7 */ +#endif + outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */ + hw_config->name = "SoundMan Wave"; + return 1; +} + +static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config) +{ + int mpu_base = hw_config->io_base; + int sb_base = devc->base; + int irq = hw_config->irq; + + unsigned char bits = 0; + unsigned long flags; + + if (irq < 0) + irq *= -1; + + if (irq < 1 || irq > 15 || + jazz_irq_bits[irq] == 0) + { + printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq); + return 0; + } + switch (sb_base) + { + case 0x220: + bits = 1; + break; + case 0x240: + bits = 2; + break; + case 0x260: + bits = 3; + break; + default: + return 0; + } + bits = jazz16_bits = bits << 5; + switch (mpu_base) + { + case 0x310: + bits |= 1; + break; + case 0x320: + bits |= 2; + break; + case 0x330: + bits |= 3; + break; + default: + printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base); + return 0; + } + /* + * Magic wake up sequence by writing to 0x201 (aka Joystick port) + */ + spin_lock_irqsave(&jazz16_lock, flags); + outb(0xAF, 0x201); + outb(0x50, 0x201); + outb(bits, 0x201); + spin_unlock_irqrestore(&jazz16_lock, flags); + + hw_config->name = "Jazz16"; + smw_midi_init(devc, hw_config); + + if (!sb_dsp_command(devc, 0xfb)) + return 0; + + if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] | + (jazz_dma_bits[devc->dma16] << 4))) + return 0; + + if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] | + (jazz_irq_bits[irq] << 4))) + return 0; + + return 1; +} + +int probe_sbmpu(struct address_info *hw_config, struct module *owner) +{ + sb_devc *devc = last_devc; + int ret; + + if (last_devc == NULL) + return 0; + + last_devc = 0; + + if (hw_config->io_base <= 0) + { + /* The real vibra16 is fine about this, but we have to go + wipe up after Cyrix again */ + + if(devc->model == MDL_SB16 && devc->minor >= 12) + { + unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; + sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ + } + return 0; + } + +#if defined(CONFIG_SOUND_ALSA_MPU401) + if (devc->model == MDL_ESS) + { + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + if (!ess_midi_init(devc, hw_config)) + return 0; + hw_config->name = "ESS1xxx MPU"; + devc->midi_irq_cookie = NULL; + if (!probe_mpu401(hw_config)) + return 0; + attach_mpu401(hw_config, owner); + if (last_sb->irq == -hw_config->irq) + last_sb->midi_irq_cookie=(void *)hw_config->slots[1]; + return 1; + } +#endif + + switch (devc->model) + { + case MDL_SB16: + if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330) + { + printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base); + return 0; + } + hw_config->name = "Sound Blaster 16"; + if (hw_config->irq < 3 || hw_config->irq == devc->irq) + hw_config->irq = -devc->irq; + if (devc->minor > 12) /* What is Vibra's version??? */ + sb16_set_mpu_port(devc, hw_config); + break; + + case MDL_JAZZ: + if (hw_config->irq < 3 || hw_config->irq == devc->irq) + hw_config->irq = -devc->irq; + if (!init_Jazz16_midi(devc, hw_config)) + return 0; + break; + + case MDL_YMPCI: + hw_config->name = "Yamaha PCI Legacy"; + printk("Yamaha PCI legacy UART401 check.\n"); + break; + default: + return 0; + } + + ret = probe_uart401(hw_config, owner); + if (ret) + last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc; + return ret; +} + +void unload_sbmpu(struct address_info *hw_config) +{ +#if defined(CONFIG_SOUND_ALSA_MPU401) + if (!strcmp (hw_config->name, "ESS1xxx MPU")) { + unload_mpu401(hw_config); + return; + } +#endif + unload_uart401(hw_config); +} + +EXPORT_SYMBOL(sb_dsp_init); +EXPORT_SYMBOL(sb_dsp_detect); +EXPORT_SYMBOL(sb_dsp_unload); +EXPORT_SYMBOL(sb_dsp_disable_midi); +EXPORT_SYMBOL(sb_be_quiet); +EXPORT_SYMBOL(probe_sbmpu); +EXPORT_SYMBOL(unload_sbmpu); +EXPORT_SYMBOL(smw_free); +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/sb_ess.c linux-2.4.19-pre5-mjc/sound/oss/sb_ess.c --- linux/sound/oss/sb_ess.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_ess.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1826 @@ +#undef FKS_LOGGING +#undef FKS_TEST + +/* + * tabs should be 4 spaces, in vi(m): set tabstop=4 + * + * TODO: consistency speed calculations!! + * cleanup! + * ????: Did I break MIDI support? + * + * History: + * + * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per + * fokkensr@vertis.nl input basis. + * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, + * ES1868, ES1869 and ES1878. Could be used for + * specific handling in the future. All except + * ES1887 and ES1888 and ES688 are handled like + * ES1688. + * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now + * have the "Dec 20" support + RECLEV + * (Jan 2 1999): Preparation for Full Duplex. This means + * Audio 2 is now used for playback when dma16 + * is specified. The next step would be to use + * Audio 1 and Audio 2 at the same time. + * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this + * includes both the ESS stuff that has been in + * sb_*[ch] before I touched it and the ESS support + * I added later + * (Jan 23 1999): Full Duplex seems to work. I wrote a small + * test proggy which works OK. Haven't found + * any applications to test it though. So why did + * I bother to create it anyway?? :) Just for + * fun. + * (May 2 1999): I tried to be too smart by "introducing" + * ess_calc_best_speed (). The idea was that two + * dividers could be used to setup a samplerate, + * ess_calc_best_speed () would choose the best. + * This works for playback, but results in + * recording problems for high samplerates. I + * fixed this by removing ess_calc_best_speed () + * and just doing what the documentation says. + * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback + * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888. + * 1879's were previously ignored by this driver; + * added (untested) support for those. + * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for + * zezo@inet.bg _ALL_ ESS models, not only ES1887 + * + * This files contains ESS chip specifics. It's based on the existing ESS + * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This + * file adds features like: + * - Chip Identification (as shown in /proc/sound) + * - RECLEV support for ES1688 and later + * - 6 bits playback level support chips later than ES1688 + * - Recording level support on a per-device basis for ES1887 + * - Full-Duplex for ES1887 + * + * Full duplex is enabled by specifying dma16. While the normal dma must + * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit + * DMA channel, while the others are 8 bit.. + * + * ESS detection isn't full proof (yet). If it fails an additional module + * parameter esstype can be specified to be one of the following: + * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888 + * -1 means: mimic 2.0 behaviour, + * 0 means: auto detect. + * others: explicitly specify chip + * -1 is default, cause auto detect still doesn't work. + */ + +/* + * About the documentation + * + * I don't know if the chips all are OK, but the documentation is buggy. 'cause + * I don't have all the cips myself, there's a lot I cannot verify. I'll try to + * keep track of my latest insights about his here. If you have additional info, + * please enlighten me (fokkensr@vertis.nl)! + * + * I had the impression that ES1688 also has 6 bit master volume control. The + * documentation about ES1888 (rev C, october '95) claims that ES1888 has + * the following features ES1688 doesn't have: + * - 6 bit master volume + * - Full Duplex + * So ES1688 apparently doesn't have 6 bit master volume control, but the + * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too? + * Without RECLEV ES688 won't be much fun I guess. + * + * From the ES1888 (rev C, october '95) documentation I got the impression + * that registers 0x68 to 0x6e don't exist which means: no recording volume + * controls. To my surprise the ES888 documentation (1/14/96) claims that + * ES888 does have these record mixer registers, but that ES1888 doesn't have + * 0x69 and 0x6b. So the rest should be there. + * + * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2 + * is both record and playback. I think I should use Audio 2 for all playback. + * + * The documentation is an adventure: it's close but not fully accurate. I + * found out that after a reset some registers are *NOT* reset, though the + * docs say the would be. Interresting ones are 0x7f, 0x7d and 0x7a. They are + * related to the Audio 2 channel. I also was suprised about the consequenses + * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves + * into ES1888 mode. This means that it claims IRQ 11, which happens to be my + * ISDN adapter. Needless to say it no longer worked. I now understand why + * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS + * did it. + * + * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed + * as if it's exactly the same as register 0xa1. This is *NOT* true. The + * description of 0x70 in ES1869 docs is accurate however. + * Well, the assumption about ES1869 was wrong: register 0x70 is very much + * like register 0xa1, except that bit 7 is allways 1, whatever you want + * it to be. + * + * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2 + * has effect. + * + * Software reset not being able to reset all registers is great! Especially + * the fact that register 0x78 isn't reset is great when you wanna change back + * to single dma operation (simplex): audio 2 is still operation, and uses the + * same dma as audio 1: your ess changes into a funny echo machine. + * + * Received the new that ES1688 is detected as a ES1788. Did some thinking: + * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register + * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If + * can be modified, it's a 1688", which lead to a correct detection + * of my ES1887. It resulted however in bad detection of 1688 (reported by mail) + * and 1868 (if no PnP detection first): they result in a 1788 being detected. + * I don't have docs on 1688, but I do have docs on 1868: The documentation is + * probably inaccurate in the fact that I should check bit 2, not bit 3. This + * is what I do now. + */ + +/* + * About recognition of ESS chips + * + * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in + * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but + * during detection the text claims that "this chip may be ..." when a step + * fails. This scheme is used to distinct between the above chips. + * It appears however that some PnP chips like ES1868 are recognized as ES1788 + * by the ES1887 detection scheme. These PnP chips can be detected in another + * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think) + * by repeatedly reading mixer register 0x40. This is done by ess_identify in + * sb_common.c. + * This results in the following detection steps: + * - distinct between ES688 and ES1688+ (as always done in this driver) + * if ES688 we're ready + * - try to detect ES1868, ES1869 or ES1878 + * if successful we're ready + * - try to detect ES1888, ES1887 or ES1788 + * if successful we're ready + * - Dunno. Must be 1688. Will do in general + * + * About RECLEV support: + * + * The existing ES1688 support didn't take care of the ES1688+ recording + * levels very well. Whenever a device was selected (recmask) for recording + * it's recording level was loud, and it couldn't be changed. The fact that + * internal register 0xb4 could take care of RECLEV, didn't work meaning until + * it's value was restored every time the chip was reset; this reset the + * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with. + * + * About ES1887 support: + * + * The ES1887 has separate registers to control the recording levels, for all + * inputs. The ES1887 specific software makes these levels the same as their + * corresponding playback levels, unless recmask says they aren't recorded. In + * the latter case the recording volumes are 0. + * Now recording levels of inputs can be controlled, by changing the playback + * levels. Futhermore several devices can be recorded together (which is not + * possible with the ES1688. + * Besides the separate recording level control for each input, the common + * recordig level can also be controlled by RECLEV as described above. + * + * Not only ES1887 have this recording mixer. I know the following from the + * documentation: + * ES688 no + * ES1688 no + * ES1868 no + * ES1869 yes + * ES1878 no + * ES1879 yes + * ES1888 no/yes Contradicting documentation; most recent: yes + * ES1946 yes This is a PCI chip; not handled by this driver + */ + +#include +#include + +#include "sound_config.h" +#include "sb_mixer.h" +#include "sb.h" + +#include "sb_ess.h" + +#define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */ +#define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */ + +#define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */ +#define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */ +#define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */ +#define SUBMDL_ES1878 0x13 /* Subtype ES1878 for specific handling */ +#define SUBMDL_ES1879 0x16 /* ES1879 was initially forgotten */ +#define SUBMDL_ES1887 0x14 /* Subtype ES1887 for specific handling */ +#define SUBMDL_ES1888 0x15 /* Subtype ES1888 for specific handling */ + +#define SB_CAP_ES18XX_RATE 0x100 + +#define ES1688_CLOCK1 795444 /* 128 - div */ +#define ES1688_CLOCK2 397722 /* 256 - div */ +#define ES18XX_CLOCK1 793800 /* 128 - div */ +#define ES18XX_CLOCK2 768000 /* 256 - div */ + +#ifdef FKS_LOGGING +static void ess_show_mixerregs (sb_devc *devc); +#endif +static int ess_read (sb_devc * devc, unsigned char reg); +static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data); +static void ess_chgmixer + (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); + +/**************************************************************************** + * * + * ESS audio * + * * + ****************************************************************************/ + +struct ess_command {short cmd; short data;}; + +/* + * Commands for initializing Audio 1 for input (record) + */ +static struct ess_command ess_i08m[] = /* input 8 bit mono */ + { {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; +static struct ess_command ess_i16m[] = /* input 16 bit mono */ + { {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; +static struct ess_command ess_i08s[] = /* input 8 bit stereo */ + { {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; +static struct ess_command ess_i16s[] = /* input 16 bit stereo */ + { {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; + +static struct ess_command *ess_inp_cmds[] = + { ess_i08m, ess_i16m, ess_i08s, ess_i16s }; + + +/* + * Commands for initializing Audio 1 for output (playback) + */ +static struct ess_command ess_o08m[] = /* output 8 bit mono */ + { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; +static struct ess_command ess_o16m[] = /* output 16 bit mono */ + { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; +static struct ess_command ess_o08s[] = /* output 8 bit stereo */ + { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; +static struct ess_command ess_o16s[] = /* output 16 bit stereo */ + { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; + +static struct ess_command *ess_out_cmds[] = + { ess_o08m, ess_o16m, ess_o08s, ess_o16s }; + +static void ess_exec_commands + (sb_devc *devc, struct ess_command *cmdtab[]) +{ + struct ess_command *cmd; + + cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ]; + + while (cmd->cmd != -1) { + ess_write (devc, cmd->cmd, cmd->data); + cmd++; + } +} + +static void ess_change + (sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val) +{ + int value; + + value = ess_read (devc, reg); + value = (value & ~mask) | (val & mask); + ess_write (devc, reg, value); +} + +static void ess_set_output_parms + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (devc->duplex) { + devc->trg_buf_16 = buf; + devc->trg_bytes_16 = nr_bytes; + devc->trg_intrflag_16 = intrflag; + devc->irq_mode_16 = IMODE_OUTPUT; + } else { + devc->trg_buf = buf; + devc->trg_bytes = nr_bytes; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_OUTPUT; + } +} + +static void ess_set_input_parms + (int dev, unsigned long buf, int count, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + devc->trg_buf = buf; + devc->trg_bytes = count; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_INPUT; +} + +static int ess_calc_div (int clock, int revert, int *speedp, int *diffp) +{ + int divider; + int speed, diff; + int retval; + + speed = *speedp; + divider = (clock + speed / 2) / speed; + retval = revert - divider; + if (retval > revert - 1) { + retval = revert - 1; + divider = revert - retval; + } + /* This line is suggested. Must be wrong I think + *speedp = (clock + divider / 2) / divider; + So I chose the next one */ + + *speedp = clock / divider; + diff = speed - *speedp; + if (diff < 0) diff =-diff; + *diffp = diff; + + return retval; +} + +static int ess_calc_best_speed + (int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp) +{ + int speed1 = *speedp, speed2 = *speedp; + int div1, div2; + int diff1, diff2; + int retval; + + div1 = ess_calc_div (clock1, rev1, &speed1, &diff1); + div2 = ess_calc_div (clock2, rev2, &speed2, &diff2); + + if (diff1 < diff2) { + *divp = div1; + *speedp = speed1; + retval = 1; + } else { + /* *divp = div2; */ + *divp = 0x80 | div2; + *speedp = speed2; + retval = 2; + } + + return retval; +} + +/* + * Depending on the audiochannel ESS devices can + * have different clock settings. These are made consistent for duplex + * however. + * callers of ess_speed only do an audionum suggestion, which means + * input suggests 1, output suggests 2. This suggestion is only true + * however when doing duplex. + */ +static void ess_common_speed (sb_devc *devc, int *speedp, int *divp) +{ + int diff = 0, div; + + if (devc->duplex) { + /* + * The 0x80 is important for the first audio channel + */ + if (devc->submodel == SUBMDL_ES1888) { + div = 0x80 | ess_calc_div (795500, 256, speedp, &diff); + } else { + div = 0x80 | ess_calc_div (795500, 128, speedp, &diff); + } + } else if(devc->caps & SB_CAP_ES18XX_RATE) { + if (devc->submodel == SUBMDL_ES1888) { + ess_calc_best_speed(397700, 128, 795500, 256, + &div, speedp); + } else { + ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, + &div, speedp); + } + } else { + if (*speedp > 22000) { + div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff); + } else { + div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff); + } + } + *divp = div; +} + +static void ess_speed (sb_devc *devc, int audionum) +{ + int speed; + int div, div2; + + ess_common_speed (devc, &(devc->speed), &div); + +#ifdef FKS_REG_LOGGING +printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div); +#endif + + /* Set filter roll-off to 90% of speed/2 */ + speed = (devc->speed * 9) / 20; + + div2 = 256 - 7160000 / (speed * 82); + + if (!devc->duplex) audionum = 1; + + if (audionum == 1) { + /* Change behaviour of register A1 * + sb_chg_mixer(devc, 0x71, 0x20, 0x20) + * For ES1869 only??? */ + ess_write (devc, 0xa1, div); + ess_write (devc, 0xa2, div2); + } else { + ess_setmixer (devc, 0x70, div); + /* + * FKS: fascinating: 0x72 doesn't seem to work. + */ + ess_write (devc, 0xa2, div2); + ess_setmixer (devc, 0x72, div2); + } +} + +static int ess_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + ess_speed(devc, 1); + + sb_dsp_command(devc, DSP_CMD_SPKOFF); + + ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */ + ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ + ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ + + ess_exec_commands (devc, ess_inp_cmds); + + ess_change (devc, 0xb1, 0xf0, 0x50); + ess_change (devc, 0xb2, 0xf0, 0x50); + + devc->trigger_bits = 0; + return 0; +} + +static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + sb_dsp_reset(devc); + ess_speed(devc, 1); + ess_write (devc, 0xb8, 4); /* Auto init DMA mode */ + ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ + ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */ + + ess_exec_commands (devc, ess_out_cmds); + + ess_change (devc, 0xb1, 0xf0, 0x50); /* Enable DMA */ + ess_change (devc, 0xb2, 0xf0, 0x50); /* Enable IRQ */ + + sb_dsp_command(devc, DSP_CMD_SPKON); /* There be sound! */ + + devc->trigger_bits = 0; + return 0; +} + +static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + unsigned char bits; + +/* FKS: qqq + sb_dsp_reset(devc); +*/ + + /* + * Auto-Initialize: + * DMA mode + demand mode (8 bytes/request, yes I want it all!) + * But leave 16-bit DMA bit untouched! + */ + ess_chgmixer (devc, 0x78, 0xd0, 0xd0); + + ess_speed(devc, 2); + + /* bits 4:3 on ES1887 represent recording source. Keep them! */ + bits = ess_getmixer (devc, 0x7a) & 0x18; + + /* Set stereo/mono */ + if (devc->channels != 1) bits |= 0x02; + + /* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */ + if (devc->bits != AFMT_U8) bits |= 0x05; /* 16 bit */ + + /* Enable DMA, IRQ will be shared (hopefully)*/ + bits |= 0x60; + + ess_setmixer (devc, 0x7a, bits); + + ess_mixer_reload (devc, SOUND_MIXER_PCM); /* There be sound! */ + + devc->trigger_bits = 0; + return 0; +} + +static int ess_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n" +, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma); +#endif + + if (devc->duplex) { + return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount); + } else { + return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount); + } +} + +static void ess_audio_halt_xfer(int dev) +{ + unsigned long flags; + sb_devc *devc = audio_devs[dev]->devc; + + spin_lock_irqsave(&devc->lock, flags); + sb_dsp_reset(devc); + spin_unlock_irqrestore(&devc->lock, flags); + + /* + * Audio 2 may still be operational! Creates awful sounds! + */ + if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00); +} + +static void ess_audio_start_input + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1; + count--; + + devc->irq_mode = IMODE_INPUT; + + ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); + ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); + + ess_change (devc, 0xb8, 0x0f, 0x0f); /* Go */ + devc->intr_active = 1; +} + +static void ess_audio_output_block_audio1 + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); + ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); + + ess_change (devc, 0xb8, 0x05, 0x05); /* Go */ + devc->intr_active = 1; +} + +static void ess_audio_output_block_audio2 + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; + + if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1; + count--; + + ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff)); + ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff)); + ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */ + + devc->irq_mode_16 = IMODE_OUTPUT; + devc->intr_active_16 = 1; +} + +static void ess_audio_output_block + (int dev, unsigned long buf, int nr_bytes, int intrflag) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (devc->duplex) { + ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag); + } else { + ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag); + } +} + +/* + * FKS: the if-statements for both bits and bits_16 are quite alike. + * Combine this... + */ +static void ess_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + int bits_16 = bits & devc->irq_mode_16; + bits &= devc->irq_mode; + + if (!bits && !bits_16) { + /* FKS oh oh.... wrong?? for dma 16? */ + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + } + + if (bits) { + switch (devc->irq_mode) + { + case IMODE_INPUT: + ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + + if (bits_16) { + switch (devc->irq_mode_16) { + case IMODE_INPUT: + ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + + case IMODE_OUTPUT: + ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16, + devc->trg_intrflag_16); + break; + } + } + + devc->trigger_bits = bits | bits_16; +} + +static int ess_audio_set_speed(int dev, int speed) +{ + sb_devc *devc = audio_devs[dev]->devc; + int minspeed, maxspeed, dummydiv; + + if (speed > 0) { + minspeed = (devc->duplex ? 6215 : 5000 ); + maxspeed = (devc->duplex ? 44100 : 48000); + if (speed < minspeed) speed = minspeed; + if (speed > maxspeed) speed = maxspeed; + + ess_common_speed (devc, &speed, &dummydiv); + + devc->speed = speed; + } + return devc->speed; +} + +/* + * FKS: This is a one-on-one copy of sb1_audio_set_bits + */ +static unsigned int ess_audio_set_bits(int dev, unsigned int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (bits != 0) { + if (bits == AFMT_U8 || bits == AFMT_S16_LE) { + devc->bits = bits; + } else { + devc->bits = AFMT_U8; + } + } + + return devc->bits; +} + +/* + * FKS: This is a one-on-one copy of sbpro_audio_set_channels + * (*) Modified it!! + */ +static short ess_audio_set_channels(int dev, short channels) +{ + sb_devc *devc = audio_devs[dev]->devc; + + if (channels == 1 || channels == 2) devc->channels = channels; + + return devc->channels; +} + +static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */ +{ + owner: THIS_MODULE, + open: sb_audio_open, + close: sb_audio_close, + output_block: ess_set_output_parms, + start_input: ess_set_input_parms, + prepare_for_input: ess_audio_prepare_for_input, + prepare_for_output: ess_audio_prepare_for_output, + halt_io: ess_audio_halt_xfer, + trigger: ess_audio_trigger, + set_speed: ess_audio_set_speed, + set_bits: ess_audio_set_bits, + set_channels: ess_audio_set_channels +}; + +/* + * ess_audio_init must be called from sb_audio_init + */ +struct audio_driver *ess_audio_init + (sb_devc *devc, int *audio_flags, int *format_mask) +{ + *audio_flags = DMA_AUTOMODE; + *format_mask |= AFMT_S16_LE; + + if (devc->duplex) { + int tmp_dma; + /* + * sb_audio_init thinks dma8 is for playback and + * dma16 is for record. Not now! So swap them. + */ + tmp_dma = devc->dma16; + devc->dma16 = devc->dma8; + devc->dma8 = tmp_dma; + + *audio_flags |= DMA_DUPLEX; + } + + return &ess_audio_driver; +} + +/**************************************************************************** + * * + * ESS common * + * * + ****************************************************************************/ +static void ess_handle_channel + (char *channel, int dev, int intr_active, unsigned char flag, int irq_mode) +{ + if (!intr_active || !flag) return; +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode); +#endif + switch (irq_mode) { + case IMODE_OUTPUT: + DMAbuf_outputintr (dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr (dev); + break; + + case IMODE_INIT: + break; + + default:; + /* printk(KERN_WARN "ESS: Unexpected interrupt\n"); */ + } +} + +/* + * FKS: TODO!!! Finish this! + * + * I think midi stuff uses uart401, without interrupts. + * So IMODE_MIDI isn't a value for devc->irq_mode. + */ +void ess_intr (sb_devc *devc) +{ + int status; + unsigned char src; + + if (devc->submodel == SUBMDL_ES1887) { + src = ess_getmixer (devc, 0x7f) >> 4; + } else { + src = 0xff; + } + +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src); +#endif + ess_handle_channel + ( "Audio 1" + , devc->dev, devc->intr_active , src & 0x01, devc->irq_mode ); + ess_handle_channel + ( "Audio 2" + , devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16); + /* + * Acknowledge interrupts + */ + if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) { + ess_chgmixer (devc, 0x7a, 0x80, 0x00); + } + + if (src & 0x01) { + status = inb(DSP_DATA_AVAIL); + } +} + +static void ess_extended (sb_devc * devc) +{ + /* Enable extended mode */ + + sb_dsp_command(devc, 0xc6); +} + +static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data) +{ +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data); +#endif + /* Write a byte to an extended mode register of ES1688 */ + + if (!sb_dsp_command(devc, reg)) + return 0; + + return sb_dsp_command(devc, data); +} + +static int ess_read (sb_devc * devc, unsigned char reg) +{ + /* Read a byte from an extended mode register of ES1688 */ + + /* Read register command */ + if (!sb_dsp_command(devc, 0xc0)) return -1; + + if (!sb_dsp_command(devc, reg )) return -1; + + return sb_dsp_get_byte(devc); +} + +int ess_dsp_reset(sb_devc * devc) +{ + int loopc; + +#ifdef FKS_REG_LOGGING +printk(KERN_INFO "FKS: ess_dsp_reset 1\n"); +ess_show_mixerregs (devc); +#endif + + DEB(printk("Entered ess_dsp_reset()\n")); + + outb(3, DSP_RESET); /* Reset FIFO too */ + + udelay(10); + outb(0, DSP_RESET); + udelay(30); + + for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); + + if (inb(DSP_READ) != 0xAA) { + DDB(printk("sb: No response to RESET\n")); + return 0; /* Sorry */ + } + ess_extended (devc); + + DEB(printk("sb_dsp_reset() OK\n")); + +#ifdef FKS_LOGGING +printk(KERN_INFO "FKS: dsp_reset 2\n"); +ess_show_mixerregs (devc); +#endif + + return 1; +} + +static int ess_irq_bits (int irq) +{ + switch (irq) { + case 2: + case 9: + return 0; + + case 5: + return 1; + + case 7: + return 2; + + case 10: + return 3; + + default: + printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq); + return -1; + } +} + +/* + * Set IRQ configuration register for all ESS models + */ +static int ess_common_set_irq_hw (sb_devc * devc) +{ + int irq_bits; + + if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0; + + if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) { + printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n"); + return 0; + } + return 1; +} + +/* + * I wanna use modern ES1887 mixer irq handling. Funny is the + * fact that my BIOS wants the same. But suppose someone's BIOS + * doesn't do this! + * This is independent of duplex. If there's a 1887 this will + * prevent it from going into 1888 mode. + */ +static void ess_es1887_set_irq_hw (sb_devc * devc) +{ + int irq_bits; + + if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return; + + ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1)); +} + +static int ess_set_irq_hw (sb_devc * devc) +{ + if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc); + + return ess_common_set_irq_hw (devc); +} + +#ifdef FKS_TEST + +/* + * FKS_test: + * for ES1887: 00, 18, non wr bits: 0001 1000 + * for ES1868: 00, b8, non wr bits: 1011 1000 + * for ES1888: 00, f8, non wr bits: 1111 1000 + * for ES1688: 00, f8, non wr bits: 1111 1000 + * + ES968 + */ + +static void FKS_test (sb_devc * devc) +{ + int val1, val2; + val1 = ess_getmixer (devc, 0x64); + ess_setmixer (devc, 0x64, ~val1); + val2 = ess_getmixer (devc, 0x64) ^ ~val1; + ess_setmixer (devc, 0x64, val1); + val1 ^= ess_getmixer (devc, 0x64); +printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff)); +}; +#endif + +static unsigned int ess_identify (sb_devc * devc) +{ + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR); + + udelay(20); + val = inb(MIXER_DATA) << 8; + udelay(20); + val |= inb(MIXER_DATA); + udelay(20); + spin_unlock_irqrestore(&devc->lock, flags); + + return val; +} + +/* + * ESS technology describes a detection scheme in their docs. It involves + * fiddling with the bits in certain mixer registers. ess_probe is supposed + * to help. + * + * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but + * should be written 0 only. Check this. + */ +static int ess_probe (sb_devc * devc, int reg, int xorval) +{ + int val1, val2, val3; + + val1 = ess_getmixer (devc, reg); + val2 = val1 ^ xorval; + ess_setmixer (devc, reg, val2); + val3 = ess_getmixer (devc, reg); + ess_setmixer (devc, reg, val1); + + return (val2 == val3); +} + +int ess_init(sb_devc * devc, struct address_info *hw_config) +{ + unsigned char cfg; + int ess_major = 0, ess_minor = 0; + int i; + static char name[100], modelname[10]; + + /* + * Try to detect ESS chips. + */ + + sb_dsp_command(devc, 0xe7); /* Return identification */ + + for (i = 1000; i; i--) { + if (inb(DSP_DATA_AVAIL) & 0x80) { + if (ess_major == 0) { + ess_major = inb(DSP_READ); + } else { + ess_minor = inb(DSP_READ); + break; + } + } + } + + if (ess_major == 0) return 0; + + if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { + sprintf(name, "ESS ES488 AudioDrive (rev %d)", + ess_minor & 0x0f); + hw_config->name = name; + devc->model = MDL_SBPRO; + return 1; + } + + /* + * This the detection heuristic of ESS technology, though somewhat + * changed to actually make it work. + * This results in the following detection steps: + * - distinct between ES688 and ES1688+ (as always done in this driver) + * if ES688 we're ready + * - try to detect ES1868, ES1869 or ES1878 (ess_identify) + * if successful we're ready + * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887) + * if successful we're ready + * - Dunno. Must be 1688. Will do in general + * + * This is the most BETA part of the software: Will the detection + * always work? + */ + devc->model = MDL_ESS; + devc->submodel = ess_minor & 0x0f; + + if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { + char *chip = NULL; + int submodel = -1; + + switch (devc->sbmo.esstype) { + case ESSTYPE_DETECT: + case ESSTYPE_LIKE20: + break; + case 688: + submodel = 0x00; + break; + case 1688: + submodel = 0x08; + break; + case 1868: + submodel = SUBMDL_ES1868; + break; + case 1869: + submodel = SUBMDL_ES1869; + break; + case 1788: + submodel = SUBMDL_ES1788; + break; + case 1878: + submodel = SUBMDL_ES1878; + break; + case 1879: + submodel = SUBMDL_ES1879; + break; + case 1887: + submodel = SUBMDL_ES1887; + break; + case 1888: + submodel = SUBMDL_ES1888; + break; + default: + printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype); + return 0; + }; + if (submodel != -1) { + devc->submodel = submodel; + sprintf (modelname, "ES%d", devc->sbmo.esstype); + chip = modelname; + }; + if (chip == NULL && (ess_minor & 0x0f) < 8) { + chip = "ES688"; + }; +#ifdef FKS_TEST +FKS_test (devc); +#endif + /* + * If Nothing detected yet, and we want 2.0 behaviour... + * Then let's assume it's ES1688. + */ + if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) { + chip = "ES1688"; + }; + + if (chip == NULL) { + int type; + + type = ess_identify (devc); + + switch (type) { + case 0x1868: + chip = "ES1868"; + devc->submodel = SUBMDL_ES1868; + break; + case 0x1869: + chip = "ES1869"; + devc->submodel = SUBMDL_ES1869; + break; + case 0x1878: + chip = "ES1878"; + devc->submodel = SUBMDL_ES1878; + break; + case 0x1879: + chip = "ES1879"; + devc->submodel = SUBMDL_ES1879; + break; + default: + if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) { + printk ("ess_init: Unrecognized %04x\n", type); + } + }; + }; +#if 0 + /* + * this one failed: + * the probing of bit 4 is another thought: from ES1788 and up, all + * chips seem to have hardware volume control. Bit 4 is readonly to + * check if a hardware volume interrupt has fired. + * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable + * for these chips. + */ + if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) { +#endif + /* + * the probing of bit 2 is my idea. The ES1887 docs want me to probe + * bit 3. This results in ES1688 being detected as ES1788. + * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have + * HardWare Volume, I think they don't have this IRQE. + */ + if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) { + if (ess_probe (devc, 0x70, 0x7f)) { + if (ess_probe (devc, 0x64, (1 << 5))) { + chip = "ES1887"; + devc->submodel = SUBMDL_ES1887; + } else { + chip = "ES1888"; + devc->submodel = SUBMDL_ES1888; + } + } else { + chip = "ES1788"; + devc->submodel = SUBMDL_ES1788; + } + }; + if (chip == NULL) { + chip = "ES1688"; + }; + + printk ( KERN_INFO "ESS chip %s %s%s\n" + , chip + , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20 + ? "detected" + : "specified" + ) + , ( devc->sbmo.esstype == ESSTYPE_LIKE20 + ? " (kernel 2.0 compatible)" + : "" + ) + ); + + sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f); + } else { + strcpy(name, "Jazz16"); + } + + /* AAS: info stolen from ALSA: these boards have different clocks */ + switch(devc->submodel) { +/* APPARENTLY NOT 1869 AND 1887 + case SUBMDL_ES1869: + case SUBMDL_ES1887: +*/ + case SUBMDL_ES1888: + devc->caps |= SB_CAP_ES18XX_RATE; + break; + } + + hw_config->name = name; + /* FKS: sb_dsp_reset to enable extended mode???? */ + sb_dsp_reset(devc); /* Turn on extended mode */ + + /* + * Enable joystick and OPL3 + */ + cfg = ess_getmixer (devc, 0x40); + ess_setmixer (devc, 0x40, cfg | 0x03); + if (devc->submodel >= 8) { /* ES1688 */ + devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */ + } + sb_dsp_reset (devc); + + /* + * This is important! If it's not done, the IRQ probe in sb_dsp_init + * may fail. + */ + return ess_set_irq_hw (devc); +} + +static int ess_set_dma_hw(sb_devc * devc) +{ + unsigned char cfg, dma_bits = 0, dma16_bits; + int dma; + +#ifdef FKS_LOGGING +printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n" +, devc->dma8, devc->dma16, devc->duplex); +#endif + + /* + * FKS: It seems as if this duplex flag isn't set yet. Check it. + */ + dma = devc->dma8; + + if (dma > 3 || dma < 0 || dma == 2) { + dma_bits = 0; + printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma); + return 0; + } else { + /* Extended mode DMA enable */ + cfg = 0x50; + + if (dma == 3) { + dma_bits = 3; + } else { + dma_bits = dma + 1; + } + } + + if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) { + printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n"); + return 0; + } + + if (devc->duplex) { + dma = devc->dma16; + dma16_bits = 0; + + if (dma >= 0) { + switch (dma) { + case 0: + dma_bits = 0x04; + break; + case 1: + dma_bits = 0x05; + break; + case 3: + dma_bits = 0x06; + break; + case 5: + dma_bits = 0x07; + dma16_bits = 0x20; + break; + default: + printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma); + return 0; + }; + ess_chgmixer (devc, 0x78, 0x20, dma16_bits); + ess_chgmixer (devc, 0x7d, 0x07, dma_bits); + } + } + return 1; +} + +/* + * This one is called from sb_dsp_init. + * + * Return values: + * 0: Failed + * 1: Succeeded or doesn't apply (not SUBMDL_ES1887) + */ +int ess_dsp_init (sb_devc *devc, struct address_info *hw_config) +{ + /* + * Caller also checks this, but anyway + */ + if (devc->model != MDL_ESS) { + printk (KERN_INFO "ess_dsp_init for non ESS chip\n"); + return 1; + } + /* + * This for ES1887 to run Full Duplex. Actually ES1888 + * is allowed to do so too. I have no idea yet if this + * will work for ES1888 however. + * + * For SB16 having both dma8 and dma16 means enable + * Full Duplex. Let's try this for ES1887 too + * + */ + if (devc->submodel == SUBMDL_ES1887) { + if (hw_config->dma2 != -1) { + devc->dma16 = hw_config->dma2; + } + /* + * devc->duplex initialization is put here, cause + * ess_set_dma_hw needs it. + */ + if (devc->dma8 != devc->dma16 && devc->dma16 != -1) { + devc->duplex = 1; + } + } + if (!ess_set_dma_hw (devc)) { + free_irq(devc->irq, devc); + return 0; + } + return 1; +} + +/**************************************************************************** + * * + * ESS mixer * + * * + ****************************************************************************/ + +#define ES688_RECORDING_DEVICES \ + ( SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD ) +#define ES688_MIXER_DEVICES \ + ( SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE \ + | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME \ + | SOUND_MASK_LINE2 | SOUND_MASK_SPEAKER ) + +#define ES1688_RECORDING_DEVICES \ + ( ES688_RECORDING_DEVICES ) +#define ES1688_MIXER_DEVICES \ + ( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV ) + +#define ES1887_RECORDING_DEVICES \ + ( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH) +#define ES1887_MIXER_DEVICES \ + ( ES1688_MIXER_DEVICES ) + +/* + * Mixer registers of ES1887 + * + * These registers specifically take care of recording levels. To make the + * mapping from playback devices to recording devices every recording + * devices = playback device + ES_REC_MIXER_RECDIFF + */ +#define ES_REC_MIXER_RECBASE (SOUND_MIXER_LINE3 + 1) +#define ES_REC_MIXER_RECDIFF (ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH) + +#define ES_REC_MIXER_RECSYNTH (SOUND_MIXER_SYNTH + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECPCM (SOUND_MIXER_PCM + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECSPEAKER (SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE (SOUND_MIXER_LINE + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECMIC (SOUND_MIXER_MIC + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECCD (SOUND_MIXER_CD + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECIMIX (SOUND_MIXER_IMIX + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECALTPCM (SOUND_MIXER_ALTPCM + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECRECLEV (SOUND_MIXER_RECLEV + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECIGAIN (SOUND_MIXER_IGAIN + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECOGAIN (SOUND_MIXER_OGAIN + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE1 (SOUND_MIXER_LINE1 + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE2 (SOUND_MIXER_LINE2 + ES_REC_MIXER_RECDIFF) +#define ES_REC_MIXER_RECLINE3 (SOUND_MIXER_LINE3 + ES_REC_MIXER_RECDIFF) + +static mixer_tab es688_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +/* + * The ES1688 specifics... hopefully correct... + * - 6 bit master volume + * I was wrong, ES1888 docs say ES1688 didn't have it. + * - RECLEV control + * These may apply to ES688 too. I have no idea. + */ +static mixer_tab es1688_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +static mixer_tab es1688later_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +/* + * This one is for all ESS chips with a record mixer. + * It's not used (yet) however + */ +static mixer_tab es_rec_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), +MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), +MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), +MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), +MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), +MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +/* + * This one is for ES1887. It's little different from es_rec_mix: it + * has 0x7c for PCM playback level. This is because ES1887 uses + * Audio 2 for playback. + */ +static mixer_tab es1887_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x7c, 7, 4, 0x7c, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), +MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), +MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), +MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), +MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), +MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), +MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), +MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) +}; + +static int ess_has_rec_mixer (int submodel) +{ + switch (submodel) { + case SUBMDL_ES1887: + return 1; + default: + return 0; + }; +}; + +#ifdef FKS_LOGGING +static int ess_mixer_mon_regs[] + = { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f + , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9 + , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9 + , 0x00}; + +static void ess_show_mixerregs (sb_devc *devc) +{ + int *mp = ess_mixer_mon_regs; + +return; + + while (*mp != 0) { + printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp))); + mp++; + } +} +#endif + +void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value) +{ + unsigned long flags; + +#ifdef FKS_LOGGING +printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value); +#endif + + spin_lock_irqsave(&devc->lock, flags); + if (port >= 0xa0) { + ess_write (devc, port, value); + } else { + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + + udelay(20); + outb(((unsigned char) (value & 0xff)), MIXER_DATA); + udelay(20); + }; + spin_unlock_irqrestore(&devc->lock, flags); +} + +unsigned int ess_getmixer (sb_devc * devc, unsigned int port) +{ + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&devc->lock, flags); + + if (port >= 0xa0) { + val = ess_read (devc, port); + } else { + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); + + udelay(20); + val = inb(MIXER_DATA); + udelay(20); + } + spin_unlock_irqrestore(&devc->lock, flags); + + return val; +} + +static void ess_chgmixer + (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) +{ + int value; + + value = ess_getmixer (devc, reg); + value = (value & ~mask) | (val & mask); + ess_setmixer (devc, reg, value); +} + +/* + * ess_mixer_init must be called from sb_mixer_init + */ +void ess_mixer_init (sb_devc * devc) +{ + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + + /* + * Take care of ES1887 specifics... + */ + switch (devc->submodel) { + case SUBMDL_ES1887: + devc->supported_devices = ES1887_MIXER_DEVICES; + devc->supported_rec_devices = ES1887_RECORDING_DEVICES; +#ifdef FKS_LOGGING +printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex); +#endif + if (devc->duplex) { + devc->iomap = &es1887_mix; + } else { + devc->iomap = &es_rec_mix; + } + break; + default: + if (devc->submodel < 8) { + devc->supported_devices = ES688_MIXER_DEVICES; + devc->supported_rec_devices = ES688_RECORDING_DEVICES; + devc->iomap = &es688_mix; + } else { + /* + * es1688 has 4 bits master vol. + * later chips have 6 bits (?) + */ + devc->supported_devices = ES1688_MIXER_DEVICES; + devc->supported_rec_devices = ES1688_RECORDING_DEVICES; + if (devc->submodel < 0x10) { + devc->iomap = &es1688_mix; + } else { + devc->iomap = &es1688later_mix; + } + } + } +} + +/* + * Changing playback levels at an ESS chip with record mixer means having to + * take care of recording levels of recorded inputs (devc->recmask) too! + */ +int ess_mixer_set(sb_devc *devc, int dev, int left, int right) +{ + if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) { + sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right); + } + return sb_common_mixer_set (devc, dev, left, right); +} + +/* + * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After + * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload + * helps. + */ +void ess_mixer_reload (sb_devc *devc, int dev) +{ + int left, right, value; + + value = devc->levels[dev]; + left = value & 0x000000ff; + right = (value & 0x0000ff00) >> 8; + + sb_common_mixer_set(devc, dev, left, right); +} + +int es_rec_set_recmask(sb_devc * devc, int mask) +{ + int i, i_mask, cur_mask, diff_mask; + int value, left, right; + +#ifdef FKS_LOGGING +printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask); +#endif + /* + * Changing the recmask on an ESS chip with recording mixer means: + * (1) Find the differences + * (2) For "turned-on" inputs: make the recording level the playback level + * (3) For "turned-off" inputs: make the recording level zero + */ + cur_mask = devc->recmask; + diff_mask = (cur_mask ^ mask); + + for (i = 0; i < 32; i++) { + i_mask = (1 << i); + if (diff_mask & i_mask) { /* Difference? (1) */ + if (mask & i_mask) { /* Turn it on (2) */ + value = devc->levels[i]; + left = value & 0x000000ff; + right = (value & 0x0000ff00) >> 8; + } else { /* Turn it off (3) */ + left = 0; + left = 0; + right = 0; + } + sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right); + } + } + return mask; +} + +int ess_set_recmask(sb_devc * devc, int *mask) +{ + /* This applies to ESS chips with record mixers only! */ + + if (ess_has_rec_mixer (devc->submodel)) { + *mask = es_rec_set_recmask (devc, *mask); + return 1; /* Applied */ + } else { + return 0; /* Not applied */ + } +} + +/* + * ess_mixer_reset must be called from sb_mixer_reset + */ +int ess_mixer_reset (sb_devc * devc) +{ + /* + * Separate actions for ESS chips with a record mixer: + */ + if (ess_has_rec_mixer (devc->submodel)) { + switch (devc->submodel) { + case SUBMDL_ES1887: + /* + * Separate actions for ES1887: + * Change registers 7a and 1c to make the record mixer the + * actual recording source. + */ + ess_chgmixer(devc, 0x7a, 0x18, 0x08); + ess_chgmixer(devc, 0x1c, 0x07, 0x07); + break; + }; + /* + * Call set_recmask for proper initialization + */ + devc->recmask = devc->supported_rec_devices; + es_rec_set_recmask(devc, 0); + devc->recmask = 0; + + return 1; /* We took care of recmask. */ + } else { + return 0; /* We didn't take care; caller do it */ + } +} + +/**************************************************************************** + * * + * ESS midi * + * * + ****************************************************************************/ + +/* + * FKS: IRQ may be shared. Hm. And if so? Then What? + */ +int ess_midi_init(sb_devc * devc, struct address_info *hw_config) +{ + unsigned char cfg, tmp; + + cfg = ess_getmixer (devc, 0x40) & 0x03; + + if (devc->submodel < 8) { + ess_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */ + return 0; /* ES688 doesn't support MPU401 mode */ + } + tmp = (hw_config->io_base & 0x0f0) >> 4; + + if (tmp > 3) { + ess_setmixer (devc, 0x40, cfg); + return 0; + } + cfg |= tmp << 3; + + tmp = 1; /* MPU enabled without interrupts */ + + /* May be shared: if so the value is -ve */ + + switch (abs(hw_config->irq)) { + case 9: + tmp = 0x4; + break; + case 5: + tmp = 0x5; + break; + case 7: + tmp = 0x6; + break; + case 10: + tmp = 0x7; + break; + default: + return 0; + } + + cfg |= tmp << 5; + ess_setmixer (devc, 0x40, cfg | 0x03); + + return 1; +} + diff -Nru linux/sound/oss/sb_ess.h linux-2.4.19-pre5-mjc/sound/oss/sb_ess.h --- linux/sound/oss/sb_ess.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_ess.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,34 @@ +/* + * Created: 9-Jan-1999 Rolf Fokkens + */ + +extern void ess_intr + (sb_devc *devc); +extern int ess_dsp_init + (sb_devc *devc, struct address_info *hw_config); + +extern struct audio_driver *ess_audio_init + (sb_devc *devc, int *audio_flags, int *format_mask); +extern int ess_midi_init + (sb_devc *devc, struct address_info *hw_config); +extern void ess_mixer_init + (sb_devc *devc); + +extern int ess_init + (sb_devc *devc, struct address_info *hw_config); +extern int ess_dsp_reset + (sb_devc *devc); + +extern void ess_setmixer + (sb_devc *devc, unsigned int port, unsigned int value); +extern unsigned int ess_getmixer + (sb_devc *devc, unsigned int port); +extern int ess_mixer_set + (sb_devc *devc, int dev, int left, int right); +extern int ess_mixer_reset + (sb_devc *devc); +extern void ess_mixer_reload + (sb_devc * devc, int dev); +extern int ess_set_recmask + (sb_devc *devc, int *mask); + diff -Nru linux/sound/oss/sb_midi.c linux-2.4.19-pre5-mjc/sound/oss/sb_midi.c --- linux/sound/oss/sb_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_midi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,205 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the Sound Blaster DS chips. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include + +#include "sound_config.h" + +#include "sb.h" +#undef SB_TEST_IRQ + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + + +static int sb_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + sb_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return -ENXIO; + + spin_lock_irqsave(&devc->lock, flags); + if (devc->opened) + { + spin_unlock_irqrestore(&devc->lock, flags); + return -EBUSY; + } + devc->opened = 1; + spin_unlock_irqrestore(&devc->lock, flags); + + devc->irq_mode = IMODE_MIDI; + devc->midi_broken = 0; + + sb_dsp_reset(devc); + + if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */ + { + devc->opened = 0; + return -EIO; + } + devc->intr_active = 1; + + if (mode & OPEN_READ) + { + devc->input_opened = 1; + devc->midi_input_intr = input; + } + return 0; +} + +static void sb_midi_close(int dev) +{ + sb_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return; + + spin_lock_irqsave(&devc->lock, flags); + sb_dsp_reset(devc); + devc->intr_active = 0; + devc->input_opened = 0; + devc->opened = 0; + spin_unlock_irqrestore(&devc->lock, flags); +} + +static int sb_midi_out(int dev, unsigned char midi_byte) +{ + sb_devc *devc = midi_devs[dev]->devc; + + if (devc == NULL) + return 1; + + if (devc->midi_broken) + return 1; + + if (!sb_dsp_command(devc, midi_byte)) + { + devc->midi_broken = 1; + return 1; + } + return 1; +} + +static int sb_midi_start_read(int dev) +{ + return 0; +} + +static int sb_midi_end_read(int dev) +{ + sb_devc *devc = midi_devs[dev]->devc; + + if (devc == NULL) + return -ENXIO; + + sb_dsp_reset(devc); + devc->intr_active = 0; + return 0; +} + +static int sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg) +{ + return -EINVAL; +} + +void sb_midi_interrupt(sb_devc * devc) +{ + unsigned long flags; + unsigned char data; + + if (devc == NULL) + return; + + spin_lock_irqsave(&devc->lock, flags); + + data = inb(DSP_READ); + if (devc->input_opened) + devc->midi_input_intr(devc->my_mididev, data); + + spin_unlock_irqrestore(&devc->lock, flags); +} + +#define MIDI_SYNTH_NAME "Sound Blaster Midi" +#define MIDI_SYNTH_CAPS 0 +#include "midi_synth.h" + +static struct midi_operations sb_midi_operations = +{ + owner: THIS_MODULE, + info: {"Sound Blaster", 0, 0, SNDCARD_SB}, + converter: &std_midi_synth, + in_info: {0}, + open: sb_midi_open, + close: sb_midi_close, + ioctl: sb_midi_ioctl, + outputc: sb_midi_out, + start_read: sb_midi_start_read, + end_read: sb_midi_end_read, +}; + +void sb_dsp_midi_init(sb_devc * devc, struct module *owner) +{ + int dev; + + if (devc->model < 2) /* No MIDI support for SB 1.x */ + return; + + dev = sound_alloc_mididev(); + + if (dev == -1) + { + printk(KERN_ERR "sb_midi: too many MIDI devices detected\n"); + return; + } + std_midi_synth.midi_dev = devc->my_mididev = dev; + midi_devs[dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL); + if (midi_devs[dev] == NULL) + { + printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); + sound_unload_mididev(dev); + return; + } + memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations, + sizeof(struct midi_operations)); + + if (owner) + midi_devs[dev]->owner = owner; + + midi_devs[dev]->devc = devc; + + + midi_devs[dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + if (midi_devs[dev]->converter == NULL) + { + printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); + kfree(midi_devs[dev]); + sound_unload_mididev(dev); + return; + } + memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth, + sizeof(struct synth_operations)); + + midi_devs[dev]->converter->id = "SBMIDI"; + sequencer_init(); +} diff -Nru linux/sound/oss/sb_mixer.c linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.c --- linux/sound/oss/sb_mixer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,757 @@ +/* + * sound/sb_mixer.c + * + * The low level mixer driver for the Sound Blaster compatible cards. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch] + * Stanislav Voronyi : Support for AWE 3DSE device (Jun 7 1999) + */ + +#include "sound_config.h" + +#define __SB_MIXER_C__ + +#include "sb.h" +#include "sb_mixer.h" + +#include "sb_ess.h" + +#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) + +/* Same as SB Pro, unless I find otherwise */ +#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES + +#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +/* SG NX Pro has treble and bass settings on the mixer. The 'speaker' + * channel is the COVOX/DisneySoundSource emulation volume control + * on the mixer. It does NOT control speaker volume. Should have own + * mask eventually? + */ +#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ + SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) + +#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD) + +#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD) + +#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | \ + SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \ + SOUND_MASK_IMIX) + +/* These are the only devices that are working at the moment. Others could + * be added once they are identified and a method is found to control them. + */ +#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \ + SOUND_MASK_PCM | SOUND_MASK_MIC | \ + SOUND_MASK_CD | \ + SOUND_MASK_VOLUME) + +static mixer_tab sbpro_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) +}; + +static mixer_tab sb16_mix = { +MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), +MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), +MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), +MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), +MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), +MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), +MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */ +MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2), +MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2) +}; + +static mixer_tab als007_mix = +{ +MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4), +MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4), +MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4), +MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4), +MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4), +MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */ +MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), +MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0) +}; + + +/* SM_GAMES Master volume is lower and PCM & FM volumes + higher than with SB Pro. This improves the + sound quality */ + +static int smg_default_levels[32] = +{ + 0x2020, /* Master Volume */ + 0x4b4b, /* Bass */ + 0x4b4b, /* Treble */ + 0x6464, /* FM */ + 0x6464, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x0000, /* Mic */ + 0x4b4b, /* CD */ + 0x4b4b, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ + 0x4b4b, /* Output gain */ + 0x4040, /* Line1 */ + 0x4040, /* Line2 */ + 0x1515 /* Line3 */ +}; + +static int sb_default_levels[32] = +{ + 0x5a5a, /* Master Volume */ + 0x4b4b, /* Bass */ + 0x4b4b, /* Treble */ + 0x4b4b, /* FM */ + 0x4b4b, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x1010, /* Mic */ + 0x4b4b, /* CD */ + 0x0000, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ + 0x4b4b, /* Output gain */ + 0x4040, /* Line1 */ + 0x4040, /* Line2 */ + 0x1515 /* Line3 */ +}; + +static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x40, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x10, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x04, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00 /* SOUND_MIXER_OGAIN */ +}; + +static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x20, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x08, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x02, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00 /* SOUND_MIXER_OGAIN */ +}; + +static char smw_mix_regs[] = /* Left mixer registers */ +{ + 0x0b, /* SOUND_MIXER_VOLUME */ + 0x0d, /* SOUND_MIXER_BASS */ + 0x0d, /* SOUND_MIXER_TREBLE */ + 0x05, /* SOUND_MIXER_SYNTH */ + 0x09, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x03, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x07, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00, /* SOUND_MIXER_OGAIN */ + 0x00, /* SOUND_MIXER_LINE1 */ + 0x00, /* SOUND_MIXER_LINE2 */ + 0x00 /* SOUND_MIXER_LINE3 */ +}; + +static int sbmixnum = 1; + +static void sb_mixer_reset(sb_devc * devc); + +void sb_mixer_set_stereo(sb_devc * devc, int mode) +{ + sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC)); +} + +static int detect_mixer(sb_devc * devc) +{ + /* Just trust the mixer is there */ + return 1; +} + +static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval) +{ + unsigned char mask; + int shift; + + mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1; + newval = (int) ((newval * mask) + 50) / 100; /* Scale */ + + shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1; + + *regval &= ~(mask << shift); /* Mask out previous value */ + *regval |= (newval & mask) << shift; /* Set the new value */ +} + +static int sb_mixer_get(sb_devc * devc, int dev) +{ + if (!((1 << dev) & devc->supported_devices)) + return -EINVAL; + return devc->levels[dev]; +} + +void smw_mixer_init(sb_devc * devc) +{ + int i; + + sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */ + sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */ + + devc->supported_devices = 0; + for (i = 0; i < sizeof(smw_mix_regs); i++) + if (smw_mix_regs[i] != 0) + devc->supported_devices |= (1 << i); + + devc->supported_rec_devices = devc->supported_devices & + ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME); + sb_mixer_reset(devc); +} + +int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right) +{ + int regoffs; + unsigned char val; + + regoffs = (*devc->iomap)[dev][LEFT_CHN].regno; + + if (regoffs == 0) + return -EINVAL; + + val = sb_getmixer(devc, regoffs); + change_bits(devc, &val, dev, LEFT_CHN, left); + + if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* + * Change register + */ + { + sb_setmixer(devc, regoffs, val); /* + * Save the old one + */ + regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno; + + if (regoffs == 0) + return left | (left << 8); /* + * Just left channel present + */ + + val = sb_getmixer(devc, regoffs); /* + * Read the new one + */ + } + change_bits(devc, &val, dev, RIGHT_CHN, right); + + sb_setmixer(devc, regoffs, val); + + return left | (right << 8); +} + +static int smw_mixer_set(sb_devc * devc, int dev, int left, int right) +{ + int reg, val; + + switch (dev) + { + case SOUND_MIXER_VOLUME: + sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */ + sb_setmixer(devc, 0x0c, 96 - (96 * right / 100)); + break; + + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + devc->levels[dev] = left | (right << 8); + /* Set left bass and treble values */ + val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4; + val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f; + sb_setmixer(devc, 0x0d, val); + + /* Set right bass and treble values */ + val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4; + val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f; + sb_setmixer(devc, 0x0e, val); + + break; + + default: + reg = smw_mix_regs[dev]; + if (reg == 0) + return -EINVAL; + sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */ + sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40); + } + + devc->levels[dev] = left | (right << 8); + return left | (right << 8); +} + +static int sb_mixer_set(sb_devc * devc, int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retval; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (dev > 31) + return -EINVAL; + + if (!(devc->supported_devices & (1 << dev))) /* + * Not supported + */ + return -EINVAL; + + /* Differentiate depending on the chipsets */ + switch (devc->model) { + case MDL_SMW: + retval = smw_mixer_set(devc, dev, left, right); + break; + case MDL_ESS: + retval = ess_mixer_set(devc, dev, left, right); + break; + default: + retval = sb_common_mixer_set(devc, dev, left, right); + } + if (retval >= 0) devc->levels[dev] = retval; + + return retval; +} + +/* + * set_recsrc doesn't apply to ES188x + */ +static void set_recsrc(sb_devc * devc, int src) +{ + sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7)); +} + +static int set_recmask(sb_devc * devc, int mask) +{ + int devmask, i; + unsigned char regimageL, regimageR; + + devmask = mask & devc->supported_rec_devices; + + switch (devc->model) + { + case MDL_SBPRO: + case MDL_ESS: + case MDL_JAZZ: + case MDL_SMW: + if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) { + break; + }; + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { + /* + * More than one device selected. Drop the + * previous selection + */ + devmask &= ~devc->recmask; + } + if (devmask != SOUND_MASK_MIC && + devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) + { + /* + * More than one device selected. Default to + * mic + */ + devmask = SOUND_MASK_MIC; + } + if (devmask ^ devc->recmask) /* + * Input source changed + */ + { + switch (devmask) + { + case SOUND_MASK_MIC: + set_recsrc(devc, SRC__MIC); + break; + + case SOUND_MASK_LINE: + set_recsrc(devc, SRC__LINE); + break; + + case SOUND_MASK_CD: + set_recsrc(devc, SRC__CD); + break; + + default: + set_recsrc(devc, SRC__MIC); + } + } + break; + + case MDL_SB16: + if (!devmask) + devmask = SOUND_MASK_MIC; + + if (devc->submodel == SUBMDL_ALS007) + { + switch (devmask) + { + case SOUND_MASK_LINE: + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE); + break; + case SOUND_MASK_CD: + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD); + break; + case SOUND_MASK_SYNTH: + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH); + break; + default: /* Also takes care of SOUND_MASK_MIC case */ + sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC); + break; + } + } + else + { + regimageL = regimageR = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + { + if ((1 << i) & devmask) + { + regimageL |= sb16_recmasks_L[i]; + regimageR |= sb16_recmasks_R[i]; + } + sb_setmixer (devc, SB16_IMASK_L, regimageL); + sb_setmixer (devc, SB16_IMASK_R, regimageR); + } + } + break; + } + devc->recmask = devmask; + return devc->recmask; +} + +static int set_outmask(sb_devc * devc, int mask) +{ + int devmask, i; + unsigned char regimage; + + devmask = mask & devc->supported_out_devices; + + switch (devc->model) + { + case MDL_SB16: + if (devc->submodel == SUBMDL_ALS007) + break; + else + { + regimage = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + { + if ((1 << i) & devmask) + { + regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]); + } + sb_setmixer (devc, SB16_OMASK, regimage); + } + } + break; + default: + break; + } + + devc->outmask = devmask; + return devc->outmask; +} + +static int sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + sb_devc *devc = mixer_devs[dev]->devc; + int val, ret; + + /* + * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1). + * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1) + * or mode==2 put 3DSE state to mode. + */ + if (devc->model == MDL_SB16) { + if (cmd == SOUND_MIXER_AGC) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + sb_setmixer(devc, 0x43, (~val) & 0x01); + return 0; + } + if (cmd == SOUND_MIXER_3DSE) + { + /* I put here 15, but I don't know the exact version. + At least my 4.13 havn't 3DSE, 4.16 has it. */ + if (devc->minor < 15) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val == 0 || val == 1) + sb_chgmixer(devc, AWE_3DSE, 0x01, val); + else if (val == 2) + { + ret = sb_getmixer(devc, AWE_3DSE)&0x01; + return put_user(ret, (int *)arg); + } + else + return -EINVAL; + return 0; + } + } + if (((cmd >> 8) & 0xff) == 'M') + { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + ret = set_recmask(devc, val); + break; + + case SOUND_MIXER_OUTSRC: + ret = set_outmask(devc, val); + break; + + default: + ret = sb_mixer_set(devc, cmd & 0xff, val); + } + } + else switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + ret = devc->recmask; + break; + + case SOUND_MIXER_OUTSRC: + ret = devc->outmask; + break; + + case SOUND_MIXER_DEVMASK: + ret = devc->supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + ret = devc->supported_devices; + /* The ESS seems to have stereo mic controls */ + if (devc->model == MDL_ESS) + ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX); + else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW) + ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + break; + + case SOUND_MIXER_RECMASK: + ret = devc->supported_rec_devices; + break; + + case SOUND_MIXER_OUTMASK: + ret = devc->supported_out_devices; + break; + + case SOUND_MIXER_CAPS: + ret = devc->mixer_caps; + break; + + default: + ret = sb_mixer_get(devc, cmd & 0xff); + break; + } + return put_user(ret, (int *)arg); + } else + return -EINVAL; +} + +static struct mixer_operations sb_mixer_operations = +{ + owner: THIS_MODULE, + id: "SB", + name: "Sound Blaster", + ioctl: sb_mixer_ioctl +}; + +static struct mixer_operations als007_mixer_operations = +{ + owner: THIS_MODULE, + id: "ALS007", + name: "Avance ALS-007", + ioctl: sb_mixer_ioctl +}; + +static void sb_mixer_reset(sb_devc * devc) +{ + char name[32]; + int i; + + sprintf(name, "SB_%d", devc->sbmixnum); + + if (devc->sbmo.sm_games) + devc->levels = load_mixer_volumes(name, smg_default_levels, 1); + else + devc->levels = load_mixer_volumes(name, sb_default_levels, 1); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + sb_mixer_set(devc, i, devc->levels[i]); + + if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) { + set_recmask(devc, SOUND_MASK_MIC); + }; +} + +int sb_mixer_init(sb_devc * devc, struct module *owner) +{ + int mixer_type = 0; + int m; + + devc->sbmixnum = sbmixnum++; + devc->levels = NULL; + + sb_setmixer(devc, 0x00, 0); /* Reset mixer */ + + if (!(mixer_type = detect_mixer(devc))) + return 0; /* No mixer. Why? */ + + switch (devc->model) + { + case MDL_ESSPCI: + case MDL_YMPCI: + case MDL_SBPRO: + case MDL_AZTECH: + case MDL_JAZZ: + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + devc->supported_devices = SBPRO_MIXER_DEVICES; + devc->supported_rec_devices = SBPRO_RECORDING_DEVICES; + devc->iomap = &sbpro_mix; + break; + + case MDL_ESS: + ess_mixer_init (devc); + break; + + case MDL_SMW: + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + devc->supported_devices = 0; + devc->supported_rec_devices = 0; + devc->iomap = &sbpro_mix; + smw_mixer_init(devc); + break; + + case MDL_SB16: + devc->mixer_caps = 0; + devc->supported_rec_devices = SB16_RECORDING_DEVICES; + devc->supported_out_devices = SB16_OUTFILTER_DEVICES; + if (devc->submodel != SUBMDL_ALS007) + { + devc->supported_devices = SB16_MIXER_DEVICES; + devc->iomap = &sb16_mix; + } + else + { + devc->supported_devices = ALS007_MIXER_DEVICES; + devc->iomap = &als007_mix; + } + break; + + default: + printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model); + return 0; + } + + m = sound_alloc_mixerdev(); + if (m == -1) + return 0; + + mixer_devs[m] = (struct mixer_operations *)kmalloc(sizeof(struct mixer_operations), GFP_KERNEL); + if (mixer_devs[m] == NULL) + { + printk(KERN_ERR "sb_mixer: Can't allocate memory\n"); + sound_unload_mixerdev(m); + return 0; + } + + if (devc->submodel != SUBMDL_ALS007) + memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations)); + else + memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations)); + + mixer_devs[m]->devc = devc; + + if (owner) + mixer_devs[m]->owner = owner; + + devc->my_mixerdev = m; + sb_mixer_reset(devc); + return 1; +} + +void sb_mixer_unload(sb_devc *devc) +{ + if (devc->my_mixerdev == -1) + return; + + kfree(mixer_devs[devc->my_mixerdev]); + sound_unload_mixerdev(devc->my_mixerdev); + sbmixnum--; +} diff -Nru linux/sound/oss/sb_mixer.h linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.h --- linux/sound/oss/sb_mixer.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sb_mixer.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,105 @@ +/* + * sound/sb_mixer.h + * + * Definitions for the SB Pro and SB16 mixers + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +/* + * Modified: + * Hunyue Yau Jan 6 1994 + * Added defines for the Sound Galaxy NX Pro mixer. + * + * Rolf Fokkens Dec 20 1998 + * Added defines for some ES188x chips. + * + * Rolf Fokkens Dec 27 1998 + * Moved static stuff to sb_mixer.c + * + */ +/* + * Mixer registers + * + * NOTE! RECORD_SRC == IN_FILTER + */ + +/* + * Mixer registers of SB Pro + */ +#define VOC_VOL 0x04 +#define MIC_VOL 0x0A +#define MIC_MIX 0x0A +#define RECORD_SRC 0x0C +#define IN_FILTER 0x0C +#define OUT_FILTER 0x0E +#define MASTER_VOL 0x22 +#define FM_VOL 0x26 +#define CD_VOL 0x28 +#define LINE_VOL 0x2E +#define IRQ_NR 0x80 +#define DMA_NR 0x81 +#define IRQ_STAT 0x82 +#define OPSW 0x3c + +/* + * Additional registers on the SG NX Pro + */ +#define COVOX_VOL 0x42 +#define TREBLE_LVL 0x44 +#define BASS_LVL 0x46 + +#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ +#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ +#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ +#define FILT_OFF (1 << 5) + +#define MONO_DAC 0x00 +#define STEREO_DAC 0x02 + +/* + * Mixer registers of SB16 + */ +#define SB16_OMASK 0x3c +#define SB16_IMASK_L 0x3d +#define SB16_IMASK_R 0x3e + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +/* + * 3DSE register of AWE32/64 + */ +#define AWE_3DSE 0x90 + +/* + * Mixer registers of ALS007 + */ +#define ALS007_RECORD_SRC 0x6c +#define ALS007_OUTPUT_CTRL1 0x3c +#define ALS007_OUTPUT_CTRL2 0x4c + +#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \ + {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}} + +/* + * Recording sources (SB Pro) + */ + +#define SRC__MIC 1 /* Select Microphone recording source */ +#define SRC__CD 3 /* Select CD recording source */ +#define SRC__LINE 7 /* Use Line-in for recording source */ + +/* + * Recording sources for ALS-007 + */ + +#define ALS007_MIC 4 +#define ALS007_LINE 6 +#define ALS007_CD 2 +#define ALS007_SYNTH 7 diff -Nru linux/sound/oss/sequencer.c linux-2.4.19-pre5-mjc/sound/oss/sequencer.c --- linux/sound/oss/sequencer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sequencer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1699 @@ +/* + * sound/sequencer.c + * + * The sequencer personality manager. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Alan Cox : reformatted and fixed a pair of null pointer bugs + */ +#include + +#define SEQUENCER_C +#include "sound_config.h" + +#include "midi_ctrl.h" + +static int sequencer_ok = 0; +static struct sound_timer_operations *tmr; +static int tmr_no = -1; /* Currently selected timer */ +static int pending_timer = -1; /* For timer change operation */ +extern unsigned long seq_time; + +static int obsolete_api_used = 0; + +/* + * Local counts for number of synth and MIDI devices. These are initialized + * by the sequencer_open. + */ +static int max_mididev = 0; +static int max_synthdev = 0; + +/* + * The seq_mode gives the operating mode of the sequencer: + * 1 = level1 (the default) + * 2 = level2 (extended capabilities) + */ + +#define SEQ_1 1 +#define SEQ_2 2 +static int seq_mode = SEQ_1; + +static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper); +static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper); + +static int midi_opened[MAX_MIDI_DEV] = { + 0 +}; + +static int midi_written[MAX_MIDI_DEV] = { + 0 +}; + +static unsigned long prev_input_time = 0; +static int prev_event_time; + +#include "tuning.h" + +#define EV_SZ 8 +#define IEV_SZ 8 + +static unsigned char *queue = NULL; +static unsigned char *iqueue = NULL; + +static volatile int qhead = 0, qtail = 0, qlen = 0; +static volatile int iqhead = 0, iqtail = 0, iqlen = 0; +static volatile int seq_playing = 0; +static volatile int sequencer_busy = 0; +static int output_threshold; +static long pre_event_timeout; +static unsigned synth_open_mask; + +static int seq_queue(unsigned char *note, char nonblock); +static void seq_startplay(void); +static int seq_sync(void); +static void seq_reset(void); + +#if MAX_SYNTH_DEV > 15 +#error Too many synthesizer devices enabled. +#endif + +int sequencer_read(int dev, struct file *file, char *buf, int count) +{ + int c = count, p = 0; + int ev_len; + unsigned long flags; + + dev = dev >> 4; + + ev_len = seq_mode == SEQ_1 ? 4 : 8; + + save_flags(flags); + cli(); + + if (!iqlen) + { + if (file->f_flags & O_NONBLOCK) { + restore_flags(flags); + return -EAGAIN; + } + + interruptible_sleep_on_timeout(&midi_sleeper, + pre_event_timeout); + if (!iqlen) + { + restore_flags(flags); + return 0; + } + } + while (iqlen && c >= ev_len) + { + char *fixit = (char *) &iqueue[iqhead * IEV_SZ]; + copy_to_user(&(buf)[p], fixit, ev_len); + p += ev_len; + c -= ev_len; + + iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; + iqlen--; + } + restore_flags(flags); + return count - c; +} + +static void sequencer_midi_output(int dev) +{ + /* + * Currently NOP + */ +} + +void seq_copy_to_input(unsigned char *event_rec, int len) +{ + unsigned long flags; + + /* + * Verify that the len is valid for the current mode. + */ + + if (len != 4 && len != 8) + return; + if ((seq_mode == SEQ_1) != (len == 4)) + return; + + if (iqlen >= (SEQ_MAX_QUEUE - 1)) + return; /* Overflow */ + + save_flags(flags); + cli(); + memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len); + iqlen++; + iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; + wake_up(&midi_sleeper); + restore_flags(flags); +} + +static void sequencer_midi_input(int dev, unsigned char data) +{ + unsigned int tstamp; + unsigned char event_rec[4]; + + if (data == 0xfe) /* Ignore active sensing */ + return; + + tstamp = jiffies - seq_time; + + if (tstamp != prev_input_time) + { + tstamp = (tstamp << 8) | SEQ_WAIT; + seq_copy_to_input((unsigned char *) &tstamp, 4); + prev_input_time = tstamp; + } + event_rec[0] = SEQ_MIDIPUTC; + event_rec[1] = data; + event_rec[2] = dev; + event_rec[3] = 0; + + seq_copy_to_input(event_rec, 4); +} + +void seq_input_event(unsigned char *event_rec, int len) +{ + unsigned long this_time; + + if (seq_mode == SEQ_2) + this_time = tmr->get_time(tmr_no); + else + this_time = jiffies - seq_time; + + if (this_time != prev_input_time) + { + unsigned char tmp_event[8]; + + tmp_event[0] = EV_TIMING; + tmp_event[1] = TMR_WAIT_ABS; + tmp_event[2] = 0; + tmp_event[3] = 0; + *(unsigned int *) &tmp_event[4] = this_time; + + seq_copy_to_input(tmp_event, 8); + prev_input_time = this_time; + } + seq_copy_to_input(event_rec, len); +} + +int sequencer_write(int dev, struct file *file, const char *buf, int count) +{ + unsigned char event_rec[EV_SZ], ev_code; + int p = 0, c, ev_size; + int err; + int mode = translate_mode(file); + + dev = dev >> 4; + + DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count)); + + if (mode == OPEN_READ) + return -EIO; + + c = count; + + while (c >= 4) + { + copy_from_user((char *) event_rec, &(buf)[p], 4); + ev_code = event_rec[0]; + + if (ev_code == SEQ_FULLSIZE) + { + int err, fmt; + + dev = *(unsigned short *) &event_rec[2]; + if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL) + return -ENXIO; + + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; + + fmt = (*(short *) &event_rec[0]) & 0xffff; + err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0); + if (err < 0) + return err; + + return err; + } + if (ev_code >= 128) + { + if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) + { + printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code); + return -EINVAL; + } + ev_size = 8; + + if (c < ev_size) + { + if (!seq_playing) + seq_startplay(); + return count - c; + } + copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4); + + } + else + { + if (seq_mode == SEQ_2) + { + printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n"); + return -EINVAL; + } + ev_size = 4; + + if (event_rec[0] != SEQ_MIDIPUTC) + obsolete_api_used = 1; + } + + if (event_rec[0] == SEQ_MIDIPUTC) + { + if (!midi_opened[event_rec[2]]) + { + int mode; + int dev = event_rec[2]; + + if (dev >= max_mididev || midi_devs[dev]==NULL) + { + /*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/ + return -ENXIO; + } + mode = translate_mode(file); + + if ((err = midi_devs[dev]->open(dev, mode, + sequencer_midi_input, sequencer_midi_output)) < 0) + { + seq_reset(); + printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev); + return err; + } + midi_opened[dev] = 1; + } + } + if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0))) + { + int processed = count - c; + + if (!seq_playing) + seq_startplay(); + + if (!processed && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + else + return processed; + } + p += ev_size; + c -= ev_size; + } + + if (!seq_playing) + seq_startplay(); + + return count; +} + +static int seq_queue(unsigned char *note, char nonblock) +{ + + /* + * Test if there is space in the queue + */ + + if (qlen >= SEQ_MAX_QUEUE) + if (!seq_playing) + seq_startplay(); /* + * Give chance to drain the queue + */ + + if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) { + /* + * Sleep until there is enough space on the queue + */ + interruptible_sleep_on(&seq_sleeper); + } + if (qlen >= SEQ_MAX_QUEUE) + { + return 0; /* + * To be sure + */ + } + memcpy(&queue[qtail * EV_SZ], note, EV_SZ); + + qtail = (qtail + 1) % SEQ_MAX_QUEUE; + qlen++; + + return 1; +} + +static int extended_event(unsigned char *q) +{ + int dev = q[2]; + + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; + + switch (q[1]) + { + case SEQ_NOTEOFF: + synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); + break; + + case SEQ_NOTEON: + if (q[4] > 127 && q[4] != 255) + return 0; + + if (q[5] == 0) + { + synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); + break; + } + synth_devs[dev]->start_note(dev, q[3], q[4], q[5]); + break; + + case SEQ_PGMCHANGE: + synth_devs[dev]->set_instr(dev, q[3], q[4]); + break; + + case SEQ_AFTERTOUCH: + synth_devs[dev]->aftertouch(dev, q[3], q[4]); + break; + + case SEQ_BALANCE: + synth_devs[dev]->panning(dev, q[3], (char) q[4]); + break; + + case SEQ_CONTROLLER: + synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8))); + break; + + case SEQ_VOLMODE: + if (synth_devs[dev]->volume_method != NULL) + synth_devs[dev]->volume_method(dev, q[3]); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int find_voice(int dev, int chn, int note) +{ + unsigned short key; + int i; + + key = (chn << 8) | (note + 1); + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if (synth_devs[dev]->alloc.map[i] == key) + return i; + return -1; +} + +static int alloc_voice(int dev, int chn, int note) +{ + unsigned short key; + int voice; + + key = (chn << 8) | (note + 1); + + voice = synth_devs[dev]->alloc_voice(dev, chn, note, + &synth_devs[dev]->alloc); + synth_devs[dev]->alloc.map[voice] = key; + synth_devs[dev]->alloc.alloc_times[voice] = + synth_devs[dev]->alloc.timestamp++; + return voice; +} + +static void seq_chn_voice_event(unsigned char *event_rec) +{ +#define dev event_rec[1] +#define cmd event_rec[2] +#define chn event_rec[3] +#define note event_rec[4] +#define parm event_rec[5] + + int voice = -1; + + if ((int) dev > max_synthdev || synth_devs[dev] == NULL) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + if (seq_mode == SEQ_2) + { + if (synth_devs[dev]->alloc_voice) + voice = find_voice(dev, chn, note); + + if (cmd == MIDI_NOTEON && parm == 0) + { + cmd = MIDI_NOTEOFF; + parm = 64; + } + } + + switch (cmd) + { + case MIDI_NOTEON: + if (note > 127 && note != 255) /* Not a seq2 feature */ + return; + + if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) + { + /* Internal synthesizer (FM, GUS, etc) */ + voice = alloc_voice(dev, chn, note); + } + if (voice == -1) + voice = chn; + + if (seq_mode == SEQ_2 && (int) dev < num_synths) + { + /* + * The MIDI channel 10 is a percussive channel. Use the note + * number to select the proper patch (128 to 255) to play. + */ + + if (chn == 9) + { + synth_devs[dev]->set_instr(dev, voice, 128 + note); + synth_devs[dev]->chn_info[chn].pgm_num = 128 + note; + } + synth_devs[dev]->setup_voice(dev, voice, chn); + } + synth_devs[dev]->start_note(dev, voice, note, parm); + break; + + case MIDI_NOTEOFF: + if (voice == -1) + voice = chn; + synth_devs[dev]->kill_note(dev, voice, note, parm); + break; + + case MIDI_KEY_PRESSURE: + if (voice == -1) + voice = chn; + synth_devs[dev]->aftertouch(dev, voice, parm); + break; + + default:; + } +#undef dev +#undef cmd +#undef chn +#undef note +#undef parm +} + + +static void seq_chn_common_event(unsigned char *event_rec) +{ + unsigned char dev = event_rec[1]; + unsigned char cmd = event_rec[2]; + unsigned char chn = event_rec[3]; + unsigned char p1 = event_rec[4]; + + /* unsigned char p2 = event_rec[5]; */ + unsigned short w14 = *(short *) &event_rec[6]; + + if ((int) dev > max_synthdev || synth_devs[dev] == NULL) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + switch (cmd) + { + case MIDI_PGM_CHANGE: + if (seq_mode == SEQ_2) + { + synth_devs[dev]->chn_info[chn].pgm_num = p1; + if ((int) dev >= num_synths) + synth_devs[dev]->set_instr(dev, chn, p1); + } + else + synth_devs[dev]->set_instr(dev, chn, p1); + + break; + + case MIDI_CTL_CHANGE: + if (seq_mode == SEQ_2) + { + if (chn > 15 || p1 > 127) + break; + + synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f; + + if (p1 < 32) /* Setting MSB should clear LSB to 0 */ + synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0; + + if ((int) dev < num_synths) + { + int val = w14 & 0x7f; + int i, key; + + if (p1 < 64) /* Combine MSB and LSB */ + { + val = ((synth_devs[dev]-> + chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) + | (synth_devs[dev]-> + chn_info[chn].controllers[p1 | 32] & 0x7f); + p1 &= ~32; + } + /* Handle all playing notes on this channel */ + + key = ((int) chn << 8); + + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) + synth_devs[dev]->controller(dev, i, p1, val); + } + else + synth_devs[dev]->controller(dev, chn, p1, w14); + } + else /* Mode 1 */ + synth_devs[dev]->controller(dev, chn, p1, w14); + break; + + case MIDI_PITCH_BEND: + if (seq_mode == SEQ_2) + { + synth_devs[dev]->chn_info[chn].bender_value = w14; + + if ((int) dev < num_synths) + { + /* Handle all playing notes on this channel */ + int i, key; + + key = (chn << 8); + + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) + synth_devs[dev]->bender(dev, i, w14); + } + else + synth_devs[dev]->bender(dev, chn, w14); + } + else /* MODE 1 */ + synth_devs[dev]->bender(dev, chn, w14); + break; + + default:; + } +} + +static int seq_timing_event(unsigned char *event_rec) +{ + unsigned char cmd = event_rec[1]; + unsigned int parm = *(int *) &event_rec[4]; + + if (seq_mode == SEQ_2) + { + int ret; + + if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED) + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + return ret; + } + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + + /* + * NOTE! No break here. Execution of TMR_WAIT_REL continues in the + * next case (TMR_WAIT_ABS) + */ + + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + time = parm; + prev_event_time = time; + + seq_playing = 1; + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + return TIMER_ARMED; + } + break; + + case TMR_START: + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + break; + + case TMR_STOP: + break; + + case TMR_CONTINUE: + break; + + case TMR_TEMPO: + break; + + case TMR_ECHO: + if (seq_mode == SEQ_2) + seq_copy_to_input(event_rec, 8); + else + { + parm = (parm << 8 | SEQ_ECHO); + seq_copy_to_input((unsigned char *) &parm, 4); + } + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static void seq_local_event(unsigned char *event_rec) +{ + unsigned char cmd = event_rec[1]; + unsigned int parm = *((unsigned int *) &event_rec[4]); + + switch (cmd) + { + case LOCL_STARTAUDIO: + DMAbuf_start_devices(parm); + break; + + default:; + } +} + +static void seq_sysex_message(unsigned char *event_rec) +{ + int dev = event_rec[1]; + int i, l = 0; + unsigned char *buf = &event_rec[2]; + + if ((int) dev > max_synthdev) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + l = 0; + for (i = 0; i < 6 && buf[i] != 0xff; i++) + l = i + 1; + + if (!synth_devs[dev]->send_sysex) + return; + if (l > 0) + synth_devs[dev]->send_sysex(dev, buf, l); +} + +static int play_event(unsigned char *q) +{ + /* + * NOTE! This routine returns + * 0 = normal event played. + * 1 = Timer armed. Suspend playback until timer callback. + * 2 = MIDI output buffer full. Restore queue and suspend until timer + */ + unsigned int *delay; + + switch (q[0]) + { + case SEQ_NOTEOFF: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->kill_note(0, q[1], 255, q[3]); + break; + + case SEQ_NOTEON: + if (q[4] < 128 || q[4] == 255) + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->start_note(0, q[1], q[2], q[3]); + break; + + case SEQ_WAIT: + delay = (unsigned int *) q; /* + * Bytes 1 to 3 are containing the * + * delay in 'ticks' + */ + *delay = (*delay >> 8) & 0xffffff; + + if (*delay > 0) + { + long time; + + seq_playing = 1; + time = *delay; + prev_event_time = time; + + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + /* + * The timer is now active and will reinvoke this function + * after the timer expires. Return to the caller now. + */ + return 1; + } + break; + + case SEQ_PGMCHANGE: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->set_instr(0, q[1], q[2]); + break; + + case SEQ_SYNCTIMER: /* + * Reset timer + */ + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + break; + + case SEQ_MIDIPUTC: /* + * Put a midi character + */ + if (midi_opened[q[2]]) + { + int dev; + + dev = q[2]; + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + break; + + if (!midi_devs[dev]->outputc(dev, q[1])) + { + /* + * Output FIFO is full. Wait one timer cycle and try again. + */ + + seq_playing = 1; + request_sound_timer(-1); + return 2; + } + else + midi_written[dev] = 1; + } + break; + + case SEQ_ECHO: + seq_copy_to_input(q, 4); /* + * Echo back to the process + */ + break; + + case SEQ_PRIVATE: + if ((int) q[1] < max_synthdev) + synth_devs[q[1]]->hw_control(q[1], q); + break; + + case SEQ_EXTENDED: + extended_event(q); + break; + + case EV_CHN_VOICE: + seq_chn_voice_event(q); + break; + + case EV_CHN_COMMON: + seq_chn_common_event(q); + break; + + case EV_TIMING: + if (seq_timing_event(q) == TIMER_ARMED) + { + return 1; + } + break; + + case EV_SEQ_LOCAL: + seq_local_event(q); + break; + + case EV_SYSEX: + seq_sysex_message(q); + break; + + default:; + } + return 0; +} + +static void seq_startplay(void) +{ + unsigned long flags; + int this_one, action; + + while (qlen > 0) + { + + save_flags(flags); + cli(); + qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; + qlen--; + restore_flags(flags); + + seq_playing = 1; + + if ((action = play_event(&queue[this_one * EV_SZ]))) + { /* Suspend playback. Next timer routine invokes this routine again */ + if (action == 2) + { + qlen++; + qhead = this_one; + } + return; + } + } + + seq_playing = 0; + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); +} + +static void reset_controllers(int dev, unsigned char *controller, int update_dev) +{ + int i; + for (i = 0; i < 128; i++) + controller[i] = ctrl_def_values[i]; +} + +static void setup_mode2(void) +{ + int dev; + + max_synthdev = num_synths; + + for (dev = 0; dev < num_midis; dev++) + { + if (midi_devs[dev] && midi_devs[dev]->converter != NULL) + { + synth_devs[max_synthdev++] = midi_devs[dev]->converter; + } + } + + for (dev = 0; dev < max_synthdev; dev++) + { + int chn; + + synth_devs[dev]->sysex_ptr = 0; + synth_devs[dev]->emulation = 0; + + for (chn = 0; chn < 16; chn++) + { + synth_devs[dev]->chn_info[chn].pgm_num = 0; + reset_controllers(dev, + synth_devs[dev]->chn_info[chn].controllers,0); + synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */ + synth_devs[dev]->chn_info[chn].bender_range = 200; + } + } + max_mididev = 0; + seq_mode = SEQ_2; +} + +int sequencer_open(int dev, struct file *file) +{ + int retval, mode, i; + int level, tmp; + unsigned long flags; + + if (!sequencer_ok) + sequencer_init(); + + level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; + + dev = dev >> 4; + mode = translate_mode(file); + + DEB(printk("sequencer_open(dev=%d)\n", dev)); + + if (!sequencer_ok) + { +/* printk("Sound card: sequencer not initialized\n");*/ + return -ENXIO; + } + if (dev) /* Patch manager device (obsolete) */ + return -ENXIO; + + if(synth_devs[dev] == NULL) + request_module("synth0"); + + if (mode == OPEN_READ) + { + if (!num_midis) + { + /*printk("Sequencer: No MIDI devices. Input not possible\n");*/ + sequencer_busy = 0; + return -ENXIO; + } + } + save_flags(flags); + cli(); + if (sequencer_busy) + { + restore_flags(flags); + return -EBUSY; + } + sequencer_busy = 1; + obsolete_api_used = 0; + restore_flags(flags); + + max_mididev = num_midis; + max_synthdev = num_synths; + pre_event_timeout = MAX_SCHEDULE_TIMEOUT; + seq_mode = SEQ_1; + + if (pending_timer != -1) + { + tmr_no = pending_timer; + pending_timer = -1; + } + if (tmr_no == -1) /* Not selected yet */ + { + int i, best; + + best = -1; + for (i = 0; i < num_sound_timers; i++) + if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best) + { + tmr_no = i; + best = sound_timer_devs[i]->priority; + } + if (tmr_no == -1) /* Should not be */ + tmr_no = 0; + } + tmr = sound_timer_devs[tmr_no]; + + if (level == 2) + { + if (tmr == NULL) + { + /*printk("sequencer: No timer for level 2\n");*/ + sequencer_busy = 0; + return -ENXIO; + } + setup_mode2(); + } + if (!max_synthdev && !max_mididev) + { + sequencer_busy=0; + return -ENXIO; + } + + synth_open_mask = 0; + + for (i = 0; i < max_mididev; i++) + { + midi_opened[i] = 0; + midi_written[i] = 0; + } + + for (i = 0; i < max_synthdev; i++) + { + if (synth_devs[i]==NULL) + continue; + + if (synth_devs[i]->owner) + __MOD_INC_USE_COUNT (synth_devs[i]->owner); + + if ((tmp = synth_devs[i]->open(i, mode)) < 0) + { + printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); + if (synth_devs[i]->midi_dev) + printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev); + } + else + { + synth_open_mask |= (1 << i); + if (synth_devs[i]->midi_dev) + midi_opened[synth_devs[i]->midi_dev] = 1; + } + } + + seq_time = jiffies; + + prev_input_time = 0; + prev_event_time = 0; + + if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) + { + /* + * Initialize midi input devices + */ + + for (i = 0; i < max_mididev; i++) + if (!midi_opened[i] && midi_devs[i]) + { + if (midi_devs[i]->owner) + __MOD_INC_USE_COUNT (midi_devs[i]->owner); + + if ((retval = midi_devs[i]->open(i, mode, + sequencer_midi_input, sequencer_midi_output)) >= 0) + { + midi_opened[i] = 1; + } + } + } + + if (seq_mode == SEQ_2) { + if (tmr->owner) + __MOD_INC_USE_COUNT (tmr->owner); + tmr->open(tmr_no, seq_mode); + } + + init_waitqueue_head(&seq_sleeper); + init_waitqueue_head(&midi_sleeper); + output_threshold = SEQ_MAX_QUEUE / 2; + + return 0; +} + +void seq_drain_midi_queues(void) +{ + int i, n; + + /* + * Give the Midi drivers time to drain their output queues + */ + + n = 1; + + while (!signal_pending(current) && n) + { + n = 0; + + for (i = 0; i < max_mididev; i++) + if (midi_opened[i] && midi_written[i]) + if (midi_devs[i]->buffer_status != NULL) + if (midi_devs[i]->buffer_status(i)) + n++; + + /* + * Let's have a delay + */ + + if (n) + interruptible_sleep_on_timeout(&seq_sleeper, + HZ/10); + } +} + +void sequencer_release(int dev, struct file *file) +{ + int i; + int mode = translate_mode(file); + + dev = dev >> 4; + + DEB(printk("sequencer_release(dev=%d)\n", dev)); + + /* + * Wait until the queue is empty (if we don't have nonblock) + */ + + if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK)) + { + while (!signal_pending(current) && qlen > 0) + { + seq_sync(); + interruptible_sleep_on_timeout(&seq_sleeper, + 3*HZ); + /* Extra delay */ + } + } + + if (mode != OPEN_READ) + seq_drain_midi_queues(); /* + * Ensure the output queues are empty + */ + seq_reset(); + if (mode != OPEN_READ) + seq_drain_midi_queues(); /* + * Flush the all notes off messages + */ + + for (i = 0; i < max_synthdev; i++) + { + if (synth_open_mask & (1 << i)) /* + * Actually opened + */ + if (synth_devs[i]) + { + synth_devs[i]->close(i); + + if (synth_devs[i]->owner) + __MOD_DEC_USE_COUNT (synth_devs[i]->owner); + + if (synth_devs[i]->midi_dev) + midi_opened[synth_devs[i]->midi_dev] = 0; + } + } + + for (i = 0; i < max_mididev; i++) + { + if (midi_opened[i]) { + midi_devs[i]->close(i); + if (midi_devs[i]->owner) + __MOD_DEC_USE_COUNT (midi_devs[i]->owner); + } + } + + if (seq_mode == SEQ_2) { + tmr->close(tmr_no); + if (tmr->owner) + __MOD_DEC_USE_COUNT (tmr->owner); + } + + if (obsolete_api_used) + printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm); + sequencer_busy = 0; +} + +static int seq_sync(void) +{ + unsigned long flags; + + if (qlen && !seq_playing && !signal_pending(current)) + seq_startplay(); + + save_flags(flags); + cli(); + if (qlen > 0) + interruptible_sleep_on_timeout(&seq_sleeper, HZ); + restore_flags(flags); + return qlen; +} + +static void midi_outc(int dev, unsigned char data) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int n; + unsigned long flags; + + /* + * This routine sends one byte to the Midi channel. + * If the output FIFO is full, it waits until there + * is space in the queue + */ + + n = 3 * HZ; /* Timeout */ + + save_flags(flags); + cli(); + while (n && !midi_devs[dev]->outputc(dev, data)) { + interruptible_sleep_on_timeout(&seq_sleeper, HZ/25); + n--; + } + restore_flags(flags); +} + +static void seq_reset(void) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int i; + int chn; + unsigned long flags; + + sound_stop_timer(); + + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + + qlen = qhead = qtail = 0; + iqlen = iqhead = iqtail = 0; + + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + synth_devs[i]->reset(i); + + if (seq_mode == SEQ_2) + { + for (chn = 0; chn < 16; chn++) + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + { + synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */ + synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */ + synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */ + } + } + else /* seq_mode == SEQ_1 */ + { + for (i = 0; i < max_mididev; i++) + if (midi_written[i]) /* + * Midi used. Some notes may still be playing + */ + { + /* + * Sending just a ACTIVE SENSING message should be enough to stop all + * playing notes. Since there are devices not recognizing the + * active sensing, we have to send some all notes off messages also. + */ + midi_outc(i, 0xfe); + + for (chn = 0; chn < 16; chn++) + { + midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */ + midi_outc(i, 0x7b); /* All notes off */ + midi_outc(i, 0); /* Dummy parameter */ + } + + midi_devs[i]->close(i); + + midi_written[i] = 0; + midi_opened[i] = 0; + } + } + + seq_playing = 0; + + save_flags(flags); + cli(); + + if (waitqueue_active(&seq_sleeper)) { + /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ + wake_up(&seq_sleeper); + } + restore_flags(flags); +} + +static void seq_panic(void) +{ + /* + * This routine is called by the application in case the user + * wants to reset the system to the default state. + */ + + seq_reset(); + + /* + * Since some of the devices don't recognize the active sensing and + * all notes off messages, we have to shut all notes manually. + * + * TO BE IMPLEMENTED LATER + */ + + /* + * Also return the controllers to their default states + */ +} + +int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) +{ + int midi_dev, orig_dev, val, err; + int mode = translate_mode(file); + struct synth_info inf; + struct seq_event_rec event_rec; + unsigned long flags; + + orig_dev = dev = dev >> 4; + + switch (cmd) + { + case SNDCTL_TMR_TIMEBASE: + case SNDCTL_TMR_TEMPO: + case SNDCTL_TMR_START: + case SNDCTL_TMR_STOP: + case SNDCTL_TMR_CONTINUE: + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SOURCE: + if (seq_mode != SEQ_2) + return -EINVAL; + return tmr->ioctl(tmr_no, cmd, arg); + + case SNDCTL_TMR_SELECT: + if (seq_mode != SEQ_2) + return -EINVAL; + if (get_user(pending_timer, (int *)arg)) + return -EFAULT; + if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL) + { + pending_timer = -1; + return -EINVAL; + } + val = pending_timer; + break; + + case SNDCTL_SEQ_PANIC: + seq_panic(); + return -EINVAL; + + case SNDCTL_SEQ_SYNC: + if (mode == OPEN_READ) + return 0; + while (qlen > 0 && !signal_pending(current)) + seq_sync(); + return qlen ? -EINTR : 0; + + case SNDCTL_SEQ_RESET: + seq_reset(); + return 0; + + case SNDCTL_SEQ_TESTMIDI: + if (__get_user(midi_dev, (int *)arg)) + return -EFAULT; + if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev]) + return -ENXIO; + + if (!midi_opened[midi_dev] && + (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input, + sequencer_midi_output)) < 0) + return err; + midi_opened[midi_dev] = 1; + return 0; + + case SNDCTL_SEQ_GETINCOUNT: + if (mode == OPEN_WRITE) + return 0; + val = iqlen; + break; + + case SNDCTL_SEQ_GETOUTCOUNT: + if (mode == OPEN_READ) + return 0; + val = SEQ_MAX_QUEUE - qlen; + break; + + case SNDCTL_SEQ_GETTIME: + if (seq_mode == SEQ_2) + return tmr->ioctl(tmr_no, cmd, arg); + val = jiffies - seq_time; + break; + + case SNDCTL_SEQ_CTRLRATE: + /* + * If *arg == 0, just return the current rate + */ + if (seq_mode == SEQ_2) + return tmr->ioctl(tmr_no, cmd, arg); + + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) + return -EINVAL; + val = HZ; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + case SNDCTL_SYNTH_REMOVESAMPLE: + case SNDCTL_SYNTH_CONTROL: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + return synth_devs[dev]->ioctl(dev, cmd, arg); + + case SNDCTL_SEQ_NRSYNTHS: + val = max_synthdev; + break; + + case SNDCTL_SEQ_NRMIDIS: + val = max_mididev; + break; + + case SNDCTL_SYNTH_MEMAVL: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + val = synth_devs[dev]->ioctl(dev, cmd, arg); + break; + + case SNDCTL_FM_4OP_ENABLE: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; + synth_devs[dev]->ioctl(dev, cmd, arg); + return 0; + + case SNDCTL_SYNTH_INFO: + if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + return synth_devs[dev]->ioctl(dev, cmd, arg); + + /* Like SYNTH_INFO but returns ID in the name field */ + case SNDCTL_SYNTH_ID: + if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + memcpy(&inf, synth_devs[dev]->info, sizeof(inf)); + strncpy(inf.name, synth_devs[dev]->id, sizeof(inf.name)); + inf.device = dev; + return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0; + + case SNDCTL_SEQ_OUTOFBAND: + if (copy_from_user(&event_rec, arg, sizeof(event_rec))) + return -EFAULT; + save_flags(flags); + cli(); + play_event(event_rec.arr); + restore_flags(flags); + return 0; + + case SNDCTL_MIDI_INFO: + if (get_user(dev, (int *)(&(((struct midi_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_mididev || !midi_devs[dev]) + return -ENXIO; + midi_devs[dev]->info.device = dev; + return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0; + + case SNDCTL_SEQ_THRESHOLD: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 1) + val = 1; + if (val >= SEQ_MAX_QUEUE) + val = SEQ_MAX_QUEUE - 1; + output_threshold = val; + return 0; + + case SNDCTL_MIDI_PRETIME: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 0) + val = 0; + val = (HZ * val) / 10; + pre_event_timeout = val; + break; + + default: + if (mode == OPEN_READ) + return -EIO; + if (!synth_devs[0]) + return -ENXIO; + if (!(synth_open_mask & (1 << 0))) + return -ENXIO; + if (!synth_devs[0]->ioctl) + return -EINVAL; + return synth_devs[0]->ioctl(0, cmd, arg); + } + return put_user(val, (int *)arg); +} + +/* No kernel lock - we're using the global irq lock here */ +unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait) +{ + unsigned long flags; + unsigned int mask = 0; + + dev = dev >> 4; + + save_flags(flags); + cli(); + /* input */ + poll_wait(file, &midi_sleeper, wait); + if (iqlen) + mask |= POLLIN | POLLRDNORM; + + /* output */ + poll_wait(file, &seq_sleeper, wait); + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + mask |= POLLOUT | POLLWRNORM; + restore_flags(flags); + return mask; +} + + +void sequencer_timer(unsigned long dummy) +{ + seq_startplay(); +} + +int note_to_freq(int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ + + int note, octave, note_freq; + static int notes[] = + { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* + * note_freq >>= 1; + */ + + return note_freq; +} + +unsigned long compute_finetune(unsigned long base_freq, int bend, int range, + int vibrato_cents) +{ + unsigned long amount; + int negative, semitones, cents, multiplier = 1; + + if (!bend) + return base_freq; + if (!range) + return base_freq; + + if (!base_freq) + return base_freq; + + if (range >= 8192) + range = 8192; + + bend = bend * range / 8192; /* Convert to cents */ + bend += vibrato_cents; + + if (!bend) + return base_freq; + + negative = bend < 0 ? 1 : 0; + + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; + + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) + { + multiplier *= 4; + bend -= 2400; + } + + semitones = bend / 100; + if (semitones > 99) + semitones = 99; + cents = bend % 100; + + amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000; + + if (negative) + return (base_freq * 10000) / amount; /* Bend down */ + else + return (base_freq * amount) / 10000; /* Bend up */ +} + + +void sequencer_init(void) +{ + /* drag in sequencer_syms.o */ + { + extern char sequencer_syms_symbol; + sequencer_syms_symbol = 0; + } + + if (sequencer_ok) + return; + MIDIbuf_init(); + queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ); + if (queue == NULL) + { + printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n"); + return; + } + iqueue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * IEV_SZ); + if (iqueue == NULL) + { + printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n"); + vfree(queue); + return; + } + sequencer_ok = 1; +} + +void sequencer_unload(void) +{ + if(queue) + { + vfree(queue); + queue=NULL; + } + if(iqueue) + { + vfree(iqueue); + iqueue=NULL; + } +} diff -Nru linux/sound/oss/sequencer_syms.c linux-2.4.19-pre5-mjc/sound/oss/sequencer_syms.c --- linux/sound/oss/sequencer_syms.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sequencer_syms.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,32 @@ +/* + * Exported symbols for sequencer driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char sequencer_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +EXPORT_SYMBOL(note_to_freq); +EXPORT_SYMBOL(compute_finetune); +EXPORT_SYMBOL(seq_copy_to_input); +EXPORT_SYMBOL(seq_input_event); +EXPORT_SYMBOL(sequencer_init); +EXPORT_SYMBOL(sequencer_timer); + +EXPORT_SYMBOL(sound_timer_init); +EXPORT_SYMBOL(sound_timer_interrupt); +EXPORT_SYMBOL(sound_timer_syncinterval); +EXPORT_SYMBOL(reprogram_timer); + +/* Tuning */ + +#define _SEQUENCER_C_ +#include "tuning.h" + +EXPORT_SYMBOL(cent_tuning); +EXPORT_SYMBOL(semitone_tuning); diff -Nru linux/sound/oss/sgalaxy.c linux-2.4.19-pre5-mjc/sound/oss/sgalaxy.c --- linux/sound/oss/sgalaxy.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sgalaxy.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,197 @@ +/* + * sound/sgalaxy.c + * + * Low level driver for Aztech Sound Galaxy cards. + * Copyright 1998 Artur Skawina + * + * Supported cards: + * Aztech Sound Galaxy Waverider Pro 32 - 3D + * Aztech Sound Galaxy Washington 16 + * + * Based on cs4232.c by Hannu Savolainen and Alan Cox. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to sb_rst() and sb_cmd() + */ + +#include +#include + +#include "sound_config.h" +#include "ad1848.h" + +static void sleep( unsigned howlong ) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +#define DPORT 0x80 + +/* Sound Blaster regs */ + +#define SBDSP_RESET 0x6 +#define SBDSP_READ 0xA +#define SBDSP_COMMAND 0xC +#define SBDSP_STATUS SBDSP_COMMAND +#define SBDSP_DATA_AVAIL 0xE + +static int __init sb_rst(int base) +{ + int i; + + outb( 1, base+SBDSP_RESET ); /* reset the DSP */ + outb( 0, base+SBDSP_RESET ); + + for ( i=0; i<500; i++ ) /* delay */ + inb(DPORT); + + for ( i=0; i<100000; i++ ) + { + if ( inb( base+SBDSP_DATA_AVAIL )&0x80 ) + break; + } + + if ( inb( base+SBDSP_READ )!=0xAA ) + return 0; + + return 1; +} + +static int __init sb_cmd( int base, unsigned char val ) +{ + int i; + + for ( i=100000; i; i-- ) + { + if ( (inb( base+SBDSP_STATUS )&0x80)==0 ) + { + outb( val, base+SBDSP_COMMAND ); + break; + } + } + return i; /* i>0 == success */ +} + + +#define ai_sgbase driver_use_1 + +static int __init probe_sgalaxy( struct address_info *ai ) +{ + if ( check_region( ai->io_base, 8 ) ) { + printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); + return 0; + } + + if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) + return probe_ms_sound(ai); /* The card is already active, check irq etc... */ + + if ( check_region( ai->ai_sgbase, 0x10 ) ) { + printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); + return 0; + } + + /* switch to MSS/WSS mode */ + + sb_rst( ai->ai_sgbase ); + + sb_cmd( ai->ai_sgbase, 9 ); + sb_cmd( ai->ai_sgbase, 0 ); + + sleep( HZ/10 ); + + return probe_ms_sound(ai); +} + +static void __init attach_sgalaxy( struct address_info *ai ) +{ + int n; + + request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" ); + + attach_ms_sound(ai, THIS_MODULE); + n=ai->slots[0]; + + if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) { + AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE ); /* Line-in */ + AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ + AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ + } +} + +static void __exit unload_sgalaxy( struct address_info *ai ) +{ + unload_ms_sound( ai ); + release_region( ai->ai_sgbase, 0x10 ); +} + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata sgbase = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(sgbase,"i"); + +static int __init init_sgalaxy(void) +{ + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + cfg.ai_sgbase = sgbase; + + if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.ai_sgbase == -1 ) { + printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n"); + return -EINVAL; + } + + if ( probe_sgalaxy(&cfg) == 0 ) + return -ENODEV; + + attach_sgalaxy(&cfg); + + return 0; +} + +static void __exit cleanup_sgalaxy(void) +{ + unload_sgalaxy(&cfg); +} + +module_init(init_sgalaxy); +module_exit(cleanup_sgalaxy); + +#ifndef MODULE +static int __init setup_sgalaxy(char *str) +{ + /* io, irq, dma, dma2, sgbase */ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + sgbase = ints[5]; + + return 1; +} + +__setup("sgalaxy=", setup_sgalaxy); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/skeleton.c linux-2.4.19-pre5-mjc/sound/oss/skeleton.c --- linux/sound/oss/skeleton.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/skeleton.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,223 @@ +/* + * PCI sound skeleton example + * + * (c) 1998 Red Hat Software + * + * This software may be used and distributed according to the + * terms of the GNU General Public License, incorporated herein by + * reference. + * + * This example is designed to be built in the linux/drivers/sound + * directory as part of a kernel build. The example is modular only + * drop me a note once you have a working modular driver and want + * to integrate it with the main code. + * -- Alan + * + * This is a first draft. Please report any errors, corrections or + * improvements to me. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "sound_config.h" + +/* + * Define our PCI vendor ID here + */ + +#ifndef PCI_VENDOR_MYIDENT +#define PCI_VENDOR_MYIDENT 0x125D + +/* + * PCI identity for the card. + */ + +#define PCI_DEVICE_ID_MYIDENT_MYCARD1 0x1969 +#endif + +#define CARD_NAME "ExampleWave 3D Pro Ultra ThingyWotsit" + +#define MAX_CARDS 8 + +/* + * Each address_info object holds the information about one of + * our card resources. In this case the MSS emulation of our + * ficticious card. Its used to manage and attach things. + */ + +static struct address_info mss_data[MAX_CARDS]; +static int cards = 0; + +/* + * Install the actual card. This is an example + */ + +static int mycard_install(struct pci_dev *pcidev) +{ + int iobase; + int mssbase; + int mpubase; + u8 x; + u16 w; + u32 v; + int i; + int dma; + + /* + * Our imaginary code has its I/O on PCI address 0, a + * MSS on PCI address 1 and an MPU on address 2 + * + * For the example we will only initialise the MSS + */ + + iobase = pci_resource_start(pcidev, 0); + mssbase = pci_resource_start(pcidev, 1); + mpubase = pci_resource_start(pcidev, 2); + + /* + * Reset the board + */ + + /* + * Wait for completion. udelay() waits in microseconds + */ + + udelay(100); + + /* + * Ok card ready. Begin setup proper. You might for example + * load the firmware here + */ + + dma = card_specific_magic(ioaddr); + + /* + * Turn on legacy mode (example), There are also byte and + * dword (32bit) PCI configuration function calls + */ + + pci_read_config_word(pcidev, 0x40, &w); + w&=~(1<<15); /* legacy decode on */ + w|=(1<<14); /* Reserved write as 1 in this case */ + w|=(1<<3)|(1<<1)|(1<<0); /* SB on , FM on, MPU on */ + pci_write_config_word(pcidev, 0x40, w); + + /* + * Let the user know we found his toy. + */ + + printk(KERN_INFO "Programmed "CARD_NAME" at 0x%X to legacy mode.\n", + iobase); + + /* + * Now set it up the description of the card + */ + + mss_data[cards].io_base = mssbase; + mss_data[cards].irq = pcidev->irq; + mss_data[cards].dma = dma; + + /* + * Check there is an MSS present + */ + + if(ad1848_detect(mssbase, NULL, mss_data[cards].osp)==0) + return 0; + + /* + * Initialize it + */ + + mss_data[cards].slots[3] = ad1848_init("MyCard MSS 16bit", + mssbase, + mss_data[cards].irq, + mss_data[cards].dma, + mss_data[cards].dma, + 0, + 0, + THIS_MODULE); + + cards++; + return 1; +} + + +/* + * This loop walks the PCI configuration database and finds where + * the sound cards are. + */ + +int init_mycard(void) +{ + struct pci_dev *pcidev=NULL; + int count=0; + + if(!pci_present()) + return -ENODEV; + + + while((pcidev = pci_find_device(PCI_VENDOR_MYIDENT, PCI_DEVICE_ID_MYIDENT_MYCARD1, pcidev))!=NULL) + { + if (pci_enable_device(pcidev)) + continue; + count+=mycard_install(pcidev); + if(count) + return 0; + if(count==MAX_CARDS) + break; + } + + if(count==0) + return -ENODEV; + return 0; +} + +/* + * This function is called when the user or kernel loads the + * module into memory. + */ + + +int init_module(void) +{ + if(init_mycard()<0) + { + printk(KERN_ERR "No "CARD_NAME" cards found.\n"); + return -ENODEV; + } + + return 0; +} + +/* + * This is called when it is removed. It will only be removed + * when its use count is 0. + */ + +void cleanup_module(void) +{ + for(i=0;i< cards; i++) + { + /* + * Free attached resources + */ + + ad1848_unload(mss_data[i].io_base, + mss_data[i].irq, + mss_data[i].dma, + mss_data[i].dma, + 0); + /* + * And disconnect the device from the kernel + */ + sound_unload_audiodevice(mss_data[i].slots[3]); + } +} + diff -Nru linux/sound/oss/sonicvibes.c linux-2.4.19-pre5-mjc/sound/oss/sonicvibes.c --- linux/sound/oss/sonicvibes.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sonicvibes.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2767 @@ +/*****************************************************************************/ + +/* + * sonicvibes.c -- S3 Sonic Vibes audio driver. + * + * Copyright (C) 1998-2001 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * none so far + * + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.1998 0.1 Initial release + * 10.05.1998 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.1998 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in sv_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.1998 0.4 Don't allow excessive interrupt rates + * 08.06.1998 0.5 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.1998 0.6 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.1998 0.7 Fix realplayer problems - dac.count issues + * 10.12.1998 0.8 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.9 Fix a few f_file & FMODE_ bugs + * 06.01.1999 0.10 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 05.04.1999 0.13 added code to sv_read and sv_write which should detect + * lockups of the sound chip and revive it. This is basically + * an ugly hack, but at least applications using this driver + * won't hang forever. I don't know why these lockups happen, + * it might well be the motherboard chipset (an early 486 PCI + * board with ALI chipset), since every busmastering 100MB + * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) + * exhibit similar behaviour (they work for a couple of packets + * and then lock up and can be revived by ifconfig down/up). + * 07.04.1999 0.14 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: dmaio hack might still be wrong on archs other than i386 + * 15.06.1999 0.15 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.16 Add pci_set_master + * 03.08.1999 0.17 adapt to Linus' new __setup/__initcall + * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" + * 12.08.1999 0.18 module_init/__setup fixes + * 24.08.1999 0.19 get rid of the dmaio kludge, replace with allocate_resource + * 31.08.1999 0.20 add spin_lock_init + * use new resource allocation to allocate DDMA IO space + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.21 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.22 More waitqueue races fixed + * 01.12.1999 0.23 New argument to allocate_resource + * 07.12.1999 0.24 More allocate_resource semantics change + * 08.01.2000 0.25 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * use Martin Mares' pci_assign_resource + * 07.02.2000 0.26 Use pci_alloc_consistent and pci_register_driver + * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.28 More dma buffer initializations, patch from + * Tjeerd Mulder + * 31.01.2001 0.29 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * 18.05.2001 0.30 PCI probing and error values cleaned up by Marcus + * Meissner + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" + + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES) + +#define SV_EXTENT_SB 0x10 +#define SV_EXTENT_ENH 0x10 +#define SV_EXTENT_SYNTH 0x4 +#define SV_EXTENT_MIDI 0x4 +#define SV_EXTENT_GAME 0x8 +#define SV_EXTENT_DMA 0x10 + +/* + * we are not a bridge and thus use a resource for DDMA that is used for bridges but + * left empty for normal devices + */ +#define RESOURCE_SB 0 +#define RESOURCE_ENH 1 +#define RESOURCE_SYNTH 2 +#define RESOURCE_MIDI 3 +#define RESOURCE_GAME 4 +#define RESOURCE_DDMA 7 + +#define SV_MIDI_DATA 0 +#define SV_MIDI_COMMAND 1 +#define SV_MIDI_STATUS 1 + +#define SV_DMA_ADDR0 0 +#define SV_DMA_ADDR1 1 +#define SV_DMA_ADDR2 2 +#define SV_DMA_ADDR3 3 +#define SV_DMA_COUNT0 4 +#define SV_DMA_COUNT1 5 +#define SV_DMA_COUNT2 6 +#define SV_DMA_MODE 0xb +#define SV_DMA_RESET 0xd +#define SV_DMA_MASK 0xf + +/* + * DONT reset the DMA controllers unless you understand + * the reset semantics. Assuming reset semantics as in + * the 8237 does not work. + */ + +#define DMA_MODE_AUTOINIT 0x10 +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ + +#define SV_CODEC_CONTROL 0 +#define SV_CODEC_INTMASK 1 +#define SV_CODEC_STATUS 2 +#define SV_CODEC_IADDR 4 +#define SV_CODEC_IDATA 5 + +#define SV_CCTRL_RESET 0x80 +#define SV_CCTRL_INTADRIVE 0x20 +#define SV_CCTRL_WAVETABLE 0x08 +#define SV_CCTRL_REVERB 0x04 +#define SV_CCTRL_ENHANCED 0x01 + +#define SV_CINTMASK_DMAA 0x01 +#define SV_CINTMASK_DMAC 0x04 +#define SV_CINTMASK_SPECIAL 0x08 +#define SV_CINTMASK_UPDOWN 0x40 +#define SV_CINTMASK_MIDI 0x80 + +#define SV_CSTAT_DMAA 0x01 +#define SV_CSTAT_DMAC 0x04 +#define SV_CSTAT_SPECIAL 0x08 +#define SV_CSTAT_UPDOWN 0x40 +#define SV_CSTAT_MIDI 0x80 + +#define SV_CIADDR_TRD 0x80 +#define SV_CIADDR_MCE 0x40 + +/* codec indirect registers */ +#define SV_CIMIX_ADCINL 0x00 +#define SV_CIMIX_ADCINR 0x01 +#define SV_CIMIX_AUX1INL 0x02 +#define SV_CIMIX_AUX1INR 0x03 +#define SV_CIMIX_CDINL 0x04 +#define SV_CIMIX_CDINR 0x05 +#define SV_CIMIX_LINEINL 0x06 +#define SV_CIMIX_LINEINR 0x07 +#define SV_CIMIX_MICIN 0x08 +#define SV_CIMIX_SYNTHINL 0x0A +#define SV_CIMIX_SYNTHINR 0x0B +#define SV_CIMIX_AUX2INL 0x0C +#define SV_CIMIX_AUX2INR 0x0D +#define SV_CIMIX_ANALOGINL 0x0E +#define SV_CIMIX_ANALOGINR 0x0F +#define SV_CIMIX_PCMINL 0x10 +#define SV_CIMIX_PCMINR 0x11 + +#define SV_CIGAMECONTROL 0x09 +#define SV_CIDATAFMT 0x12 +#define SV_CIENABLE 0x13 +#define SV_CIUPDOWN 0x14 +#define SV_CIREVISION 0x15 +#define SV_CIADCOUTPUT 0x16 +#define SV_CIDMAABASECOUNT1 0x18 +#define SV_CIDMAABASECOUNT0 0x19 +#define SV_CIDMACBASECOUNT1 0x1c +#define SV_CIDMACBASECOUNT0 0x1d +#define SV_CIPCMSR0 0x1e +#define SV_CIPCMSR1 0x1f +#define SV_CISYNTHSR0 0x20 +#define SV_CISYNTHSR1 0x21 +#define SV_CIADCCLKSOURCE 0x22 +#define SV_CIADCALTSR 0x23 +#define SV_CIADCPLLM 0x24 +#define SV_CIADCPLLN 0x25 +#define SV_CISYNTHPLLM 0x26 +#define SV_CISYNTHPLLN 0x27 +#define SV_CIUARTCONTROL 0x2a +#define SV_CIDRIVECONTROL 0x2b +#define SV_CISRSSPACE 0x2c +#define SV_CISRSCENTER 0x2d +#define SV_CIWAVETABLESRC 0x2e +#define SV_CIANALOGPWRDOWN 0x30 +#define SV_CIDIGITALPWRDOWN 0x31 + + +#define SV_CIMIX_ADCSRC_CD 0x20 +#define SV_CIMIX_ADCSRC_DAC 0x40 +#define SV_CIMIX_ADCSRC_AUX2 0x60 +#define SV_CIMIX_ADCSRC_LINE 0x80 +#define SV_CIMIX_ADCSRC_AUX1 0xa0 +#define SV_CIMIX_ADCSRC_MIC 0xc0 +#define SV_CIMIX_ADCSRC_MIXOUT 0xe0 +#define SV_CIMIX_ADCSRC_MASK 0xe0 + +#define SV_CFMT_STEREO 0x01 +#define SV_CFMT_16BIT 0x02 +#define SV_CFMT_MASK 0x03 +#define SV_CFMT_ASHIFT 0 +#define SV_CFMT_CSHIFT 4 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define SV_CENABLE_PPE 0x4 +#define SV_CENABLE_RE 0x2 +#define SV_CENABLE_PE 0x1 + + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 2 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +/* --------------------------------------------------------------------- */ + +struct sv_state { + /* magic */ + unsigned int magic; + + /* list of sonicvibes devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned long iosb, ioenh, iosynth, iomidi; /* long for SPARC */ + unsigned int iodmaa, iodmac, irq; + + /* mixer stuff */ + struct { + unsigned int modcnt; +#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS + unsigned short vol[13]; +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } mix; + + /* wave stuff */ + unsigned int rateadc, ratedac; + unsigned char fmt, enable; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct gameport gameport; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); +static unsigned long wavetable_mem = 0; + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +static inline unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* --------------------------------------------------------------------- */ + +/* + * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. + */ + +#undef DMABYTEIO + +static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa, u; + + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count--; + outl(addr, s->iodmaa + SV_DMA_ADDR0); + outl(count, s->iodmaa + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x18, s->iodmaa + SV_DMA_MODE); +} + +static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac, u; + + count >>= 1; + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count >>= 1; + count--; + outl(addr, s->iodmac + SV_DMA_ADDR0); + outl(count, s->iodmac + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x14, s->iodmac + SV_DMA_MODE); +} + +static inline unsigned get_dmaa(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return v + 1; +#else /* DMABYTEIO */ + return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1; +#endif /* DMABYTEIO */ +} + +static inline unsigned get_dmac(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return (v + 1) << 1; +#else /* DMABYTEIO */ + return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +#endif /* DMABYTEIO */ +} + +static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +static unsigned char rdindir(struct sv_state *s, unsigned char idx) +{ + unsigned char v; + + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + v = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + return v; +} + +static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR); + if (mask) { + s->fmt = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(0, s->ioenh + SV_CODEC_IADDR); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); +} + +static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +#define REFFREQUENCY 24576000 +#define ADCMULT 512 +#define FULLRATE 48000 + +static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) +{ + unsigned long flags; + unsigned char r, m=0, n=0; + unsigned xm, xn, xr, xd, metric = ~0U; + /* the warnings about m and n used uninitialized are bogus and may safely be ignored */ + + if (rate < 625000/ADCMULT) + rate = 625000/ADCMULT; + if (rate > 150000000/ADCMULT) + rate = 150000000/ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 35; xn++) + for (xm = 3; xm < 130; xm++) { + xr = REFFREQUENCY/ADCMULT * xm / xn; + xd = abs((signed)(xr - rate)); + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(m, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(r | n, s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7); +} + +#if 0 + +static unsigned getpll(struct sv_state *s, unsigned char reg) +{ + unsigned long flags; + unsigned char m, n; + + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + m = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + n = inb(s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7); +} + +#endif + +static void set_dac_rate(struct sv_state *s, unsigned rate) +{ + unsigned div; + unsigned long flags; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + div = (rate * 65536 + FULLRATE/2) / FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIPCMSR1, div >> 8); + wrindir(s, SV_CIPCMSR0, div); + spin_unlock_irqrestore(&s->lock, flags); + s->ratedac = (div * FULLRATE + 32768) / 65536; +} + +static void set_adc_rate(struct sv_state *s, unsigned rate) +{ + unsigned long flags; + unsigned rate1, rate2, div; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + rate1 = setpll(s, SV_CIADCPLLM, rate); + div = (48000 + rate/2) / rate; + if (div > 8) + div = 8; + rate2 = (48000 + div/2) / div; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIADCALTSR, (div-1) << 4); + if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) { + wrindir(s, SV_CIADCCLKSOURCE, 0x10); + s->rateadc = rate2; + } else { + wrindir(s, SV_CIADCCLKSOURCE, 0x00); + s->rateadc = rate1; + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE); + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->enable |= SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static void dealloc_dmabuf(struct sv_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + + +/* DMAA is used for playback, DMAC is used for recording */ + +static int prog_dmabuf(struct sv_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + s->enable &= ~SV_CENABLE_RE; + fmt >>= SV_CFMT_CSHIFT; + } else { + s->enable &= ~SV_CENABLE_PE; + fmt >>= SV_CFMT_ASHIFT; + } + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); + fmt &= SV_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + set_dmac(s, db->dmaaddr, db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1); + } else { + set_dmaa(s, db->dmaaddr, db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1); + } + spin_unlock_irqrestore(&s->lock, flags); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline void clear_advance(struct sv_state *s) +{ + unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void sv_update_ptr(struct sv_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->enable &= ~SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* hold spinlock for the following! */ +static void sv_handle_midi(struct sv_state *s) +{ + unsigned char ch; + int wake; + + wake = 0; + while (!(inb(s->iomidi+1) & 0x80)) { + ch = inb(s->iomidi); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->iomidi); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static void sv_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sv_state *s = (struct sv_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->ioenh + SV_CODEC_STATUS); + if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI))) + return; + spin_lock(&s->lock); + sv_update_ptr(s); + sv_handle_midi(s); + spin_unlock(&s->lock); +} + +static void sv_midi_timer(unsigned long data) +{ + struct sv_state *s = (struct sv_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SV_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 + +static const struct { + unsigned left:5; + unsigned right:5; + unsigned type:3; + unsigned rec:3; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL, SV_CIMIX_ADCINR, MT_4, 0 }, + [SOUND_MIXER_LINE1] = { SV_CIMIX_AUX1INL, SV_CIMIX_AUX1INR, MT_5MUTE, 5 }, + [SOUND_MIXER_CD] = { SV_CIMIX_CDINL, SV_CIMIX_CDINR, MT_5MUTE, 1 }, + [SOUND_MIXER_LINE] = { SV_CIMIX_LINEINL, SV_CIMIX_LINEINR, MT_5MUTE, 4 }, + [SOUND_MIXER_MIC] = { SV_CIMIX_MICIN, SV_CIMIX_ADCINL, MT_4MUTEMONO, 6 }, + [SOUND_MIXER_SYNTH] = { SV_CIMIX_SYNTHINL, SV_CIMIX_SYNTHINR, MT_5MUTE, 2 }, + [SOUND_MIXER_LINE2] = { SV_CIMIX_AUX2INL, SV_CIMIX_AUX2INR, MT_5MUTE, 3 }, + [SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE, 7 }, + [SOUND_MIXER_PCM] = { SV_CIMIX_PCMINL, SV_CIMIX_PCMINR, MT_6MUTE, 0 } +}; + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + +static int return_mixval(struct sv_state *s, unsigned i, int *arg) +{ + unsigned long flags; + unsigned char l, r, rl, rr; + + spin_lock_irqsave(&s->lock, flags); + l = rdindir(s, mixtable[i].left); + r = rdindir(s, mixtable[i].right); + spin_unlock_irqrestore(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + r &= 0xf; + l &= 0xf; + rl = 10 + 6 * (l & 15); + rr = 10 + 6 * (r & 15); + break; + + case MT_4MUTEMONO: + rl = 55 - 3 * (l & 15); + if (r & 0x10) + rl += 45; + rr = rl; + r = l; + break; + + case MT_5MUTE: + default: + rl = 100 - 3 * (l & 31); + rr = 100 - 3 * (r & 31); + break; + + case MT_6MUTE: + rl = 100 - 3 * (l & 63) / 2; + rr = 100 - 3 * (r & 63) / 2; + break; + } + if (l & 0x80) + rl = 0; + if (r & 0x80) + rr = 0; + return put_user((rr << 8) | rl, arg); +} + +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = +{ + [SOUND_MIXER_RECLEV] = 1, + [SOUND_MIXER_LINE1] = 2, + [SOUND_MIXER_CD] = 3, + [SOUND_MIXER_LINE] = 4, + [SOUND_MIXER_MIC] = 5, + [SOUND_MIXER_SYNTH] = 6, + [SOUND_MIXER_LINE2] = 7, + [SOUND_MIXER_VOLUME] = 8, + [SOUND_MIXER_PCM] = 9 +}; + +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static unsigned mixer_recmask(struct sv_state *s) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&s->lock, flags); + j = rdindir(s, SV_CIMIX_ADCINL) >> 5; + spin_unlock_irqrestore(&s->lock, flags); + j &= 7; + for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++); + return 1 << i; +} + +static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (cmd == SOUND_MIXER_PRIVATE1) { /* SRS settings */ + if (get_user(val, (int *)arg)) + return -EFAULT; + spin_lock_irqsave(&s->lock, flags); + if (val & 1) { + if (val & 2) { + l = 4 - ((val >> 2) & 7); + if (l & ~3) + l = 4; + r = 4 - ((val >> 5) & 7); + if (r & ~3) + r = 4; + wrindir(s, SV_CISRSSPACE, l); + wrindir(s, SV_CISRSCENTER, r); + } else + wrindir(s, SV_CISRSSPACE, 0x80); + } + l = rdindir(s, SV_CISRSSPACE); + r = rdindir(s, SV_CISRSCENTER); + spin_unlock_irqrestore(&s->lock, flags); + if (l & 0x80) + return put_user(0, (int *)arg); + return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, (int *)arg); + } + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return return_mixval(s, i, (int *)arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, (int *)arg)) + return -EFAULT; + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s);*/ + else if (i > 1) + val &= ~mixer_recmask(s); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (mixtable[i].rec) + break; + } + if (!mixtable[i].rec) + return 0; + spin_lock_irqsave(&s->lock, flags); + frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5); + frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + l = val & 0xff; + r = (val >> 8) & 0xff; + if (mixtable[i].type == MT_4MUTEMONO) + l = (r + l) / 2; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rr = 0; + if (l < 10) + rl = 0x80; + else { + if (l >= 55) { + rr = 0x10; + l -= 45; + } + rl = (55 - l) / 3; + } + wrindir(s, mixtable[i].left, rl); + frobindir(s, mixtable[i].right, ~0x10, rr); + break; + + case MT_5MUTE: + if (l < 7) + rl = 0x80; + else + rl = (100 - l) / 3; + if (r < 7) + rr = 0x80; + else + rr = (100 - r) / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x80; + else + rl = (100 - l) * 2 / 3; + if (r < 6) + rr = 0x80; + else + rr = (100 - r) * 2 / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return return_mixval(s, i, (int *)arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + s->mix.vol[volidx[i]-1] = val; + return put_user(s->mix.vol[volidx[i]-1], (int *)arg); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } +} + +/* --------------------------------------------------------------------- */ + +static int sv_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return 0; +} + +static int sv_release_mixdev(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations sv_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: sv_ioctl_mixdev, + open: sv_open_mixdev, + release: sv_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct sv_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; + tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "sv: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac.enabled) + start_dac(s); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf(s, 1)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf(s, 0)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int sv_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + stop_dac(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int sv_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned char fmtm = ~0, fmts = 0; + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + return 0; +} + +static int sv_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: sv_read, + write: sv_write, + poll: sv_poll, + ioctl: sv_ioctl, + mmap: sv_mmap, + open: sv_open, + release: sv_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + sv_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int sv_midi_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + //outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL); + outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */ + wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */ + outb(0xff, s->iomidi+1); /* reset command */ + outb(0x3f, s->iomidi+1); /* uart command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = sv_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + return 0; +} + +static int sv_midi_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + unlock_kernel(); + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "sv: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_midi_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: sv_midi_read, + write: sv_midi_write, + poll: sv_midi_poll, + open: sv_midi_open, + release: sv_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct sv_state *s = (struct sv_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->iosynth + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->iosynth); + outb((p.kbd_split & 1) << 6, s->iosynth+1); + outb(0xbd, s->iosynth); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->iosynth+2); + outb(arg, s->iosynth+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->iosynth+2); + outb(arg & 1, s->iosynth+3); + return 0; + + default: + return -EINVAL; + } +} + +static int sv_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_dmfm == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + /* init the stuff */ + outb(1, s->iosynth); + outb(0x20, s->iosynth+1); /* enable waveforms */ + outb(4, s->iosynth+2); + outb(0, s->iosynth+3); /* no 4op enabled */ + outb(5, s->iosynth+2); + outb(1, s->iosynth+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + return 0; +} + +static int sv_dmfm_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_dmfm_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: sv_dmfm_ioctl, + open: sv_dmfm_open, + release: sv_dmfm_release, +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int reverb[NR_DEVICE] = { 0, }; + +#if 0 +static int wavetable[NR_DEVICE] = { 0, }; +#endif + +static unsigned int devindex = 0; + +MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM"); +#if 0 +MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); +#endif + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("S3 SonicVibes Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 } +}; + +#define RSRCISIOREGION(dev,num) (pci_resource_start((dev), (num)) != 0 && \ + (pci_resource_flags((dev), (num)) & IORESOURCE_IO)) + +static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + static char __initdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller"; + struct sv_state *s; + mm_segment_t fs; + int i, val, ret; + char *ddmaname; + unsigned ddmanamelen; + + if ((ret=pci_enable_device(pcidev))) + return ret; + + if (!RSRCISIOREGION(pcidev, RESOURCE_SB) || + !RSRCISIOREGION(pcidev, RESOURCE_ENH) || + !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) || + !RSRCISIOREGION(pcidev, RESOURCE_MIDI) || + !RSRCISIOREGION(pcidev, RESOURCE_GAME)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + if (pci_set_dma_mask(pcidev, 0x00ffffff)) { + printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n"); + return -ENODEV; + } + /* try to allocate a DDMA resource if not already available */ + if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) { + pcidev->resource[RESOURCE_DDMA].start = 0; + pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1; + pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO; + ddmanamelen = strlen(sv_ddma_name)+1; + if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL))) + return -1; + memcpy(ddmaname, sv_ddma_name, ddmanamelen); + pcidev->resource[RESOURCE_DDMA].name = ddmaname; + if (pci_assign_resource(pcidev, RESOURCE_DDMA)) { + pcidev->resource[RESOURCE_DDMA].name = NULL; + kfree(ddmaname); + printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n"); + return -EBUSY; + } + } + if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { + printk(KERN_WARNING "sv: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct sv_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = SV_MAGIC; + s->dev = pcidev; + s->iosb = pci_resource_start(pcidev, RESOURCE_SB); + s->ioenh = pci_resource_start(pcidev, RESOURCE_ENH); + s->iosynth = pci_resource_start(pcidev, RESOURCE_SYNTH); + s->iomidi = pci_resource_start(pcidev, RESOURCE_MIDI); + s->iodmaa = pci_resource_start(pcidev, RESOURCE_DDMA); + s->iodmac = pci_resource_start(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA; + s->gameport.io = pci_resource_start(pcidev, RESOURCE_GAME); + pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9); /* enable and use extended mode */ + pci_write_config_dword(pcidev, 0x48, s->iodmac | 9); /* enable */ + printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#x %#x %#x\n", + s->iosb, s->ioenh, s->iosynth, s->iomidi, s->gameport.io, s->iodmaa, s->iodmac); + s->irq = pcidev->irq; + + /* hack */ + pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ + + ret = -EBUSY; + if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); + goto err_region5; + } + if (!request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA")) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); + goto err_region4; + } + if (!request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC")) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); + goto err_region3; + } + if (!request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); + goto err_region2; + } + if (!request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); + goto err_region1; + } + if (s->gameport.io && !request_region(s->gameport.io, SV_EXTENT_GAME, "ESS Solo1")) { + printk(KERN_ERR "sv: gameport io ports in use\n"); + s->gameport.io = 0; + } + /* initialize codec registers */ + outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ + udelay(50); + outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ + udelay(50); + outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */ + | (reverb[devindex] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL); + inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ + wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ + wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ + outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); + /* outb(0xff, s->iodmaa + SV_DMA_RESET); */ + /* outb(0xff, s->iodmac + SV_DMA_RESET); */ + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ + wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ + wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ + setpll(s, SV_CIADCPLLM, 8000); + wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ + wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); + wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); + wrindir(s, SV_CIADCOUTPUT, 0); + /* request irq */ + if ((ret=request_irq(s->irq,sv_interrupt,SA_SHIRQ,"S3 SonicVibes",s))) { + printk(KERN_ERR "sv: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n", + s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev3; + } + if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) { + ret = s->dev_dmfm; + goto err_dev4; + } + pci_set_master(pcidev); /* enable bus mastering */ + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* register gameport */ + gameport_register_port(&s->gameport); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev4: + unregister_sound_midi(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "sv: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + if (s->gameport.io) + release_region(s->gameport.io, SV_EXTENT_GAME); + release_region(s->iosynth, SV_EXTENT_SYNTH); + err_region1: + release_region(s->iomidi, SV_EXTENT_MIDI); + err_region2: + release_region(s->iodmac, SV_EXTENT_DMA); + err_region3: + release_region(s->iodmaa, SV_EXTENT_DMA); + err_region4: + release_region(s->ioenh, SV_EXTENT_ENH); + err_region5: + kfree(s); + return ret; +} + +static void __devinit sv_remove(struct pci_dev *dev) +{ + struct sv_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); + outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */ + synchronize_irq(); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ + /*outb(0, s->iodmaa + SV_DMA_RESET);*/ + /*outb(0, s->iodmac + SV_DMA_RESET);*/ + free_irq(s->irq, s); + if (s->gameport.io) { + gameport_unregister_port(&s->gameport); + release_region(s->gameport.io, SV_EXTENT_GAME); + } + release_region(s->iodmac, SV_EXTENT_DMA); + release_region(s->iodmaa, SV_EXTENT_DMA); + release_region(s->ioenh, SV_EXTENT_ENH); + release_region(s->iomidi, SV_EXTENT_MIDI); + release_region(s->iosynth, SV_EXTENT_SYNTH); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver sv_driver = { + name: "sonicvibes", + id_table: id_table, + probe: sv_probe, + remove: sv_remove +}; + +static int __init init_sonicvibes(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "sv: version v0.30 time " __TIME__ " " __DATE__ "\n"); +#if 0 + if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) + printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); +#endif + return pci_module_init(&sv_driver); +} + +static void __exit cleanup_sonicvibes(void) +{ + printk(KERN_INFO "sv: unloading\n"); + pci_unregister_driver(&sv_driver); + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); +} + +module_init(init_sonicvibes); +module_exit(cleanup_sonicvibes); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: sonicvibes=[reverb] sonicvibesdmaio=dmaioaddr */ + +static int __init sonicvibes_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; +#if 0 + if (get_option(&str, &reverb[nr_dev]) == 2) + (void)get_option(&str, &wavetable[nr_dev]); +#else + (void)get_option(&str, &reverb[nr_dev]); +#endif + + nr_dev++; + return 1; +} + +__setup("sonicvibes=", sonicvibes_setup); + +#endif /* MODULE */ diff -Nru linux/sound/oss/sound_calls.h linux-2.4.19-pre5-mjc/sound/oss/sound_calls.h --- linux/sound/oss/sound_calls.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sound_calls.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,91 @@ +/* + * DMA buffer calls + */ + +int DMAbuf_open(int dev, int mode); +int DMAbuf_release(int dev, int mode); +int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock); +int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock); +int DMAbuf_rmchars(int dev, int buff_no, int c); +int DMAbuf_start_output(int dev, int buff_no, int l); +int DMAbuf_move_wrpointer(int dev, int l); +/* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */ +void DMAbuf_init(int dev, int dma1, int dma2); +void DMAbuf_deinit(int dev); +int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); +int DMAbuf_open_dma (int dev); +void DMAbuf_close_dma (int dev); +void DMAbuf_inputintr(int dev); +void DMAbuf_outputintr(int dev, int underflow_flag); +struct dma_buffparms; +int DMAbuf_space_in_queue (int dev); +int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap); +int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction); +void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap); +unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait); +void DMAbuf_start_devices(unsigned int devmask); +void DMAbuf_reset (int dev); +int DMAbuf_sync (int dev); + +/* + * System calls for /dev/dsp and /dev/audio (audio.c) + */ + +int audio_read (int dev, struct file *file, char *buf, int count); +int audio_write (int dev, struct file *file, const char *buf, int count); +int audio_open (int dev, struct file *file); +void audio_release (int dev, struct file *file); +int audio_ioctl (int dev, struct file *file, + unsigned int cmd, caddr_t arg); +void audio_init_devices (void); +void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording); +int dma_ioctl (int dev, unsigned int cmd, caddr_t arg); + +/* + * System calls for the /dev/sequencer + */ + +int sequencer_read (int dev, struct file *file, char *buf, int count); +int sequencer_write (int dev, struct file *file, const char *buf, int count); +int sequencer_open (int dev, struct file *file); +void sequencer_release (int dev, struct file *file); +int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg); +unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait); + +void sequencer_init (void); +void sequencer_unload (void); +void sequencer_timer(unsigned long dummy); +int note_to_freq(int note_num); +unsigned long compute_finetune(unsigned long base_freq, int bend, int range, + int vibrato_bend); +void seq_input_event(unsigned char *event, int len); +void seq_copy_to_input (unsigned char *event, int len); + +/* + * System calls for the /dev/midi + */ + +int MIDIbuf_read (int dev, struct file *file, char *buf, int count); +int MIDIbuf_write (int dev, struct file *file, const char *buf, int count); +int MIDIbuf_open (int dev, struct file *file); +void MIDIbuf_release (int dev, struct file *file); +int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg); +unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait); +int MIDIbuf_avail(int dev); + +void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); +void MIDIbuf_init(void); + + +/* From soundcard.c */ +void request_sound_timer (int count); +void sound_stop_timer(void); +void conf_printf(char *name, struct address_info *hw_config); +void conf_printf2(char *name, int base, int irq, int dma, int dma2); + +/* From sound_timer.c */ +void sound_timer_interrupt(void); +void sound_timer_syncinterval(unsigned int new_usecs); + +/* From midi_synth.c */ +void do_midi_msg (int synthno, unsigned char *msg, int mlen); diff -Nru linux/sound/oss/sound_config.h linux-2.4.19-pre5-mjc/sound/oss/sound_config.h --- linux/sound/oss/sound_config.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sound_config.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,154 @@ +/* sound_config.h + * + * A driver for sound cards, misc. configuration parameters. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + + +#ifndef _SOUND_CONFIG_H_ +#define _SOUND_CONFIG_H_ + +#include +#include +#include + +#include "os.h" +#include "soundvers.h" + + +#ifndef SND_DEFAULT_ENABLE +#define SND_DEFAULT_ENABLE 1 +#endif + +#ifndef MAX_REALTIME_FACTOR +#define MAX_REALTIME_FACTOR 4 +#endif + +/* + * Use always 64k buffer size. There is no reason to use shorter. + */ +#undef DSP_BUFFSIZE +#define DSP_BUFFSIZE (64*1024) + +#ifndef DSP_BUFFCOUNT +#define DSP_BUFFCOUNT 1 /* 1 is recommended. */ +#endif + +#define FM_MONO 0x388 /* This is the I/O address used by AdLib */ + +#ifndef CONFIG_PAS_BASE +#define CONFIG_PAS_BASE 0x388 +#endif + +/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the + driver. (There is no need to alter this) */ +#define SEQ_MAX_QUEUE 1024 + +#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */ +/* 128 instruments for general MIDI setup and 16 unassigned */ + +#define SND_NDEVS 256 /* Number of supported devices */ + +#define DSP_DEFAULT_SPEED 8000 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 5 +#define MAX_SYNTH_DEV 5 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 4 + +struct address_info { + int io_base; + int irq; + int dma; + int dma2; + int always_detect; /* 1=Trust me, it's there */ + char *name; + int driver_use_1; /* Driver defined field 1 */ + int driver_use_2; /* Driver defined field 2 */ + int *osp; /* OS specific info */ + int card_subtype; /* Driver specific. Usually 0 */ + void *memptr; /* Module memory chainer */ + int slots[6]; /* To remember driver slot ids */ +}; + +#define SYNTH_MAX_VOICES 32 + +struct voice_alloc_info { + int max_voice; + int used_voices; + int ptr; /* For device specific use */ + unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */ + int timestamp; + int alloc_times[SYNTH_MAX_VOICES]; + }; + +struct channel_info { + int pgm_num; + int bender_value; + int bender_range; + unsigned char controllers[128]; + }; + +/* + * Process wakeup reasons + */ +#define WK_NONE 0x00 +#define WK_WAKEUP 0x01 +#define WK_TIMEOUT 0x02 +#define WK_SIGNAL 0x04 +#define WK_SLEEP 0x08 +#define WK_SELECT 0x10 +#define WK_ABORT 0x20 + +#define OPEN_READ PCM_ENABLE_INPUT +#define OPEN_WRITE PCM_ENABLE_OUTPUT +#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE) + +#if OPEN_READ == FMODE_READ && OPEN_WRITE == FMODE_WRITE + +extern __inline__ int translate_mode(struct file *file) +{ + return file->f_mode; +} + +#else + +extern __inline__ int translate_mode(struct file *file) +{ + return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) | + ((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0); +} + +#endif + + +#include "sound_calls.h" +#include "dev_table.h" + +#ifndef DEB +#define DEB(x) +#endif + +#ifndef DDB +#define DDB(x) {} +#endif + +#ifndef MDB +#ifdef MODULE +#define MDB(x) x +#else +#define MDB(x) +#endif +#endif + +#define TIMER_ARMED 121234 +#define TIMER_NOT_ARMED 1 + +#endif diff -Nru linux/sound/oss/sound_firmware.h linux-2.4.19-pre5-mjc/sound/oss/sound_firmware.h --- linux/sound/oss/sound_firmware.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sound_firmware.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2 @@ +extern int mod_firmware_load(const char *fn, char **fp); + diff -Nru linux/sound/oss/sound_syms.c linux-2.4.19-pre5-mjc/sound/oss/sound_syms.c --- linux/sound/oss/sound_syms.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sound_syms.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,53 @@ +/* + * The sound core exports the following symbols to the rest of + * modulespace. + * + * (C) Copyright 1997 Alan Cox, Licensed under the GNU GPL + * + * Thu May 27 1999 Andrew J. Kroll + * left out exported symbol... fixed + */ + +#include +#include "sound_config.h" +#include "sound_calls.h" + +char sound_syms_symbol; + +EXPORT_SYMBOL(mixer_devs); +EXPORT_SYMBOL(audio_devs); +EXPORT_SYMBOL(num_mixers); +EXPORT_SYMBOL(num_audiodevs); + +EXPORT_SYMBOL(midi_devs); +EXPORT_SYMBOL(num_midis); +EXPORT_SYMBOL(synth_devs); +EXPORT_SYMBOL(num_synths); + +EXPORT_SYMBOL(sound_timer_devs); +EXPORT_SYMBOL(num_sound_timers); + +EXPORT_SYMBOL(sound_install_audiodrv); +EXPORT_SYMBOL(sound_install_mixer); +EXPORT_SYMBOL(sound_alloc_dma); +EXPORT_SYMBOL(sound_free_dma); +EXPORT_SYMBOL(sound_open_dma); +EXPORT_SYMBOL(sound_close_dma); +EXPORT_SYMBOL(sound_alloc_audiodev); +EXPORT_SYMBOL(sound_alloc_mididev); +EXPORT_SYMBOL(sound_alloc_mixerdev); +EXPORT_SYMBOL(sound_alloc_timerdev); +EXPORT_SYMBOL(sound_alloc_synthdev); +EXPORT_SYMBOL(sound_unload_audiodev); +EXPORT_SYMBOL(sound_unload_mididev); +EXPORT_SYMBOL(sound_unload_mixerdev); +EXPORT_SYMBOL(sound_unload_timerdev); +EXPORT_SYMBOL(sound_unload_synthdev); + +EXPORT_SYMBOL(load_mixer_volumes); + +EXPORT_SYMBOL(conf_printf); +EXPORT_SYMBOL(conf_printf2); + +MODULE_DESCRIPTION("OSS Sound subsystem"); +MODULE_AUTHOR("Hannu Savolainen, et al."); diff -Nru linux/sound/oss/sound_timer.c linux-2.4.19-pre5-mjc/sound/oss/sound_timer.c --- linux/sound/oss/sound_timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sound_timer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,318 @@ +/* + * sound/sound_timer.c + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + */ +#include + + +#include "sound_config.h" + +static volatile int initialized, opened, tmr_running; +static volatile time_t tmr_offs, tmr_ctr; +static volatile unsigned long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile unsigned long curr_ticks; +static volatile unsigned long next_event_time; +static unsigned long prev_event_time; +static volatile unsigned long usecs_per_tmr; /* Length of the current interval */ + +static struct sound_lowlev_timer *tmr; + +static unsigned long tmr2ticks(int tmr_value) +{ + /* + * Convert timer ticks to MIDI ticks + */ + + unsigned long tmp; + unsigned long scale; + + tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ + scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ + return (tmp + (scale / 2)) / scale; +} + +void reprogram_timer(void) +{ + unsigned long usecs_per_tick; + + /* + * The user is changing the timer rate before setting a timer + * slap, bad bad not allowed. + */ + + if(!tmr) + return; + + usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase); + + /* + * Don't kill the system by setting too high timer rate + */ + if (usecs_per_tick < 2000) + usecs_per_tick = 2000; + + usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick); +} + +void sound_timer_syncinterval(unsigned int new_usecs) +{ + /* + * This routine is called by the hardware level if + * the clock frequency has changed for some reason. + */ + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + usecs_per_tmr = new_usecs; +} + +static void tmr_reset(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = 0; + restore_flags(flags); +} + +static int timer_open(int dev, int mode) +{ + if (opened) + return -EBUSY; + tmr_reset(); + curr_tempo = 60; + curr_timebase = 100; + opened = 1; + reprogram_timer(); + return 0; +} + +static void timer_close(int dev) +{ + opened = tmr_running = 0; + tmr->tmr_disable(tmr->dev); +} + +static int timer_event(int dev, unsigned char *event) +{ + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; + + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + time = parm; + next_event_time = prev_event_time = time; + return TIMER_ARMED; + } + break; + + case TMR_START: + tmr_reset(); + tmr_running = 1; + reprogram_timer(); + break; + + case TMR_STOP: + tmr_running = 0; + break; + + case TMR_CONTINUE: + tmr_running = 1; + reprogram_timer(); + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + reprogram_timer(); + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + default:; + } + return TIMER_NOT_ARMED; +} + +static unsigned long timer_get_time(int dev) +{ + if (!opened) + return 0; + return curr_ticks; +} + +static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + switch (cmd) + { + case SNDCTL_TMR_SOURCE: + val = TMR_INTERNAL; + break; + + case SNDCTL_TMR_START: + tmr_reset(); + tmr_running = 1; + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + return 0; + + case SNDCTL_TMR_CONTINUE: + tmr_running = 1; + return 0; + + case SNDCTL_TMR_TIMEBASE: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val) + { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + val = curr_timebase; + break; + + case SNDCTL_TMR_TEMPO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = val; + reprogram_timer(); + } + val = curr_tempo; + break; + + case SNDCTL_SEQ_CTRLRATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) /* Can't change */ + return -EINVAL; + val = ((curr_tempo * curr_timebase) + 30) / 60; + break; + + case SNDCTL_SEQ_GETTIME: + val = curr_ticks; + break; + + case SNDCTL_TMR_METRONOME: + default: + return -EINVAL; + } + return put_user(val, (int *)arg); +} + +static void timer_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + return; +} + +static struct sound_timer_operations sound_timer = +{ + owner: THIS_MODULE, + info: {"Sound Timer", 0}, + priority: 1, /* Priority */ + devlink: 0, /* Local device link */ + open: timer_open, + close: timer_close, + event: timer_event, + get_time: timer_get_time, + ioctl: timer_ioctl, + arm_timer: timer_arm +}; + +void sound_timer_interrupt(void) +{ + if (!opened) + return; + + tmr->tmr_restart(tmr->dev); + + if (!tmr_running) + return; + + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); + + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } +} + +void sound_timer_init(struct sound_lowlev_timer *t, char *name) +{ + int n; + + if (initialized) + { + if (t->priority <= tmr->priority) + return; /* There is already a similar or better timer */ + tmr = t; + return; + } + initialized = 1; + tmr = t; + + n = sound_alloc_timerdev(); + if (n == -1) + n = 0; /* Overwrite the system timer */ + strcpy(sound_timer.info.name, name); + sound_timer_devs[n] = &sound_timer; +} diff -Nru linux/sound/oss/soundcard.c linux-2.4.19-pre5-mjc/sound/oss/soundcard.c --- linux/sound/oss/soundcard.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/soundcard.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,782 @@ +/* + * linux/drivers/sound/soundcard.c + * + * Sound card driver for Linux + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * integrated sound_switch.c + * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat, + * which should disappear in the near future) + * Eric Dumas : devfs support (22-Jan-98) with + * fixups by C. Scott Ananian + * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c + * Rob Riggs : Added persistent DMA buffers support (1998/10/17) + * Christoph Hellwig : Some cleanup work (2000/03/01) + */ + +#include + +#include "sound_config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This ought to be moved into include/asm/dma.h + */ +#ifndef valid_dma +#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4) +#endif + +/* + * Table for permanently allocated memory (used when unloading the module) + */ +caddr_t sound_mem_blocks[1024]; +int sound_nblocks = 0; + +/* Persistent DMA buffers */ +#ifdef CONFIG_SOUND_ALSA_DMAP +int sound_dmap_flag = 1; +#else +int sound_dmap_flag = 0; +#endif + +static char dma_alloc_map[MAX_DMA_CHANNELS] = {0}; + +#define DMA_MAP_UNAVAIL 0 +#define DMA_MAP_FREE 1 +#define DMA_MAP_BUSY 2 + + +unsigned long seq_time = 0; /* Time for /dev/sequencer */ + +/* + * Table for configurable mixer volume handling + */ +static mixer_vol_table mixer_vols[MAX_MIXER_DEV]; +static int num_mixer_volumes = 0; + +int *load_mixer_volumes(char *name, int *levels, int present) +{ + int i, n; + + for (i = 0; i < num_mixer_volumes; i++) { + if (strcmp(name, mixer_vols[i].name) == 0) { + if (present) + mixer_vols[i].num = i; + return mixer_vols[i].levels; + } + } + if (num_mixer_volumes >= MAX_MIXER_DEV) { + printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); + return levels; + } + n = num_mixer_volumes++; + + strcpy(mixer_vols[n].name, name); + + if (present) + mixer_vols[n].num = n; + else + mixer_vols[n].num = -1; + + for (i = 0; i < 32; i++) + mixer_vols[n].levels[i] = levels[i]; + return mixer_vols[n].levels; +} + +static int set_mixer_levels(caddr_t arg) +{ + /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */ + mixer_vol_table buf; + + if (__copy_from_user(&buf, arg, sizeof(buf))) + return -EFAULT; + load_mixer_volumes(buf.name, buf.levels, 0); + if (__copy_to_user(arg, &buf, sizeof(buf))) + return -EFAULT; + return 0; +} + +static int get_mixer_levels(caddr_t arg) +{ + int n; + + if (__get_user(n, (int *)(&(((mixer_vol_table *)arg)->num)))) + return -EFAULT; + if (n < 0 || n >= num_mixer_volumes) + return -EINVAL; + if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table))) + return -EFAULT; + return 0; +} + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* 4K page size but our output routines use some slack for overruns */ +#define PROC_BLOCK_SIZE (3*1024) + +static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + int dev = minor(file->f_dentry->d_inode->i_rdev); + int ret = -EINVAL; + + /* + * The OSS drivers aren't remotely happy without this locking, + * and unless someone fixes them when they are about to bite the + * big one anyway, we might as well bandage here.. + */ + + lock_kernel(); + + DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); + switch (dev & 0x0f) { + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + ret = audio_read(dev, file, buf, count); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + ret = sequencer_read(dev, file, buf, count); + break; + + case SND_DEV_MIDIN: + ret = MIDIbuf_read(dev, file, buf, count); + } + unlock_kernel(); + return ret; +} + +static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + int dev = minor(file->f_dentry->d_inode->i_rdev); + int ret = -EINVAL; + + lock_kernel(); + DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); + switch (dev & 0x0f) { + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + ret = sequencer_write(dev, file, buf, count); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + ret = audio_write(dev, file, buf, count); + break; + + case SND_DEV_MIDIN: + ret = MIDIbuf_write(dev, file, buf, count); + break; + } + unlock_kernel(); + return ret; +} + +static int sound_open(struct inode *inode, struct file *file) +{ + int dev = minor(inode->i_rdev); + int retval; + + DEB(printk("sound_open(dev=%d)\n", dev)); + if ((dev >= SND_NDEVS) || (dev < 0)) { + printk(KERN_ERR "Invalid minor device %d\n", dev); + return -ENXIO; + } + switch (dev & 0x0f) { + case SND_DEV_CTL: + dev >>= 4; + if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { + char modname[20]; + sprintf(modname, "mixer%d", dev); + request_module(modname); + } + if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) + return -ENXIO; + + if (mixer_devs[dev]->owner) + __MOD_INC_USE_COUNT (mixer_devs[dev]->owner); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + if ((retval = sequencer_open(dev, file)) < 0) + return retval; + break; + + case SND_DEV_MIDIN: + if ((retval = MIDIbuf_open(dev, file)) < 0) + return retval; + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + if ((retval = audio_open(dev, file)) < 0) + return retval; + break; + + default: + printk(KERN_ERR "Invalid minor device %d\n", dev); + return -ENXIO; + } + + return 0; +} + +static int sound_release(struct inode *inode, struct file *file) +{ + int dev = minor(inode->i_rdev); + + lock_kernel(); + DEB(printk("sound_release(dev=%d)\n", dev)); + switch (dev & 0x0f) { + case SND_DEV_CTL: + dev >>= 4; + if (mixer_devs[dev]->owner) + __MOD_DEC_USE_COUNT (mixer_devs[dev]->owner); + break; + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + sequencer_release(dev, file); + break; + + case SND_DEV_MIDIN: + MIDIbuf_release(dev, file); + break; + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + audio_release(dev, file); + break; + + default: + printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); + } + unlock_kernel(); + + return 0; +} + +static int get_mixer_info(int dev, caddr_t arg) +{ + mixer_info info; + + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer_devs[dev]->modify_counter; + if (__copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int get_old_mixer_info(int dev, caddr_t arg) +{ + _old_mixer_info info; + + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg) +{ + if (mixdev < 0 || mixdev >= MAX_MIXER_DEV) + return -ENXIO; + /* Try to load the mixer... */ + if (mixer_devs[mixdev] == NULL) { + char modname[20]; + sprintf(modname, "mixer%d", mixdev); + request_module(modname); + } + if (mixdev >= num_mixers || !mixer_devs[mixdev]) + return -ENXIO; + if (cmd == SOUND_MIXER_INFO) + return get_mixer_info(mixdev, arg); + if (cmd == SOUND_OLD_MIXER_INFO) + return get_old_mixer_info(mixdev, arg); + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + mixer_devs[mixdev]->modify_counter++; + if (!mixer_devs[mixdev]->ioctl) + return -EINVAL; + return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg); +} + +static int sound_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err, len = 0, dtype; + int dev = minor(inode->i_rdev); + + if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) { + /* + * Have to validate the address given by the process. + */ + len = _SIOC_SIZE(cmd); + if (len < 1 || len > 65536 || arg == 0) + return -EFAULT; + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if ((err = verify_area(VERIFY_READ, (void *)arg, len)) < 0) + return err; + if (_SIOC_DIR(cmd) & _SIOC_READ) + if ((err = verify_area(VERIFY_WRITE, (void *)arg, len)) < 0) + return err; + } + DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + if (cmd == OSS_GETVERSION) + return __put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ + (dev & 0x0f) != SND_DEV_CTL) { + dtype = dev & 0x0f; + switch (dtype) { + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev, + cmd, (caddr_t)arg); + + default: + return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); + } + } + switch (dev & 0x0f) { + case SND_DEV_CTL: + if (cmd == SOUND_MIXER_GETLEVELS) + return get_mixer_levels((caddr_t)arg); + if (cmd == SOUND_MIXER_SETLEVELS) + return set_mixer_levels((caddr_t)arg); + return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); + + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_ioctl(dev, file, cmd, (caddr_t)arg); + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_ioctl(dev, file, cmd, (caddr_t)arg); + break; + + case SND_DEV_MIDIN: + return MIDIbuf_ioctl(dev, file, cmd, (caddr_t)arg); + break; + + } + return -EINVAL; +} + +static unsigned int sound_poll(struct file *file, poll_table * wait) +{ + struct inode *inode = file->f_dentry->d_inode; + int dev = minor(inode->i_rdev); + + DEB(printk("sound_poll(dev=%d)\n", dev)); + switch (dev & 0x0f) { + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_poll(dev, file, wait); + + case SND_DEV_MIDIN: + return MIDIbuf_poll(dev, file, wait); + + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return DMAbuf_poll(file, dev >> 4, wait); + } + return 0; +} + +static int sound_mmap(struct file *file, struct vm_area_struct *vma) +{ + int dev_class; + unsigned long size; + struct dma_buffparms *dmap = NULL; + int dev = minor(file->f_dentry->d_inode->i_rdev); + + dev_class = dev & 0x0f; + dev >>= 4; + + if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) { + printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n"); + return -EINVAL; + } + lock_kernel(); + if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */ + dmap = audio_devs[dev]->dmap_out; + else if (vma->vm_flags & VM_READ) + dmap = audio_devs[dev]->dmap_in; + else { + printk(KERN_ERR "Sound: Undefined mmap() access\n"); + unlock_kernel(); + return -EINVAL; + } + + if (dmap == NULL) { + printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n"); + unlock_kernel(); + return -EIO; + } + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n"); + unlock_kernel(); + return -EIO; + } + if (dmap->mapping_flags) { + printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n"); + unlock_kernel(); + return -EIO; + } + if (vma->vm_pgoff != 0) { + printk(KERN_ERR "Sound: mmap() offset must be 0.\n"); + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + + if (size != dmap->bytes_in_use) { + printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); + } + if (remap_page_range(vma->vm_start, virt_to_phys(dmap->raw_buf), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + + dmap->mapping_flags |= DMA_MAP_MAPPED; + + if( audio_devs[dev]->d->mmap) + audio_devs[dev]->d->mmap(dev); + + memset(dmap->raw_buf, + dmap->neutral_byte, + dmap->bytes_in_use); + unlock_kernel(); + return 0; +} + +struct file_operations oss_sound_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: sound_read, + write: sound_write, + poll: sound_poll, + ioctl: sound_ioctl, + mmap: sound_mmap, + open: sound_open, + release: sound_release, +}; + +/* + * Create the required special subdevices + */ + +static int create_special_devices(void) +{ + int seq1,seq2; + seq1=register_sound_special(&oss_sound_fops, 1); + if(seq1==-1) + goto bad; + seq2=register_sound_special(&oss_sound_fops, 8); + if(seq2!=-1) + return 0; + unregister_sound_special(1); +bad: + return -1; +} + + +/* These device names follow the official Linux device list, + * Documentation/devices.txt. Let us know if there are other + * common names we should support for compatibility. + * Only those devices not created by the generic code in sound_core.c are + * registered here. + */ +static const struct { + unsigned short minor; + char *name; + umode_t mode; + int *num; +} dev_list[] = { /* list of minor devices */ +/* seems to be some confusion here -- this device is not in the device list */ + {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP, + &num_audiodevs}, + {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP, + &num_audiodevs}, +}; + +static char * +soundcard_make_name(char *buf, char *name, int idx) { + if (idx==0) + sprintf(buf, "sound/%s", name); + else + sprintf(buf, "sound/%s%d", name, idx); + return buf; +} + +/* Register/unregister audio entries */ +static void soundcard_register_devfs (int do_register) +{ + char name_buf[32]; + int i, j, num; + + for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { + num = (dev_list[i].num == NULL) ? 0 : *dev_list[i].num; + for (j = 0; j < num || j == 0; j++) { + soundcard_make_name (name_buf, dev_list[i].name, j); + if (do_register) + devfs_register (NULL, name_buf, DEVFS_FL_NONE, + SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), + S_IFCHR | dev_list[i].mode, + &oss_sound_fops, NULL); + else { + devfs_handle_t de; + + de = devfs_find_handle (NULL, name_buf, 0, 0, + DEVFS_SPECIAL_CHR, 0); + devfs_unregister (de); + } + } + } +} + + +static int dmabuf = 0; +static int dmabug = 0; + +MODULE_PARM(dmabuf, "i"); +MODULE_PARM(dmabug, "i"); + +static int __init oss_init(void) +{ + int err; + + /* drag in sound_syms.o */ + { + extern char sound_syms_symbol; + sound_syms_symbol = 0; + } + +#ifdef CONFIG_PCI + if(dmabug) + isa_dma_bridge_buggy = dmabug; +#endif + + err = create_special_devices(); + if (err) { + printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); + return err; + } + + /* Protecting the innocent */ + sound_dmap_flag = (dmabuf > 0 ? 1 : 0); + + soundcard_register_devfs(1); + + if (sound_nblocks >= 1024) + printk(KERN_ERR "Sound warning: Deallocation table was too small.\n"); + + return 0; +} + +static void __exit oss_cleanup(void) +{ + int i; + + if (MOD_IN_USE) + return; + + soundcard_register_devfs (0); + + unregister_sound_special(1); + unregister_sound_special(8); + + sound_stop_timer(); + + sequencer_unload(); + + for (i = 0; i < MAX_DMA_CHANNELS; i++) + if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) { + printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i); + sound_free_dma(i); + } + + for (i = 0; i < sound_nblocks; i++) + vfree(sound_mem_blocks[i]); + +} + +module_init(oss_init); +module_exit(oss_cleanup); +MODULE_LICENSE("GPL"); + + +int sound_alloc_dma(int chn, char *deviceID) +{ + int err; + + if ((err = request_dma(chn, deviceID)) != 0) + return err; + + dma_alloc_map[chn] = DMA_MAP_FREE; + + return 0; +} + +int sound_open_dma(int chn, char *deviceID) +{ + unsigned long flags; + + if (!valid_dma(chn)) { + printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn); + return 1; + } + save_flags(flags); + cli(); + + if (dma_alloc_map[chn] != DMA_MAP_FREE) { + printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]); + restore_flags(flags); + return 1; + } + dma_alloc_map[chn] = DMA_MAP_BUSY; + restore_flags(flags); + return 0; +} + +void sound_free_dma(int chn) +{ + if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) { + /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */ + return; + } + free_dma(chn); + dma_alloc_map[chn] = DMA_MAP_UNAVAIL; +} + +void sound_close_dma(int chn) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + if (dma_alloc_map[chn] != DMA_MAP_BUSY) { + printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn); + restore_flags(flags); + return; + } + dma_alloc_map[chn] = DMA_MAP_FREE; + restore_flags(flags); +} + +static void do_sequencer_timer(unsigned long dummy) +{ + sequencer_timer(0); +} + + +static struct timer_list seq_timer = +{function: do_sequencer_timer}; + +void request_sound_timer(int count) +{ + extern unsigned long seq_time; + + if (count < 0) { + seq_timer.expires = (-count) + jiffies; + add_timer(&seq_timer); + return; + } + count += seq_time; + + count -= jiffies; + + if (count < 1) + count = 1; + + seq_timer.expires = (count) + jiffies; + add_timer(&seq_timer); +} + +void sound_stop_timer(void) +{ + del_timer(&seq_timer);; +} + +void conf_printf(char *name, struct address_info *hw_config) +{ +#ifndef CONFIG_SOUND_ALSA_TRACEINIT + return; +#else + printk("<%s> at 0x%03x", name, hw_config->io_base); + + if (hw_config->irq) + printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq); + + if (hw_config->dma != -1 || hw_config->dma2 != -1) + { + printk(" dma %d", hw_config->dma); + if (hw_config->dma2 != -1) + printk(",%d", hw_config->dma2); + } + printk("\n"); +#endif +} + +void conf_printf2(char *name, int base, int irq, int dma, int dma2) +{ +#ifndef CONFIG_SOUND_ALSA_TRACEINIT + return; +#else + printk("<%s> at 0x%03x", name, base); + + if (irq) + printk(" irq %d", (irq > 0) ? irq : -irq); + + if (dma != -1 || dma2 != -1) + { + printk(" dma %d", dma); + if (dma2 != -1) + printk(",%d", dma2); + } + printk("\n"); +#endif +} diff -Nru linux/sound/oss/soundvers.h linux-2.4.19-pre5-mjc/sound/oss/soundvers.h --- linux/sound/oss/soundvers.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/soundvers.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2 @@ +#define SOUND_VERSION_STRING "3.8s2++-971130" +#define SOUND_INTERNAL_VERSION 0x030804 diff -Nru linux/sound/oss/sscape.c linux-2.4.19-pre5-mjc/sound/oss/sscape.c --- linux/sound/oss/sscape.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sscape.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1530 @@ +/* + * sound/sscape.c + * + * Low level driver for Ensoniq SoundScape + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Sergey Smitienko : ensoniq p'n'p support + * Christoph Hellwig : adapted to module_init/module_exit + * Bartlomiej Zolnierkiewicz : added __init to attach_sscape() + * Chris Rankin : Specify that this module owns the coprocessor + * Arnaldo C. de Melo : added missing restore_flags in sscape_pnp_upload_file + */ + +#include +#include + +#include "sound_config.h" +#include "sound_firmware.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coproc.h" + +#include "ad1848.h" +#include "mpu401.h" + +/* + * I/O ports + */ +#define MIDI_DATA 0 +#define MIDI_CTRL 1 +#define HOST_CTRL 2 +#define TX_READY 0x02 +#define RX_READY 0x01 +#define HOST_DATA 3 +#define ODIE_ADDR 4 +#define ODIE_DATA 5 + +/* + * Indirect registers + */ + +#define GA_INTSTAT_REG 0 +#define GA_INTENA_REG 1 +#define GA_DMAA_REG 2 +#define GA_DMAB_REG 3 +#define GA_INTCFG_REG 4 +#define GA_DMACFG_REG 5 +#define GA_CDCFG_REG 6 +#define GA_SMCFGA_REG 7 +#define GA_SMCFGB_REG 8 +#define GA_HMCTL_REG 9 + +/* + * DMA channel identifiers (A and B) + */ + +#define SSCAPE_DMA_A 0 +#define SSCAPE_DMA_B 1 + +#define PORT(name) (devc->base+name) + +/* + * Host commands recognized by the OBP microcode + */ + +#define CMD_GEN_HOST_ACK 0x80 +#define CMD_GEN_MPU_ACK 0x81 +#define CMD_GET_BOARD_TYPE 0x82 +#define CMD_SET_CONTROL 0x88 /* Old firmware only */ +#define CMD_GET_CONTROL 0x89 /* Old firmware only */ +#define CTL_MASTER_VOL 0 +#define CTL_MIC_MODE 2 +#define CTL_SYNTH_VOL 4 +#define CTL_WAVE_VOL 7 +#define CMD_SET_EXTMIDI 0x8a +#define CMD_GET_EXTMIDI 0x8b +#define CMD_SET_MT32 0x8c +#define CMD_GET_MT32 0x8d + +#define CMD_ACK 0x80 + +#define IC_ODIE 1 +#define IC_OPUS 2 + +typedef struct sscape_info +{ + int base, irq, dma; + + int codec, codec_irq; /* required to setup pnp cards*/ + int codec_type; + int ic_type; + char* raw_buf; + unsigned long raw_buf_phys; + int buffsize; /* -------------------------- */ + + int ok; /* Properly detected */ + int failed; + int dma_allocated; + int codec_audiodev; + int opened; + int *osp; + int my_audiodev; +} sscape_info; + +static struct sscape_info adev_info = { + 0 +}; + +static struct sscape_info *devc = &adev_info; +static int sscape_mididev = -1; + +/* Some older cards have assigned interrupt bits differently than new ones */ +static char valid_interrupts_old[] = { + 9, 7, 5, 15 +}; + +static char valid_interrupts_new[] = { + 9, 5, 7, 10 +}; + +static char *valid_interrupts = valid_interrupts_new; + +/* + * See the bottom of the driver. This can be set by spea =0/1. + */ + +#ifdef REVEAL_SPEA +static char old_hardware = 1; +#else +static char old_hardware = 0; +#endif + +static void sleep(unsigned howlong) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +static unsigned char sscape_read(struct sscape_info *devc, int reg) +{ + unsigned long flags; + unsigned char val; + + save_flags(flags); + cli(); + outb(reg, PORT(ODIE_ADDR)); + val = inb(PORT(ODIE_DATA)); + restore_flags(flags); + return val; +} + +static void sscape_write(struct sscape_info *devc, int reg, int data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb(reg, PORT(ODIE_ADDR)); + outb(data, PORT(ODIE_DATA)); + restore_flags(flags); +} + +static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg) +{ + unsigned char res; + unsigned long flags; + + save_flags(flags); + cli(); + outb( reg, devc -> codec); + res = inb (devc -> codec + 1); + restore_flags(flags); + return res; + +} + +static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb( reg, devc -> codec); + outb( data, devc -> codec + 1); + restore_flags(flags); +} + +static void host_open(struct sscape_info *devc) +{ + outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */ +} + +static void host_close(struct sscape_info *devc) +{ + outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */ +} + +static int host_write(struct sscape_info *devc, unsigned char *data, int count) +{ + unsigned long flags; + int i, timeout_val; + + save_flags(flags); + cli(); + + /* + * Send the command and data bytes + */ + + for (i = 0; i < count; i++) + { + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & TX_READY) + break; + + if (timeout_val <= 0) + { + restore_flags(flags); + return 0; + } + outb(data[i], PORT(HOST_DATA)); + } + restore_flags(flags); + return 1; +} + +static int host_read(struct sscape_info *devc) +{ + unsigned long flags; + int timeout_val; + unsigned char data; + + save_flags(flags); + cli(); + + /* + * Read a byte + */ + + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & RX_READY) + break; + + if (timeout_val <= 0) + { + restore_flags(flags); + return -1; + } + data = inb(PORT(HOST_DATA)); + restore_flags(flags); + return data; +} + +#if 0 /* unused */ +static int host_command1(struct sscape_info *devc, int cmd) +{ + unsigned char buf[10]; + buf[0] = (unsigned char) (cmd & 0xff); + return host_write(devc, buf, 1); +} +#endif /* unused */ + + +static int host_command2(struct sscape_info *devc, int cmd, int parm1) +{ + unsigned char buf[10]; + + buf[0] = (unsigned char) (cmd & 0xff); + buf[1] = (unsigned char) (parm1 & 0xff); + + return host_write(devc, buf, 2); +} + +static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2) +{ + unsigned char buf[10]; + + buf[0] = (unsigned char) (cmd & 0xff); + buf[1] = (unsigned char) (parm1 & 0xff); + buf[2] = (unsigned char) (parm2 & 0xff); + return host_write(devc, buf, 3); +} + +static void set_mt32(struct sscape_info *devc, int value) +{ + host_open(devc); + host_command2(devc, CMD_SET_MT32, value ? 1 : 0); + if (host_read(devc) != CMD_ACK) + { + /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */ + } + host_close(devc); +} + +static void set_control(struct sscape_info *devc, int ctrl, int value) +{ + host_open(devc); + host_command3(devc, CMD_SET_CONTROL, ctrl, value); + if (host_read(devc) != CMD_ACK) + { + /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */ + } + host_close(devc); +} + +static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode) +{ + unsigned char temp; + + if (dma_chan != SSCAPE_DMA_A) + { + printk(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n"); + return; + } + audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE; + DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode); + audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE; + + temp = devc->dma << 4; /* Setup DMA channel select bits */ + if (devc->dma <= 3) + temp |= 0x80; /* 8 bit DMA channel */ + + temp |= 1; /* Trigger DMA */ + sscape_write(devc, GA_DMAA_REG, temp); + temp &= 0xfe; /* Clear DMA trigger */ + sscape_write(devc, GA_DMAA_REG, temp); +} + +static int verify_mpu(struct sscape_info *devc) +{ + /* + * The SoundScape board could be in three modes (MPU, 8250 and host). + * If the card is not in the MPU mode, enabling the MPU driver will + * cause infinite loop (the driver believes that there is always some + * received data in the buffer. + * + * Detect this by looking if there are more than 10 received MIDI bytes + * (0x00) in the buffer. + */ + + int i; + + for (i = 0; i < 10; i++) + { + if (inb(devc->base + HOST_CTRL) & 0x80) + return 1; + + if (inb(devc->base) != 0x00) + return 1; + } + printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n"); + return 0; +} + +static int sscape_coproc_open(void *dev_info, int sub_device) +{ + if (sub_device == COPR_MIDI) + { + set_mt32(devc, 0); + if (!verify_mpu(devc)) + return -EIO; + } + return 0; +} + +static void sscape_coproc_close(void *dev_info, int sub_device) +{ + struct sscape_info *devc = dev_info; + unsigned long flags; + + save_flags(flags); + cli(); + if (devc->dma_allocated) + { + sscape_write(devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ + devc->dma_allocated = 0; + } + restore_flags(flags); + return; +} + +static void sscape_coproc_reset(void *dev_info) +{ +} + +static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag) +{ + unsigned long flags; + unsigned char temp; + volatile int done, timeout_val; + static unsigned char codec_dma_bits = 0; + + if (flag & CPF_FIRST) + { + /* + * First block. Have to allocate DMA and to reset the board + * before continuing. + */ + + save_flags(flags); + cli(); + codec_dma_bits = sscape_read(devc, GA_CDCFG_REG); + + if (devc->dma_allocated == 0) + devc->dma_allocated = 1; + + restore_flags(flags); + + sscape_write(devc, GA_HMCTL_REG, + (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ + + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + sscape_read(devc, GA_HMCTL_REG); /* Delay */ + + /* Take board out of reset */ + sscape_write(devc, GA_HMCTL_REG, + (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80); + } + /* + * Transfer one code block using DMA + */ + if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL) + { + printk(KERN_WARNING "soundscape: DMA buffer not available\n"); + return 0; + } + memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size); + + save_flags(flags); + cli(); + + /******** INTERRUPTS DISABLED NOW ********/ + + do_dma(devc, SSCAPE_DMA_A, + audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys, + size, DMA_MODE_WRITE); + + /* + * Wait until transfer completes. + */ + + done = 0; + timeout_val = 30; + while (!done && timeout_val-- > 0) + { + int resid; + + if (HZ / 50) + sleep(HZ / 50); + clear_dma_ff(devc->dma); + if ((resid = get_dma_residue(devc->dma)) == 0) + done = 1; + } + + restore_flags(flags); + if (!done) + return 0; + + if (flag & CPF_LAST) + { + /* + * Take the board out of reset + */ + outb((0x00), PORT(HOST_CTRL)); + outb((0x00), PORT(MIDI_CTRL)); + + temp = sscape_read(devc, GA_HMCTL_REG); + temp |= 0x40; + sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */ + + /* + * Wait until the ODB wakes up + */ + + save_flags(flags); + cli(); + done = 0; + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + unsigned char x; + + sleep(1); + x = inb(PORT(HOST_DATA)); + if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ + { + DDB(printk("Soundscape: Acknowledge = %x\n", x)); + done = 1; + } + } + sscape_write(devc, GA_CDCFG_REG, codec_dma_bits); + + restore_flags(flags); + if (!done) + { + printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n"); + return 0; + } + save_flags(flags); + cli(); + done = 0; + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + sleep(1); + if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */ + done = 1; + } + restore_flags(flags); + if (!done) + { + printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); + return 0; + } + printk(KERN_INFO "SoundScape board initialized OK\n"); + set_control(devc, CTL_MASTER_VOL, 100); + set_control(devc, CTL_SYNTH_VOL, 100); + +#ifdef SSCAPE_DEBUG3 + /* + * Temporary debugging aid. Print contents of the registers after + * downloading the code. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); + } +#endif + + } + return 1; +} + +static int download_boot_block(void *dev_info, copr_buffer * buf) +{ + if (buf->len <= 0 || buf->len > sizeof(buf->data)) + return -EINVAL; + + if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags)) + { + printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n"); + return -EIO; + } + return 0; +} + +static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) +{ + copr_buffer *buf; + int err; + + switch (cmd) + { + case SNDCTL_COPR_RESET: + sscape_coproc_reset(dev_info); + return 0; + + case SNDCTL_COPR_LOAD: + buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); + if (buf == NULL) + return -ENOSPC; + if (copy_from_user(buf, arg, sizeof(copr_buffer))) + { + vfree(buf); + return -EFAULT; + } + err = download_boot_block(dev_info, buf); + vfree(buf); + return err; + + default: + return -EINVAL; + } +} + +static coproc_operations sscape_coproc_operations = +{ + "SoundScape M68K", + THIS_MODULE, + sscape_coproc_open, + sscape_coproc_close, + sscape_coproc_ioctl, + sscape_coproc_reset, + &adev_info +}; + +static int sscape_detected = 0; +static int sscape_is_pnp = 0; + +void __init attach_sscape(struct address_info *hw_config) +{ +#ifndef SSCAPE_REGS + /* + * Config register values for Spea/V7 Media FX and Ensoniq S-2000. + * These values are card + * dependent. If you have another SoundScape based card, you have to + * find the correct values. Do the following: + * - Compile this driver with SSCAPE_DEBUG1 defined. + * - Shut down and power off your machine. + * - Boot with DOS so that the SSINIT.EXE program is run. + * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed + * when detecting the SoundScape. + * - Modify the following list to use the values printed during boot. + * Undefine the SSCAPE_DEBUG1 + */ +#define SSCAPE_REGS { \ +/* I0 */ 0x00, \ +/* I1 */ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ +/* I2 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ +/* I3 */ 0x20, /* Note! Ignored. Set always to 0x20 */ \ +/* I4 */ 0xf5, /* Ignored */ \ +/* I5 */ 0x10, \ +/* I6 */ 0x00, \ +/* I7 */ 0x2e, /* I7 MEM config A. Likely to vary between models */ \ +/* I8 */ 0x00, /* I8 MEM config B. Likely to vary between models */ \ +/* I9 */ 0x40 /* Ignored */ \ + } +#endif + + unsigned long flags; + static unsigned char regs[10] = SSCAPE_REGS; + + int i, irq_bits = 0xff; + + if (sscape_detected != hw_config->io_base) + return; + + request_region(devc->base + 2, 6, "SoundScape"); + if (old_hardware) + { + valid_interrupts = valid_interrupts_old; + conf_printf("Ensoniq SoundScape (old)", hw_config); + } + else + conf_printf("Ensoniq SoundScape", hw_config); + + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff)) + { + printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq); + return; + } + + if (!sscape_is_pnp) { + + save_flags(flags); + cli(); + for (i = 1; i < 10; i++) + { + switch (i) + { + case 1: /* Host interrupt enable */ + sscape_write(devc, i, 0xf0); /* All interrupts enabled */ + break; + + case 2: /* DMA A status/trigger register */ + case 3: /* DMA B status/trigger register */ + sscape_write(devc, i, 0x20); /* DMA channel disabled */ + break; + + case 4: /* Host interrupt config reg */ + sscape_write(devc, i, 0xf0 | (irq_bits << 2) | irq_bits); + break; + + case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */ + sscape_write(devc, i, (regs[i] & 0x3f) | (sscape_read(devc, i) & 0xc0)); + break; + + case 6: /* CD-ROM config (WSS codec actually) */ + sscape_write(devc, i, regs[i]); + break; + + case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ + sscape_write(devc, i, (sscape_read(devc, i) & 0xf0) | 0x08); + break; + + default: + sscape_write(devc, i, regs[i]); + } + } + restore_flags(flags); + } +#ifdef SSCAPE_DEBUG2 + /* + * Temporary debugging aid. Print contents of the registers after + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); + } +#endif + + if (probe_mpu401(hw_config)) + hw_config->always_detect = 1; + hw_config->name = "SoundScape"; + + hw_config->irq *= -1; /* Negative value signals IRQ sharing */ + attach_mpu401(hw_config, THIS_MODULE); + hw_config->irq *= -1; /* Restore it */ + + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + { + sscape_mididev = hw_config->slots[1]; + midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations; + } + sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */ + devc->ok = 1; + devc->failed = 0; +} + +static int detect_ga(sscape_info * devc) +{ + unsigned char save; + + DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base)); + + if (check_region(devc->base, 8)) + return 0; + + /* + * First check that the address register of "ODIE" is + * there and that it has exactly 4 writable bits. + * First 4 bits + */ + + if ((save = inb(PORT(ODIE_ADDR))) & 0xf0) + { + DDB(printk("soundscape: Detect error A\n")); + return 0; + } + outb((0x00), PORT(ODIE_ADDR)); + if (inb(PORT(ODIE_ADDR)) != 0x00) + { + DDB(printk("soundscape: Detect error B\n")); + return 0; + } + outb((0xff), PORT(ODIE_ADDR)); + if (inb(PORT(ODIE_ADDR)) != 0x0f) + { + DDB(printk("soundscape: Detect error C\n")); + return 0; + } + outb((save), PORT(ODIE_ADDR)); + + /* + * Now verify that some indirect registers return zero on some bits. + * This may break the driver with some future revisions of "ODIE" but... + */ + + if (sscape_read(devc, 0) & 0x0c) + { + DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0))); + return 0; + } + if (sscape_read(devc, 1) & 0x0f) + { + DDB(printk("soundscape: Detect error E\n")); + return 0; + } + if (sscape_read(devc, 5) & 0x0f) + { + DDB(printk("soundscape: Detect error F\n")); + return 0; + } + return 1; +} + +static int sscape_read_host_ctrl(sscape_info* devc) +{ + return host_read(devc); +} + +static void sscape_write_host_ctrl2(sscape_info *devc, int a, int b) +{ + host_command2(devc, a, b); +} + +static int sscape_alloc_dma(sscape_info *devc) +{ + char *start_addr, *end_addr; + int dma_pagesize; + int sz, size; + struct page *page; + + if (devc->raw_buf != NULL) return 0; /* Already done */ + dma_pagesize = (devc->dma < 4) ? (64 * 1024) : (128 * 1024); + devc->raw_buf = NULL; + devc->buffsize = 8192*4; + if (devc->buffsize > dma_pagesize) devc->buffsize = dma_pagesize; + start_addr = NULL; + /* + * Now loop until we get a free buffer. Try to get smaller buffer if + * it fails. Don't accept smaller than 8k buffer for performance + * reasons. + */ + while (start_addr == NULL && devc->buffsize > PAGE_SIZE) { + for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); + devc->buffsize = PAGE_SIZE * (1 << sz); + start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); + if (start_addr == NULL) devc->buffsize /= 2; + } + + if (start_addr == NULL) { + printk(KERN_ERR "sscape pnp init error: Couldn't allocate DMA buffer\n"); + return 0; + } else { + /* make some checks */ + end_addr = start_addr + devc->buffsize - 1; + /* now check if it fits into the same dma-pagesize */ + + if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) + || end_addr >= (char *) (MAX_DMA_ADDRESS)) { + printk(KERN_ERR "sscape pnp: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, devc->buffsize); + return 0; + } + } + devc->raw_buf = start_addr; + devc->raw_buf_phys = virt_to_bus(start_addr); + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_reserve(page); + return 1; +} + +static void sscape_free_dma(sscape_info *devc) +{ + int sz, size; + unsigned long start_addr, end_addr; + struct page *page; + + if (devc->raw_buf == NULL) return; + for (sz = 0, size = PAGE_SIZE; size < devc->buffsize; sz++, size <<= 1); + start_addr = (unsigned long) devc->raw_buf; + end_addr = start_addr + devc->buffsize; + + for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) + mem_map_unreserve(page); + + free_pages((unsigned long) devc->raw_buf, sz); + devc->raw_buf = NULL; +} + +/* Intel version !!!!!!!!! */ + +static int sscape_start_dma(int chan, unsigned long physaddr, int count, int dma_mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, dma_mode); + set_dma_addr(chan, physaddr); + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); + return 0; +} + +static void sscape_pnp_start_dma(sscape_info* devc, int arg ) +{ + int reg; + if (arg == 0) reg = 2; + else reg = 3; + + sscape_write(devc, reg, sscape_read( devc, reg) | 0x01); + sscape_write(devc, reg, sscape_read( devc, reg) & 0xFE); +} + +static int sscape_pnp_wait_dma (sscape_info* devc, int arg ) +{ + int reg; + unsigned long i; + unsigned char d; + + if (arg == 0) reg = 2; + else reg = 3; + + sleep ( 1 ); + i = 0; + do { + d = sscape_read(devc, reg) & 1; + if ( d == 1) break; + i++; + } while (i < 500000); + d = sscape_read(devc, reg) & 1; + return d; +} + +static int sscape_pnp_alloc_dma(sscape_info* devc) +{ + /* printk(KERN_INFO "sscape: requesting dma\n"); */ + if (request_dma(devc -> dma, "sscape")) return 0; + /* printk(KERN_INFO "sscape: dma channel allocated\n"); */ + if (!sscape_alloc_dma(devc)) { + free_dma(devc -> dma); + return 0; + }; + return 1; +} + +static void sscape_pnp_free_dma(sscape_info* devc) +{ + sscape_free_dma( devc); + free_dma(devc -> dma ); + /* printk(KERN_INFO "sscape: dma released\n"); */ +} + +static int sscape_pnp_upload_file(sscape_info* devc, char* fn) +{ + int done = 0; + int timeout_val; + char* data,*dt; + int len,l; + unsigned long flags; + + sscape_write( devc, 9, sscape_read(devc, 9 ) & 0x3F ); + sscape_write( devc, 2, (devc -> dma << 4) | 0x80 ); + sscape_write( devc, 3, 0x20 ); + sscape_write( devc, 9, sscape_read( devc, 9 ) | 0x80 ); + + len = mod_firmware_load(fn, &data); + if (len == 0) { + printk(KERN_ERR "sscape: file not found: %s\n", fn); + return 0; + } + dt = data; + save_flags(flags); + cli(); + while ( len > 0 ) { + if (len > devc -> buffsize) l = devc->buffsize; + else l = len; + len -= l; + memcpy(devc->raw_buf, dt, l); dt += l; + sscape_start_dma(devc->dma, devc->raw_buf_phys, l, 0x48); + sscape_pnp_start_dma ( devc, 0 ); + if (sscape_pnp_wait_dma ( devc, 0 ) == 0) { + restore_flags(flags); + return 0; + } + } + + restore_flags(flags); + vfree(data); + + outb(0, devc -> base + 2); + outb(0, devc -> base); + + sscape_write ( devc, 9, sscape_read( devc, 9 ) | 0x40); + + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + unsigned char x; + sleep(1); + x = inb( devc -> base + 3); + if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ + { + //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); + done = 1; + } + } + timeout_val = 5 * HZ; + done = 0; + while (!done && timeout_val-- > 0) + { + unsigned char x; + sleep(1); + x = inb( devc -> base + 3); + if (x == 0xfe) /* OBP startup acknowledge */ + { + //printk(KERN_ERR "Soundscape: Acknowledge = %x\n", x); + done = 1; + } + } + + if ( !done ) printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); + + sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); + sscape_write( devc, 3, (devc -> dma << 4) + 0x80); + return 1; +} + +static void __init sscape_pnp_init_hw(sscape_info* devc) +{ + unsigned char midi_irq = 0, sb_irq = 0; + unsigned i; + static char code_file_name[23] = "/sndscape/sndscape.cox"; + + int sscape_sb_enable = 0; + int sscape_joystic_enable = 0x7f; + int sscape_mic_enable = 0; + int sscape_ext_midi = 0; + + if ( !sscape_pnp_alloc_dma(devc) ) { + printk(KERN_ERR "sscape: faild to allocate dma\n"); + return; + } + + for (i = 0; i < 4; i++) { + if ( devc -> irq == valid_interrupts[i] ) + midi_irq = i; + if ( devc -> codec_irq == valid_interrupts[i] ) + sb_irq = i; + } + + sscape_write( devc, 5, 0x50); + sscape_write( devc, 7, 0x2e); + sscape_write( devc, 8, 0x00); + + sscape_write( devc, 2, devc->ic_type == IC_ODIE ? 0x70 : 0x40); + sscape_write( devc, 3, ( devc -> dma << 4) | 0x80); + + if ( sscape_sb_enable ) + sscape_write (devc, 4, 0xF0 | (sb_irq << 2) | midi_irq); + else + sscape_write (devc, 4, 0xF0 | (midi_irq<<2) | midi_irq); + + i = 0x10; //sscape_read(devc, 9) & (devc->ic_type == IC_ODIE ? 0xf0 : 0xc0); + if ( sscape_sb_enable ) + i |= devc->ic_type == IC_ODIE ? 0x05 : 0x07; + if (sscape_joystic_enable) i |= 8; + + sscape_write (devc, 9, i); + sscape_write (devc, 6, 0x80); + sscape_write (devc, 1, 0x80); + + if (devc -> codec_type == 2) { + sscape_pnp_write_codec( devc, 0x0C, 0x50); + sscape_pnp_write_codec( devc, 0x10, sscape_pnp_read_codec( devc, 0x10) & 0x3F); + sscape_pnp_write_codec( devc, 0x11, sscape_pnp_read_codec( devc, 0x11) | 0xC0); + sscape_pnp_write_codec( devc, 29, 0x20); + } + + if (sscape_pnp_upload_file(devc, "/sndscape/scope.cod") == 0 ) { + printk(KERN_ERR "sscape: faild to upload file /sndscape/scope.cod\n"); + sscape_pnp_free_dma(devc); + return; + } + + i = sscape_read_host_ctrl( devc ); + + if ( (i & 0x0F) > 7 ) { + printk(KERN_ERR "sscape: scope.cod faild\n"); + sscape_pnp_free_dma(devc); + return; + } + if ( i & 0x10 ) sscape_write( devc, 7, 0x2F); + code_file_name[21] = (char) ( i & 0x0F) + 0x30; + if (sscape_pnp_upload_file( devc, code_file_name) == 0) { + printk(KERN_ERR "sscape: faild to upload file %s\n", code_file_name); + sscape_pnp_free_dma(devc); + return; + } + + if (devc->ic_type != IC_ODIE) { + sscape_pnp_write_codec( devc, 10, (sscape_pnp_read_codec(devc, 10) & 0x7f) | + ( sscape_mic_enable == 0 ? 0x00 : 0x80) ); + } + sscape_write_host_ctrl2( devc, 0x84, 0x64 ); /* MIDI volume */ + sscape_write_host_ctrl2( devc, 0x86, 0x64 ); /* MIDI volume?? */ + sscape_write_host_ctrl2( devc, 0x8A, sscape_ext_midi); + + sscape_pnp_write_codec ( devc, 6, 0x3f ); //WAV_VOL + sscape_pnp_write_codec ( devc, 7, 0x3f ); //WAV_VOL + sscape_pnp_write_codec ( devc, 2, 0x1F ); //WD_CDXVOLL + sscape_pnp_write_codec ( devc, 3, 0x1F ); //WD_CDXVOLR + + if (devc -> codec_type == 1) { + sscape_pnp_write_codec ( devc, 4, 0x1F ); + sscape_pnp_write_codec ( devc, 5, 0x1F ); + sscape_write_host_ctrl2( devc, 0x88, sscape_mic_enable); + } else { + int t; + sscape_pnp_write_codec ( devc, 0x10, 0x1F << 1); + sscape_pnp_write_codec ( devc, 0x11, 0xC0 | (0x1F << 1)); + + t = sscape_pnp_read_codec( devc, 0x00) & 0xDF; + if ( (sscape_mic_enable == 0)) t |= 0; + else t |= 0x20; + sscape_pnp_write_codec ( devc, 0x00, t); + t = sscape_pnp_read_codec( devc, 0x01) & 0xDF; + if ( (sscape_mic_enable == 0) ) t |= 0; + else t |= 0x20; + sscape_pnp_write_codec ( devc, 0x01, t); + sscape_pnp_write_codec ( devc, 0x40 | 29 , 0x20); + outb(0, devc -> codec); + } + if (devc -> ic_type == IC_OPUS ) { + int i = sscape_read( devc, 9 ); + sscape_write( devc, 9, i | 3 ); + sscape_write( devc, 3, 0x40); + + if (check_region(0x228, 1)) { + outb(0, 0x228); + release_region(0x228,1); + } + sscape_write( devc, 3, (devc -> dma << 4) | 0x80); + sscape_write( devc, 9, i ); + } + + host_close ( devc ); + sscape_pnp_free_dma(devc); +} + +static int __init detect_sscape_pnp(sscape_info* devc) +{ + long i, irq_bits = 0xff; + unsigned int d; + + DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base)); + + if (check_region(devc->base, 8)) { + printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->base); + return 0; + } + + if (check_region(devc->codec, 2)) { + printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec); + return 0; + } + + if ( (inb( devc -> base + 2) & 0x78) != 0) return 0; + + d = inb ( devc -> base + 4) & 0xF0; + if ( (d & 0x80) != 0) return 0; + + if (d == 0) { + devc->codec_type = 1; + devc->ic_type = IC_ODIE; + } + else if ( (d & 0x60) != 0) { + devc->codec_type = 2; + devc->ic_type = IC_OPUS; + } + else if ( (d & 0x40) != 0) { + devc->codec_type = 2; + devc->ic_type = IC_ODIE; + } + else return 0; + + sscape_is_pnp = 1; + + outb(0xFA, devc -> base+4); + if ((inb( devc -> base+4) & 0x9F) != 0x0A) + return 0; + outb(0xFE, devc -> base+4); + if ( (inb(devc -> base+4) & 0x9F) != 0x0E) + return 0; + if ( (inb(devc -> base+5) & 0x9F) != 0x0E) + return 0; + + if (devc->codec_type == 2) { + if (devc -> codec != devc -> base + 8) + printk("soundscape warning: incorrect codec port specified\n"); + devc -> codec = devc -> base + 8; + d = 0x10 | (sscape_read(devc, 9) & 0xCF); + sscape_write(devc, 9, d); + sscape_write(devc, 6, 0x80); + } else { + //todo: check codec is not base + 8 + } + + d = (sscape_read(devc, 9) & 0x3F) | 0xC0; + sscape_write(devc, 9, d); + + for (i = 0; i < 550000; i++) + if ( !(inb(devc -> codec) & 0x80) ) break; + + d = inb(devc -> codec); + if (d & 0x80) + return 0; + if ( inb(devc -> codec + 2) == 0xFF) + return 0; + + sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F ); + + d = inb(devc -> codec) & 0x80; + if ( d == 0) { + printk(KERN_INFO "soundscape: hardware detected\n"); + valid_interrupts = valid_interrupts_new; + } else { + printk(KERN_INFO "soundscape: board looks like media fx\n"); + valid_interrupts = valid_interrupts_old; + old_hardware = 1; + } + + sscape_write( devc, 9, 0xC0 | (sscape_read(devc, 9) & 0x3F) ); + + for (i = 0; i < 550000; i++) + if ( !(inb(devc -> codec) & 0x80)) + break; + + sscape_pnp_init_hw(devc); + + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (devc->codec_irq == valid_interrupts[i]) { + irq_bits = i; + break; + } + } + sscape_write(devc, GA_INTENA_REG, 0x00); + sscape_write(devc, GA_DMACFG_REG, 0x50); + sscape_write(devc, GA_DMAA_REG, 0x70); + sscape_write(devc, GA_DMAB_REG, 0x20); + sscape_write(devc, GA_INTCFG_REG, 0xf0); + sscape_write(devc, GA_CDCFG_REG, 0x89 | (devc->dma << 4) | (irq_bits << 1)); + + sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20); + sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20); + + return 1; +} + +static int __init probe_sscape(struct address_info *hw_config) +{ + + if (sscape_detected != 0 && sscape_detected != hw_config->io_base) + return 0; + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma = hw_config->dma; + devc->osp = hw_config->osp; + +#ifdef SSCAPE_DEBUG1 + /* + * Temporary debugging aid. Print contents of the registers before + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (old value)\n", i, sscape_read(devc, i)); + } +#endif + devc->failed = 1; + + if (!detect_ga(devc)) { + if (detect_sscape_pnp(devc)) { + sscape_detected = hw_config->io_base; + return 1; + } + else return 0; + } + + if (old_hardware) /* Check that it's really an old Spea/Reveal card. */ + { + unsigned char tmp; + int cc; + + if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0)) + { + sscape_write(devc, GA_HMCTL_REG, tmp | 0x80); + for (cc = 0; cc < 200000; ++cc) + inb(devc->base + ODIE_ADDR); + } + } + sscape_detected = hw_config->io_base; + return 1; +} + +static int __init probe_ss_ms_sound(struct address_info *hw_config) +{ + int i, irq_bits = 0xff; + int ad_flags = 0; + + if (devc->failed) + { + printk(KERN_ERR "soundscape: Card not detected\n"); + return 0; + } + if (devc->ok == 0) + { + printk(KERN_ERR "soundscape: Invalid initialization order.\n"); + return 0; + } + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + if (hw_config->irq > 15 || irq_bits == 0xff) + { + printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq); + return 0; + } + + if (!sscape_is_pnp) { + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); + } + else { + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + else + ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */ + return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); + } +} + +static void __init attach_ss_ms_sound(struct address_info *hw_config) +{ + /* + * This routine configures the SoundScape card for use with the + * Win Sound System driver. The AD1848 codec interface uses the CD-ROM + * config registers of the "ODIE". + */ + + int i, irq_bits = 0xff; + + + if (!sscape_is_pnp) /*pnp is already setup*/ + { + /* + * Setup the DMA polarity. + */ + sscape_write(devc, GA_DMACFG_REG, 0x50); + + /* + * Take the gate-array off of the DMA channel. + */ + sscape_write(devc, GA_DMAB_REG, 0x20); + + /* + * Init the AD1848 (CD-ROM) config reg. + */ + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); + } + + if (hw_config->irq == devc->irq) + printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n"); + + hw_config->slots[0] = ad1848_init( + sscape_is_pnp ? "SoundScape" : "SoundScape PNP", + hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, + 0, + devc->osp, + THIS_MODULE); + + + if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */ + { + audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations; + devc->codec_audiodev = hw_config->slots[0]; + devc->my_audiodev = hw_config->slots[0]; + + /* Set proper routings here (what are they) */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); + } + +#ifdef SSCAPE_DEBUG5 + /* + * Temporary debugging aid. Print contents of the registers + * after the AD1848 device has been initialized. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x\n", i, sscape_read(devc, i)); + } +#endif + +} + +static void __exit unload_sscape(struct address_info *hw_config) +{ + release_region(devc->base + 2, 6); + unload_mpu401(hw_config); +} + +static void __exit unload_ss_ms_sound(struct address_info *hw_config) +{ + ad1848_unload(hw_config->io_base, + hw_config->irq, + devc->dma, + devc->dma, + 0); + sound_unload_audiodev(hw_config->slots[0]); +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata spea = -1; +static int __initdata mss = 0; +static int __initdata dma = -1; +static int __initdata irq = -1; +static int __initdata io = -1; +static int __initdata mpu_irq = -1; +static int __initdata mpu_io = -1; + +MODULE_PARM(dma, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(io, "i"); +MODULE_PARM(spea, "i"); /* spea=0/1 set the old_hardware */ +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(mss, "i"); + +static int __init init_sscape(void) +{ + printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.irq = irq; + cfg.dma = dma; + cfg.io_base = io; + + cfg_mpu.irq = mpu_irq; + cfg_mpu.io_base = mpu_io; + /* WEH - Try to get right dma channel */ + cfg_mpu.dma = dma; + + devc->codec = cfg.io_base; + devc->codec_irq = cfg.irq; + devc->codec_type = 0; + devc->ic_type = 0; + devc->raw_buf = NULL; + + if (cfg.dma == -1 || cfg.irq == -1 || cfg.io_base == -1) { + printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n"); + return -EINVAL; + } + + if (cfg_mpu.irq == -1 && cfg_mpu.io_base != -1) { + printk(KERN_ERR "MPU_IRQ must be specified if MPU_IO is set.\n"); + return -EINVAL; + } + + if(spea != -1) { + old_hardware = spea; + printk(KERN_INFO "Forcing %s hardware support.\n", + spea?"new":"old"); + } + if (probe_sscape(&cfg_mpu) == 0) + return -ENODEV; + + attach_sscape(&cfg_mpu); + + mss = probe_ss_ms_sound(&cfg); + + if (mss) + attach_ss_ms_sound(&cfg); + + return 0; +} + +static void __exit cleanup_sscape(void) +{ + if (mss) + unload_ss_ms_sound(&cfg); + unload_sscape(&cfg_mpu); +} + +module_init(init_sscape); +module_exit(cleanup_sscape); + +#ifndef MODULE +static int __init setup_sscape(char *str) +{ + /* io, irq, dma, mpu_io, mpu_irq */ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + mpu_io = ints[4]; + mpu_irq = ints[5]; + + return 1; +} + +__setup("sscape=", setup_sscape); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/sys_timer.c linux-2.4.19-pre5-mjc/sound/oss/sys_timer.c --- linux/sound/oss/sys_timer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/sys_timer.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,287 @@ +/* + * sound/sys_timer.c + * + * The default timer for the Level 2 sequencer interface + * Uses the (1/HZ sec) timer of kernel. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Andrew Veliath : adapted tmr2ticks from level 1 sequencer (avoid overflow) + */ +#include "sound_config.h" + +static volatile int opened = 0, tmr_running = 0; +static volatile time_t tmr_offs, tmr_ctr; +static volatile unsigned long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile unsigned long curr_ticks; +static volatile unsigned long next_event_time; +static unsigned long prev_event_time; + +static void poll_def_tmr(unsigned long dummy); + + +static struct timer_list def_tmr = +{function: poll_def_tmr}; + +static unsigned long +tmr2ticks(int tmr_value) +{ + /* + * Convert timer ticks to MIDI ticks + */ + + unsigned long tmp; + unsigned long scale; + + /* tmr_value (ticks per sec) * + 1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */ + tmp = tmr_value * (1000000 / HZ); + scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ + return (tmp + scale / 2) / scale; +} + +static void +poll_def_tmr(unsigned long dummy) +{ + + if (opened) + { + + { + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); + }; + + if (tmr_running) + { + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); + + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } + } + } +} + +static void +tmr_reset(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = 0; + restore_flags(flags); +} + +static int +def_tmr_open(int dev, int mode) +{ + if (opened) + return -EBUSY; + + tmr_reset(); + curr_tempo = 60; + curr_timebase = 100; + opened = 1; + + ; + + { + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); + }; + + return 0; +} + +static void +def_tmr_close(int dev) +{ + opened = tmr_running = 0; + del_timer(&def_tmr);; +} + +static int +def_tmr_event(int dev, unsigned char *event) +{ + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; + + switch (cmd) + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + tmr_reset(); + tmr_running = 1; + break; + + case TMR_STOP: + tmr_running = 0; + break; + + case TMR_CONTINUE: + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 360) + parm = 360; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static unsigned long +def_tmr_get_time(int dev) +{ + if (!opened) + return 0; + + return curr_ticks; +} + +/* same as sound_timer.c:timer_ioctl!? */ +static int def_tmr_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val; + + switch (cmd) { + case SNDCTL_TMR_SOURCE: + return __put_user(TMR_INTERNAL, (int *)arg); + + case SNDCTL_TMR_START: + tmr_reset(); + tmr_running = 1; + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + return 0; + + case SNDCTL_TMR_CONTINUE: + tmr_running = 1; + return 0; + + case SNDCTL_TMR_TIMEBASE: + if (__get_user(val, (int *)arg)) + return -EFAULT; + if (val) { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + return __put_user(curr_timebase, (int *)arg); + + case SNDCTL_TMR_TEMPO: + if (__get_user(val, (int *)arg)) + return -EFAULT; + if (val) { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = val; + reprogram_timer(); + } + return __put_user(curr_tempo, (int *)arg); + + case SNDCTL_SEQ_CTRLRATE: + if (__get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) /* Can't change */ + return -EINVAL; + val = ((curr_tempo * curr_timebase) + 30) / 60; + return __put_user(val, (int *)arg); + + case SNDCTL_SEQ_GETTIME: + return __put_user(curr_ticks, (int *)arg); + + case SNDCTL_TMR_METRONOME: + /* NOP */ + break; + + default:; + } + return -EINVAL; +} + +static void +def_tmr_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + + return; +} + +struct sound_timer_operations default_sound_timer = +{ + owner: THIS_MODULE, + info: {"System clock", 0}, + priority: 0, /* Priority */ + devlink: 0, /* Local device link */ + open: def_tmr_open, + close: def_tmr_close, + event: def_tmr_event, + get_time: def_tmr_get_time, + ioctl: def_tmr_ioctl, + arm_timer: def_tmr_arm +}; diff -Nru linux/sound/oss/trident.c linux-2.4.19-pre5-mjc/sound/oss/trident.c --- linux/sound/oss/trident.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/trident.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,4231 @@ +/* + * OSS driver for Linux 2.4.x for + * + * Trident 4D-Wave + * SiS 7018 + * ALi 5451 + * Tvia/IGST CyberPro 5050 + * + * Driver: Alan Cox + * + * Built from: + * Low level code: from ALSA + * Framework: Thomas Sailer + * Extended by: Zach Brown + * + * Hacked up by: + * Aaron Holtzman + * Ollie Lho SiS 7018 Audio Core Support + * Ching-Ling Lee ALi 5451 Audio Core Support + * Matt Wu ALi 5451 Audio Core Support + * Peter Wächtler CyberPro5050 support + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + * History + * v0.14.9d + * October 8 2001 Arnaldo Carvalho de Melo + * use set_current_state, properly release resources on failure in + * trident_probe, get rid of check_region + * v0.14.9c + * August 10 2001 Peter Wächtler + * added support for Tvia (formerly Integraphics/IGST) CyberPro5050 + * this chip is often found in settop boxes (combined video+audio) + * v0.14.9b + * Switch to static inline not extern inline (gcc 3) + * v0.14.9a + * Aug 6 2001 Alan Cox + * 0.14.9 crashed on rmmod due to a timer/bh left running. Simplified + * the existing logic (the BH doesnt help as ac97 is lock_irqsave) + * and used del_timer_sync to clean up + * Fixed a problem where the ALi change broke my generic card + * v0.14.9 + * Jul 10 2001 Matt Wu + * Add H/W Volume Control + * v0.14.8a + * July 7 2001 Alan Cox + * Moved Matt Wu's ac97 register cache into the card structure + * v0.14.8 + * Apr 30 2001 Matt Wu + * Set EBUF1 and EBUF2 to still mode + * Add dc97/ac97 reset function + * Fix power management: ali_restore_regs + * unreleased + * Mar 09 2001 Matt Wu + * Add cache for ac97 access + * v0.14.7 + * Feb 06 2001 Matt Wu + * Fix ac97 initialization + * Fix bug: an extra tail will be played when playing + * Jan 05 2001 Matt Wu + * Implement multi-channels and S/PDIF in support for ALi 1535+ + * v0.14.6 + * Nov 1 2000 Ching-Ling Lee + * Fix the bug of memory leak when switching 5.1-channels to 2 channels. + * Add lock protection into dynamic changing format of data. + * Oct 18 2000 Ching-Ling Lee + * 5.1-channels support for ALi + * June 28 2000 Ching-Ling Lee + * S/PDIF out/in(playback/record) support for ALi 1535+, using /proc to be selected by user + * Simple Power Management support for ALi + * v0.14.5 May 23 2000 Ollie Lho + * Misc bug fix from the Net + * v0.14.4 May 20 2000 Aaron Holtzman + * Fix kfree'd memory access in release + * Fix race in open while looking for a free virtual channel slot + * remove open_wait wq (which appears to be unused) + * v0.14.3 May 10 2000 Ollie Lho + * fixed a small bug in trident_update_ptr, xmms 1.0.1 no longer uses 100% CPU + * v0.14.2 Mar 29 2000 Ching-Ling Lee + * Add clear to silence advance in trident_update_ptr + * fix invalid data of the end of the sound + * v0.14.1 Mar 24 2000 Ching-Ling Lee + * ALi 5451 support added, playback and recording O.K. + * ALi 5451 originally developed and structured based on sonicvibes, and + * suggested to merge into this file by Alan Cox. + * v0.14 Mar 15 2000 Ollie Lho + * 5.1 channel output support with channel binding. What's the Matrix ? + * v0.13.1 Mar 10 2000 Ollie Lho + * few minor bugs on dual codec support, needs more testing + * v0.13 Mar 03 2000 Ollie Lho + * new pci_* for 2.4 kernel, back ported to 2.2 + * v0.12 Feb 23 2000 Ollie Lho + * Preliminary Recording support + * v0.11.2 Feb 19 2000 Ollie Lho + * removed incomplete full-dulplex support + * v0.11.1 Jan 28 2000 Ollie Lho + * small bug in setting sample rate for 4d-nx (reported by Aaron) + * v0.11 Jan 27 2000 Ollie Lho + * DMA bug, scheduler latency, second try + * v0.10 Jan 24 2000 Ollie Lho + * DMA bug fixed, found kernel scheduling problem + * v0.09 Jan 20 2000 Ollie Lho + * Clean up of channel register access routine (prepare for channel binding) + * v0.08 Jan 14 2000 Ollie Lho + * Isolation of AC97 codec code + * v0.07 Jan 13 2000 Ollie Lho + * Get rid of ugly old low level access routines (e.g. CHRegs.lp****) + * v0.06 Jan 11 2000 Ollie Lho + * Preliminary support for dual (more ?) AC97 codecs + * v0.05 Jan 08 2000 Luca Montecchiani + * adapt to 2.3.x new __setup/__init call + * v0.04 Dec 31 1999 Ollie Lho + * Multiple Open, using Middle Loop Interrupt to smooth playback + * v0.03 Dec 24 1999 Ollie Lho + * mem leak in prog_dmabuf and dealloc_dmabuf removed + * v0.02 Dec 15 1999 Ollie Lho + * SiS 7018 support added, playback O.K. + * v0.01 Alan Cox et. al. + * Initial Release in kernel 2.3.30, does not work + * + * ToDo + * Clean up of low level channel register access code. (done) + * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done) + * Dual AC97 codecs support (done) + * Recording support (done) + * Mmap support + * "Channel Binding" ioctl extension (done) + * new pci device driver interface for 2.4 kernel (done) + * + * Lock order (high->low) + * lock - hardware lock + * open_sem - guard opens + * sem - guard dmabuf, write re-entry etc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC +#include +#endif + +#include "trident.h" + +#include + +#define DRIVER_VERSION "0.14.9d" + +/* magic numbers to protect our data structures */ +#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ +#define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */ + +#define TRIDENT_DMA_MASK 0x3fffffff /* DMA buffer mask for pci_alloc_consist */ +#define ALI_DMA_MASK 0xffffffff /* ALI Tridents lack the 30-bit limitation */ + +#define NR_HW_CH 32 + +/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only + have 2 SDATA_IN lines (currently) */ +#define NR_AC97 2 + +/* minor number of /dev/swmodem (temporary, experimental) */ +#define SND_DEV_SWMODEM 7 + +static const unsigned ali_multi_channels_5_1[] = { /*ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL,*/ ALI_CENTER_CHANNEL, ALI_LEF_CHANNEL, ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL}; + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n"; + +enum { + TRIDENT_4D_DX = 0, + TRIDENT_4D_NX, + SIS_7018, + ALI_5451, + CYBER5050 +}; + +static char * card_names[] = { + "Trident 4DWave DX", + "Trident 4DWave NX", + "SiS 7018 PCI Audio", + "ALi Audio Accelerator", + "Tvia/IGST CyberPro 5050" +}; + +static struct pci_device_id trident_pci_tbl [] __devinitdata = { + {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX}, + {PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX}, + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018}, + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI_5451}, + { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5050, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, CYBER5050}, + {0,} +}; + +MODULE_DEVICE_TABLE (pci, trident_pci_tbl); + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct trident_state { + unsigned int magic; + struct trident_card *card; /* Card info */ + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable; + + /* hardware channel */ + struct trident_channel *channel; + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, updated by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be comsumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + unsigned error; /* number of over/underruns */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned update_flag; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + + } dmabuf; + + /* 5.1channels */ + struct trident_state *other_states[4]; + int multi_channels_adjust_count; + unsigned chans_num; + unsigned fmt_flag:1; + /* Guard against mmap/write/read races */ + struct semaphore sem; + +}; + +/* hardware channels */ +struct trident_channel { + int num; /* channel number */ + u32 lba; /* Loop Begine Address, where dma buffer starts */ + u32 eso; /* End Sample Offset, wehre dma buffer ends (in the unit of samples) */ + u32 delta; /* delta value, sample rate / 48k for playback, 48k/sample rate for recording */ + u16 attribute; /* control where PCM data go and come */ + u16 fm_vol; + u32 control; /* signed/unsigned, 8/16 bits, mono/stereo */ +}; + +struct trident_pcm_bank_address { + u32 start; + u32 stop; + u32 aint; + u32 aint_en; +}; +static struct trident_pcm_bank_address bank_a_addrs = +{ + T4D_START_A, + T4D_STOP_A, + T4D_AINT_A, + T4D_AINTEN_A +}; +static struct trident_pcm_bank_address bank_b_addrs = +{ + T4D_START_B, + T4D_STOP_B, + T4D_AINT_B, + T4D_AINTEN_B +}; +struct trident_pcm_bank { + /* register addresses to control bank operations */ + struct trident_pcm_bank_address *addresses; + /* each bank has 32 channels */ + u32 bitmap; /* channel allocation bitmap */ + struct trident_channel channels[32]; +}; + +struct trident_card { + unsigned int magic; + + /* We keep trident cards in a linked list */ + struct trident_card *next; + + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + + /* The trident has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + + /* PCI device stuff */ + struct pci_dev * pci_dev; + u16 pci_id; + u8 revision; + + /* soundcore stuff */ + int dev_audio; + + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct trident_pcm_bank banks[NR_BANKS]; + struct trident_state *states[NR_HW_CH]; + + /* hardware resources */ + unsigned long iobase; + u32 irq; + + /* Function support */ + struct trident_channel *(*alloc_pcm_channel)(struct trident_card *); + struct trident_channel *(*alloc_rec_pcm_channel)(struct trident_card *); + void (*free_pcm_channel)(struct trident_card *, unsigned int chan); + void (*address_interrupt)(struct trident_card *); + + /* Added by Matt Wu 01-05-2001 for spdif in */ + int multi_channel_use_count; + int rec_channel_use_count; + u16 mixer_regs[64][NR_AC97]; /* Made card local by Alan */ + int mixer_regs_ready; + + /* Added for hardware volume control */ + int hwvolctl; + struct timer_list timer; +}; + +/* table to map from CHANNELMASK to channel attribute for SiS 7018 */ +static u16 mask2attr [] = +{ + PCM_LR, PCM_LR, SURR_LR, CENTER_LFE, + HSET, MIC, MODEM_LINE1, MODEM_LINE2, + I2S_LR, SPDIF_LR +}; +/* table to map from channel attribute to CHANNELMASK for SiS 7018 */ +static int attr2mask [] = { + DSP_BIND_MODEM1, DSP_BIND_MODEM2, DSP_BIND_FRONT, DSP_BIND_HANDSET, + DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF +}; + +/* Added by Matt Wu 01-05-2001 for spdif in */ +static int ali_close_multi_channels(void); +static void ali_delay(struct trident_card *card,int interval); +static void ali_detect_spdif_rate(struct trident_card *card); + +static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val); +static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg); + +static struct trident_card *devs; + +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val); +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg); + +static int trident_open_mixdev(struct inode *inode, struct file *file); +static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg); + +static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val); +static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg); +static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate); +static void ali_enable_special_channel(struct trident_state *stat); +static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card); +static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card); +static void ali_restore_regs(struct trident_card *card); +static void ali_save_regs(struct trident_card *card); +static int trident_suspend(struct pci_dev *dev, u32 unused); +static int trident_resume(struct pci_dev *dev); +static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel); +static int ali_setup_multi_channels(struct trident_card *card, int chan_nums); +static unsigned int ali_get_spdif_in_rate(struct trident_card *card); +static void ali_setup_spdif_in(struct trident_card *card); +static void ali_disable_spdif_in(struct trident_card *card); +static void ali_disable_special_channel(struct trident_card *card, int ch); +static void ali_setup_spdif_out(struct trident_card *card, int flag); +static int ali_write_5_1(struct trident_state *state, const char *buffer,int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt); +static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums); +static void ali_free_other_states_resources(struct trident_state *state); + + +/* save registers for ALi Power Management */ +static struct ali_saved_registers { + unsigned long global_regs[ALI_GLOBAL_REGS]; + unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; + unsigned mixer_regs[ALI_MIXER_REGS]; +} ali_registers; + +#define seek_offset(dma_ptr, buffer, cnt, offset, copy_count) (dma_ptr) += (offset); \ + (buffer) += (offset); \ + (cnt) -= (offset); \ + (copy_count) += (offset); + +#define lock_set_fmt(state) {spin_lock_irqsave(&state->card->lock, flags); \ + if (state->fmt_flag) { \ + spin_unlock_irqrestore(&state->card->lock, flags); \ + return -EFAULT; \ + } \ + state->fmt_flag = 1; \ + spin_unlock_irqrestore(&state->card->lock, flags);} + +#define unlock_set_fmt(state) {spin_lock_irqsave(&state->card->lock, flags); \ + state->fmt_flag = 0; \ + spin_unlock_irqrestore(&state->card->lock, flags);} + +static int trident_enable_loop_interrupts(struct trident_card * card) +{ + u32 global_control; + + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); + + switch (card->pci_id) + { + case PCI_DEVICE_ID_SI_7018: + global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN); + break; + case PCI_DEVICE_ID_ALI_5451: + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + case PCI_DEVICE_ID_INTERG_5050: + global_control |= (ENDLP_IE | MIDLP_IE); + break; + default: + return FALSE; + } + + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); + +#ifdef DEBUG + printk("trident: Enable Loop Interrupts, globctl = 0x%08X\n", + inl(TRID_REG(card, T4D_LFO_GC_CIR))); +#endif + return (TRUE); +} + +static int trident_disable_loop_interrupts(struct trident_card * card) +{ + u32 global_control; + + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); + global_control &= ~(ENDLP_IE | MIDLP_IE); + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); + +#ifdef DEBUG + printk("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", + global_control); +#endif + return (TRUE); +} + +static void trident_enable_voice_irq(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; + + reg = inl(TRID_REG(card, addr)); + reg |= mask; + outl(reg, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: enabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); +#endif +} + +static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; + + reg = inl(TRID_REG(card, addr)); + reg &= ~mask; + outl(reg, TRID_REG(card, addr)); + + /* Ack the channel in case the interrupt was set before we disable it. */ + outl(mask, TRID_REG(card, bank->addresses->aint)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: disabled IRQ on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_AINTEN_B? "AINTEN_B":"AINTEN_A",reg,addr); +#endif +} + +static void trident_start_voice(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->start; + +#ifdef DEBUG + u32 reg; +#endif + + outl(mask, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: start voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_START_B? "START_B":"START_A",reg,addr); +#endif +} + +static void trident_stop_voice(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->stop; + +#ifdef DEBUG + u32 reg; +#endif + + outl(mask, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, addr)); + printk("trident: stop voice on channel %d, %s = 0x%08x(addr:%X)\n", + channel, addr==T4D_STOP_B? "STOP_B":"STOP_A",reg,addr); +#endif +} + +static u32 trident_get_interrupt_mask (struct trident_card * card, unsigned int channel) +{ + struct trident_pcm_bank *bank = &card->banks[channel]; + u32 addr = bank->addresses->aint; + return inl(TRID_REG(card, addr)); +} + +static int trident_check_channel_interrupt(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + u32 reg = trident_get_interrupt_mask (card, channel >> 5); + +#ifdef DEBUG + if (reg & mask) + printk("trident: channel %d has interrupt, %s = 0x%08x\n", + channel,reg==T4D_AINT_B? "AINT_B":"AINT_A", reg); +#endif + return (reg & mask) ? TRUE : FALSE; +} + +static void trident_ack_channel_interrupt(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint; + + reg = inl(TRID_REG(card, addr)); + reg &= mask; + outl(reg, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, T4D_AINT_B)); + printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", + channel, reg); +#endif +} + +static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + bank = &card->banks[BANK_B]; + + for (idx = 31; idx >= 0; idx--) { + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx + 32; + return channel; + } + } + + /* no more free channels available */ + printk(KERN_ERR "trident: no more channels available on Bank B.\n"); + return NULL; +} + +static void trident_free_pcm_channel(struct trident_card *card, unsigned int channel) +{ + int bank; + unsigned char b; + + if (channel < 31 || channel > 63) + return; + + if (card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_DX || + card->pci_id == PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) { + b = inb (TRID_REG(card, T4D_REC_CH)); + if ((b & ~0x80) == channel) + outb(0x0, TRID_REG(card, T4D_REC_CH)); + } + + bank = channel >> 5; + channel = channel & 0x1f; + + card->banks[bank].bitmap &= ~(1 << (channel)); +} + +static struct trident_channel * cyber_alloc_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + /* The cyberpro 5050 has only 32 voices and one bank */ + /* .. at least they are not documented (if you want to call that + * crap documentation), perhaps broken ? */ + + bank = &card->banks[BANK_A]; + + for (idx = 31; idx >= 0; idx--) { + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + } + + /* no more free channels available */ + printk(KERN_ERR "cyberpro5050: no more channels available on Bank A.\n"); + return NULL; +} + +static void cyber_free_pcm_channel(struct trident_card *card, unsigned int channel) +{ + if (channel > 31) + return; + card->banks[BANK_A].bitmap &= ~(1 << (channel)); +} + +static inline void cyber_outidx(int port,int idx,int data) +{ + outb(idx,port); + outb(data,port+1); +} + +static inline int cyber_inidx(int port,int idx) +{ + outb(idx,port); + return inb(port+1); +} + +static int cyber_init_ritual(struct trident_card *card) +{ + /* some black magic, taken from SDK samples */ + /* remove this and nothing will work */ + int portDat; + int ret = 0; + unsigned long flags; + + /* + * Keep interrupts off for the configure - we don't want to + * clash with another cyberpro config event + */ + + save_flags(flags); + cli(); + portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE); + /* enable, if it was disabled */ + if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE ) { + printk(KERN_INFO "cyberpro5050: enabling audio controller\n" ); + cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE, + portDat | CYBER_BMSK_AUENZ_ENABLE ); + /* check again if hardware is enabled now */ + portDat = cyber_inidx(CYBER_PORT_AUDIO, CYBER_IDX_AUDIO_ENABLE); + } + if( (portDat & CYBER_BMSK_AUENZ) != CYBER_BMSK_AUENZ_ENABLE ) + { + printk(KERN_ERR "cyberpro5050: initAudioAccess: no success\n" ); + ret = -1; + } + else + { + cyber_outidx( CYBER_PORT_AUDIO, CYBER_IDX_IRQ_ENABLE, CYBER_BMSK_AUDIO_INT_ENABLE ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x01 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xba, 0x20 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbb, 0x08 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x02 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xb3, 0x06 ); + cyber_outidx( CYBER_PORT_AUDIO, 0xbf, 0x00 ); + } + restore_flags(flags); + return ret; +} + +/* called with spin lock held */ + +static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel) +{ + int i; + + if (channel > 63) + return FALSE; + + /* select hardware channel to write */ + outb(channel, TRID_REG(card, T4D_LFO_GC_CIR)); + + /* Output the channel registers, but don't write register + three to an ALI chip. */ + for (i = 0; i < CHANNEL_REGS; i++) { + if (i == 3 && card->pci_id == PCI_DEVICE_ID_ALI_5451) + continue; + outl(data[i], TRID_REG(card, CHANNEL_START + 4*i)); + } + if (card->pci_id == PCI_DEVICE_ID_ALI_5451 || + card->pci_id == PCI_DEVICE_ID_INTERG_5050) { + outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF1)); + outl(ALI_EMOD_Still, TRID_REG(card, ALI_EBUF2)); + } + return TRUE; +} + +/* called with spin lock held */ +static int trident_write_voice_regs(struct trident_state *state) +{ + unsigned int data[CHANNEL_REGS + 1]; + struct trident_channel *channel; + + channel = state->dmabuf.channel; + + data[1] = channel->lba; + data[4] = channel->control; + + switch (state->card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = 0; + break; + case PCI_DEVICE_ID_SI_7018: + case PCI_DEVICE_ID_INTERG_5050: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = channel->fm_vol & 0xffff; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + data[0] = (channel->delta << 24); + data[2] = ((channel->delta << 16) & 0xff000000) | (channel->eso & 0x00ffffff); + data[3] = channel->fm_vol & 0xffff; + break; + default: + return FALSE; + } + + return trident_load_channel_registers(state->card, data, channel->num); +} + +static int compute_rate_play(u32 rate) +{ + int delta; + /* We special case 44100 and 8000 since rounding with the equation + does not give us an accurate enough value. For 11025 and 22050 + the equation gives us the best answer. All other frequencies will + also use the equation. JDW */ + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; + return delta; +} + +static int compute_rate_rec(u32 rate) +{ + int delta; + + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + + return delta; +} +/* set playback sample rate */ +static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate_play(rate); + + trident_write_voice_regs(state); + +#ifdef DEBUG + printk("trident: called trident_set_dac_rate : rate = %d\n", rate); +#endif + + return rate; +} + +/* set recording sample rate */ +static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate_rec(rate); + + trident_write_voice_regs(state); + +#ifdef DEBUG + printk("trident: called trident_set_adc_rate : rate = %d\n", rate); +#endif + return rate; +} + +/* prepare channel attributes for playback */ +static void trident_play_setup(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct trident_channel *channel = dmabuf->channel; + + channel->lba = dmabuf->dma_handle; + channel->delta = compute_rate_play(dmabuf->rate); + + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; + + if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { + channel->attribute = 0; + if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if ((channel->num == ALI_SPDIF_IN_CHANNEL) || (channel->num == ALI_PCM_IN_CHANNEL)) + ali_disable_special_channel(state->card, channel->num); + else if ((inl(TRID_REG(state->card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE) + && (channel->num == ALI_SPDIF_OUT_CHANNEL)) + { + ali_set_spdif_out_rate(state->card, state->dmabuf.rate); + state->dmabuf.channel->delta = 0x1000; + } + } + } + + channel->fm_vol = 0x0; + + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { + /* 16-bits */ + channel->control |= CHANNEL_16BITS; + /* signed */ + channel->control |= CHANNEL_SIGNED; + } + if (dmabuf->fmt & TRIDENT_FMT_STEREO) + /* stereo */ + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("trident: trident_play_setup, LBA = 0x%08x, " + "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); +#endif + trident_write_voice_regs(state); +} + +/* prepare channel attributes for recording */ +static void trident_rec_setup(struct trident_state *state) +{ + u16 w; + u8 bval; + + struct trident_card *card = state->card; + struct dmabuf *dmabuf = &state->dmabuf; + struct trident_channel *channel = dmabuf->channel; + unsigned int rate; + + /* Enable AC-97 ADC (capture) */ + switch (card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + ali_enable_special_channel(state); + break; + case PCI_DEVICE_ID_SI_7018: + /* for 7018, the ac97 is always in playback/record (duplex) mode */ + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + /* enable and set record channel */ + outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + w = inw(TRID_REG(card, T4D_MISCINT)); + outw(w | 0x1000, TRID_REG(card, T4D_MISCINT)); + /* enable and set record channel */ + outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); + break; + case PCI_DEVICE_ID_INTERG_5050: + /* don't know yet, using special channel 22 in GC1(0xd4)? */ + break; + default: + return; + } + + channel->lba = dmabuf->dma_handle; + channel->delta = compute_rate_rec(dmabuf->rate); + if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) && (channel->num == ALI_SPDIF_IN_CHANNEL)) { + rate = ali_get_spdif_in_rate(card); + if (rate == 0) + { + printk(KERN_WARNING "trident: ALi 5451 S/PDIF input setup error!\n"); + rate = 48000; + } + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); + if (bval & 0x10) + { + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); + printk(KERN_WARNING "trident: cleared ALi 5451 S/PDIF parity error flag.\n"); + } + + if (rate != 48000) + channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff; + } + + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; + + if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { + channel->attribute = 0; + } + + channel->fm_vol = 0x0; + + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { + /* 16-bits */ + channel->control |= CHANNEL_16BITS; + /* signed */ + channel->control |= CHANNEL_SIGNED; + } + if (dmabuf->fmt & TRIDENT_FMT_STEREO) + /* stereo */ + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("trident: trident_rec_setup, LBA = 0x%08x, " + "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); +#endif + trident_write_voice_regs(state); +} + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ +static inline unsigned trident_get_dma_addr(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 cso; + + if (!dmabuf->enable) + return 0; + + outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR)); + + switch (state->card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + case PCI_DEVICE_ID_SI_7018: + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + case PCI_DEVICE_ID_INTERG_5050: + /* 16 bits ESO, CSO for 7018 and DX */ + cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + /* 24 bits ESO, CSO for NX */ + cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff; + break; + default: + return 0; + } + +#ifdef DEBUG + printk("trident: trident_get_dma_addr: chip reported channel: %d, " + "cso = 0x%04x\n", + dmabuf->channel->num, cso); +#endif + /* ESO and CSO are in units of Samples, convert to byte offset */ + cso <<= sample_shift[dmabuf->fmt]; + + return (cso % dmabuf->dmasize); +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + trident_stop_voice(card, chan_num); + trident_disable_voice_irq(card, chan_num); +} + +static void stop_adc(struct trident_state *state) +{ + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void start_adc(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) { + dmabuf->enable |= ADC_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); + } + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + trident_stop_voice(card, chan_num); + if (state->chans_num == 6) { + trident_stop_voice(card, state->other_states[0]->dmabuf.channel->num); + trident_stop_voice(card, state->other_states[1]->dmabuf.channel->num); + trident_stop_voice(card, state->other_states[2]->dmabuf.channel->num); + trident_stop_voice(card, state->other_states[3]->dmabuf.channel->num); + } + trident_disable_voice_irq(card, chan_num); +} + +static void stop_dac(struct trident_state *state) +{ + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void start_dac(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { + dmabuf->enable |= DAC_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); + if (state->chans_num == 6) { + trident_start_voice(card, state->other_states[0]->dmabuf.channel->num); + trident_start_voice(card, state->other_states[1]->dmabuf.channel->num); + trident_start_voice(card, state->other_states[2]->dmabuf.channel->num); + trident_start_voice(card, state->other_states[3]->dmabuf.channel->num); + } + } + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ +static int alloc_dmabuf(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf = NULL; + int order; + struct page *page, *pend; + + /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + if (!rawbuf) + return -ENOMEM; + +#ifdef DEBUG + printk("trident: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(rawbuf); page <= pend; page++) + mem_map_reserve(page); + + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct page *page, *pend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_handle); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct trident_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned bytepersec; + struct trident_state *s = state; + unsigned bufsize, dma_nums; + unsigned long flags; + int ret, i, order; + struct page *page, *pend; + + lock_set_fmt(state); + if (state->chans_num == 6) + dma_nums = 5; + else dma_nums = 1; + + for (i = 0; i < dma_nums; i++) { + if (i > 0) { + s = state->other_states[i - 1]; + dmabuf = &s->dmabuf; + dmabuf->fmt = state->dmabuf.fmt; + dmabuf->rate = state->dmabuf.rate; + } + + spin_lock_irqsave(&s->card->lock, flags); + dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = 0; + spin_unlock_irqrestore(&s->card->lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) { + if (i == 0) { + if ((ret = alloc_dmabuf(state))) { + unlock_set_fmt(state); + return ret; + } + } + else { + if ((order = state->dmabuf.buforder - 1) >= DMABUF_MINORDER) { + dmabuf->rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle); + } + if (!dmabuf->rawbuf) { + free_pages((unsigned long)state->dmabuf.rawbuf, state->dmabuf.buforder); + state->dmabuf.rawbuf = NULL; + i-=2; + for (; i >= 0; i--) { + pci_free_consistent(state->card->pci_dev, + PAGE_SIZE << state->other_states[i]->dmabuf.buforder, + state->other_states[i]->dmabuf.rawbuf, + state->other_states[i]->dmabuf.dma_handle); + } + unlock_set_fmt(state); + return -ENOMEM; + } + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->buforder = order; + pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + } + /* FIXME: figure out all this OSS fragment stuff */ + bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt]; + bufsize = PAGE_SIZE << dmabuf->buforder; + if (dmabuf->ossfragshift) { + if ((1000 << dmabuf->ossfragshift) < bytepersec) + dmabuf->fragshift = ld2(bytepersec/1000); + else + dmabuf->fragshift = dmabuf->ossfragshift; + } else { + /* lets hand out reasonable big ass buffers by default */ + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); + } + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { + dmabuf->fragshift--; + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + } + dmabuf->fragsize = 1 << dmabuf->fragshift; + if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag) + dmabuf->numfrag = dmabuf->ossmaxfrags; + dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt]; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + + memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); + + spin_lock_irqsave(&s->card->lock, flags); + if (rec) { + trident_rec_setup(s); + } else { + trident_play_setup(s); + } + spin_unlock_irqrestore(&s->card->lock, flags); + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + +#ifdef DEBUG + printk("trident: prog_dmabuf(%d), sample rate = %d, format = %d, numfrag = %d, " + "fragsize = %d dmasize = %d\n", + dmabuf->channel->num, dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + } + unlock_set_fmt(state); + return 0; +} + +/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e. + |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx| + but we almost always get this + |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------| + so we have to clear the tail space to "silence" + |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000| +*/ +static void trident_clear_tail(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned swptr; + unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80; + unsigned int len; + unsigned long flags; + + spin_lock_irqsave(&state->card->lock, flags); + swptr = dmabuf->swptr; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize) + return; + + if (swptr < dmabuf->dmasize/2) + len = dmabuf->dmasize/2 - swptr; + else + len = dmabuf->dmasize - swptr; + + memset(dmabuf->rawbuf + swptr, silence, len); + if(state->card->pci_id != PCI_DEVICE_ID_ALI_5451) + { + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr += len; + dmabuf->count += len; + spin_unlock_irqrestore(&state->card->lock, flags); + } + + /* restart the dma machine in case it is halted */ + start_dac(state); +} + +static int drain_dac(struct trident_state *state, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + unsigned long diff = 0; + + if (dmabuf->mapped || !dmabuf->ready) + return 0; + + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + /* It seems that we have to set the current state to TASK_INTERRUPTIBLE + every time to make the process really go to sleep */ + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&state->card->lock, flags); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + if (signal_pending(current)) + break; + + if (nonblock) { + remove_wait_queue(&dmabuf->wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + + /* No matter how much data is left in the buffer, we have to wait until + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451 || + state->card->pci_id == PCI_DEVICE_ID_INTERG_5050) + { + diff = dmabuf->swptr - trident_get_dma_addr(state) + dmabuf->dmasize ; + diff = diff % (dmabuf->dmasize); + tmo = (diff * HZ) / dmabuf->rate; + } + else + { + tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; + } + tmo >>= sample_shift[dmabuf->fmt]; + if (!schedule_timeout(tmo ? tmo : 1) && tmo){ + break; + } + } + remove_wait_queue(&dmabuf->wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + + return 0; +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void trident_update_ptr(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned hwptr, swptr; + int clear_cnt = 0; + int diff; + unsigned char silence; + unsigned half_dmasize; + + /* update hardware pointer */ + hwptr = trident_get_dma_addr(state); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + + /* error handling and process wake up for ADC */ + if (dmabuf->enable == ADC_RUNNING) { + if (dmabuf->mapped) { + dmabuf->count -= diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + dmabuf->count += diff; + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun, we have no way to recover + it here, just stop the machine and let the process force hwptr + and swptr to sync */ + __stop_adc(state); + dmabuf->error++; + } + if (dmabuf->count < (signed)dmabuf->dmasize/2) + wake_up(&dmabuf->wait); + } + } + + /* error handling and process wake up for DAC */ + if (dmabuf->enable == DAC_RUNNING) { + if (dmabuf->mapped) { + dmabuf->count += diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + dmabuf->count -= diff; + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun, we have no way to recover + it here, just stop the machine and let the process force hwptr + and swptr to sync */ + __stop_dac(state); + dmabuf->error++; + } + else if (!dmabuf->endcleared) { + swptr = dmabuf->swptr; + silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80); + if (dmabuf->update_flag & ALI_ADDRESS_INT_UPDATE) { + /* We must clear end data of 1/2 dmabuf if needed. + According to 1/2 algorithm of Address Engine Interrupt, + check the validation of the data of half dmasize. */ + half_dmasize = dmabuf->dmasize / 2; + if ((diff = hwptr - half_dmasize) < 0 ) + diff = hwptr; + if ((dmabuf->count + diff) < half_dmasize) { + //there is invalid data in the end of half buffer + if ((clear_cnt = half_dmasize - swptr) < 0) + clear_cnt += half_dmasize; + //clear the invalid data + memset (dmabuf->rawbuf + swptr, + silence, clear_cnt); + if (state->chans_num == 6) { + clear_cnt = clear_cnt / 2; + swptr = swptr / 2; + memset (state->other_states[0]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[1]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[2]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[3]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + } + dmabuf->endcleared = 1; + } + } else if (dmabuf->count < (signed) dmabuf->fragsize) { + clear_cnt = dmabuf->fragsize; + if ((swptr + clear_cnt) > dmabuf->dmasize) + clear_cnt = dmabuf->dmasize - swptr; + memset (dmabuf->rawbuf + swptr, silence, clear_cnt); + if (state->chans_num == 6) { + clear_cnt = clear_cnt / 2; + swptr = swptr / 2; + memset (state->other_states[0]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[1]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[2]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + memset (state->other_states[3]->dmabuf.rawbuf + swptr, + silence, clear_cnt); + } + dmabuf->endcleared = 1; + } + } + /* trident_update_ptr is called by interrupt handler or by process via + ioctl/poll, we only wake up the waiting process when we have more + than 1/2 buffer free (always true for interrupt handler) */ + if (dmabuf->count < (signed)dmabuf->dmasize/2) + wake_up(&dmabuf->wait); + } + } + dmabuf->update_flag &= ~ALI_ADDRESS_INT_UPDATE; +} + +static void trident_address_interrupt(struct trident_card *card) +{ + int i; + struct trident_state *state; + + /* Update the pointers for all channels we are running. */ + /* FIXME: should read interrupt status only once */ + for (i = 0; i < NR_HW_CH; i++) { + if (trident_check_channel_interrupt(card, 63 - i)) { + trident_ack_channel_interrupt(card, 63 - i); + if ((state = card->states[i]) != NULL) { + trident_update_ptr(state); + } else { + printk("trident: spurious channel irq %d.\n", + 63 - i); + trident_stop_voice(card, 63 - i); + trident_disable_voice_irq(card, 63 - i); + } + } + } +} + +static void ali_hwvol_control(struct trident_card *card, int opt) +{ + u16 dwTemp, volume[2], mute, diff, *pVol[2]; + + dwTemp = ali_ac97_read(card->ac97_codec[0], 0x02); + mute = dwTemp & 0x8000; + volume[0] = dwTemp & 0x001f; + volume[1] = (dwTemp & 0x1f00) >> 8; + if (volume[0] < volume [1]) { + pVol[0] = &volume[0]; + pVol[1] = &volume[1]; + } else { + pVol[1] = &volume[0]; + pVol[0] = &volume[1]; + } + diff = *(pVol[1]) - *(pVol[0]); + + if (opt == 1) { // MUTE + dwTemp ^= 0x8000; + ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); + } else if (opt == 2) { // Down + if (mute) + return; + if (*(pVol[1]) < 0x001f) { + (*pVol[1])++; + *(pVol[0]) = *(pVol[1]) - diff; + } + dwTemp &= 0xe0e0; + dwTemp |= (volume[0]) | (volume[1] << 8); + ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); + card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8); + } else if (opt == 4) { // Up + if (mute) + return; + if (*(pVol[0]) >0) { + (*pVol[0])--; + *(pVol[1]) = *(pVol[0]) + diff; + } + dwTemp &= 0xe0e0; + dwTemp |= (volume[0]) | (volume[1] << 8); + ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp); + card->ac97_codec[0]->mixer_state[0] = ((32-volume[0])*25/8) | (((32-volume[1])*25/8) << 8); + } + else + { + /* Nothing needs doing */ + } +} + +/* + * Re-enable reporting of vol change after 0.1 seconds + */ + +static void ali_timeout(unsigned long ptr) +{ + struct trident_card *card = (struct trident_card *)ptr; + u16 temp = 0; + + /* Enable GPIO IRQ (MISCINT bit 18h)*/ + temp = inw(TRID_REG(card, T4D_MISCINT + 2)); + temp |= 0x0004; + outw(temp, TRID_REG(card, T4D_MISCINT + 2)); +} + +/* + * Set up the timer to clear the vol change notification + */ + +static void ali_set_timer(struct trident_card *card) +{ + /* Add Timer Routine to Enable GPIO IRQ */ + del_timer(&card->timer); /* Never queue twice */ + card->timer.function = ali_timeout; + card->timer.data = (unsigned long) card; + card->timer.expires = jiffies + HZ/10; + add_timer(&card->timer); +} + +/* + * Process a GPIO event + */ + +static void ali_queue_task(struct trident_card *card, int opt) +{ + u16 temp; + + /* Disable GPIO IRQ (MISCINT bit 18h)*/ + temp = inw(TRID_REG(card, T4D_MISCINT + 2)); + temp &= (u16)(~0x0004); + outw(temp, TRID_REG(card, T4D_MISCINT + 2)); + + /* Adjust the volume */ + ali_hwvol_control(card, opt); + + /* Set the timer for 1/10th sec */ + ali_set_timer(card); +} + +static void cyber_address_interrupt(struct trident_card *card) +{ + int i,irq_status; + struct trident_state *state; + + /* Update the pointers for all channels we are running. */ + /* FIXED: read interrupt status only once */ + irq_status=inl(TRID_REG(card, T4D_AINT_A) ); +#ifdef DEBUG + printk("cyber_address_interrupt: irq_status 0x%X\n",irq_status); +#endif + for (i = 0; i < NR_HW_CH; i++) { + if (irq_status & ( 1 << (31 - i)) ) { + + /* clear bit by writing a 1, zeroes are ignored */ + outl( (1 <<(31-i)), TRID_REG(card, T4D_AINT_A)); + +#ifdef DEBUG + printk("cyber_interrupt: channel %d\n", 31-i); +#endif + if ((state = card->states[i]) != NULL) { + trident_update_ptr(state); + } else { + printk("cyber5050: spurious channel irq %d.\n", + 31 - i); + trident_stop_voice(card, 31 - i); + trident_disable_voice_irq(card, 31 - i); + } + } + } +} + +static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct trident_card *card = (struct trident_card *)dev_id; + u32 event; + u32 gpio; + + spin_lock(&card->lock); + event = inl(TRID_REG(card, T4D_MISCINT)); + +#ifdef DEBUG + printk("trident: trident_interrupt called, MISCINT = 0x%08x\n", event); +#endif + + if (event & ADDRESS_IRQ) { + card->address_interrupt(card); + } + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) + { + /* GPIO IRQ (H/W Volume Control) */ + event = inl(TRID_REG(card, T4D_MISCINT)); + if (event & (1<<25)) { + gpio = inl(TRID_REG(card, ALI_GPIO)); + if (!timer_pending(&card->timer)) + ali_queue_task(card, gpio&0x07); + } + event = inl(TRID_REG(card, T4D_MISCINT)); + outl(event | (ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(card, T4D_MISCINT)); + spin_unlock(&card->lock); + return; + } + + /* manually clear interrupt status, bad hardware design, blame T^2 */ + outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), + TRID_REG(card, T4D_MISCINT)); + spin_unlock(&card->lock); +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to + the user's buffer. it is filled by the dma machine and drained by this loop. */ +static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + +#ifdef DEBUG + printk("trident: trident_read called, count = %d\n", count); +#endif + + VALIDATE_STATE(state); + if (ppos != &file->f_pos) + return -ESPIPE; + + if (dmabuf->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + down(&state->sem); + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + goto out; + + while (count > 0) { + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count > (signed) dmabuf->dmasize) { + /* buffer overrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr, make process flush the buffer */ + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + start_adc(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + + up(&state->sem); + /* No matter how much space left in the buffer, we have to wait until + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); + tmo >>= sample_shift[dmabuf->fmt]; + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { +#ifdef DEBUG + printk(KERN_ERR "trident: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer overrun, we delay the recovery until next time the + while loop begin and we REALLY have space to record */ + } + if (signal_pending(current)) { + if(!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if(dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) ret = -EFAULT; + goto out; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(state); + } +out: + up(&state->sem); + return ret; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to + the soundcard. it is drained by the dma machine and filled by this loop. */ + +static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned int state_cnt; + unsigned int copy_count; + +#ifdef DEBUG + printk("trident: trident_write called, count = %d\n", count); +#endif + VALIDATE_STATE(state); + if (ppos != &file->f_pos) + return -ESPIPE; + + /* + * Guard against an mmap or ioctl while writing + */ + + down(&state->sem); + + if (dmabuf->mapped) + { + ret = -ENXIO; + goto out; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + goto out; + + if (!access_ok(VERIFY_READ, buffer, count)) + { + ret= -EFAULT; + goto out; + } + + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count < 0) { + /* buffer underrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr */ + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize) + cnt = dmabuf->dmasize - dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is full, start the dma machine and wait for data to be + played */ + start_dac(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + goto out; + } + /* No matter how much data left in the buffer, we have to wait until + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + lock_set_fmt(state); + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); + tmo >>= sample_shift[dmabuf->fmt]; + unlock_set_fmt(state); + up(&state->sem); + + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer underrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { +#ifdef DEBUG + printk(KERN_ERR "trident: playback schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer underrun, we delay the recovery until next time the + while loop begin and we REALLY have data to play */ + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto out; + } + down(&state->sem); + if(dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; + } + continue; + } + lock_set_fmt(state); + if (state->chans_num == 6) { + copy_count = 0; + state_cnt = 0; + if (ali_write_5_1(state, buffer, cnt, ©_count, &state_cnt) == -EFAULT) { + if (state_cnt){ + swptr = (swptr + state_cnt) % dmabuf->dmasize; + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count += state_cnt; + dmabuf->endcleared = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + } + ret += copy_count; + if (!ret) ret = -EFAULT; + unlock_set_fmt(state); + goto out; + } + } + else { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + unlock_set_fmt(state); + goto out; + } + state_cnt = cnt; + } + unlock_set_fmt(state); + + swptr = (swptr + state_cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count += state_cnt; + dmabuf->endcleared = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(state); + } +out: + up(&state->sem); + return ret; +} + + +/* No kernel lock - we have our own spinlock */ +static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(state); + + /* + * Guard against a parallel poll and write causing multiple + * prog_dmabuf events + */ + + down(&state->sem); + + if (file->f_mode & FMODE_WRITE) { + if (!dmabuf->ready && prog_dmabuf(state, 0)) + { + up(&state->sem); + return 0; + } + poll_wait(file, &dmabuf->wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!dmabuf->ready && prog_dmabuf(state, 1)) + { + up(&state->sem); + return 0; + } + poll_wait(file, &dmabuf->wait, wait); + } + + up(&state->sem); + + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + if (file->f_mode & FMODE_READ) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&state->card->lock, flags); + + return mask; +} + +static int trident_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(state); + lock_kernel(); + + /* + * Lock against poll read write or mmap creating buffers. Also lock + * a read or write against an mmap. + */ + + down(&state->sem); + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(state, 0)) != 0) + goto out; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(state, 1)) != 0) + goto out; + } else + goto out; + + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + goto out; + ret = -EAGAIN; + if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) + goto out; + dmabuf->mapped = 1; + ret = 0; +out: + up(&state->sem); + unlock_kernel(); + return ret; +} + +static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret = 0; + + struct trident_card *card = state->card; + + VALIDATE_STATE(state); + mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) || + ((file->f_mode & FMODE_READ) && dmabuf->mapped); +#ifdef DEBUG + printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", + _IOC_NR(cmd), arg ? *(int *)arg : 0); +#endif + + switch (cmd) + { + case OSS_GETVERSION: + ret = put_user(SOUND_VERSION, (int *)arg); + break; + + case SNDCTL_DSP_RESET: + /* FIXME: spin_lock ? */ + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + synchronize_irq(); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + synchronize_irq(); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + break; + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + ret = drain_dac(state, file->f_flags & O_NONBLOCK); + break; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + trident_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + trident_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + ret = put_user(dmabuf->rate, (int *)arg); + break; + + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + lock_set_fmt(state); + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + if (val) + dmabuf->fmt |= TRIDENT_FMT_STEREO; + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + if (val) + dmabuf->fmt |= TRIDENT_FMT_STEREO; + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + } + unlock_set_fmt(state); + break; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(state, 0))) + ret = val; + else + ret = put_user(dmabuf->fragsize, (int *)arg); + break; + } + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + ret = val; + else + ret = put_user(dmabuf->fragsize, (int *)arg); + break; + } + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + break; + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + lock_set_fmt(state); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + if (val == AFMT_S16_LE) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + else + dmabuf->fmt &= ~TRIDENT_FMT_16BIT; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + if (val == AFMT_S16_LE) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + else + dmabuf->fmt &= ~TRIDENT_FMT_16BIT; + } + } + unlock_set_fmt(state); + ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + break; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val != 0) { + lock_set_fmt(state); + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + + //prevent from memory leak + if ((state->chans_num > 2) && (state->chans_num != val)) { + ali_free_other_states_resources(state); + state->chans_num = 1; + } + + if (val >= 2) + { + + dmabuf->fmt |= TRIDENT_FMT_STEREO; + if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) { + + if( card->rec_channel_use_count > 0 ) + { + printk(KERN_ERR "trident: Record is working on the card!\n"); + ret = -EBUSY; + break; + } + + ret = ali_setup_multi_channels(state->card, 6); + if (ret < 0) { + unlock_set_fmt(state); + break; + } + down(&state->card->open_sem); + ret = ali_allocate_other_states_resources(state, 6); + if (ret < 0) { + up(&state->card->open_sem); + unlock_set_fmt(state); + break; + } + state->card->multi_channel_use_count ++; + up(&state->card->open_sem); + } + else val = 2; /*yield to 2-channels*/ + } + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + state->chans_num = val; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + if (val >= 2) { + if (!((file->f_mode & FMODE_WRITE) && (val == 6))) + val = 2; + dmabuf->fmt |= TRIDENT_FMT_STEREO; + } + else + dmabuf->fmt &= ~TRIDENT_FMT_STEREO; + state->chans_num = val; + } + unlock_set_fmt(state); + } + ret = put_user(val, (int *)arg); + break; + + case SNDCTL_DSP_POST: + /* Cause the working fragment to be output */ + break; + + case SNDCTL_DSP_SUBDIVIDE: + if (dmabuf->subdivision) + { + ret = -EINVAL; + break; + } + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val != 1 && val != 2 && val != 4) + { + ret = -EINVAL; + break; + } + dmabuf->subdivision = val; + break; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (dmabuf->ossfragshift < 4) + dmabuf->ossfragshift = 4; + if (dmabuf->ossfragshift > 15) + dmabuf->ossfragshift = 15; + if (dmabuf->ossmaxfrags < 4) + dmabuf->ossmaxfrags = 4; + + break; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->dmasize - dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + break; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + break; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + break; + + case SNDCTL_DSP_GETCAPS: + ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, + (int *)arg); + break; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && dmabuf->enable) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && dmabuf->enable) + val |= PCM_ENABLE_OUTPUT; + ret = put_user(val, (int *)arg); + break; + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + break; + start_adc(state); + } else + stop_adc(state); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + break; + start_dac(state); + } else + stop_dac(state); + } + break; + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped) + dmabuf->count &= dmabuf->fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + break; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + { + ret = val; + break; + } + + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped) + dmabuf->count &= dmabuf->fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0; + break; + + case SNDCTL_DSP_SETDUPLEX: + ret = -EINVAL; + break; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + { + ret = -EINVAL; + break; + } + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + ret = put_user(val, (int *)arg); + break; + + case SOUND_PCM_READ_RATE: + ret = put_user(dmabuf->rate, (int *)arg); + break; + + case SOUND_PCM_READ_CHANNELS: + ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); + break; + + case SOUND_PCM_READ_BITS: + ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + break; + + case SNDCTL_DSP_GETCHANNELMASK: + ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE, + (int *)arg); + break; + + case SNDCTL_DSP_BIND_CHANNEL: + if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) + { + ret = -EINVAL; + break; + } + + if (get_user(val, (int *)arg)) + { + ret = -EFAULT; + break; + } + if (val == DSP_BIND_QUERY) { + val = dmabuf->channel->attribute | 0x3c00; + val = attr2mask[val >> 8]; + } else { + dmabuf->ready = 0; + if (file->f_mode & FMODE_READ) + dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE); + if (file->f_mode & FMODE_WRITE) + dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE); + dmabuf->channel->attribute |= mask2attr[ffs(val)]; + } + ret = put_user(val, (int *)arg); + break; + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + default: + ret = -EINVAL; + break; + + } + return ret; +} + +static int trident_open(struct inode *inode, struct file *file) +{ + int i = 0; + int minor = minor(inode->i_rdev); + struct trident_card *card = devs; + struct trident_state *state = NULL; + struct dmabuf *dmabuf = NULL; + + /* Added by Matt Wu 01-05-2001 */ + if(file->f_mode & FMODE_READ) + { + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if (card->multi_channel_use_count > 0) + return -EBUSY; + } + } + + /* find an available virtual channel (instance of /dev/dsp) */ + while (card != NULL) { + down(&card->open_sem); + if(file->f_mode & FMODE_READ) + { + /* Skip opens on cards that are in 6 channel mode */ + if (card->multi_channel_use_count > 0) + { + up(&card->open_sem); + card = card->next; + continue; + } + } + for (i = 0; i < NR_HW_CH; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct trident_state *) + kmalloc(sizeof(struct trident_state), GFP_KERNEL); + if (state == NULL) { + return -ENOMEM; + } + memset(state, 0, sizeof(struct trident_state)); + init_MUTEX(&state->sem); + dmabuf = &state->dmabuf; + goto found_virt; + } + } + up(&card->open_sem); + card = card->next; + } + /* no more virtual channel avaiable */ + if (!state) { + return -ENODEV; + } + found_virt: + /* found a free virtual channel, allocate hardware channels */ + if(file->f_mode & FMODE_READ) + dmabuf->channel = card->alloc_rec_pcm_channel(card); + else + dmabuf->channel = card->alloc_pcm_channel(card); + + if (dmabuf->channel == NULL) { + kfree (card->states[i]); + card->states[i] = NULL; + return -ENODEV; + } + + /* initialize the virtual channel */ + state->virt = i; + state->card = card; + state->magic = TRIDENT_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + file->private_data = state; + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample */ + if (file->f_mode & FMODE_WRITE) { + dmabuf->fmt &= ~TRIDENT_FMT_MASK; + if ((minor & 0x0f) == SND_DEV_DSP16) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + if (card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* set default channel attribute to normal playback */ + dmabuf->channel->attribute = CHANNEL_PB; + } + trident_set_dac_rate(state, 8000); + } + + if (file->f_mode & FMODE_READ) { + /* FIXME: Trident 4d can only record in signed 16-bits stereo, 48kHz sample, + to be dealed with in trident_set_adc_rate() ?? */ + dmabuf->fmt &= ~TRIDENT_FMT_MASK; + if ((minor & 0x0f) == SND_DEV_DSP16) + dmabuf->fmt |= TRIDENT_FMT_16BIT; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + if (card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* set default channel attribute to 0x8a80, record from + PCM L/R FIFO and mono = (left + right + 1)/2*/ + dmabuf->channel->attribute = + (CHANNEL_REC|PCM_LR|MONO_MIX); + } + trident_set_adc_rate(state, 8000); + + /* Added by Matt Wu 01-05-2001 */ + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) + card->rec_channel_use_count ++; + } + + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&card->open_sem); + +#ifdef DEBUG + printk(KERN_ERR "trident: open virtual channel %d, hard channel %d\n", + state->virt, dmabuf->channel->num); +#endif + + return 0; +} + +static int trident_release(struct inode *inode, struct file *file) +{ + struct trident_state *state = (struct trident_state *)file->private_data; + struct trident_card *card; + struct dmabuf *dmabuf; + unsigned long flags; + + lock_kernel(); + card = state->card; + dmabuf = &state->dmabuf; + VALIDATE_STATE(state); + + if (file->f_mode & FMODE_WRITE) { + trident_clear_tail(state); + drain_dac(state, file->f_flags & O_NONBLOCK); + } + +#ifdef DEBUG + printk(KERN_ERR "trident: closing virtual channel %d, hard channel %d\n", + state->virt, dmabuf->channel->num); +#endif + + /* stop DMA state machine and free DMA buffers/channels */ + down(&card->open_sem); + + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + lock_set_fmt(state); + + unlock_set_fmt(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + + /* Added by Matt Wu */ + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if (state->chans_num > 2) { + if (card->multi_channel_use_count-- < 0) + card->multi_channel_use_count = 0; + if (card->multi_channel_use_count == 0) + ali_close_multi_channels(); + ali_free_other_states_resources(state); + } + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + + /* Added by Matt Wu */ + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + if( card->rec_channel_use_count-- < 0 ) + card->rec_channel_use_count = 0; + } + } + + card->states[state->virt] = NULL; + kfree(state); + + /* we're covered by the open_sem */ + up(&card->open_sem); + unlock_kernel(); + + return 0; +} + +static /*const*/ struct file_operations trident_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: trident_read, + write: trident_write, + poll: trident_poll, + ioctl: trident_ioctl, + mmap: trident_mmap, + open: trident_open, + release: trident_release, +}; + +/* trident specific AC97 functions */ +/* Write AC97 codec registers */ +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + data = ((u32) val) << 16; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_WRITE; + mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR0_AC97_W; + mask = busy = DX_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + address = NX_ACR1_AC97_W; + mask = NX_AC97_BUSY_WRITE; + if (codec->id) + mask |= NX_AC97_WRITE_SECONDARY; + busy = NX_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_INTERG_5050: + address = SI_AC97_WRITE; + mask = busy = SI_AC97_BUSY_WRITE; + if (codec->id) + mask |= SI_AC97_SECONDARY; + break; + } + + spin_lock_irqsave(&card->lock, flags); + do { + if ((inw(TRID_REG(card, address)) & busy) == 0) + break; + } while (count--); + + + data |= (mask | (reg & AC97_REG_ADDR)); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC write timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return; + } + + outl(data, TRID_REG(card, address)); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* Read AC97 codec registers */ +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_READ; + mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR1_AC97_R; + mask = busy = DX_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + if (codec->id) + address = NX_ACR3_AC97_R_SECONDARY; + else + address = NX_ACR2_AC97_R_PRIMARY; + mask = NX_AC97_BUSY_READ; + busy = NX_AC97_BUSY_READ | NX_AC97_BUSY_DATA; + break; + case PCI_DEVICE_ID_INTERG_5050: + address = SI_AC97_READ; + mask = busy = SI_AC97_BUSY_READ; + if (codec->id) + mask |= SI_AC97_SECONDARY; + break; + } + + data = (mask | (reg & AC97_REG_ADDR)); + + spin_lock_irqsave(&card->lock, flags); + outl(data, TRID_REG(card, address)); + do { + data = inl(TRID_REG(card, address)); + if ((data & busy) == 0) + break; + } while (count--); + spin_unlock_irqrestore(&card->lock, flags); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC read timed out.\n"); + data = 0; + } + return ((u16) (data >> 16)); +} + +/* Write AC97 codec registers for ALi*/ +static void ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val) +{ + unsigned int address, mask; + unsigned int wCount1 = 0xffff; + unsigned int wCount2= 0xffff; + unsigned long chk1, chk2; + unsigned long flags; + u32 data; + + data = ((u32) val) << 16; + + if(!card) + BUG(); + + address = ALI_AC97_WRITE; + mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY; + if (secondary) + mask |= ALI_AC97_SECONDARY; + if (card->revision == ALI_5451_V02) + mask |= ALI_AC97_WRITE_MIXER_REGISTER; + + spin_lock_irqsave(&card->lock, flags); + while (wCount1--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_WRITE) == 0) { + data |= (mask | (reg & AC97_REG_ADDR)); + + chk1 = inl(TRID_REG(card, ALI_STIMER)); + chk2 = inl(TRID_REG(card, ALI_STIMER)); + while (wCount2-- && (chk1 == chk2)) + chk2 = inl(TRID_REG(card, ALI_STIMER)); + if (wCount2 == 0) { + spin_unlock_irqrestore(&card->lock, flags); + return; + } + outl(data, TRID_REG(card, address)); //write! + spin_unlock_irqrestore(&card->lock, flags); + return; //success + } + inw(TRID_REG(card, address)); //wait for a read cycle + } + + printk(KERN_ERR "ali: AC97 CODEC write timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return; +} + +/* Read AC97 codec registers for ALi*/ +static u16 ali_ac97_get(struct trident_card *card, int secondary, u8 reg) +{ + unsigned int address, mask; + unsigned int wCount1 = 0xffff; + unsigned int wCount2= 0xffff; + unsigned long chk1, chk2; + unsigned long flags; + u32 data; + + if(!card) + BUG(); + + address = ALI_AC97_READ; + if (card->revision == ALI_5451_V02) { + address = ALI_AC97_WRITE; + } + mask = ALI_AC97_READ_ACTION | ALI_AC97_AUDIO_BUSY; + if (secondary) + mask |= ALI_AC97_SECONDARY; + + spin_lock_irqsave(&card->lock, flags); + data = (mask | (reg & AC97_REG_ADDR)); + while (wCount1--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { + chk1 = inl(TRID_REG(card, ALI_STIMER)); + chk2 = inl(TRID_REG(card, ALI_STIMER)); + while (wCount2-- && (chk1 == chk2)) + chk2 = inl(TRID_REG(card, ALI_STIMER)); + if (wCount2 == 0) { + printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return 0; + } + outl(data, TRID_REG(card, address)); //read! + wCount2 = 0xffff; + while (wCount2--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { + data = inl(TRID_REG(card, address)); + spin_unlock_irqrestore(&card->lock, flags); + return ((u16) (data >> 16)); + } + } + } + inw(TRID_REG(card, address)); //wait a read cycle + } + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + return 0; +} + +static void ali_enable_special_channel(struct trident_state *stat) +{ + struct trident_card *card = stat->card; + unsigned long s_channels; + + s_channels = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + s_channels |= (1<dmabuf.channel->num); + outl(s_channels, TRID_REG(card, ALI_GLOBAL_CONTROL)); +} + +static u16 ali_ac97_read(struct ac97_codec *codec, u8 reg) +{ + int id; + u16 data; + struct trident_card *card = NULL; + + /* Added by Matt Wu */ + if (!codec) + BUG(); + + card = (struct trident_card *)codec->private_data; + + if(!card->mixer_regs_ready) + return ali_ac97_get(card, codec->id, reg); + + if(codec->id) + id = 1; + else + id = 0; + + data = card->mixer_regs[reg/2][id]; + return data; +} + +static void ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + int id; + struct trident_card *card; + + /* Added by Matt Wu */ + if (!codec) + BUG(); + + card = (struct trident_card *)codec->private_data; + + if (!card->mixer_regs_ready) + { + ali_ac97_set(card, codec->id, reg, val); + return; + } + + if(codec->id) + id = 1; + else + id = 0; + + card->mixer_regs[reg/2][id] = val; + ali_ac97_set(card, codec->id, reg, val); +} + +/* +flag: ALI_SPDIF_OUT_TO_SPDIF_OUT + ALI_PCM_TO_SPDIF_OUT +*/ + +static void ali_setup_spdif_out(struct trident_card *card, int flag) +{ + unsigned long spdif; + unsigned char ch; + + char temp; + struct pci_dev *pci_dev = NULL; + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); + if (pci_dev == NULL) + return; + pci_read_config_byte(pci_dev, 0x61, &temp); + temp |= 0x40; + pci_write_config_byte(pci_dev, 0x61, temp); + pci_read_config_byte(pci_dev, 0x7d, &temp); + temp |= 0x01; + pci_write_config_byte(pci_dev, 0x7d, temp); + pci_read_config_byte(pci_dev, 0x7e, &temp); + temp &= (~0x20); + temp |= 0x10; + pci_write_config_byte(pci_dev, 0x7e, temp); + + ch = inb(TRID_REG(card, ALI_SCTRL)); + outb(ch | ALI_SPDIF_OUT_ENABLE, TRID_REG(card, ALI_SCTRL)); + ch = inb(TRID_REG(card, ALI_SPDIF_CTRL)); + outb(ch & ALI_SPDIF_OUT_CH_STATUS, TRID_REG(card, ALI_SPDIF_CTRL)); + + if (flag & ALI_SPDIF_OUT_TO_SPDIF_OUT) { + spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_OUT_CH_ENABLE; + spdif &= ALI_SPDIF_OUT_SEL_SPDIF; + outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif = inw(TRID_REG(card, ALI_SPDIF_CS)); + if (flag & ALI_SPDIF_OUT_NON_PCM) + spdif |= 0x0002; + else spdif &= (~0x0002); + outw(spdif, TRID_REG(card, ALI_SPDIF_CS)); + } + else { + spdif = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_OUT_SEL_PCM; + outw(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + } +} + +static void ali_disable_special_channel(struct trident_card *card, int ch) +{ + unsigned long sc; + + sc = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + sc &= ~(1 << ch); + outl(sc, TRID_REG(card, ALI_GLOBAL_CONTROL)); +} + +static void ali_disable_spdif_in(struct trident_card *card) +{ + unsigned long spdif; + + spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif &= (~ALI_SPDIF_IN_SUPPORT); + outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + ali_disable_special_channel(card, ALI_SPDIF_IN_CHANNEL); +} + +static void ali_setup_spdif_in(struct trident_card *card) +{ + unsigned long spdif; + + //Set SPDIF IN Supported + spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_IN_SUPPORT; + outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + //Set SPDIF IN Rec + spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL)); + spdif |= ALI_SPDIF_IN_CH_ENABLE; + outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)); + spdif |= ALI_SPDIF_IN_CH_STATUS; + outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL)); +/* + spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)); + spdif |= ALI_SPDIF_IN_FUNC_ENABLE; + outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL)); +*/ +} + +static void ali_delay(struct trident_card *card,int interval) +{ + unsigned long begintimer,currenttimer; + + begintimer = inl(TRID_REG(card, ALI_STIMER)); + currenttimer = inl(TRID_REG(card, ALI_STIMER)); + + while (currenttimer < begintimer + interval) + currenttimer = inl(TRID_REG(card, ALI_STIMER)); +} + +static void ali_detect_spdif_rate(struct trident_card *card) +{ + u16 wval = 0; + u16 count = 0; + u8 bval = 0, R1 = 0, R2 = 0; + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); + bval |= 0x02; + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); + bval |= 0x1F; + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL + 1)); + + while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) + { + count ++; + + ali_delay(card, 6); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); + R1 = bval & 0x1F; + } + + if (count > 50000) + { + printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n"); + return; + } + + count = 0; + + while (count <= 50000) + { + count ++; + + ali_delay(card, 6); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1)); + R2 = bval & 0x1F; + + if(R2 != R1) + R1 = R2; + else + break; + } + + if (count > 50000) + { + printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n"); + return; + } + + switch (R2) + { + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + wval = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x09 << 8 | (u16)0x05; + outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x02,TRID_REG(card,ALI_SPDIF_CS + 3)); + break; + + case 0x12: + wval = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x0E << 8 | (u16)0x08; + outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x03,TRID_REG(card,ALI_SPDIF_CS + 3)); + break; + + default: + break; + } + +} + +static unsigned int ali_get_spdif_in_rate(struct trident_card *card) +{ + u32 dwRate = 0; + u8 bval = 0; + + ali_detect_spdif_rate(card); + + bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); + bval &= 0x7F; + bval |= 0x40; + outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); + + bval = inb(TRID_REG(card,ALI_SPDIF_CS + 3)); + bval &= 0x0F; + + switch (bval) + { + case 0: + dwRate = 44100; + break; + case 1: + dwRate = 48000; + break; + case 2: + dwRate = 32000; + break; + default: + // Error occurs + break; + } + + return dwRate; + +} + +static int ali_close_multi_channels(void) +{ + char temp = 0; + struct pci_dev *pci_dev = NULL; + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev); + if (pci_dev == NULL) + return -1; + temp = 0x80; + pci_write_config_byte(pci_dev, 0x59, ~temp); + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev); + if (pci_dev == NULL) + return -1; + + temp = 0x20; + pci_write_config_byte(pci_dev, 0xB8, ~temp); + + return 0; +} + +static int ali_setup_multi_channels(struct trident_card *card, int chan_nums) +{ + unsigned long dwValue; + char temp = 0; + struct pci_dev *pci_dev = NULL; + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); + if (pci_dev == NULL) + return -1; + temp = 0x80; + pci_write_config_byte(pci_dev, 0x59, temp); + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev); + if (pci_dev == NULL) + return -1; + temp = 0x20; + pci_write_config_byte(pci_dev, (int)0xB8,(u8) temp); + if (chan_nums == 6) { + dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000; + outl(dwValue, TRID_REG(card, ALI_SCTRL)); + mdelay(4); + dwValue = inl(TRID_REG(card, ALI_SCTRL)); + if (dwValue & 0x2000000) { + ali_ac97_write(card->ac97_codec[0], 0x02, 8080); + ali_ac97_write(card->ac97_codec[0], 0x36, 0); + ali_ac97_write(card->ac97_codec[0], 0x38, 0); + ali_ac97_write(card->ac97_codec[1], 0x36, 0); + ali_ac97_write(card->ac97_codec[1], 0x38, 0); + ali_ac97_write(card->ac97_codec[1], 0x02, 0x0606); + ali_ac97_write(card->ac97_codec[1], 0x18, 0x0303); + ali_ac97_write(card->ac97_codec[1], 0x74, 0x3); + return 1; + } + } + return -EINVAL; +} + +static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel) +{ + int bank; + + if (channel > 31) + return; + + bank = channel >> 5; + channel = channel & 0x1f; + + card->banks[bank].bitmap &= ~(1 << (channel)); +} + +static int ali_allocate_other_states_resources(struct trident_state *state, int chan_nums) +{ + struct trident_card *card = state->card; + struct trident_state *s; + int i, state_count = 0; + struct trident_pcm_bank *bank; + struct trident_channel *channel; + + bank = &card->banks[BANK_A]; + + if (chan_nums == 6) { + for(i = 0;(i < ALI_CHANNELS) && (state_count != 4); i++) { + if (!card->states[i]) { + if (!(bank->bitmap & (1 << ali_multi_channels_5_1[state_count]))) { + bank->bitmap |= (1 << ali_multi_channels_5_1[state_count]); + channel = &bank->channels[ali_multi_channels_5_1[state_count]]; + channel->num = ali_multi_channels_5_1[state_count]; + } + else { + state_count--; + for (; state_count >= 0; state_count--) { + kfree(state->other_states[state_count]); + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + } + return -EBUSY; + } + s = card->states[i] = (struct trident_state *) + kmalloc(sizeof(struct trident_state), GFP_KERNEL); + if (!s) { + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + state_count--; + for (; state_count >= 0; state_count--) { + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + kfree(state->other_states[state_count]); + } + return -ENOMEM; + } + memset(s, 0, sizeof(struct trident_state)); + + s->dmabuf.channel = channel; + s->dmabuf.ossfragshift = s->dmabuf.ossmaxfrags = s->dmabuf.subdivision = 0; + init_waitqueue_head(&s->dmabuf.wait); + s->magic = card->magic; + s->card = card; + s->virt = i; + ali_enable_special_channel(s); + state->other_states[state_count++] = s; + } + } + + if (state_count != 4) { + state_count--; + for (; state_count >= 0; state_count--) { + kfree(state->other_states[state_count]); + ali_free_pcm_channel(card, ali_multi_channels_5_1[state_count]); + } + return -EBUSY; + } + } + return 0; +} + +static void ali_save_regs(struct trident_card *card) +{ + unsigned long flags; + int i, j; + + save_flags(flags); + cli(); + + ali_registers.global_regs[0x2c] = inl(TRID_REG(card,T4D_MISCINT)); + //ali_registers.global_regs[0x20] = inl(TRID_REG(card,T4D_START_A)); + ali_registers.global_regs[0x21] = inl(TRID_REG(card,T4D_STOP_A)); + + //disable all IRQ bits + outl(ALI_DISABLE_ALL_IRQ, TRID_REG(card, T4D_MISCINT)); + + for (i = 1; i < ALI_MIXER_REGS; i++) + ali_registers.mixer_regs[i] = ali_ac97_read (card->ac97_codec[0], i*2); + + for (i = 0; i < ALI_GLOBAL_REGS; i++) + { + if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A)) + continue; + ali_registers.global_regs[i] = inl(TRID_REG(card, i*4)); + } + + for (i = 0; i < ALI_CHANNELS; i++) + { + outb(i,TRID_REG(card, T4D_LFO_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + ali_registers.channel_regs[i][j] = inl(TRID_REG(card, j*4 + 0xe0)); + } + + //Stop all HW channel + outl(ALI_STOP_ALL_CHANNELS, TRID_REG(card, T4D_STOP_A)); + + restore_flags(flags); +} + +static void ali_restore_regs(struct trident_card *card) +{ + unsigned long flags; + int i, j; + + save_flags(flags); + cli(); + + for (i = 1; i < ALI_MIXER_REGS; i++) + ali_ac97_write(card->ac97_codec[0], i*2, ali_registers.mixer_regs[i]); + + for (i = 0; i < ALI_CHANNELS; i++) + { + outb(i,TRID_REG(card, T4D_LFO_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + outl(ali_registers.channel_regs[i][j], TRID_REG(card, j*4 + 0xe0)); + } + + for (i = 0; i < ALI_GLOBAL_REGS; i++) + { + if ((i*4 == T4D_MISCINT) || (i*4 == T4D_STOP_A) || (i*4 == T4D_START_A)) + continue; + outl(ali_registers.global_regs[i], TRID_REG(card, i*4)); + } + + //start HW channel + outl(ali_registers.global_regs[0x20], TRID_REG(card,T4D_START_A)); + //restore IRQ enable bits + outl(ali_registers.global_regs[0x2c], TRID_REG(card,T4D_MISCINT)); + + restore_flags(flags); +} + +static int trident_suspend(struct pci_dev *dev, u32 unused) +{ + struct trident_card *card = (struct trident_card *) dev; + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_save_regs(card); + } + return 0; +} + +static int trident_resume(struct pci_dev *dev) +{ + struct trident_card *card = (struct trident_card *) dev; + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_restore_regs(card); + } + return 0; +} + +static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + bank = &card->banks[BANK_A]; + + if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & (ALI_SPDIF_OUT_CH_ENABLE)) { + idx = ALI_SPDIF_OUT_CHANNEL; + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + } + + for (idx = ALI_PCM_OUT_CHANNEL_FIRST; idx <= ALI_PCM_OUT_CHANNEL_LAST ; idx++) { + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + } + + /* no more free channels avaliable */ +// printk(KERN_ERR "ali: no more channels available on Bank A.\n"); + return NULL; +} + +static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + if (inl(TRID_REG(card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT) + idx = ALI_SPDIF_IN_CHANNEL; + else idx = ALI_PCM_IN_CHANNEL; + + bank = &card->banks[BANK_A]; + + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + + /* no free recordable channels avaliable */ +// printk(KERN_ERR "ali: no recordable channels available on Bank A.\n"); + return NULL; +} + +static void ali_set_spdif_out_rate(struct trident_card *card, unsigned int rate) +{ + unsigned char ch_st_sel; + unsigned short status_rate; + + switch(rate) { + case 44100: + status_rate = 0; + break; + case 32000: + status_rate = 0x300; + break; + case 48000: + default: + status_rate = 0x200; + break; + } + + ch_st_sel = inb(TRID_REG(card, ALI_SPDIF_CTRL)) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out + + ch_st_sel |= 0x80; //select right + outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL)); + outb(status_rate | 0x20, TRID_REG(card, ALI_SPDIF_CS + 2)); + + ch_st_sel &= (~0x80); //select left + outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL)); + outw(status_rate | 0x10, TRID_REG(card, ALI_SPDIF_CS + 2)); +} + +static void ali_address_interrupt(struct trident_card *card) +{ + int i, channel; + struct trident_state *state; + u32 mask, channel_mask; + + mask = trident_get_interrupt_mask (card, 0); + for (i = 0; i < NR_HW_CH; i++) { + if ((state = card->states[i]) == NULL) + continue; + channel = state->dmabuf.channel->num; + if ((channel_mask = 1 << channel) & mask) { + mask &= ~channel_mask; + trident_ack_channel_interrupt(card, channel); + udelay(100); + state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE; + trident_update_ptr(state); + } + } + if (mask) { + for (i = 0; i < NR_HW_CH; i++) { + if (mask & (1 << i)) { + printk("ali: spurious channel irq %d.\n", i); + trident_ack_channel_interrupt(card, i); + trident_stop_voice(card, i); + trident_disable_voice_irq(card, i); + } + } + } +} + +/* Updating the values of counters of other_states' DMAs without lock +protection is no harm because all DMAs of multi-channels and interrupt +depend on a master state's DMA, and changing the counters of the master +state DMA is protected by a spinlock. +*/ +static int ali_write_5_1(struct trident_state *state, const char *buf, int cnt_for_multi_channel, unsigned int *copy_count, unsigned int *state_cnt) +{ + + struct dmabuf *dmabuf = &state->dmabuf; + struct dmabuf *dmabuf_temp; + const char *buffer = buf; + unsigned swptr, other_dma_nums, sample_s; + unsigned int i, loop; + + other_dma_nums = 4; + sample_s = sample_size[dmabuf->fmt] >> 1; + swptr = dmabuf->swptr; + + if ((i = state->multi_channels_adjust_count) > 0) { + if (i == 1) { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + i--; + (*state_cnt) += sample_s; + state->multi_channels_adjust_count++; + } + else i = i - (state->chans_num - other_dma_nums); + for (; (i < other_dma_nums) && (cnt_for_multi_channel > 0); i++) { + dmabuf_temp = &state->other_states[i]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + } + if (cnt_for_multi_channel == 0) + state->multi_channels_adjust_count += i; + } + if (cnt_for_multi_channel > 0) { + loop = cnt_for_multi_channel / (state->chans_num * sample_s); + for (i = 0; i < loop; i++) { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s * 2)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s * 2, *copy_count); + (*state_cnt) += (sample_s * 2); + + dmabuf_temp = &state->other_states[0]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + + dmabuf_temp = &state->other_states[1]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + + dmabuf_temp = &state->other_states[2]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + + dmabuf_temp = &state->other_states[3]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + } + + if (cnt_for_multi_channel > 0) { + state->multi_channels_adjust_count = cnt_for_multi_channel / sample_s; + + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + (*state_cnt) += sample_s; + + if (cnt_for_multi_channel > 0) { + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + (*state_cnt) += sample_s; + + if (cnt_for_multi_channel > 0) { + loop = state->multi_channels_adjust_count - (state->chans_num - other_dma_nums); + for (i = 0; i < loop; i++) { + dmabuf_temp = &state->other_states[i]->dmabuf; + if (copy_from_user(dmabuf_temp->rawbuf + dmabuf_temp->swptr, buffer, sample_s)) + return -EFAULT; + seek_offset(dmabuf_temp->swptr, buffer, cnt_for_multi_channel, sample_s, *copy_count); + } + } + } + } + else + state->multi_channels_adjust_count = 0; + } + for (i = 0; i < other_dma_nums; i++) { + dmabuf_temp = &state->other_states[i]->dmabuf; + dmabuf_temp->swptr = dmabuf_temp->swptr % dmabuf_temp->dmasize; + } + return *state_cnt; +} + +static void ali_free_other_states_resources(struct trident_state *state) +{ + int i; + struct trident_card *card = state->card; + struct trident_state *s; + unsigned other_states_count; + + other_states_count = state->chans_num - 2; /* except PCM L/R channels*/ + for ( i = 0; i < other_states_count; i++) { + s = state->other_states[i]; + dealloc_dmabuf(s); + ali_disable_special_channel(s->card, s->dmabuf.channel->num); + state->card->free_pcm_channel(s->card, s->dmabuf.channel->num); + card->states[s->virt] = NULL; + kfree(s); + } +} + +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *res; +static int ali_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) +{ + struct trident_card *card = (struct trident_card *)data; + unsigned long flags; + char c; + + if (count<0) + return -EINVAL; + if (count == 0) + return 0; + if (get_user(c, buffer)) + return -EFAULT; + + spin_lock_irqsave(&card->lock, flags); + switch (c) { + case '0': + ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); + ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL); + break; + case '1': + ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_PCM); + break; + case '2': + ali_setup_spdif_out(card, ALI_SPDIF_OUT_TO_SPDIF_OUT|ALI_SPDIF_OUT_NON_PCM); + break; + case '3': + ali_disable_spdif_in(card); //default + break; + case '4': + ali_setup_spdif_in(card); + break; + } + spin_unlock_irqrestore(&card->lock, flags); + + return count; +} +#endif + +/* OSS /dev/mixer file operation methods */ +static int trident_open_mixdev(struct inode *inode, struct file *file) +{ + int i = 0; + int minor = minor(inode->i_rdev); + struct trident_card *card = devs; + + for (card = devs; card != NULL; card = card->next) + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + + if (!card) { + return -ENODEV; + } + match: + file->private_data = card->ac97_codec[i]; + + + return 0; +} + +static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations trident_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: trident_ioctl_mixdev, + open: trident_open_mixdev, +}; + +static int ali_reset_5451(struct trident_card *card) +{ + struct pci_dev *pci_dev = NULL; + unsigned int dwVal; + unsigned short wCount, wReg; + + pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev); + if (pci_dev == NULL) + return -1; + + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + + pci_dev = card->pci_dev; + if (pci_dev == NULL) + return -1; + + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); + udelay(500); + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); + udelay(5000); + + wCount = 200; + while(wCount--) { + wReg = ali_ac97_get(card, 0, AC97_POWER_CONTROL); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(500); + } + return 0; +} + +/* AC97 codec initialisation. */ +static int __init trident_ac97_init(struct trident_card *card) +{ + int num_ac97 = 0; + unsigned long ready_2nd = 0; + struct ac97_codec *codec; + int i = 0; + + + /* initialize controller side of AC link, and find out if secondary codes + really exist */ + switch (card->pci_id) + { + case PCI_DEVICE_ID_ALI_5451: + if (ali_reset_5451(card)) + { + printk(KERN_ERR "trident_ac97_init: error resetting 5451.\n"); + return -1; + } + outl(0x80000001,TRID_REG(card, ALI_GLOBAL_CONTROL)); + outl(0x00000000,TRID_REG(card, T4D_AINTEN_A)); + outl(0xffffffff,TRID_REG(card, T4D_AINT_A)); + outl(0x00000000,TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); + outb(0x10, TRID_REG(card, ALI_MPUR2)); + ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); + ready_2nd &= 0x3fff; + outl(ready_2nd | PCMOUT | 0x8000, TRID_REG(card, ALI_SCTRL)); + ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + if (card->revision < ALI_5451_V02) + ready_2nd = 0; + break; + case PCI_DEVICE_ID_SI_7018: + /* disable AC97 GPIO interrupt */ + outl(0x00, TRID_REG(card, SI_AC97_GPIO)); + /* when power up the AC link is in cold reset mode so stop it */ + outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID, + TRID_REG(card, SI_SERIAL_INTF_CTRL)); + /* it take a long time to recover from a cold reset (especially when you have + more than one codec) */ + udelay(2000); + ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + /* playback on */ + outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */ + outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd &= NX_AC97_SECONDARY_READY; + break; + case PCI_DEVICE_ID_INTERG_5050: + /* disable AC97 GPIO interrupt */ + outl(0x00, TRID_REG(card, SI_AC97_GPIO)); + /* when power up, the AC link is in cold reset mode, so stop it */ + outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT, + TRID_REG(card, SI_SERIAL_INTF_CTRL)); + /* it take a long time to recover from a cold reset (especially when you have + more than one codec) */ + udelay(2000); + ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + break; + } + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + codec->codec_read = ali_ac97_read; + codec->codec_write = ali_ac97_write; + } + else { + codec->codec_read = trident_ac97_get; + codec->codec_write = trident_ac97_set; + } + + if (ac97_probe_codec(codec) == 0) + break; + + if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { + printk(KERN_ERR "trident: couldn't register mixer!\n"); + kfree(codec); + break; + } + + card->ac97_codec[num_ac97] = codec; + + /* if there is no secondary codec at all, don't probe any more */ + if (!ready_2nd) + break; + } + + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if (card->ac97_codec[num_ac97] == NULL) + break; + for (i=0; i<64;i++) + card->mixer_regs[i][num_ac97] = ali_ac97_get(card, num_ac97,i*2); + } + } + return num_ac97+1; +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ +static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + unsigned long iobase; + struct trident_card *card; + u8 bits; + u8 revision; + int i = 0; + u16 temp; + struct pci_dev *pci_dev_m1533 = NULL; + int rc = -ENODEV; + u64 dma_mask; + + if (pci_enable_device(pci_dev)) + goto out; + + if (pci_dev->device == PCI_DEVICE_ID_ALI_5451) + dma_mask = ALI_DMA_MASK; + else + dma_mask = TRIDENT_DMA_MASK; + if (pci_set_dma_mask(pci_dev, dma_mask)) { + printk(KERN_ERR "trident: architecture does not support" + " %s PCI busmaster DMA\n", + pci_dev->device == PCI_DEVICE_ID_ALI_5451 ? + "32-bit" : "30-bit"); + goto out; + } + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); + + if (pci_id->device == PCI_DEVICE_ID_INTERG_5050) + iobase = pci_resource_start(pci_dev, 1); + else + iobase = pci_resource_start(pci_dev, 0); + + if (!request_region(iobase, 256, card_names[pci_id->driver_data])) { + printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n", + iobase); + goto out; + } + + rc = -ENOMEM; + if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "trident: out of memory\n"); + goto out_release_region; + } + memset(card, 0, sizeof(*card)); + + card->iobase = iobase; + card->pci_dev = pci_dev; + card->pci_id = pci_id->device; + card->revision = revision; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = TRIDENT_CARD_MAGIC; + card->banks[BANK_A].addresses = &bank_a_addrs; + card->banks[BANK_A].bitmap = 0UL; + card->banks[BANK_B].addresses = &bank_b_addrs; + card->banks[BANK_B].bitmap = 0UL; + + init_MUTEX(&card->open_sem); + spin_lock_init(&card->lock); + init_timer(&card->timer); + + devs = card; + + pci_set_master(pci_dev); + + printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->irq); + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + /* ALi channel Management */ + card->alloc_pcm_channel = ali_alloc_pcm_channel; + card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; + card->free_pcm_channel = ali_free_pcm_channel; + + card->address_interrupt = ali_address_interrupt; + + /* Added by Matt Wu 01-05-2001 for spdif in */ + card->multi_channel_use_count = 0; + card->rec_channel_use_count = 0; + + /* ALi SPDIF OUT function */ + if(card->revision == ALI_5451_V02) { + ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); +#ifdef CONFIG_PROC_FS + res = create_proc_entry("ALi5451", 0, NULL); + if (res) { + res->write_proc = ali_write_proc; + res->data = card; + } +#endif + } + + /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */ + card->hwvolctl = 0; + pci_dev_m1533 = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev_m1533); + rc = -ENODEV; + if (pci_dev_m1533 == NULL) + goto out_proc_fs; + pci_read_config_byte(pci_dev_m1533, 0x63, &bits); + if (bits & (1<<5)) + card->hwvolctl = 1; + if (card->hwvolctl) + { + /* Clear m1533 pci cfg 78h bit 30 to zero, which makes + GPIO11/12/13 work as ACGP_UP/DOWN/MUTE. */ + pci_read_config_byte(pci_dev_m1533, 0x7b, &bits); + bits &= 0xbf; /*clear bit 6 */ + pci_write_config_byte(pci_dev_m1533, 0x7b, bits); + } + } + else if(card->pci_id == PCI_DEVICE_ID_INTERG_5050) + { + card->alloc_pcm_channel = cyber_alloc_pcm_channel; + card->alloc_rec_pcm_channel = cyber_alloc_pcm_channel; + card->free_pcm_channel = cyber_free_pcm_channel; + card->address_interrupt = cyber_address_interrupt; + cyber_init_ritual(card); + } + else + { + card->alloc_pcm_channel = trident_alloc_pcm_channel; + card->alloc_rec_pcm_channel = trident_alloc_pcm_channel; + card->free_pcm_channel = trident_free_pcm_channel; + card->address_interrupt = trident_address_interrupt; + } + + /* claim our irq */ + rc = -ENODEV; + if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq); + goto out_proc_fs; + } + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) { + printk(KERN_ERR "trident: couldn't register DSP device!\n"); + goto out_free_irq; + } + card->mixer_regs_ready = 0; + /* initialize AC97 codec and register /dev/mixer */ + if (trident_ac97_init(card) <= 0) { + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) { + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree (card->ac97_codec[i]); + } + } + goto out_unregister_sound_dsp; + } + card->mixer_regs_ready = 1; + outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); + + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + /* Add H/W Volume Control By Matt Wu Jul. 06, 2001 */ + if(card->hwvolctl) + { + /* Enable GPIO IRQ (MISCINT bit 18h)*/ + temp = inw(TRID_REG(card, T4D_MISCINT + 2)); + temp |= 0x0004; + outw(temp, TRID_REG(card, T4D_MISCINT + 2)); + + /* Enable H/W Volume Control GLOVAL CONTROL bit 0*/ + temp = inw(TRID_REG(card, ALI_GLOBAL_CONTROL)); + temp |= 0x0001; + outw(temp, TRID_REG(card, ALI_GLOBAL_CONTROL)); + + } + if(card->revision == ALI_5451_V02) + ali_close_multi_channels(); + /* edited by HMSEO for GT sound */ +#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC + { + u16 ac97_data; + extern struct hwrpb_struct *hwrpb; + + if ((hwrpb->sys_type) == 201) { + printk(KERN_INFO "trident: Running on Alpha system type Nautilus\n"); + ac97_data = ali_ac97_get(card, 0, AC97_POWER_CONTROL); + ali_ac97_set(card, 0, AC97_POWER_CONTROL, ac97_data | ALI_EAPD_POWER_DOWN); + } + } +#endif + /* edited by HMSEO for GT sound*/ + } + rc = 0; + pci_set_drvdata(pci_dev, card); + + /* Enable Address Engine Interrupts */ + trident_enable_loop_interrupts(card); +out: return rc; +out_unregister_sound_dsp: + unregister_sound_dsp(card->dev_audio); +out_free_irq: + free_irq(card->irq, card); +out_proc_fs: +#ifdef CONFIG_PROC_FS + if (res) { + remove_proc_entry("ALi5451", NULL); + res = NULL; + } +#endif + kfree(card); + devs = NULL; +out_release_region: + release_region(iobase, 256); + goto out; +} + +static void __devexit trident_remove(struct pci_dev *pci_dev) +{ + int i; + struct trident_card *card = pci_get_drvdata(pci_dev); + + /* + * Kill running timers before unload. We can't have them + * going off after rmmod! + */ + if(card->hwvolctl) + del_timer_sync(&card->timer); + + /* ALi S/PDIF and Power Management */ + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT); + ali_disable_special_channel(card, ALI_SPDIF_OUT_CHANNEL); + ali_disable_spdif_in(card); +#ifdef CONFIG_PROC_FS + remove_proc_entry("ALi5451", NULL); +#endif + } + + /* Kill interrupts, and SP/DIF */ + trident_disable_loop_interrupts(card); + + /* free hardware resources */ + free_irq(card->irq, card); + release_region(card->iobase, 256); + + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree (card->ac97_codec[i]); + } + unregister_sound_dsp(card->dev_audio); + + kfree(card); + + pci_set_drvdata(pci_dev, NULL); +} + +MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho, Ching Ling Lee"); +MODULE_DESCRIPTION("Trident 4DWave/SiS 7018/ALi 5451 and Tvia/IGST CyberPro5050 PCI Audio Driver"); +MODULE_LICENSE("GPL"); + + +#define TRIDENT_MODULE_NAME "trident" + +static struct pci_driver trident_pci_driver = { + name: TRIDENT_MODULE_NAME, + id_table: trident_pci_tbl, + probe: trident_probe, + remove: __devexit_p(trident_remove), + suspend: trident_suspend, + resume: trident_resume +}; + +static int __init trident_init_module (void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + + printk(KERN_INFO "Trident 4DWave/SiS 7018/ALi 5451,Tvia CyberPro 5050 PCI Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (!pci_register_driver(&trident_pci_driver)) { + pci_unregister_driver(&trident_pci_driver); + return -ENODEV; + } + return 0; +} + +static void __exit trident_cleanup_module (void) +{ + pci_unregister_driver(&trident_pci_driver); +} + +module_init(trident_init_module); +module_exit(trident_cleanup_module); diff -Nru linux/sound/oss/trident.h linux-2.4.19-pre5-mjc/sound/oss/trident.h --- linux/sound/oss/trident.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/trident.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,362 @@ +#ifndef __TRID4DWAVE_H +#define __TRID4DWAVE_H + +/* + * audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Definitions for Trident 4DWave DX/NX chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* PCI vendor and device ID */ +#ifndef PCI_VENDOR_ID_TRIDENT +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#endif + +#ifndef PCI_VENDOR_ID_SI +#define PCI_VENDOR_ID_SI 0x1039 +#endif + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#endif + +#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#endif + +#ifndef PCI_DEVICE_ID_SI_7018 +#define PCI_DEVICE_ID_SI_7018 0x7018 +#endif + +#ifndef PCI_DEVICE_ID_ALI_5451 +#define PCI_DEVICE_ID_ALI_5451 0x5451 +#endif + +#ifndef PCI_DEVICE_ID_ALI_1533 +#define PCI_DEVICE_ID_ALI_1533 0x1533 +#endif + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +#define CHANNEL_REGS 5 +#define CHANNEL_START 0xe0 // The first bytes of the contiguous register space. + +#define BANK_A 0 +#define BANK_B 1 +#define NR_BANKS 2 + +#define TRIDENT_FMT_STEREO 0x01 +#define TRIDENT_FMT_16BIT 0x02 +#define TRIDENT_FMT_MASK 0x03 + +#define DAC_RUNNING 0x01 +#define ADC_RUNNING 0x02 + +/* Register Addresses */ + +/* operational registers common to DX, NX, 7018 */ +enum trident_op_registers { + T4D_REC_CH = 0x70, + T4D_START_A = 0x80, T4D_STOP_A = 0x84, + T4D_DLY_A = 0x88, T4D_SIGN_CSO_A = 0x8c, + T4D_CSPF_A = 0x90, T4D_CEBC_A = 0x94, + T4D_AINT_A = 0x98, T4D_EINT_A = 0x9c, + T4D_LFO_GC_CIR = 0xa0, T4D_AINTEN_A = 0xa4, + T4D_MUSICVOL_WAVEVOL = 0xa8, T4D_SBDELTA_DELTA_R = 0xac, + T4D_MISCINT = 0xb0, T4D_START_B = 0xb4, + T4D_STOP_B = 0xb8, T4D_CSPF_B = 0xbc, + T4D_SBBL_SBCL = 0xc0, T4D_SBCTRL_SBE2R_SBDD = 0xc4, + T4D_STIMER = 0xc8, T4D_LFO_B_I2S_DELTA = 0xcc, + T4D_AINT_B = 0xd8, T4D_AINTEN_B = 0xdc, + ALI_MPUR2 = 0x22, ALI_GPIO = 0x7c, + ALI_EBUF1 = 0xf4, + ALI_EBUF2 = 0xf8 +}; + +enum ali_op_registers { + ALI_SCTRL = 0x48, + ALI_GLOBAL_CONTROL = 0xd4, + ALI_STIMER = 0xc8, + ALI_SPDIF_CS = 0x70, + ALI_SPDIF_CTRL = 0x74 +}; + +enum ali_registers_number { + ALI_GLOBAL_REGS = 56, + ALI_CHANNEL_REGS = 8, + ALI_MIXER_REGS = 20 +}; + +enum ali_sctrl_control_bit { + ALI_SPDIF_OUT_ENABLE = 0x20 +}; + +enum ali_global_control_bit { + ALI_SPDIF_OUT_SEL_PCM = 0x00000400, + ALI_SPDIF_IN_SUPPORT = 0x00000800, + ALI_SPDIF_OUT_CH_ENABLE = 0x00008000, + ALI_SPDIF_IN_CH_ENABLE = 0x00080000, + ALI_PCM_IN_DISABLE = 0x7fffffff, + ALI_PCM_IN_ENABLE = 0x80000000, + ALI_SPDIF_IN_CH_DISABLE = 0xfff7ffff, + ALI_SPDIF_OUT_CH_DISABLE = 0xffff7fff, + ALI_SPDIF_OUT_SEL_SPDIF = 0xfffffbff + +}; + +enum ali_spdif_control_bit { + ALI_SPDIF_IN_FUNC_ENABLE = 0x02, + ALI_SPDIF_IN_CH_STATUS = 0x40, + ALI_SPDIF_OUT_CH_STATUS = 0xbf + +}; + +enum ali_control_all { + ALI_DISABLE_ALL_IRQ = 0, + ALI_CHANNELS = 32, + ALI_STOP_ALL_CHANNELS = 0xffffffff, + ALI_MULTI_CHANNELS_START_STOP = 0x07800000 +}; + +enum ali_EMOD_control_bit { + ALI_EMOD_DEC = 0x00000000, + ALI_EMOD_INC = 0x10000000, + ALI_EMOD_Delay = 0x20000000, + ALI_EMOD_Still = 0x30000000 +}; + +enum ali_pcm_in_channel_num { + ALI_NORMAL_CHANNEL = 0, + ALI_SPDIF_OUT_CHANNEL = 15, + ALI_SPDIF_IN_CHANNEL = 19, + ALI_LEF_CHANNEL = 23, + ALI_CENTER_CHANNEL = 24, + ALI_SURR_RIGHT_CHANNEL = 25, + ALI_SURR_LEFT_CHANNEL = 26, + ALI_PCM_IN_CHANNEL = 31 +}; + +enum ali_pcm_out_channel_num { + ALI_PCM_OUT_CHANNEL_FIRST = 0, + ALI_PCM_OUT_CHANNEL_LAST = 31 +}; + +enum ali_ac97_power_control_bit { + ALI_EAPD_POWER_DOWN = 0x8000 +}; + +enum ali_update_ptr_flags { + ALI_ADDRESS_INT_UPDATE = 0x01 +}; + +enum ali_revision { + ALI_5451_V02 = 0x02 +}; + +enum ali_spdif_out_control { + ALI_PCM_TO_SPDIF_OUT = 0, + ALI_SPDIF_OUT_TO_SPDIF_OUT = 1, + ALI_SPDIF_OUT_PCM = 0, + ALI_SPDIF_OUT_NON_PCM = 2 +}; + +/* S/PDIF Operational Registers for 4D-NX */ +enum nx_spdif_registers { + NX_SPCTRL_SPCSO = 0x24, NX_SPLBA = 0x28, + NX_SPESO = 0x2c, NX_SPCSTATUS = 0x64 +}; + +/* OP registers to access each hardware channel */ +enum channel_registers { + CH_DX_CSO_ALPHA_FMS = 0xe0, CH_DX_ESO_DELTA = 0xe8, + CH_DX_FMC_RVOL_CVOL = 0xec, + CH_NX_DELTA_CSO = 0xe0, CH_NX_DELTA_ESO = 0xe8, + CH_NX_ALPHA_FMS_FMC_RVOL_CVOL = 0xec, + CH_LBA = 0xe4, + CH_GVSEL_PAN_VOL_CTRL_EC = 0xf0 +}; + +/* registers to read/write/control AC97 codec */ +enum dx_ac97_registers { + DX_ACR0_AC97_W = 0x40, DX_ACR1_AC97_R = 0x44, + DX_ACR2_AC97_COM_STAT = 0x48 +}; + +enum nx_ac97_registers { + NX_ACR0_AC97_COM_STAT = 0x40, NX_ACR1_AC97_W = 0x44, + NX_ACR2_AC97_R_PRIMARY = 0x48, NX_ACR3_AC97_R_SECONDARY = 0x4c +}; + +enum si_ac97_registers { + SI_AC97_WRITE = 0x40, SI_AC97_READ = 0x44, + SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c +}; + +enum ali_ac97_registers { + ALI_AC97_WRITE = 0x40, ALI_AC97_READ = 0x44 +}; + +/* Bit mask for operational registers */ +#define AC97_REG_ADDR 0x000000ff + +enum ali_ac97_bits { + ALI_AC97_BUSY_WRITE = 0x8000, ALI_AC97_BUSY_READ = 0x8000, + ALI_AC97_WRITE_ACTION = 0x8000, ALI_AC97_READ_ACTION = 0x8000, + ALI_AC97_AUDIO_BUSY = 0x4000, ALI_AC97_SECONDARY = 0x0080, + ALI_AC97_READ_MIXER_REGISTER = 0xfeff, + ALI_AC97_WRITE_MIXER_REGISTER = 0x0100 +}; + +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000, + SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000, + SI_AC97_SECONDARY = 0x0080 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800, + NX_AC97_BUSY_DATA = 0x0400, NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + +enum serial_intf_ctrl_bits { + WARM_REST = 0x00000001, COLD_RESET = 0x00000002, + I2S_CLOCK = 0x00000004, PCM_SEC_AC97= 0x00000008, + AC97_DBL_RATE = 0x00000010, SPDIF_EN = 0x00000020, + I2S_OUTPUT_EN = 0x00000040, I2S_INPUT_EN = 0x00000080, + PCMIN = 0x00000100, LINE1IN = 0x00000200, + MICIN = 0x00000400, LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00004000, */ + SECONDARY_ID= 0x00004000, + PCMOUT = 0x00010000, SURROUT = 0x00020000, + CENTEROUT = 0x00040000, LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, +}; + +enum global_control_bits { + CHANNLE_IDX = 0x0000003f, PB_RESET = 0x00000100, + PAUSE_ENG = 0x00000200, + OVERRUN_IE = 0x00000400, UNDERRUN_IE = 0x00000800, + ENDLP_IE = 0x00001000, MIDLP_IE = 0x00002000, + ETOG_IE = 0x00004000, + EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000 +}; + +enum channel_control_bits { + CHANNEL_LOOP = 0x00001000, CHANNEL_SIGNED = 0x00002000, + CHANNEL_STEREO = 0x00004000, CHANNEL_16BITS = 0x00008000, +}; + +enum channel_attribute { + /* playback/record select */ + CHANNEL_PB = 0x0000, CHANNEL_SPC_PB = 0x4000, + CHANNEL_REC = 0x8000, CHANNEL_REC_PB = 0xc000, + /* playback destination/record source select */ + MODEM_LINE1 = 0x0000, MODEM_LINE2 = 0x0400, + PCM_LR = 0x0800, HSET = 0x0c00, + I2S_LR = 0x1000, CENTER_LFE = 0x1400, + SURR_LR = 0x1800, SPDIF_LR = 0x1c00, + MIC = 0x1400, + /* mist stuff */ + MONO_LEFT = 0x0000, MONO_RIGHT = 0x0100, + MONO_MIX = 0x0200, SRC_ENABLE = 0x0080, +}; + +enum miscint_bits { + PB_UNDERRUN_IRO = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, + SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, + OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, + ENVELOPE_IRQ = 0x00000040, ST_IRQ = 0x00000080, + PB_UNDERRUN = 0x00000100, REC_OVERRUN = 0x00000200, + MIXER_UNDERFLOW = 0x00000400, MIXER_OVERFLOW = 0x00000800, + ST_TARGET_REACHED = 0x00008000, PB_24K_MODE = 0x00010000, + ST_IRQ_EN = 0x00800000, ACGPIO_IRQ = 0x01000000 +}; + +#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) ) + +#define CYBER_PORT_AUDIO 0x3CE +#define CYBER_IDX_AUDIO_ENABLE 0x7B +#define CYBER_BMSK_AUDIO_INT_ENABLE 0x09 +#define CYBER_BMSK_AUENZ 0x01 +#define CYBER_BMSK_AUENZ_ENABLE 0x00 +#define CYBER_IDX_IRQ_ENABLE 0x12 + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC) + +extern __inline__ unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +#endif /* __TRID4DWAVE_H */ + diff -Nru linux/sound/oss/trix.c linux-2.4.19-pre5-mjc/sound/oss/trix.c --- linux/sound/oss/trix.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/trix.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,546 @@ +/* + * sound/trix.c + * + * Low level driver for the MediaTrix AudioTrix Pro + * (MT-0002-PC Control Chip) + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes + * Alan Cox Modularisation, cleanup. + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo Got rid of attach_uart401 + */ + +#include +#include + +#include "sound_config.h" +#include "sb.h" +#include "sound_firmware.h" + +#include "ad1848.h" +#include "mpu401.h" + +#include "trix_boot.h" + +static int kilroy_was_here = 0; /* Don't detect twice */ +static int sb_initialized = 0; +static int mpu_initialized = 0; + +static int *trix_osp = NULL; + +static int mpu = 0; + +static int joystick=0; + +static unsigned char trix_read(int addr) +{ + outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ + return inb(0x391); /* MT-0002-PC ASIC data */ +} + +static void trix_write(int addr, int data) +{ + outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ + outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */ +} + +static void download_boot(int base) +{ + int i = 0, n = trix_boot_len; + + if (trix_boot_len == 0) + return; + + trix_write(0xf8, 0x00); /* ??????? */ + outb((0x01), base + 6); /* Clear the internal data pointer */ + outb((0x00), base + 6); /* Restart */ + + /* + * Write the boot code to the RAM upload/download register. + * Each write increments the internal data pointer. + */ + outb((0x01), base + 6); /* Clear the internal data pointer */ + outb((0x1A), 0x390); /* Select RAM download/upload port */ + + for (i = 0; i < n; i++) + outb((trix_boot[i]), 0x391); + for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */ + outb((0x00), 0x391); + outb((0x00), base + 6); /* Reset */ + outb((0x50), 0x390); /* ?????? */ + +} + +static int trix_set_wss_port(struct address_info *hw_config) +{ + unsigned char addr_bits; + + if (check_region(0x390, 2)) + { + printk(KERN_ERR "AudioTrix: Config port I/O conflict\n"); + return 0; + } + if (kilroy_was_here) /* Already initialized */ + return 0; + + if (trix_read(0x15) != 0x71) /* No ASIC signature */ + { + MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n")); + return 0; + } + kilroy_was_here = 1; + + /* + * Reset some registers. + */ + + trix_write(0x13, 0); + trix_write(0x14, 0); + + /* + * Configure the ASIC to place the codec to the proper I/O location + */ + + switch (hw_config->io_base) + { + case 0x530: + addr_bits = 0; + break; + case 0x604: + addr_bits = 1; + break; + case 0xE80: + addr_bits = 2; + break; + case 0xF40: + addr_bits = 3; + break; + default: + return 0; + } + + trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits); + return 1; +} + +/* + * Probe and attach routines for the Windows Sound System mode of + * AudioTrix Pro + */ + +static int __init probe_trix_wss(struct address_info *hw_config) +{ + int ret; + + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00. + */ + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + trix_osp = hw_config->osp; + + if (!trix_set_wss_port(hw_config)) + return 0; + + if ((inb(hw_config->io_base + 3) & 0x3f) != 0x00) + { + MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base)); + return 0; + } + if (hw_config->irq > 11) + { + printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", hw_config->dma); + return 0; + } + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3) + { + printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", hw_config->dma2); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq); + return 0; + } + ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + + if (ret) + { + if(joystick==1) + trix_write(0x15, 0x80); + request_region(0x390, 2, "AudioTrix"); + } + return ret; +} + +static void __init attach_trix_wss(struct address_info *hw_config) +{ + static unsigned char interrupt_bits[12] = { + 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20 + }; + char bits; + + static unsigned char dma_bits[4] = { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + int old_num_mixers = num_mixers; + + trix_osp = hw_config->osp; + + if (!kilroy_was_here) + { + DDB(printk("AudioTrix: Attach called but not probed yet???\n")); + return; + } + + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == 0) + { + printk("AudioTrix: Bad IRQ (%d)\n", hw_config->irq); + return; + } + outb((bits | 0x40), config_port); + + if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma) + { + bits |= dma_bits[dma1]; + dma2 = dma1; + } + else + { + unsigned char tmp; + + tmp = trix_read(0x13) & ~30; + trix_write(0x13, tmp | 0x80 | (dma1 << 4)); + + tmp = trix_read(0x14) & ~30; + trix_write(0x14, tmp | 0x80 | (dma2 << 4)); + } + + outb((bits), config_port); /* Write IRQ+DMA setup */ + + hw_config->slots[0] = ad1848_init("AudioTrix Pro", hw_config->io_base + 4, + hw_config->irq, + dma1, + dma2, + 0, + hw_config->osp, + THIS_MODULE); + request_region(hw_config->io_base, 4, "MSS config"); + + if (num_mixers > old_num_mixers) /* Mixer got installed */ + { + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */ + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */ + AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */ + } +} + +static int __init probe_trix_sb(struct address_info *hw_config) +{ + + int tmp; + unsigned char conf; + static signed char irq_translate[] = { + -1, -1, -1, 0, 1, 2, -1, 3 + }; + + if (trix_boot_len == 0) + return 0; /* No boot code -> no fun */ + + if (!kilroy_was_here) + return 0; /* AudioTrix Pro has not been detected earlier */ + + if (sb_initialized) + return 0; + + if (check_region(hw_config->io_base, 16)) + { + printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + if ((hw_config->io_base & 0xffffff8f) != 0x200) + return 0; + + tmp = hw_config->irq; + if (tmp > 7) + return 0; + if (irq_translate[tmp] == -1) + return 0; + + tmp = hw_config->dma; + if (tmp != 1 && tmp != 3) + return 0; + + conf = 0x84; /* DMA and IRQ enable */ + conf |= hw_config->io_base & 0x70; /* I/O address bits */ + conf |= irq_translate[hw_config->irq]; + if (hw_config->dma == 3) + conf |= 0x08; + trix_write(0x1b, conf); + + download_boot(hw_config->io_base); + sb_initialized = 1; + + hw_config->name = "AudioTrix SB"; + return sb_dsp_detect(hw_config, 0, 0, NULL); +} + +static void __init attach_trix_sb(struct address_info *hw_config) +{ + extern int sb_be_quiet; + int old_quiet; + + hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING; + + /* Prevent false alarms */ + old_quiet = sb_be_quiet; + sb_be_quiet = 1; + + sb_dsp_init(hw_config, THIS_MODULE); + + sb_be_quiet = old_quiet; +} + +static int __init probe_trix_mpu(struct address_info *hw_config) +{ + unsigned char conf; + static int irq_bits[] = { + -1, -1, -1, 1, 2, 3, -1, 4, -1, 5 + }; + + if (!kilroy_was_here) + { + DDB(printk("Trix: WSS and SB modes must be initialized before MPU\n")); + return 0; /* AudioTrix Pro has not been detected earlier */ + } + if (!sb_initialized) + { + DDB(printk("Trix: SB mode must be initialized before MPU\n")); + return 0; + } + if (mpu_initialized) + { + DDB(printk("Trix: MPU mode already initialized\n")); + return 0; + } + if (hw_config->irq > 9) + { + printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + if (irq_bits[hw_config->irq] == -1) + { + printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + switch (hw_config->io_base) + { + case 0x330: + conf = 0x00; + break; + case 0x370: + conf = 0x04; + break; + case 0x3b0: + conf = 0x08; + break; + case 0x3f0: + conf = 0x0c; + break; + default: + return 0; /* Invalid port */ + } + + conf |= irq_bits[hw_config->irq] << 4; + trix_write(0x19, (trix_read(0x19) & 0x83) | conf); + mpu_initialized = 1; + hw_config->name = "AudioTrix Pro"; + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_trix_wss(struct address_info *hw_config) +{ + int dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = hw_config->dma; + + release_region(0x390, 2); + release_region(hw_config->io_base, 4); + + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); + sound_unload_audiodev(hw_config->slots[0]); +} + +static inline void __exit unload_trix_mpu(struct address_info *hw_config) +{ + unload_uart401(hw_config); +} + +static inline void __exit unload_trix_sb(struct address_info *hw_config) +{ + sb_dsp_unload(hw_config, mpu); +} + +static struct address_info cfg; +static struct address_info cfg2; +static struct address_info cfg_mpu; + +static int sb = 0; +static int fw_load; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; /* Set this for modules that need it */ +static int __initdata sb_io = -1; +static int __initdata sb_dma = -1; +static int __initdata sb_irq = -1; +static int __initdata mpu_io = -1; +static int __initdata mpu_irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(sb_io,"i"); +MODULE_PARM(sb_dma,"i"); +MODULE_PARM(sb_irq,"i"); +MODULE_PARM(mpu_io,"i"); +MODULE_PARM(mpu_irq,"i"); +MODULE_PARM(joystick, "i"); + +static int __init init_trix(void) +{ + printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg2.io_base = sb_io; + cfg2.irq = sb_irq; + cfg2.dma = sb_dma; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); + return -EINVAL; + } + + if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) { + printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n"); + return -EINVAL; + } + if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) { + printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n"); + return -EINVAL; + } + if (!trix_boot) + { + fw_load = 1; + trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin", + (char **) &trix_boot); + } + if (!probe_trix_wss(&cfg)) + return -ENODEV; + attach_trix_wss(&cfg); + + /* + * We must attach in the right order to get the firmware + * loaded up in time. + */ + + if (cfg2.io_base != -1) { + sb = probe_trix_sb(&cfg2); + if (sb) + attach_trix_sb(&cfg2); + } + + if (cfg_mpu.io_base != -1) + mpu = probe_trix_mpu(&cfg_mpu); + + return 0; +} + +static void __exit cleanup_trix(void) +{ + if (fw_load && trix_boot) + vfree(trix_boot); + if (sb) + unload_trix_sb(&cfg2); + if (mpu) + unload_trix_mpu(&cfg_mpu); + unload_trix_wss(&cfg); +} + +module_init(init_trix); +module_exit(cleanup_trix); + +#ifndef MODULE +static int __init setup_trix (char *str) +{ + /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */ + int ints[9]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + sb_io = ints[5]; + sb_irq = ints[6]; + sb_dma = ints[6]; + mpu_io = ints[7]; + mpu_irq = ints[8]; + + return 1; +} + +__setup("trix=", setup_trix); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/tuning.h linux-2.4.19-pre5-mjc/sound/oss/tuning.h --- linux/sound/oss/tuning.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/tuning.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,29 @@ +#ifdef SEQUENCER_C + +unsigned short semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; +#else +extern unsigned short semitone_tuning[24]; +extern unsigned short cent_tuning[100]; +#endif diff -Nru linux/sound/oss/uart401.c linux-2.4.19-pre5-mjc/sound/oss/uart401.c --- linux/sound/oss/uart401.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/uart401.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,481 @@ +/* + * sound/uart401.c + * + * MPU-401 UART driver (formerly uart401_midi.c) + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox Reformatted, removed sound_mem usage, use normal Linux + * interrupt allocation. Protect against bogus unload + * Fixed to allow IRQ > 15 + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo got rid of check_region + * + * Status: + * Untested + */ + +#include +#include + +#include "sound_config.h" + +#include "mpu401.h" + +typedef struct uart401_devc +{ + int base; + int irq; + int *osp; + void (*midi_input_intr) (int dev, unsigned char data); + int opened, disabled; + volatile unsigned char input_byte; + int my_dev; + int share_irq; +} +uart401_devc; + +#define DATAPORT (devc->base) +#define COMDPORT (devc->base+1) +#define STATPORT (devc->base+1) + +static int uart401_status(uart401_devc * devc) +{ + return inb(STATPORT); +} + +#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL)) +#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY)) + +static void uart401_cmd(uart401_devc * devc, unsigned char cmd) +{ + outb((cmd), COMDPORT); +} + +static int uart401_read(uart401_devc * devc) +{ + return inb(DATAPORT); +} + +static void uart401_write(uart401_devc * devc, unsigned char byte) +{ + outb((byte), DATAPORT); +} + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static int reset_uart401(uart401_devc * devc); +static void enter_uart_mode(uart401_devc * devc); + +static void uart401_input_loop(uart401_devc * devc) +{ + int work_limit=30000; + + while (input_avail(devc) && --work_limit) + { + unsigned char c = uart401_read(devc); + + if (c == MPU_ACK) + devc->input_byte = c; + else if (devc->opened & OPEN_READ && devc->midi_input_intr) + devc->midi_input_intr(devc->my_dev, c); + } + if(work_limit==0) + printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base); +} + +void uart401intr(int irq, void *dev_id, struct pt_regs *dummy) +{ + uart401_devc *devc = dev_id; + + if (devc == NULL) + { + printk(KERN_ERR "uart401: bad devc\n"); + return; + } + + if (input_avail(devc)) + uart401_input_loop(devc); +} + +static int +uart401_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + + if (devc->opened) + return -EBUSY; + + /* Flush the UART */ + + while (input_avail(devc)) + uart401_read(devc); + + devc->midi_input_intr = input; + devc->opened = mode; + enter_uart_mode(devc); + devc->disabled = 0; + + return 0; +} + +static void uart401_close(int dev) +{ + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + + reset_uart401(devc); + devc->opened = 0; +} + +static int uart401_out(int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + + if (devc->disabled) + return 1; + /* + * Test for input since pending input seems to block the output. + */ + + save_flags(flags); + cli(); + + if (input_avail(devc)) + uart401_input_loop(devc); + + restore_flags(flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + + if (!output_ready(devc)) + { + printk(KERN_WARNING "uart401: Timeout - Device not responding\n"); + devc->disabled = 1; + reset_uart401(devc); + enter_uart_mode(devc); + return 1; + } + uart401_write(devc, midi_byte); + return 1; +} + +static inline int uart401_start_read(int dev) +{ + return 0; +} + +static inline int uart401_end_read(int dev) +{ + return 0; +} + +static inline void uart401_kick(int dev) +{ +} + +static inline int uart401_buffer_status(int dev) +{ + return 0; +} + +#define MIDI_SYNTH_NAME "MPU-401 UART" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static const struct midi_operations uart401_operations = +{ + owner: THIS_MODULE, + info: {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401}, + converter: &std_midi_synth, + in_info: {0}, + open: uart401_open, + close: uart401_close, + outputc: uart401_out, + start_read: uart401_start_read, + end_read: uart401_end_read, + kick: uart401_kick, + buffer_status: uart401_buffer_status, +}; + +static void enter_uart_mode(uart401_devc * devc) +{ + int ok, timeout; + unsigned long flags; + + save_flags(flags); + cli(); + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + + devc->input_byte = 0; + uart401_cmd(devc, UART_MODE_ON); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (devc->input_byte == MPU_ACK) + ok = 1; + else if (input_avail(devc)) + if (uart401_read(devc) == MPU_ACK) + ok = 1; + + restore_flags(flags); +} + +static int reset_uart401(uart401_devc * devc) +{ + int ok, timeout, n; + + /* + * Send the RESET command. Try again if no success at the first time. + */ + + ok = 0; + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + devc->input_byte = 0; + uart401_cmd(devc, MPU_RESET); + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = 50000; timeout > 0 && !ok; timeout--) + { + if (devc->input_byte == MPU_ACK) /* Interrupt */ + ok = 1; + else if (input_avail(devc)) + { + if (uart401_read(devc) == MPU_ACK) + ok = 1; + } + } + } + + + if (ok) + { + DEB(printk("Reset UART401 OK\n")); + } + else + DDB(printk("Reset UART401 failed - No hardware detected.\n")); + + if (ok) + uart401_input_loop(devc); /* + * Flush input before enabling interrupts + */ + + return ok; +} + +int probe_uart401(struct address_info *hw_config, struct module *owner) +{ + uart401_devc *devc; + char *name = "MPU-401 (UART) MIDI"; + int ok = 0; + unsigned long flags; + + DDB(printk("Entered probe_uart401()\n")); + + /* Default to "not found" */ + hw_config->slots[4] = -1; + + if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) { + printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base); + return 0; + } + + devc = kmalloc(sizeof(uart401_devc), GFP_KERNEL); + if (!devc) { + printk(KERN_WARNING "uart401: Can't allocate memory\n"); + goto cleanup_region; + } + + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->osp = hw_config->osp; + devc->midi_input_intr = NULL; + devc->opened = 0; + devc->input_byte = 0; + devc->my_dev = 0; + devc->share_irq = 0; + + save_flags(flags); + cli(); + ok = reset_uart401(devc); + restore_flags(flags); + + if (!ok) + goto cleanup_devc; + + if (hw_config->name) + name = hw_config->name; + + if (devc->irq < 0) { + devc->share_irq = 1; + devc->irq *= -1; + } else + devc->share_irq = 0; + + if (!devc->share_irq) + if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) { + printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq); + devc->share_irq = 1; + } + devc->my_dev = sound_alloc_mididev(); + enter_uart_mode(devc); + + if (devc->my_dev == -1) { + printk(KERN_INFO "uart401: Too many midi devices detected\n"); + goto cleanup_irq; + } + conf_printf(name, hw_config); + midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL); + if (!midi_devs[devc->my_dev]) { + printk(KERN_ERR "uart401: Failed to allocate memory\n"); + goto cleanup_unload_mididev; + } + memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations)); + + if (owner) + midi_devs[devc->my_dev]->owner = owner; + + midi_devs[devc->my_dev]->devc = devc; + midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + if (!midi_devs[devc->my_dev]->converter) { + printk(KERN_WARNING "uart401: Failed to allocate memory\n"); + goto cleanup_midi_devs; + } + memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations)); + strcpy(midi_devs[devc->my_dev]->info.name, name); + midi_devs[devc->my_dev]->converter->id = "UART401"; + midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev; + + if (owner) + midi_devs[devc->my_dev]->converter->owner = owner; + + hw_config->slots[4] = devc->my_dev; + sequencer_init(); + devc->opened = 0; + return 1; +cleanup_midi_devs: + kfree(midi_devs[devc->my_dev]); +cleanup_unload_mididev: + sound_unload_mididev(devc->my_dev); +cleanup_irq: + if (!devc->share_irq) + free_irq(devc->irq, devc); +cleanup_devc: + kfree(devc); +cleanup_region: + release_region(hw_config->io_base, 4); + return 0; +} + +void unload_uart401(struct address_info *hw_config) +{ + uart401_devc *devc; + int n=hw_config->slots[4]; + + /* Not set up */ + if(n==-1 || midi_devs[n]==NULL) + return; + + /* Not allocated (erm ??) */ + + devc = midi_devs[hw_config->slots[4]]->devc; + if (devc == NULL) + return; + + reset_uart401(devc); + release_region(hw_config->io_base, 4); + + if (!devc->share_irq) + free_irq(devc->irq, devc); + if (devc) + { + kfree(midi_devs[devc->my_dev]->converter); + kfree(midi_devs[devc->my_dev]); + kfree(devc); + devc = NULL; + } + /* This kills midi_devs[x] */ + sound_unload_mididev(hw_config->slots[4]); +} + +EXPORT_SYMBOL(probe_uart401); +EXPORT_SYMBOL(unload_uart401); +EXPORT_SYMBOL(uart401intr); + +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); + + +static int __init init_uart401(void) +{ + cfg_mpu.irq = irq; + cfg_mpu.io_base = io; + + /* Can be loaded either for module use or to provide functions + to others */ + if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) { + printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997"); + if (!probe_uart401(&cfg_mpu, THIS_MODULE)) + return -ENODEV; + } + + return 0; +} + +static void __exit cleanup_uart401(void) +{ + if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) + unload_uart401(&cfg_mpu); +} + +module_init(init_uart401); +module_exit(cleanup_uart401); + +#ifndef MODULE +static int __init setup_uart401(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} + +__setup("uart401=", setup_uart401); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/uart6850.c linux-2.4.19-pre5-mjc/sound/oss/uart6850.c --- linux/sound/oss/uart6850.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/uart6850.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,364 @@ +/* + * sound/uart6850.c + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver. + * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2. + * + * Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now + * uses native linux resources + * Christoph Hellwig: Adapted to module_init/module_exit + * Jeff Garzik: Made it work again, in theory + * FIXME: If the request_irq() succeeds, the probe succeeds. Ug. + * + * Status: Testing required (no shit -jgarzik) + * + * + */ + +#include +#include + +/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: + * added 6850 support, used with COVOX SoundMaster II and custom cards. + */ + +#include "sound_config.h" + +static int uart6850_base = 0x330; + +static int *uart6850_osp; + +#define DATAPORT (uart6850_base) +#define COMDPORT (uart6850_base+1) +#define STATPORT (uart6850_base+1) + +static int uart6850_status(void) +{ + return inb(STATPORT); +} + +#define input_avail() (uart6850_status()&INPUT_AVAIL) +#define output_ready() (uart6850_status()&OUTPUT_READY) + +static void uart6850_cmd(unsigned char cmd) +{ + outb(cmd, COMDPORT); +} + +static int uart6850_read(void) +{ + return inb(DATAPORT); +} + +static void uart6850_write(unsigned char byte) +{ + outb(byte, DATAPORT); +} + +#define OUTPUT_READY 0x02 /* Mask for data ready Bit */ +#define INPUT_AVAIL 0x01 /* Mask for Data Send Ready Bit */ + +#define UART_RESET 0x95 +#define UART_MODE_ON 0x03 + +static int uart6850_opened; +static int uart6850_irq; +static int uart6850_detected; +static int my_dev; + +static void (*midi_input_intr) (int dev, unsigned char data); +static void poll_uart6850(unsigned long dummy); + + +static struct timer_list uart6850_timer = { + function: poll_uart6850 +}; + +static void uart6850_input_loop(void) +{ + int count = 10; + + while (count) + { + /* + * Not timed out + */ + if (input_avail()) + { + unsigned char c = uart6850_read(); + count = 100; + if (uart6850_opened & OPEN_READ) + midi_input_intr(my_dev, c); + } + else + { + while (!input_avail() && count) + count--; + } + } +} + +void m6850intr(int irq, void *dev_id, struct pt_regs *dummy) +{ + if (input_avail()) + uart6850_input_loop(); +} + +/* + * It looks like there is no input interrupts in the UART mode. Let's try + * polling. + */ + +static void poll_uart6850(unsigned long dummy) +{ + unsigned long flags; + + if (!(uart6850_opened & OPEN_READ)) + return; /* Device has been closed */ + + save_flags(flags); + cli(); + + if (input_avail()) + uart6850_input_loop(); + + uart6850_timer.expires = 1 + jiffies; + add_timer(&uart6850_timer); + + /* + * Come back later + */ + + restore_flags(flags); +} + +static int uart6850_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + if (uart6850_opened) + { +/* printk("Midi6850: Midi busy\n");*/ + return -EBUSY; + }; + + uart6850_cmd(UART_RESET); + uart6850_input_loop(); + midi_input_intr = input; + uart6850_opened = mode; + poll_uart6850(0); /* + * Enable input polling + */ + + return 0; +} + +static void uart6850_close(int dev) +{ + uart6850_cmd(UART_MODE_ON); + del_timer(&uart6850_timer); + uart6850_opened = 0; +} + +static int uart6850_out(int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + save_flags(flags); + cli(); + + if (input_avail()) + uart6850_input_loop(); + + restore_flags(flags); + + /* + * Sometimes it takes about 13000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* + * Wait + */ + if (!output_ready()) + { + printk(KERN_WARNING "Midi6850: Timeout\n"); + return 0; + } + uart6850_write(midi_byte); + return 1; +} + +static inline int uart6850_command(int dev, unsigned char *midi_byte) +{ + return 1; +} + +static inline int uart6850_start_read(int dev) +{ + return 0; +} + +static inline int uart6850_end_read(int dev) +{ + return 0; +} + +static inline void uart6850_kick(int dev) +{ +} + +static inline int uart6850_buffer_status(int dev) +{ + return 0; /* + * No data in buffers + */ +} + +#define MIDI_SYNTH_NAME "6850 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations uart6850_operations = +{ + owner: THIS_MODULE, + info: {"6850 UART", 0, 0, SNDCARD_UART6850}, + converter: &std_midi_synth, + in_info: {0}, + open: uart6850_open, + close: uart6850_close, + outputc: uart6850_out, + start_read: uart6850_start_read, + end_read: uart6850_end_read, + kick: uart6850_kick, + command: uart6850_command, + buffer_status: uart6850_buffer_status +}; + + +static void __init attach_uart6850(struct address_info *hw_config) +{ + int ok, timeout; + unsigned long flags; + + if (!uart6850_detected) + return; + + if ((my_dev = sound_alloc_mididev()) == -1) + { + printk(KERN_INFO "uart6850: Too many midi devices detected\n"); + return; + } + uart6850_base = hw_config->io_base; + uart6850_osp = hw_config->osp; + uart6850_irq = hw_config->irq; + + save_flags(flags); + cli(); + + for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* + * Wait + */ + uart6850_cmd(UART_MODE_ON); + ok = 1; + restore_flags(flags); + + conf_printf("6850 Midi Interface", hw_config); + + std_midi_synth.midi_dev = my_dev; + hw_config->slots[4] = my_dev; + midi_devs[my_dev] = &uart6850_operations; + sequencer_init(); +} + +static inline int reset_uart6850(void) +{ + uart6850_read(); + return 1; /* + * OK + */ +} + +static int __init probe_uart6850(struct address_info *hw_config) +{ + int ok; + + uart6850_osp = hw_config->osp; + uart6850_base = hw_config->io_base; + uart6850_irq = hw_config->irq; + + if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0) + return 0; + + ok = reset_uart6850(); + uart6850_detected = ok; + return ok; +} + +static void __exit unload_uart6850(struct address_info *hw_config) +{ + free_irq(hw_config->irq, NULL); + sound_unload_mididev(hw_config->slots[4]); +} + +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); + +static int __init init_uart6850(void) +{ + cfg_mpu.io_base = io; + cfg_mpu.irq = irq; + + if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) { + printk(KERN_INFO "uart6850: irq and io must be set.\n"); + return -EINVAL; + } + + if (probe_uart6850(&cfg_mpu)) + return -ENODEV; + attach_uart6850(&cfg_mpu); + + return 0; +} + +static void __exit cleanup_uart6850(void) +{ + unload_uart6850(&cfg_mpu); +} + +module_init(init_uart6850); +module_exit(cleanup_uart6850); + +#ifndef MODULE +static int __init setup_uart6850(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} +__setup("uart6850=", setup_uart6850); +#endif +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/ulaw.h linux-2.4.19-pre5-mjc/sound/oss/ulaw.h --- linux/sound/oss/ulaw.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ulaw.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,69 @@ +static unsigned char ulaw_dsp[] = { + 3, 7, 11, 15, 19, 23, 27, 31, + 35, 39, 43, 47, 51, 55, 59, 63, + 66, 68, 70, 72, 74, 76, 78, 80, + 82, 84, 86, 88, 90, 92, 94, 96, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, + 113, 114, 114, 115, 115, 116, 116, 117, + 117, 118, 118, 119, 119, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 123, 123, + 123, 123, 124, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 126, 126, 126, 126, + 126, 126, 126, 126, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 253, 249, 245, 241, 237, 233, 229, 225, + 221, 217, 213, 209, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 174, 172, 170, 168, 166, 164, 162, 160, + 158, 157, 156, 155, 154, 153, 152, 151, + 150, 149, 148, 147, 146, 145, 144, 143, + 143, 142, 142, 141, 141, 140, 140, 139, + 139, 138, 138, 137, 137, 136, 136, 135, + 135, 135, 134, 134, 134, 134, 133, 133, + 133, 133, 132, 132, 132, 132, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 130, 130, 130, 130, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, +}; + +static unsigned char dsp_ulaw[] = { + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 3, 3, 3, + 3, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, + 15, 16, 16, 17, 17, 18, 18, 19, + 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 25, 25, 26, 26, 27, + 27, 28, 28, 29, 29, 30, 30, 31, + 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, + 47, 49, 51, 53, 55, 57, 59, 61, + 63, 66, 70, 74, 78, 84, 92, 104, + 254, 231, 219, 211, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 175, 174, 173, 172, 171, 170, 169, 168, + 167, 166, 165, 164, 163, 162, 161, 160, + 159, 159, 158, 158, 157, 157, 156, 156, + 155, 155, 154, 154, 153, 153, 152, 152, + 151, 151, 150, 150, 149, 149, 148, 148, + 147, 147, 146, 146, 145, 145, 144, 144, + 143, 143, 143, 143, 142, 142, 142, 142, + 141, 141, 141, 141, 140, 140, 140, 140, + 139, 139, 139, 139, 138, 138, 138, 138, + 137, 137, 137, 137, 136, 136, 136, 136, + 135, 135, 135, 135, 134, 134, 134, 134, + 133, 133, 133, 133, 132, 132, 132, 132, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 129, 128, 128, 128, 128, +}; diff -Nru linux/sound/oss/v_midi.c linux-2.4.19-pre5-mjc/sound/oss/v_midi.c --- linux/sound/oss/v_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/v_midi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,290 @@ +/* + * sound/v_midi.c + * + * The low level driver for the Sound Blaster DS chips. + * + * + * Copyright (C) by Hannu Savolainen 1993-1996 + * + * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * ?? + * + * Changes + * Alan Cox Modularisation, changed memory allocations + * Christoph Hellwig Adapted to module_init/module_exit + * + * Status + * Untested + */ + +#include +#include + +#include "sound_config.h" + +#include "v_midi.h" + +static vmidi_devc *v_devc[2] = { NULL, NULL}; +static int midi1,midi2; +static void *midi_mem = NULL; + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + + +void (*midi_input_intr) (int dev, unsigned char data); + +static int v_midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return -(ENXIO); + + save_flags (flags); + cli(); + if (devc->opened) + { + restore_flags (flags); + return -(EBUSY); + } + devc->opened = 1; + restore_flags (flags); + + devc->intr_active = 1; + + if (mode & OPEN_READ) + { + devc->input_opened = 1; + devc->midi_input_intr = input; + } + + return 0; +} + +static void v_midi_close (int dev) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return; + + save_flags (flags); + cli (); + devc->intr_active = 0; + devc->input_opened = 0; + devc->opened = 0; + restore_flags (flags); +} + +static int v_midi_out (int dev, unsigned char midi_byte) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc; + + if (devc == NULL) + return -(ENXIO); + + if (pdevc->input_opened > 0){ + if (MIDIbuf_avail(pdevc->my_mididev) > 500) + return 0; + pdevc->midi_input_intr (pdevc->my_mididev, midi_byte); + } + return 1; +} + +static inline int v_midi_start_read (int dev) +{ + return 0; +} + +static int v_midi_end_read (int dev) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + if (devc == NULL) + return -ENXIO; + + devc->intr_active = 0; + return 0; +} + +/* why -EPERM and not -EINVAL?? */ + +static inline int v_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + return -EPERM; +} + + +#define MIDI_SYNTH_NAME "Loopback MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT + +#include "midi_synth.h" + +static struct midi_operations v_midi_operations = +{ + owner: THIS_MODULE, + info: {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI}, + converter: &std_midi_synth, + in_info: {0}, + open: v_midi_open, + close: v_midi_close, + ioctl: v_midi_ioctl, + outputc: v_midi_out, + start_read: v_midi_start_read, + end_read: v_midi_end_read, +}; + +static struct midi_operations v_midi_operations2 = +{ + owner: THIS_MODULE, + info: {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI}, + converter: &std_midi_synth, + in_info: {0}, + open: v_midi_open, + close: v_midi_close, + ioctl: v_midi_ioctl, + outputc: v_midi_out, + start_read: v_midi_start_read, + end_read: v_midi_end_read, +}; + +/* + * We kmalloc just one of these - it makes life simpler and the code + * cleaner and the memory handling far more efficient + */ + +struct vmidi_memory +{ + /* Must be first */ + struct midi_operations m_ops[2]; + struct synth_operations s_ops[2]; + struct vmidi_devc v_ops[2]; +}; + +static void __init attach_v_midi (struct address_info *hw_config) +{ + struct vmidi_memory *m; + /* printk("Attaching v_midi device.....\n"); */ + + midi1 = sound_alloc_mididev(); + if (midi1 == -1) + { + printk(KERN_ERR "v_midi: Too many midi devices detected\n"); + return; + } + + m=(struct vmidi_memory *)kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL); + if (m == NULL) + { + printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + return; + } + + midi_mem = m; + + midi_devs[midi1] = &m->m_ops[0]; + + + midi2 = sound_alloc_mididev(); + if (midi2 == -1) + { + printk (KERN_ERR "v_midi: Too many midi devices detected\n"); + kfree(m); + sound_unload_mididev(midi1); + return; + } + + midi_devs[midi2] = &m->m_ops[1]; + + /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */ + + /* for MIDI-1 */ + v_devc[0] = &m->v_ops[0]; + memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations, + sizeof (struct midi_operations)); + + v_devc[0]->my_mididev = midi1; + v_devc[0]->pair_mididev = midi2; + v_devc[0]->opened = v_devc[0]->input_opened = 0; + v_devc[0]->intr_active = 0; + v_devc[0]->midi_input_intr = NULL; + + midi_devs[midi1]->devc = v_devc[0]; + + midi_devs[midi1]->converter = &m->s_ops[0]; + std_midi_synth.midi_dev = midi1; + memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth, + sizeof (struct synth_operations)); + midi_devs[midi1]->converter->id = "V_MIDI 1"; + + /* for MIDI-2 */ + v_devc[1] = &m->v_ops[1]; + + memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2, + sizeof (struct midi_operations)); + + v_devc[1]->my_mididev = midi2; + v_devc[1]->pair_mididev = midi1; + v_devc[1]->opened = v_devc[1]->input_opened = 0; + v_devc[1]->intr_active = 0; + v_devc[1]->midi_input_intr = NULL; + + midi_devs[midi2]->devc = v_devc[1]; + midi_devs[midi2]->converter = &m->s_ops[1]; + + std_midi_synth.midi_dev = midi2; + memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth, + sizeof (struct synth_operations)); + midi_devs[midi2]->converter->id = "V_MIDI 2"; + + sequencer_init(); + /* printk("Attached v_midi device\n"); */ +} + +static inline int __init probe_v_midi(struct address_info *hw_config) +{ + return(1); /* always OK */ +} + + +static void __exit unload_v_midi(struct address_info *hw_config) +{ + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + kfree(midi_mem); +} + +static struct address_info cfg; /* dummy */ + +static int __init init_vmidi(void) +{ + printk("MIDI Loopback device driver\n"); + if (!probe_v_midi(&cfg)) + return -ENODEV; + attach_v_midi(&cfg); + + return 0; +} + +static void __exit cleanup_vmidi(void) +{ + unload_v_midi(&cfg); +} + +module_init(init_vmidi); +module_exit(cleanup_vmidi); +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/v_midi.h linux-2.4.19-pre5-mjc/sound/oss/v_midi.h --- linux/sound/oss/v_midi.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/v_midi.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,15 @@ +typedef struct vmidi_devc { + int dev; + + /* State variables */ + int opened; + + + /* MIDI fields */ + int my_mididev; + int pair_mididev; + int input_opened; + int intr_active; + void (*midi_input_intr) (int dev, unsigned char data); + } vmidi_devc; + diff -Nru linux/sound/oss/via82cxxx_audio.c linux-2.4.19-pre5-mjc/sound/oss/via82cxxx_audio.c --- linux/sound/oss/via82cxxx_audio.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/via82cxxx_audio.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,3539 @@ +/* + * Support for VIA 82Cxxx Audio Codecs + * Copyright 1999,2000 Jeff Garzik + * + * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2. + * See the "COPYING" file distributed with this software for more info. + * + * For a list of known bugs (errata) and documentation, + * see via-audio.pdf in linux/Documentation/DocBook. + * If this documentation does not exist, run "make pdfdocs". + * + */ + + +#define VIA_VERSION "1.9.1" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "dev_table.h" +#include "mpu401.h" + + +#undef VIA_DEBUG /* define to enable debugging output and checks */ +#ifdef VIA_DEBUG +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#undef VIA_NDEBUG /* define to disable lightweight runtime checks */ +#ifdef VIA_NDEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#if defined(CONFIG_PROC_FS) && \ + defined(CONFIG_SOUND_ALSA_VIA82CXXX_PROCFS) +#define VIA_PROC_FS 1 +#endif + +#define VIA_SUPPORT_MMAP 1 /* buggy, for now... */ + +#define MAX_CARDS 1 + +#define VIA_CARD_NAME "VIA 82Cxxx Audio driver " VIA_VERSION +#define VIA_MODULE_NAME "via82cxxx" +#define PFX VIA_MODULE_NAME ": " + +#define VIA_COUNTER_LIMIT 100000 + +/* size of DMA buffers */ +#define VIA_MAX_BUFFER_DMA_PAGES 32 + +/* buffering default values in ms */ +#define VIA_DEFAULT_FRAG_TIME 20 +#define VIA_DEFAULT_BUFFER_TIME 500 + +#define VIA_MAX_FRAG_SIZE PAGE_SIZE +#define VIA_MIN_FRAG_SIZE 64 + +#define VIA_MIN_FRAG_NUMBER 2 + +/* 82C686 function 5 (audio codec) PCI configuration registers */ +#define VIA_ACLINK_STATUS 0x40 +#define VIA_ACLINK_CTRL 0x41 +#define VIA_FUNC_ENABLE 0x42 +#define VIA_PNP_CONTROL 0x43 +#define VIA_FM_NMI_CTRL 0x48 + +/* + * controller base 0 (scatter-gather) registers + * + * NOTE: Via datasheet lists first channel as "read" + * channel and second channel as "write" channel. + * I changed the naming of the constants to be more + * clear than I felt the datasheet to be. + */ + +#define VIA_BASE0_PCM_OUT_CHAN 0x00 /* output PCM to user */ +#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00 +#define VIA_BASE0_PCM_OUT_CHAN_CTRL 0x01 +#define VIA_BASE0_PCM_OUT_CHAN_TYPE 0x02 + +#define VIA_BASE0_PCM_IN_CHAN 0x10 /* input PCM from user */ +#define VIA_BASE0_PCM_IN_CHAN_STATUS 0x10 +#define VIA_BASE0_PCM_IN_CHAN_CTRL 0x11 +#define VIA_BASE0_PCM_IN_CHAN_TYPE 0x12 + +/* offsets from base */ +#define VIA_PCM_STATUS 0x00 +#define VIA_PCM_CONTROL 0x01 +#define VIA_PCM_TYPE 0x02 +#define VIA_PCM_TABLE_ADDR 0x04 +#define VIA_PCM_BLOCK_COUNT 0x0C + +/* XXX unused DMA channel for FM PCM data */ +#define VIA_BASE0_FM_OUT_CHAN 0x20 +#define VIA_BASE0_FM_OUT_CHAN_STATUS 0x20 +#define VIA_BASE0_FM_OUT_CHAN_CTRL 0x21 +#define VIA_BASE0_FM_OUT_CHAN_TYPE 0x22 + +#define VIA_BASE0_AC97_CTRL 0x80 +#define VIA_BASE0_SGD_STATUS_SHADOW 0x84 +#define VIA_BASE0_GPI_INT_ENABLE 0x8C +#define VIA_INTR_OUT ((1<<0) | (1<<4) | (1<<8)) +#define VIA_INTR_IN ((1<<1) | (1<<5) | (1<<9)) +#define VIA_INTR_FM ((1<<2) | (1<<6) | (1<<10)) +#define VIA_INTR_MASK (VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM) + +/* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */ +#define VIA_IRQ_ON_FLAG (1<<0) /* int on each flagged scatter block */ +#define VIA_IRQ_ON_EOL (1<<1) /* int at end of scatter list */ +#define VIA_INT_SEL_PCI_LAST_LINE_READ (0) /* int at PCI read of last line */ +#define VIA_INT_SEL_LAST_SAMPLE_SENT (1<<2) /* int at last sample sent */ +#define VIA_INT_SEL_ONE_LINE_LEFT (1<<3) /* int at less than one line to send */ +#define VIA_PCM_FMT_STEREO (1<<4) /* PCM stereo format (bit clear == mono) */ +#define VIA_PCM_FMT_16BIT (1<<5) /* PCM 16-bit format (bit clear == 8-bit) */ +#define VIA_PCM_REC_FIFO (1<<6) /* PCM Recording FIFO */ +#define VIA_RESTART_SGD_ON_EOL (1<<7) /* restart scatter-gather at EOL */ +#define VIA_PCM_FMT_MASK (VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT) +#define VIA_CHAN_TYPE_MASK (VIA_RESTART_SGD_ON_EOL | \ + VIA_IRQ_ON_FLAG | \ + VIA_IRQ_ON_EOL) +#define VIA_CHAN_TYPE_INT_SELECT (VIA_INT_SEL_LAST_SAMPLE_SENT) + +/* PCI configuration register bits and masks */ +#define VIA_CR40_AC97_READY 0x01 +#define VIA_CR40_AC97_LOW_POWER 0x02 +#define VIA_CR40_SECONDARY_READY 0x04 + +#define VIA_CR41_AC97_ENABLE 0x80 /* enable AC97 codec */ +#define VIA_CR41_AC97_RESET 0x40 /* clear bit to reset AC97 */ +#define VIA_CR41_AC97_WAKEUP 0x20 /* wake up from power-down mode */ +#define VIA_CR41_AC97_SDO 0x10 /* force Serial Data Out (SDO) high */ +#define VIA_CR41_VRA 0x08 /* enable variable sample rate */ +#define VIA_CR41_PCM_ENABLE 0x04 /* AC Link SGD Read Channel PCM Data Output */ +#define VIA_CR41_FM_PCM_ENABLE 0x02 /* AC Link FM Channel PCM Data Out */ +#define VIA_CR41_SB_PCM_ENABLE 0x01 /* AC Link SB PCM Data Output */ +#define VIA_CR41_BOOT_MASK (VIA_CR41_AC97_ENABLE | \ + VIA_CR41_AC97_WAKEUP | \ + VIA_CR41_AC97_SDO) +#define VIA_CR41_RUN_MASK (VIA_CR41_AC97_ENABLE | \ + VIA_CR41_AC97_RESET | \ + VIA_CR41_VRA | \ + VIA_CR41_PCM_ENABLE) + +#define VIA_CR42_SB_ENABLE 0x01 +#define VIA_CR42_MIDI_ENABLE 0x02 +#define VIA_CR42_FM_ENABLE 0x04 +#define VIA_CR42_GAME_ENABLE 0x08 +#define VIA_CR42_MIDI_IRQMASK 0x40 +#define VIA_CR42_MIDI_PNP 0x80 + +#define VIA_CR44_SECOND_CODEC_SUPPORT (1 << 6) +#define VIA_CR44_AC_LINK_ACCESS (1 << 7) + +#define VIA_CR48_FM_TRAP_TO_NMI (1 << 2) + +/* controller base 0 register bitmasks */ +#define VIA_INT_DISABLE_MASK (~(0x01|0x02)) +#define VIA_SGD_STOPPED (1 << 2) +#define VIA_SGD_PAUSED (1 << 6) +#define VIA_SGD_ACTIVE (1 << 7) +#define VIA_SGD_TERMINATE (1 << 6) +#define VIA_SGD_FLAG (1 << 0) +#define VIA_SGD_EOL (1 << 1) +#define VIA_SGD_START (1 << 7) + +#define VIA_CR80_FIRST_CODEC 0 +#define VIA_CR80_SECOND_CODEC (1 << 30) +#define VIA_CR80_FIRST_CODEC_VALID (1 << 25) +#define VIA_CR80_VALID (1 << 25) +#define VIA_CR80_SECOND_CODEC_VALID (1 << 27) +#define VIA_CR80_BUSY (1 << 24) +#define VIA_CR83_BUSY (1) +#define VIA_CR83_FIRST_CODEC_VALID (1 << 1) +#define VIA_CR80_READ (1 << 23) +#define VIA_CR80_WRITE_MODE 0 +#define VIA_CR80_REG_IDX(idx) ((((idx) & 0xFF) >> 1) << 16) + +/* capabilities we announce */ +#ifdef VIA_SUPPORT_MMAP +#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | DSP_CAP_MMAP | \ + DSP_CAP_TRIGGER | DSP_CAP_REALTIME) +#else +#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | \ + DSP_CAP_TRIGGER | DSP_CAP_REALTIME) +#endif + +/* scatter-gather DMA table entry, exactly as passed to hardware */ +struct via_sgd_table { + u32 addr; + u32 count; /* includes additional VIA_xxx bits also */ +}; + +#define VIA_EOL (1 << 31) +#define VIA_FLAG (1 << 30) +#define VIA_STOP (1 << 29) + + +enum via_channel_states { + sgd_stopped = 0, + sgd_in_progress = 1, +}; + + +struct via_buffer_pgtbl { + dma_addr_t handle; + void *cpuaddr; +}; + + +struct via_channel { + atomic_t n_frags; + atomic_t hw_ptr; + wait_queue_head_t wait; + + unsigned int sw_ptr; + unsigned int slop_len; + unsigned int n_irqs; + int bytes; + + unsigned is_active : 1; + unsigned is_record : 1; + unsigned is_mapped : 1; + unsigned is_enabled : 1; + u8 pcm_fmt; /* VIA_PCM_FMT_xxx */ + + unsigned rate; /* sample rate */ + unsigned int frag_size; + unsigned int frag_number; + + volatile struct via_sgd_table *sgtable; + dma_addr_t sgt_handle; + + unsigned int page_number; + struct via_buffer_pgtbl pgtbl[VIA_MAX_BUFFER_DMA_PAGES]; + + long iobase; + + const char *name; +}; + + +/* data stored for each chip */ +struct via_info { + struct pci_dev *pdev; + long baseaddr; + + struct ac97_codec ac97; + spinlock_t lock; + int card_num; /* unique card number, from 0 */ + + int dev_dsp; /* /dev/dsp index from register_sound_dsp() */ + + unsigned rev_h : 1; + + int locked_rate : 1; + + struct semaphore syscall_sem; + struct semaphore open_sem; + + struct via_channel ch_in; + struct via_channel ch_out; + struct via_channel ch_fm; + +#ifdef CONFIG_MIDI_VIA82CXXX + void *midi_devc; + struct address_info midi_info; +#endif +}; + + +/* number of cards, used for assigning unique numbers to cards */ +static unsigned via_num_cards = 0; + + + +/**************************************************************** + * + * prototypes + * + * + */ + +static int via_init_one (struct pci_dev *dev, const struct pci_device_id *id); +static void __devexit via_remove_one (struct pci_dev *pdev); + +static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos); +static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos); +static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait); +static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int via_dsp_open (struct inode *inode, struct file *file); +static int via_dsp_release(struct inode *inode, struct file *file); +static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma); + +static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg); +static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value); +static u8 via_ac97_wait_idle (struct via_info *card); + +static void via_chan_free (struct via_info *card, struct via_channel *chan); +static void via_chan_clear (struct via_info *card, struct via_channel *chan); +static void via_chan_pcm_fmt (struct via_channel *chan, int reset); +static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan); + +#ifdef VIA_PROC_FS +static int via_init_proc (void); +static void via_cleanup_proc (void); +static int via_card_init_proc (struct via_info *card); +static void via_card_cleanup_proc (struct via_info *card); +#else +static inline int via_init_proc (void) { return 0; } +static inline void via_cleanup_proc (void) {} +static inline int via_card_init_proc (struct via_info *card) { return 0; } +static inline void via_card_cleanup_proc (struct via_info *card) {} +#endif + + +/**************************************************************** + * + * Various data the driver needs + * + * + */ + + +static struct pci_device_id via_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci,via_pci_tbl); + + +static struct pci_driver via_driver = { + name: VIA_MODULE_NAME, + id_table: via_pci_tbl, + probe: via_init_one, + remove: __devexit_p(via_remove_one), +}; + + +/**************************************************************** + * + * Low-level base 0 register read/write helpers + * + * + */ + +/** + * via_chan_stop - Terminate DMA on specified PCM channel + * @iobase: PCI base address for SGD channel registers + * + * Terminate scatter-gather DMA operation for given + * channel (derived from @iobase), if DMA is active. + * + * Note that @iobase is not the PCI base address, + * but the PCI base address plus an offset to + * one of three PCM channels supported by the chip. + * + */ + +static inline void via_chan_stop (long iobase) +{ + if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE) + outb (VIA_SGD_TERMINATE, iobase + VIA_PCM_CONTROL); +} + + +/** + * via_chan_status_clear - Clear status flags on specified DMA channel + * @iobase: PCI base address for SGD channel registers + * + * Clear any pending status flags for the given + * DMA channel (derived from @iobase), if any + * flags are asserted. + * + * Note that @iobase is not the PCI base address, + * but the PCI base address plus an offset to + * one of three PCM channels supported by the chip. + * + */ + +static inline void via_chan_status_clear (long iobase) +{ + u8 tmp = inb (iobase + VIA_PCM_STATUS); + + if (tmp != 0) + outb (tmp, iobase + VIA_PCM_STATUS); +} + + +/** + * sg_begin - Begin recording or playback on a PCM channel + * @chan: Channel for which DMA operation shall begin + * + * Start scatter-gather DMA for the given channel. + * + */ + +static inline void sg_begin (struct via_channel *chan) +{ + outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL); +} + + +static int sg_active (long iobase) +{ + u8 tmp = inb (iobase + VIA_PCM_STATUS); + if ((tmp & VIA_SGD_STOPPED) || (tmp & VIA_SGD_PAUSED)) { + printk(KERN_WARNING "via82cxxx warning: SG stopped or paused\n"); + return 0; + } + if (tmp & VIA_SGD_ACTIVE) + return 1; + return 0; +} + + +/**************************************************************** + * + * Miscellaneous debris + * + * + */ + + +/** + * via_syscall_down - down the card-specific syscell semaphore + * @card: Private info for specified board + * @nonblock: boolean, non-zero if O_NONBLOCK is set + * + * Encapsulates standard method of acquiring the syscall sem. + * + * Returns negative errno on error, or zero for success. + */ + +static inline int via_syscall_down (struct via_info *card, int nonblock) +{ + /* Thomas Sailer: + * EAGAIN is supposed to be used if IO is pending, + * not if there is contention on some internal + * synchronization primitive which should be + * held only for a short time anyway + */ + nonblock = 0; + + if (nonblock) { + if (down_trylock (&card->syscall_sem)) + return -EAGAIN; + } else { + if (down_interruptible (&card->syscall_sem)) + return -ERESTARTSYS; + } + + return 0; +} + + +/** + * via_stop_everything - Stop all audio operations + * @card: Private info for specified board + * + * Stops all DMA operations and interrupts, and clear + * any pending status bits resulting from those operations. + */ + +static void via_stop_everything (struct via_info *card) +{ + u8 tmp, new_tmp; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + + /* + * terminate any existing operations on audio read/write channels + */ + via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); + via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); + via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + + /* + * clear any existing stops / flags (sanity check mainly) + */ + via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); + via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); + via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + + /* + * clear any enabled interrupt bits + */ + tmp = inb (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); + + tmp = inb (card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); + + tmp = inb (card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); + new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL); + if (tmp != new_tmp) + outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); + + udelay(10); + + /* + * clear any existing flags + */ + via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); + via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); + via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + + DPRINTK ("EXIT\n"); +} + + +/** + * via_set_rate - Set PCM rate for given channel + * @ac97: Pointer to generic codec info struct + * @chan: Private info for specified channel + * @rate: Desired PCM sample rate, in Khz + * + * Sets the PCM sample rate for a channel. + * + * Values for @rate are clamped to a range of 4000 Khz through 48000 Khz, + * due to hardware constraints. + */ + +static int via_set_rate (struct ac97_codec *ac97, + struct via_channel *chan, unsigned rate) +{ + struct via_info *card = ac97->private_data; + int rate_reg; + + DPRINTK ("ENTER, rate = %d\n", rate); + + if (chan->rate == rate) + goto out; + if (card->locked_rate) { + chan->rate = 48000; + goto out; + } + + if (rate > 48000) rate = 48000; + if (rate < 4000) rate = 4000; + + rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE : + AC97_PCM_FRONT_DAC_RATE; + + via_ac97_write_reg (ac97, AC97_POWER_CONTROL, + (via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) | + 0x0200); + + via_ac97_write_reg (ac97, rate_reg, rate); + + via_ac97_write_reg (ac97, AC97_POWER_CONTROL, + via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200); + + udelay (10); + + /* the hardware might return a value different than what we + * passed to it, so read the rate value back from hardware + * to see what we came up with + */ + chan->rate = via_ac97_read_reg (ac97, rate_reg); + + if (chan->rate == 0) { + card->locked_rate = 1; + chan->rate = 48000; + printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); + } + +out: + DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate); + return chan->rate; +} + + +/**************************************************************** + * + * Channel-specific operations + * + * + */ + + +/** + * via_chan_init_defaults - Initialize a struct via_channel + * @card: Private audio chip info + * @chan: Channel to be initialized + * + * Zero @chan, and then set all static defaults for the structure. + */ + +static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan) +{ + memset (chan, 0, sizeof (*chan)); + + if (chan == &card->ch_out) { + chan->name = "PCM-OUT"; + chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN; + } else if (chan == &card->ch_in) { + chan->name = "PCM-IN"; + chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN; + chan->is_record = 1; + } else if (chan == &card->ch_fm) { + chan->name = "PCM-OUT-FM"; + chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN; + } else { + BUG(); + } + + init_waitqueue_head (&chan->wait); + + chan->pcm_fmt = VIA_PCM_FMT_MASK; + chan->is_enabled = 1; + + chan->frag_number = 0; + chan->frag_size = 0; + atomic_set(&chan->n_frags, 0); + atomic_set (&chan->hw_ptr, 0); +} + +/** + * via_chan_init - Initialize PCM channel + * @card: Private audio chip info + * @chan: Channel to be initialized + * + * Performs some of the preparations necessary to begin + * using a PCM channel. + * + * Currently the preparations consist in + * setting the + * PCM channel to a known state. + */ + + +static void via_chan_init (struct via_info *card, struct via_channel *chan) +{ + + DPRINTK ("ENTER\n"); + + /* bzero channel structure, and init members to defaults */ + via_chan_init_defaults (card, chan); + + /* stop any existing channel output */ + via_chan_clear (card, chan); + via_chan_status_clear (chan->iobase); + via_chan_pcm_fmt (chan, 1); + + DPRINTK ("EXIT\n"); +} + +/** + * via_chan_buffer_init - Initialize PCM channel buffer + * @card: Private audio chip info + * @chan: Channel to be initialized + * + * Performs some of the preparations necessary to begin + * using a PCM channel. + * + * Currently the preparations include allocating the + * scatter-gather DMA table and buffers, + * and passing the + * address of the DMA table to the hardware. + * + * Note that special care is taken when passing the + * DMA table address to hardware, because it was found + * during driver development that the hardware did not + * always "take" the address. + */ + +static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan) +{ + int page, offset; + int i; + + DPRINTK ("ENTER\n"); + + if (chan->sgtable != NULL) { + DPRINTK ("EXIT\n"); + return 0; + } + + /* alloc DMA-able memory for scatter-gather table */ + chan->sgtable = pci_alloc_consistent (card->pdev, + (sizeof (struct via_sgd_table) * chan->frag_number), + &chan->sgt_handle); + if (!chan->sgtable) { + printk (KERN_ERR PFX "DMA table alloc fail, aborting\n"); + DPRINTK ("EXIT\n"); + return -ENOMEM; + } + + memset ((void*)chan->sgtable, 0, + (sizeof (struct via_sgd_table) * chan->frag_number)); + + /* alloc DMA-able memory for scatter-gather buffers */ + + chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE + + (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0); + + for (i = 0; i < chan->page_number; i++) { + chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE, + &chan->pgtbl[i].handle); + + if (!chan->pgtbl[i].cpuaddr) { + chan->page_number = i; + goto err_out_nomem; + } + +#ifndef VIA_NDEBUG + memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size); +#endif + +#if 1 + DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n", + i, (long)chan->pgtbl[i].handle, + virt_to_phys(chan->pgtbl[i].cpuaddr), + chan->pgtbl[i].cpuaddr); +#endif + } + + for (i = 0; i < chan->frag_number; i++) { + + page = i / (PAGE_SIZE / chan->frag_size); + offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size; + + chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG); + chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset); + +#if 1 + DPRINTK ("dmabuf #%d (32(h)=%lx)\n", + i, + (long)chan->sgtable[i].addr); +#endif + } + + /* overwrite the last buffer information */ + chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL); + + /* set location of DMA-able scatter-gather info table */ + DPRINTK ("outl (0x%X, 0x%04lX)\n", + chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); + + via_ac97_wait_idle (card); + outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR); + udelay (20); + via_ac97_wait_idle (card); + + DPRINTK ("inl (0x%lX) = %x\n", + chan->iobase + VIA_PCM_TABLE_ADDR, + inl(chan->iobase + VIA_PCM_TABLE_ADDR)); + + DPRINTK ("EXIT\n"); + return 0; + +err_out_nomem: + printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n"); + via_chan_buffer_free (card, chan); + DPRINTK ("EXIT\n"); + return -ENOMEM; +} + + +/** + * via_chan_free - Release a PCM channel + * @card: Private audio chip info + * @chan: Channel to be released + * + * Performs all the functions necessary to clean up + * an initialized channel. + * + * Currently these functions include disabled any + * active DMA operations, setting the PCM channel + * back to a known state, and releasing any allocated + * sound buffers. + */ + +static void via_chan_free (struct via_info *card, struct via_channel *chan) +{ + DPRINTK ("ENTER\n"); + + spin_lock_irq (&card->lock); + + /* stop any existing channel output */ + via_chan_status_clear (chan->iobase); + via_chan_stop (chan->iobase); + via_chan_status_clear (chan->iobase); + + spin_unlock_irq (&card->lock); + + synchronize_irq(); + + DPRINTK ("EXIT\n"); +} + +static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan) +{ + int i; + + DPRINTK ("ENTER\n"); + + /* zero location of DMA-able scatter-gather info table */ + via_ac97_wait_idle(card); + outl (0, chan->iobase + VIA_PCM_TABLE_ADDR); + + for (i = 0; i < chan->page_number; i++) + if (chan->pgtbl[i].cpuaddr) { + pci_free_consistent (card->pdev, PAGE_SIZE, + chan->pgtbl[i].cpuaddr, + chan->pgtbl[i].handle); + chan->pgtbl[i].cpuaddr = NULL; + chan->pgtbl[i].handle = 0; + } + + chan->page_number = 0; + + if (chan->sgtable) { + pci_free_consistent (card->pdev, + (sizeof (struct via_sgd_table) * chan->frag_number), + (void*)chan->sgtable, chan->sgt_handle); + chan->sgtable = NULL; + } + + DPRINTK ("EXIT\n"); +} + + +/** + * via_chan_pcm_fmt - Update PCM channel settings + * @chan: Channel to be updated + * @reset: Boolean. If non-zero, channel will be reset + * to 8-bit mono mode. + * + * Stores the settings of the current PCM format, + * 8-bit or 16-bit, and mono/stereo, into the + * hardware settings for the specified channel. + * If @reset is non-zero, the channel is reset + * to 8-bit mono mode. Otherwise, the channel + * is set to the values stored in the channel + * information struct @chan. + */ + +static void via_chan_pcm_fmt (struct via_channel *chan, int reset) +{ + DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n", + chan->pcm_fmt, reset ? "yes" : "no"); + + assert (chan != NULL); + + if (reset) + /* reset to 8-bit mono mode */ + chan->pcm_fmt = 0; + + /* enable interrupts on FLAG and EOL */ + chan->pcm_fmt |= VIA_CHAN_TYPE_MASK; + + /* if we are recording, enable recording fifo bit */ + if (chan->is_record) + chan->pcm_fmt |= VIA_PCM_REC_FIFO; + /* set interrupt select bits where applicable (PCM in & out channels) */ + if (!chan->is_record) + chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; + + outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE); + + DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n", + chan->pcm_fmt, + inb (chan->iobase + VIA_PCM_TYPE)); +} + + +/** + * via_chan_clear - Stop DMA channel operation, and reset pointers + * @card: the chip to accessed + * @chan: Channel to be cleared + * + * Call via_chan_stop to halt DMA operations, and then resets + * all software pointers which track DMA operation. + */ + +static void via_chan_clear (struct via_info *card, struct via_channel *chan) +{ + DPRINTK ("ENTER\n"); + via_chan_stop (chan->iobase); + via_chan_buffer_free(card, chan); + chan->is_active = 0; + chan->is_mapped = 0; + chan->is_enabled = 1; + chan->slop_len = 0; + chan->sw_ptr = 0; + chan->n_irqs = 0; + atomic_set (&chan->hw_ptr, 0); + DPRINTK ("EXIT\n"); +} + + +/** + * via_chan_set_speed - Set PCM sample rate for given channel + * @card: Private info for specified board + * @chan: Channel whose sample rate will be adjusted + * @val: New sample rate, in Khz + * + * Helper function for the %SNDCTL_DSP_SPEED ioctl. OSS semantics + * demand that all audio operations halt (if they are not already + * halted) when the %SNDCTL_DSP_SPEED is given. + * + * This function halts all audio operations for the given channel + * @chan, and then calls via_set_rate to set the audio hardware + * to the new rate. + */ + +static int via_chan_set_speed (struct via_info *card, + struct via_channel *chan, int val) +{ + DPRINTK ("ENTER, requested rate = %d\n", val); + + via_chan_clear (card, chan); + + val = via_set_rate (&card->ac97, chan, val); + + DPRINTK ("EXIT, returning %d\n", val); + return val; +} + + +/** + * via_chan_set_fmt - Set PCM sample size for given channel + * @card: Private info for specified board + * @chan: Channel whose sample size will be adjusted + * @val: New sample size, use the %AFMT_xxx constants + * + * Helper function for the %SNDCTL_DSP_SETFMT ioctl. OSS semantics + * demand that all audio operations halt (if they are not already + * halted) when the %SNDCTL_DSP_SETFMT is given. + * + * This function halts all audio operations for the given channel + * @chan, and then calls via_chan_pcm_fmt to set the audio hardware + * to the new sample size, either 8-bit or 16-bit. + */ + +static int via_chan_set_fmt (struct via_info *card, + struct via_channel *chan, int val) +{ + DPRINTK ("ENTER, val=%s\n", + val == AFMT_U8 ? "AFMT_U8" : + val == AFMT_S16_LE ? "AFMT_S16_LE" : + "unknown"); + + via_chan_clear (card, chan); + + assert (val != AFMT_QUERY); /* this case is handled elsewhere */ + + switch (val) { + case AFMT_S16_LE: + if ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) == 0) { + chan->pcm_fmt |= VIA_PCM_FMT_16BIT; + via_chan_pcm_fmt (chan, 0); + } + break; + + case AFMT_U8: + if (chan->pcm_fmt & VIA_PCM_FMT_16BIT) { + chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT; + via_chan_pcm_fmt (chan, 0); + } + break; + + default: + DPRINTK ("unknown AFMT: 0x%X\n", val); + val = AFMT_S16_LE; + } + + DPRINTK ("EXIT\n"); + return val; +} + + +/** + * via_chan_set_stereo - Enable or disable stereo for a DMA channel + * @card: Private info for specified board + * @chan: Channel whose stereo setting will be adjusted + * @val: New sample size, use the %AFMT_xxx constants + * + * Helper function for the %SNDCTL_DSP_CHANNELS and %SNDCTL_DSP_STEREO ioctls. OSS semantics + * demand that all audio operations halt (if they are not already + * halted) when %SNDCTL_DSP_CHANNELS or SNDCTL_DSP_STEREO is given. + * + * This function halts all audio operations for the given channel + * @chan, and then calls via_chan_pcm_fmt to set the audio hardware + * to enable or disable stereo. + */ + +static int via_chan_set_stereo (struct via_info *card, + struct via_channel *chan, int val) +{ + DPRINTK ("ENTER, channels = %d\n", val); + + via_chan_clear (card, chan); + + switch (val) { + + /* mono */ + case 1: + chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; + via_chan_pcm_fmt (chan, 0); + break; + + /* stereo */ + case 2: + chan->pcm_fmt |= VIA_PCM_FMT_STEREO; + via_chan_pcm_fmt (chan, 0); + break; + + /* unknown */ + default: + printk (KERN_WARNING PFX "unknown number of channels\n"); + val = -EINVAL; + break; + } + + DPRINTK ("EXIT, returning %d\n", val); + return val; +} + +static int via_chan_set_buffering (struct via_info *card, + struct via_channel *chan, int val) +{ + int shift; + + DPRINTK ("ENTER\n"); + + /* in both cases the buffer cannot be changed */ + if (chan->is_active || chan->is_mapped) { + DPRINTK ("EXIT\n"); + return -EINVAL; + } + + /* called outside SETFRAGMENT */ + /* set defaults or do nothing */ + if (val < 0) { + + if (chan->frag_size && chan->frag_number) + goto out; + + DPRINTK ("\n"); + + chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate * + ((chan->pcm_fmt & VIA_PCM_FMT_STEREO) ? 2 : 1) * + ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1; + + shift = 0; + while (chan->frag_size) { + chan->frag_size >>= 1; + shift++; + } + chan->frag_size = 1 << shift; + + chan->frag_number = (VIA_DEFAULT_BUFFER_TIME / VIA_DEFAULT_FRAG_TIME); + + DPRINTK ("setting default values %d %d\n", chan->frag_size, chan->frag_number); + } else { + chan->frag_size = 1 << (val & 0xFFFF); + chan->frag_number = (val >> 16) & 0xFFFF; + + DPRINTK ("using user values %d %d\n", chan->frag_size, chan->frag_number); + } + + /* quake3 wants frag_number to be a power of two */ + shift = 0; + while (chan->frag_number) { + chan->frag_number >>= 1; + shift++; + } + chan->frag_number = 1 << shift; + + if (chan->frag_size > VIA_MAX_FRAG_SIZE) + chan->frag_size = VIA_MAX_FRAG_SIZE; + else if (chan->frag_size < VIA_MIN_FRAG_SIZE) + chan->frag_size = VIA_MIN_FRAG_SIZE; + + if (chan->frag_number < VIA_MIN_FRAG_NUMBER) + chan->frag_number = VIA_MIN_FRAG_NUMBER; + + if ((chan->frag_number * chan->frag_size) / PAGE_SIZE > VIA_MAX_BUFFER_DMA_PAGES) + chan->frag_number = (VIA_MAX_BUFFER_DMA_PAGES * PAGE_SIZE) / chan->frag_size; + +out: + if (chan->is_record) + atomic_set (&chan->n_frags, 0); + else + atomic_set (&chan->n_frags, chan->frag_number); + + DPRINTK ("EXIT\n"); + + return 0; +} + +#ifdef VIA_CHAN_DUMP_BUFS +/** + * via_chan_dump_bufs - Display DMA table contents + * @chan: Channel whose DMA table will be displayed + * + * Debugging function which displays the contents of the + * scatter-gather DMA table for the given channel @chan. + */ + +static void via_chan_dump_bufs (struct via_channel *chan) +{ + int i; + + for (i = 0; i < chan->frag_number; i++) { + DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n", + i, chan->sgtable[i].addr, + chan->sgtable[i].count & 0x00FFFFFF, + chan->sgtable[i].count & VIA_FLAG ? 1 : 0, + chan->sgtable[i].count & VIA_EOL ? 1 : 0); + } + DPRINTK ("buf_in_use = %d, nextbuf = %d\n", + atomic_read (&chan->buf_in_use), + atomic_read (&chan->sw_ptr)); +} +#endif /* VIA_CHAN_DUMP_BUFS */ + + +/** + * via_chan_flush_frag - Flush partially-full playback buffer to hardware + * @chan: Channel whose DMA table will be displayed + * + * Flushes partially-full playback buffer to hardware. + */ + +static void via_chan_flush_frag (struct via_channel *chan) +{ + DPRINTK ("ENTER\n"); + + assert (chan->slop_len > 0); + + if (chan->sw_ptr == (chan->frag_number - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + chan->slop_len = 0; + + assert (atomic_read (&chan->n_frags) > 0); + atomic_dec (&chan->n_frags); + + DPRINTK ("EXIT\n"); +} + + + +/** + * via_chan_maybe_start - Initiate audio hardware DMA operation + * @chan: Channel whose DMA is to be started + * + * Initiate DMA operation, if the DMA engine for the given + * channel @chan is not already active. + */ + +static inline void via_chan_maybe_start (struct via_channel *chan) +{ + assert (chan->is_active == sg_active(chan->iobase)); + + if (!chan->is_active && chan->is_enabled) { + chan->is_active = 1; + sg_begin (chan); + DPRINTK ("starting channel %s\n", chan->name); + } +} + + +/**************************************************************** + * + * Interface to ac97-codec module + * + * + */ + +/** + * via_ac97_wait_idle - Wait until AC97 codec is not busy + * @card: Private info for specified board + * + * Sleep until the AC97 codec is no longer busy. + * Returns the final value read from the SGD + * register being polled. + */ + +static u8 via_ac97_wait_idle (struct via_info *card) +{ + u8 tmp8; + int counter = VIA_COUNTER_LIMIT; + + DPRINTK ("ENTER/EXIT\n"); + + assert (card != NULL); + assert (card->pdev != NULL); + + do { + udelay (15); + + tmp8 = inb (card->baseaddr + 0x83); + } while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0)); + + if (tmp8 & VIA_CR83_BUSY) + printk (KERN_WARNING PFX "timeout waiting on AC97 codec\n"); + return tmp8; +} + + +/** + * via_ac97_read_reg - Read AC97 standard register + * @codec: Pointer to generic AC97 codec info + * @reg: Index of AC97 register to be read + * + * Read the value of a single AC97 codec register, + * as defined by the Intel AC97 specification. + * + * Defines the standard AC97 read-register operation + * required by the kernel's ac97_codec interface. + * + * Returns the 16-bit value stored in the specified + * register. + */ + +static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg) +{ + unsigned long data; + struct via_info *card; + int counter; + + DPRINTK ("ENTER\n"); + + assert (codec != NULL); + assert (codec->private_data != NULL); + + card = codec->private_data; + + /* Every time we write to register 80 we cause a transaction. + The only safe way to clear the valid bit is to write it at + the same time as the command */ + data = (reg << 16) | VIA_CR80_READ | VIA_CR80_VALID; + + outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); + udelay (20); + + for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { + udelay (1); + if ((((data = inl(card->baseaddr + VIA_BASE0_AC97_CTRL)) & + (VIA_CR80_VALID|VIA_CR80_BUSY)) == VIA_CR80_VALID)) + goto out; + } + + printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data); + goto err_out; + +out: + /* Once the valid bit has become set, we must wait a complete AC97 + frame before the data has settled. */ + udelay(25); + data = (unsigned long) inl (card->baseaddr + VIA_BASE0_AC97_CTRL); + + outb (0x02, card->baseaddr + 0x83); + + if (((data & 0x007F0000) >> 16) == reg) { + DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n", + data, data & 0x0000FFFF); + return data & 0x0000FFFF; + } + + printk (KERN_WARNING "via82cxxx_audio: not our index: reg=0x%x, newreg=0x%lx\n", + reg, ((data & 0x007F0000) >> 16)); + +err_out: + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/** + * via_ac97_write_reg - Write AC97 standard register + * @codec: Pointer to generic AC97 codec info + * @reg: Index of AC97 register to be written + * @value: Value to be written to AC97 register + * + * Write the value of a single AC97 codec register, + * as defined by the Intel AC97 specification. + * + * Defines the standard AC97 write-register operation + * required by the kernel's ac97_codec interface. + */ + +static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value) +{ + u32 data; + struct via_info *card; + int counter; + + DPRINTK ("ENTER\n"); + + assert (codec != NULL); + assert (codec->private_data != NULL); + + card = codec->private_data; + + data = (reg << 16) + value; + outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); + udelay (10); + + for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { + if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0) + goto out; + + udelay (15); + } + + printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value); + +out: + DPRINTK ("EXIT\n"); +} + + +static int via_mixer_open (struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct via_info *card; + struct pci_dev *pdev; + struct pci_driver *drvr; + + DPRINTK ("ENTER\n"); + + pci_for_each_dev(pdev) { + drvr = pci_dev_driver (pdev); + if (drvr == &via_driver) { + assert (pci_get_drvdata (pdev) != NULL); + + card = pci_get_drvdata (pdev); + if (card->ac97.dev_mixer == minor) + goto match; + } + } + + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + +match: + file->private_data = &card->ac97; + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + +static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = file->private_data; + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; + + DPRINTK ("ENTER\n"); + + assert (codec != NULL); + card = codec->private_data; + assert (card != NULL); + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + rc = codec->mixer_ioctl(codec, cmd, arg); + + up (&card->syscall_sem); + +out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static struct file_operations via_mixer_fops = { + owner: THIS_MODULE, + open: via_mixer_open, + llseek: no_llseek, + ioctl: via_mixer_ioctl, +}; + + +static int __init via_ac97_reset (struct via_info *card) +{ + struct pci_dev *pdev = card->pdev; + u8 tmp8; + u16 tmp16; + + DPRINTK ("ENTER\n"); + + assert (pdev != NULL); + +#ifndef NDEBUG + { + u8 r40,r41,r42,r43,r44,r48; + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x43, &r43); + pci_read_config_byte (card->pdev, 0x44, &r44); + pci_read_config_byte (card->pdev, 0x48, &r48); + DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", + r40,r41,r42,r43,r44,r48); + + spin_lock_irq (&card->lock); + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + spin_unlock_irq (&card->lock); + + } +#endif + + /* + * Reset AC97 controller: enable, disable, enable, + * pausing after each command for good luck. Only + * do this if the codec is not ready, because it causes + * loud pops and such due to such a hard codec reset. + */ + pci_read_config_byte (pdev, VIA_ACLINK_STATUS, &tmp8); + if ((tmp8 & VIA_CR40_AC97_READY) == 0) { + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_AC97_RESET | + VIA_CR41_AC97_WAKEUP); + udelay (100); + + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0); + udelay (100); + + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_PCM_ENABLE | + VIA_CR41_VRA | VIA_CR41_AC97_RESET); + udelay (100); + } + + /* Make sure VRA is enabled, in case we didn't do a + * complete codec reset, above + */ + pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8); + if (((tmp8 & VIA_CR41_VRA) == 0) || + ((tmp8 & VIA_CR41_AC97_ENABLE) == 0) || + ((tmp8 & VIA_CR41_PCM_ENABLE) == 0) || + ((tmp8 & VIA_CR41_AC97_RESET) == 0)) { + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, + VIA_CR41_AC97_ENABLE | + VIA_CR41_PCM_ENABLE | + VIA_CR41_VRA | VIA_CR41_AC97_RESET); + udelay (100); + } + +#if 0 /* this breaks on K7M */ + /* disable legacy stuff */ + pci_write_config_byte (pdev, 0x42, 0x00); + udelay(10); +#endif + + /* route FM trap to IRQ, disable FM trap */ + pci_write_config_byte (pdev, 0x48, 0x05); + udelay(10); + + /* disable all codec GPI interrupts */ + outl (0, pci_resource_start (pdev, 0) + 0x8C); + + /* WARNING: this line is magic. Remove this + * and things break. */ + /* enable variable rate */ + tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + if ((tmp16 & 1) == 0) + via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void via_ac97_codec_wait (struct ac97_codec *codec) +{ + assert (codec->private_data != NULL); + via_ac97_wait_idle (codec->private_data); +} + + +static int __init via_ac97_init (struct via_info *card) +{ + int rc; + u16 tmp16; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + + memset (&card->ac97, 0, sizeof (card->ac97)); + card->ac97.private_data = card; + card->ac97.codec_read = via_ac97_read_reg; + card->ac97.codec_write = via_ac97_write_reg; + card->ac97.codec_wait = via_ac97_codec_wait; + + card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1); + if (card->ac97.dev_mixer < 0) { + printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n"); + DPRINTK ("EXIT, returning -EIO\n"); + return -EIO; + } + + rc = via_ac97_reset (card); + if (rc) { + printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); + goto err_out; + } + + if (ac97_probe_codec (&card->ac97) == 0) { + printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n"); + rc = -EIO; + goto err_out; + } + + /* enable variable rate */ + tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + + /* + * If we cannot enable VRA, we have a locked-rate codec. + * We try again to enable VRA before assuming so, however. + */ + tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + if ((tmp16 & 1) == 0) { + via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); + tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); + if ((tmp16 & 1) == 0) { + card->locked_rate = 1; + printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); + } + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out: + unregister_sound_mixer (card->ac97.dev_mixer); + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static void via_ac97_cleanup (struct via_info *card) +{ + DPRINTK ("ENTER\n"); + + assert (card != NULL); + assert (card->ac97.dev_mixer >= 0); + + unregister_sound_mixer (card->ac97.dev_mixer); + + DPRINTK ("EXIT\n"); +} + + + +/**************************************************************** + * + * Interrupt-related code + * + */ + +/** + * via_intr_channel - handle an interrupt for a single channel + * @chan: handle interrupt for this channel + * + * This is the "meat" of the interrupt handler, + * containing the actions taken each time an interrupt + * occurs. All communication and coordination with + * userspace takes place here. + * + * Locking: inside card->lock + */ + +static void via_intr_channel (struct via_channel *chan) +{ + u8 status; + int n; + + /* check pertinent bits of status register for action bits */ + status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED); + if (!status) + return; + + /* acknowledge any flagged bits ASAP */ + outb (status, chan->iobase); + + if (!chan->sgtable) /* XXX: temporary solution */ + return; + + /* grab current h/w ptr value */ + n = atomic_read (&chan->hw_ptr); + + /* sanity check: make sure our h/w ptr doesn't have a weird value */ + assert (n >= 0); + assert (n < chan->frag_number); + + /* reset SGD data structure in memory to reflect a full buffer, + * and advance the h/w ptr, wrapping around to zero if needed + */ + if (n == (chan->frag_number - 1)) { + chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_EOL); + atomic_set (&chan->hw_ptr, 0); + } else { + chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_FLAG); + atomic_inc (&chan->hw_ptr); + } + + /* accounting crap for SNDCTL_DSP_GETxPTR */ + chan->n_irqs++; + chan->bytes += chan->frag_size; + if (chan->bytes < 0) /* handle overflow of 31-bit value */ + chan->bytes = chan->frag_size; + + /* wake up anyone listening to see when interrupts occur */ + if (waitqueue_active (&chan->wait)) + wake_up_all (&chan->wait); + + DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n", + chan->name, status, (long) inl (chan->iobase + 0x04), + atomic_read (&chan->hw_ptr)); + + /* all following checks only occur when not in mmap(2) mode */ + if (chan->is_mapped) + return; + + /* If we are recording, then n_frags represents the number + * of fragments waiting to be handled by userspace. + * If we are playback, then n_frags represents the number + * of fragments remaining to be filled by userspace. + * We increment here. If we reach max number of fragments, + * this indicates an underrun/overrun. For this case under OSS, + * we stop the record/playback process. + */ + if (atomic_read (&chan->n_frags) < chan->frag_number) + atomic_inc (&chan->n_frags); + assert (atomic_read (&chan->n_frags) <= chan->frag_number); + + if (atomic_read (&chan->n_frags) == chan->frag_number) { + chan->is_active = 0; + via_chan_stop (chan->iobase); + } + + DPRINTK ("%s intr, channel n_frags == %d\n", chan->name, + atomic_read (&chan->n_frags)); +} + + +static void via_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct via_info *card = dev_id; + u32 status32; + + /* to minimize interrupt sharing costs, we use the SGD status + * shadow register to check the status of all inputs and + * outputs with a single 32-bit bus read. If no interrupt + * conditions are flagged, we exit immediately + */ + status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW); + if (!(status32 & VIA_INTR_MASK)) + { +#ifdef CONFIG_MIDI_VIA82CXXX + if (card->midi_devc) + uart401intr(irq, card->midi_devc, regs); +#endif + return; + } + DPRINTK ("intr, status32 == 0x%08X\n", status32); + + /* synchronize interrupt handling under SMP. this spinlock + * goes away completely on UP + */ + spin_lock (&card->lock); + + if (status32 & VIA_INTR_OUT) + via_intr_channel (&card->ch_out); + if (status32 & VIA_INTR_IN) + via_intr_channel (&card->ch_in); + if (status32 & VIA_INTR_FM) + via_intr_channel (&card->ch_fm); + + spin_unlock (&card->lock); +} + + +/** + * via_interrupt_init - Initialize interrupt handling + * @card: Private info for specified board + * + * Obtain and reserve IRQ for using in handling audio events. + * Also, disable any IRQ-generating resources, to make sure + * we don't get interrupts before we want them. + */ + +static int via_interrupt_init (struct via_info *card) +{ + u8 tmp8; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + assert (card->pdev != NULL); + + /* check for sane IRQ number. can this ever happen? */ + if (card->pdev->irq < 2) { + printk (KERN_ERR PFX "insane IRQ %d, aborting\n", + card->pdev->irq); + DPRINTK ("EXIT, returning -EIO\n"); + return -EIO; + } + + /* make sure FM irq is not routed to us */ + pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); + if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { + tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; + pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); + } + + if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { + printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", + card->pdev->irq); + DPRINTK ("EXIT, returning -EBUSY\n"); + return -EBUSY; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/**************************************************************** + * + * OSS DSP device + * + */ + +static struct file_operations via_dsp_fops = { + owner: THIS_MODULE, + open: via_dsp_open, + release: via_dsp_release, + read: via_dsp_read, + write: via_dsp_write, + poll: via_dsp_poll, + llseek: no_llseek, + ioctl: via_dsp_ioctl, + mmap: via_dsp_mmap, +}; + + +static int __init via_dsp_init (struct via_info *card) +{ + u8 tmp8; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + + /* turn off legacy features, if not already */ + pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8); + if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE)) { + tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE); + pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); + } + + via_stop_everything (card); + + card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1); + if (card->dev_dsp < 0) { + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + } + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void via_dsp_cleanup (struct via_info *card) +{ + DPRINTK ("ENTER\n"); + + assert (card != NULL); + assert (card->dev_dsp >= 0); + + via_stop_everything (card); + + unregister_sound_dsp (card->dev_dsp); + + DPRINTK ("EXIT\n"); +} + + +static struct page * via_mm_nopage (struct vm_area_struct * vma, + unsigned long address, int write_access) +{ + struct via_info *card = vma->vm_private_data; + struct via_channel *chan = &card->ch_out; + struct page *dmapage; + unsigned long pgoff; + int rd, wr; + + DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n", + vma->vm_start, + address - vma->vm_start, + (address - vma->vm_start) >> PAGE_SHIFT, + address, + write_access); + + if (address > vma->vm_end) { + DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n"); + return NOPAGE_SIGBUS; /* Disallow mremap */ + } + if (!card) { + DPRINTK ("EXIT, returning NOPAGE_OOM\n"); + return NOPAGE_OOM; /* Nothing allocated */ + } + + pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); + rd = card->ch_in.is_mapped; + wr = card->ch_out.is_mapped; + +#ifndef VIA_NDEBUG + { + unsigned long max_bufs = chan->frag_number; + if (rd && wr) max_bufs *= 2; + /* via_dsp_mmap() should ensure this */ + assert (pgoff < max_bufs); + } +#endif + + /* if full-duplex (read+write) and we have two sets of bufs, + * then the playback buffers come first, sez soundcard.c */ + if (pgoff >= chan->page_number) { + pgoff -= chan->page_number; + chan = &card->ch_in; + } else if (!wr) + chan = &card->ch_in; + + assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0); + + dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr); + DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n", + dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr); + get_page (dmapage); + return dmapage; +} + + +#ifndef VM_RESERVED +static int via_mm_swapout (struct page *page, struct file *filp) +{ + return 0; +} +#endif /* VM_RESERVED */ + + +struct vm_operations_struct via_mm_ops = { + nopage: via_mm_nopage, + +#ifndef VM_RESERVED + swapout: via_mm_swapout, +#endif +}; + + +static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc = -EINVAL, rd=0, wr=0; + unsigned long max_size, size, start, offset; + + assert (file != NULL); + assert (vma != NULL); + card = file->private_data; + assert (card != NULL); + + DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n", + vma->vm_start, + vma->vm_end - vma->vm_start, + vma->vm_pgoff); + + max_size = 0; + if (vma->vm_flags & VM_READ) { + rd = 1; + via_chan_set_buffering(card, &card->ch_in, -1); + via_chan_buffer_init (card, &card->ch_in); + max_size += card->ch_in.page_number << PAGE_SHIFT; + } + if (vma->vm_flags & VM_WRITE) { + wr = 1; + via_chan_set_buffering(card, &card->ch_out, -1); + via_chan_buffer_init (card, &card->ch_out); + max_size += card->ch_out.page_number << PAGE_SHIFT; + } + + start = vma->vm_start; + offset = (vma->vm_pgoff << PAGE_SHIFT); + size = vma->vm_end - vma->vm_start; + + /* some basic size/offset sanity checks */ + if (size > max_size) + goto out; + if (offset > max_size - size) + goto out; + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + vma->vm_ops = &via_mm_ops; + vma->vm_private_data = card; + +#ifdef VM_RESERVED + vma->vm_flags |= VM_RESERVED; +#endif + + if (rd) + card->ch_in.is_mapped = 1; + if (wr) + card->ch_out.is_mapped = 1; + + up (&card->syscall_sem); + rc = 0; + +out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static ssize_t via_dsp_do_read (struct via_info *card, + char *userbuf, size_t count, + int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + const char *orig_userbuf = userbuf; + struct via_channel *chan = &card->ch_in; + size_t size; + int n, tmp; + ssize_t ret = 0; + + /* if SGD has not yet been started, start it */ + via_chan_maybe_start (chan); + +handle_one_block: + /* just to be a nice neighbor */ + /* Thomas Sailer: + * But also to ourselves, release semaphore if we do so */ + if (current->need_resched) { + up(&card->syscall_sem); + schedule (); + ret = via_syscall_down (card, nonblock); + if (ret) + goto out; + } + + /* grab current channel software pointer. In the case of + * recording, this is pointing to the next buffer that + * will receive data from the audio hardware. + */ + n = chan->sw_ptr; + + /* n_frags represents the number of fragments waiting + * to be copied to userland. sleep until at least + * one buffer has been read from the audio hardware. + */ + add_wait_queue(&chan->wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + tmp = atomic_read (&chan->n_frags); + assert (tmp >= 0); + assert (tmp <= chan->frag_number); + if (tmp) + break; + if (nonblock || !chan->is_active) { + ret = -EAGAIN; + break; + } + + up(&card->syscall_sem); + + DPRINTK ("Sleeping on block %d\n", n); + schedule(); + + ret = via_syscall_down (card, nonblock); + if (ret) + break; + + if (signal_pending (current)) { + ret = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&chan->wait, &wait); + if (ret) + goto out; + + /* Now that we have a buffer we can read from, send + * as much as sample data possible to userspace. + */ + while ((count > 0) && (chan->slop_len < chan->frag_size)) { + size_t slop_left = chan->frag_size - chan->slop_len; + void *base = chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr; + unsigned ofs = n % (PAGE_SIZE / chan->frag_size); + + size = (count < slop_left) ? count : slop_left; + if (copy_to_user (userbuf, + base + ofs + chan->slop_len, + size)) { + ret = -EFAULT; + goto out; + } + + count -= size; + chan->slop_len += size; + userbuf += size; + } + + /* If we didn't copy the buffer completely to userspace, + * stop now. + */ + if (chan->slop_len < chan->frag_size) + goto out; + + /* + * If we get to this point, we copied one buffer completely + * to userspace, give the buffer back to the hardware. + */ + + /* advance channel software pointer to point to + * the next buffer from which we will copy + */ + if (chan->sw_ptr == (chan->frag_number - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + /* mark one less buffer waiting to be processed */ + assert (atomic_read (&chan->n_frags) > 0); + atomic_dec (&chan->n_frags); + + /* we are at a block boundary, there is no fragment data */ + chan->slop_len = 0; + + DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n", + n, chan->sw_ptr, atomic_read (&chan->n_frags)); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + + if (count > 0) + goto handle_one_block; + +out: + return (userbuf != orig_userbuf) ? (userbuf - orig_userbuf) : ret; +} + + +static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; + + DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", + file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); + + assert (file != NULL); + assert (buffer != NULL); + card = file->private_data; + assert (card != NULL); + + if (ppos != &file->f_pos) { + DPRINTK ("EXIT, returning -ESPIPE\n"); + return -ESPIPE; + } + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + if (card->ch_in.is_mapped) { + rc = -ENXIO; + goto out_up; + } + + via_chan_set_buffering(card, &card->ch_in, -1); + rc = via_chan_buffer_init (card, &card->ch_in); + + if (rc) + goto out_up; + + rc = via_dsp_do_read (card, buffer, count, nonblock); + +out_up: + up (&card->syscall_sem); +out: + DPRINTK ("EXIT, returning %ld\n",(long) rc); + return rc; +} + + +static ssize_t via_dsp_do_write (struct via_info *card, + const char *userbuf, size_t count, + int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + const char *orig_userbuf = userbuf; + struct via_channel *chan = &card->ch_out; + volatile struct via_sgd_table *sgtable = chan->sgtable; + size_t size; + int n, tmp; + ssize_t ret = 0; + +handle_one_block: + /* just to be a nice neighbor */ + /* Thomas Sailer: + * But also to ourselves, release semaphore if we do so */ + if (current->need_resched) { + up(&card->syscall_sem); + schedule (); + ret = via_syscall_down (card, nonblock); + if (ret) + goto out; + } + + /* grab current channel fragment pointer. In the case of + * playback, this is pointing to the next fragment that + * should receive data from userland. + */ + n = chan->sw_ptr; + + /* n_frags represents the number of fragments remaining + * to be filled by userspace. Sleep until + * at least one fragment is available for our use. + */ + add_wait_queue(&chan->wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + tmp = atomic_read (&chan->n_frags); + assert (tmp >= 0); + assert (tmp <= chan->frag_number); + if (tmp) + break; + if (nonblock || !chan->is_active) { + ret = -EAGAIN; + break; + } + + up(&card->syscall_sem); + + DPRINTK ("Sleeping on page %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record); + schedule(); + + ret = via_syscall_down (card, nonblock); + if (ret) + break; + + if (signal_pending (current)) { + ret = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&chan->wait, &wait); + if (ret) + goto out; + + /* Now that we have at least one fragment we can write to, fill the buffer + * as much as possible with data from userspace. + */ + while ((count > 0) && (chan->slop_len < chan->frag_size)) { + size_t slop_left = chan->frag_size - chan->slop_len; + + size = (count < slop_left) ? count : slop_left; + if (copy_from_user (chan->pgtbl[n / (PAGE_SIZE / chan->frag_size)].cpuaddr + (n % (PAGE_SIZE / chan->frag_size)) * chan->frag_size + chan->slop_len, + userbuf, size)) { + ret = -EFAULT; + goto out; + } + + count -= size; + chan->slop_len += size; + userbuf += size; + } + + /* If we didn't fill up the buffer with data, stop now. + * Put a 'stop' marker in the DMA table too, to tell the + * audio hardware to stop if it gets here. + */ + if (chan->slop_len < chan->frag_size) { + sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP); + goto out; + } + + /* + * If we get to this point, we have filled a buffer with + * audio data, flush the buffer to audio hardware. + */ + + /* Record the true size for the audio hardware to notice */ + if (n == (chan->frag_number - 1)) + sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_EOL); + else + sgtable[n].count = cpu_to_le32 (chan->frag_size | VIA_FLAG); + + /* advance channel software pointer to point to + * the next buffer we will fill with data + */ + if (chan->sw_ptr == (chan->frag_number - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + /* mark one less buffer as being available for userspace consumption */ + assert (atomic_read (&chan->n_frags) > 0); + atomic_dec (&chan->n_frags); + + /* we are at a block boundary, there is no fragment data */ + chan->slop_len = 0; + + /* if SGD has not yet been started, start it */ + via_chan_maybe_start (chan); + + DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n", + n, chan->sw_ptr, atomic_read (&chan->n_frags)); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + + if (count > 0) + goto handle_one_block; + +out: + return userbuf - orig_userbuf; +} + + +static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct via_info *card; + ssize_t rc; + int nonblock = (file->f_flags & O_NONBLOCK); + + DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", + file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); + + assert (file != NULL); + assert (buffer != NULL); + card = file->private_data; + assert (card != NULL); + + if (ppos != &file->f_pos) { + DPRINTK ("EXIT, returning -ESPIPE\n"); + return -ESPIPE; + } + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + if (card->ch_out.is_mapped) { + rc = -ENXIO; + goto out_up; + } + + via_chan_set_buffering(card, &card->ch_out, -1); + rc = via_chan_buffer_init (card, &card->ch_out); + + if (rc) + goto out_up; + + rc = via_dsp_do_write (card, buffer, count, nonblock); + +out_up: + up (&card->syscall_sem); +out: + DPRINTK ("EXIT, returning %ld\n",(long) rc); + return rc; +} + + +static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait) +{ + struct via_info *card; + struct via_channel *chan; + unsigned int mask = 0; + + DPRINTK ("ENTER\n"); + + assert (file != NULL); + card = file->private_data; + assert (card != NULL); + + if (file->f_mode & FMODE_READ) { + chan = &card->ch_in; + if (sg_active (chan->iobase)) + poll_wait(file, &chan->wait, wait); + if (atomic_read (&chan->n_frags) > 0) + mask |= POLLIN | POLLRDNORM; + } + + if (file->f_mode & FMODE_WRITE) { + chan = &card->ch_out; + if (sg_active (chan->iobase)) + poll_wait(file, &chan->wait, wait); + if (atomic_read (&chan->n_frags) > 0) + mask |= POLLOUT | POLLWRNORM; + } + + DPRINTK ("EXIT, returning %u\n", mask); + return mask; +} + + +/** + * via_dsp_drain_playback - sleep until all playback samples are flushed + * @card: Private info for specified board + * @chan: Channel to drain + * @nonblock: boolean, non-zero if O_NONBLOCK is set + * + * Sleeps until all playback has been flushed to the audio + * hardware. + * + * Locking: inside card->syscall_sem + */ + +static int via_dsp_drain_playback (struct via_info *card, + struct via_channel *chan, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + DPRINTK ("ENTER, nonblock = %d\n", nonblock); + + if (chan->slop_len > 0) + via_chan_flush_frag (chan); + + if (atomic_read (&chan->n_frags) == chan->frag_number) + goto out; + + via_chan_maybe_start (chan); + + add_wait_queue(&chan->wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read (&chan->n_frags) >= chan->frag_number) + break; + + if (nonblock) { + DPRINTK ("EXIT, returning -EAGAIN\n"); + ret = -EAGAIN; + break; + } + +#ifdef VIA_DEBUG + { + u8 r40,r41,r42,r43,r44,r48; + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x43, &r43); + pci_read_config_byte (card->pdev, 0x44, &r44); + pci_read_config_byte (card->pdev, 0x48, &r48); + DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", + r40,r41,r42,r43,r44,r48); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + } + + if (!chan->is_active) + printk (KERN_ERR "sleeping but not active\n"); +#endif + + up(&card->syscall_sem); + + DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_frags)); + schedule(); + + if ((ret = via_syscall_down (card, nonblock))) + break; + + if (signal_pending (current)) { + DPRINTK ("EXIT, returning -ERESTARTSYS\n"); + ret = -ERESTARTSYS; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&chan->wait, &wait); + +#ifdef VIA_DEBUG + { + u8 r40,r41,r42,r43,r44,r48; + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x43, &r43); + pci_read_config_byte (card->pdev, 0x44, &r44); + pci_read_config_byte (card->pdev, 0x48, &r48); + DPRINTK ("PCI config: %02X %02X %02X %02X %02X %02X\n", + r40,r41,r42,r43,r44,r48); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + + DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_frags)); + } +#endif + +out: + DPRINTK ("EXIT, returning %d\n", ret); + return ret; +} + + +/** + * via_dsp_ioctl_space - get information about channel buffering + * @card: Private info for specified board + * @chan: pointer to channel-specific info + * @arg: user buffer for returned information + * + * Handles SNDCTL_DSP_GETISPACE and SNDCTL_DSP_GETOSPACE. + * + * Locking: inside card->syscall_sem + */ + +static int via_dsp_ioctl_space (struct via_info *card, + struct via_channel *chan, + void *arg) +{ + audio_buf_info info; + + via_chan_set_buffering(card, chan, -1); + + info.fragstotal = chan->frag_number; + info.fragsize = chan->frag_size; + + /* number of full fragments we can read/write without blocking */ + info.fragments = atomic_read (&chan->n_frags); + + if ((chan->slop_len % chan->frag_size > 0) && (info.fragments > 0)) + info.fragments--; + + /* number of bytes that can be read or written immediately + * without blocking. + */ + info.bytes = (info.fragments * chan->frag_size); + if (chan->slop_len % chan->frag_size > 0) + info.bytes += chan->frag_size - (chan->slop_len % chan->frag_size); + + DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n", + info.fragstotal, + info.fragsize, + info.fragments, + info.bytes); + + return copy_to_user (arg, &info, sizeof (info)); +} + + +/** + * via_dsp_ioctl_ptr - get information about hardware buffer ptr + * @card: Private info for specified board + * @chan: pointer to channel-specific info + * @arg: user buffer for returned information + * + * Handles SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR. + * + * Locking: inside card->syscall_sem + */ + +static int via_dsp_ioctl_ptr (struct via_info *card, + struct via_channel *chan, + void *arg) +{ + count_info info; + + spin_lock_irq (&card->lock); + + info.bytes = chan->bytes; + info.blocks = chan->n_irqs; + chan->n_irqs = 0; + + spin_unlock_irq (&card->lock); + + if (chan->is_active) { + unsigned long extra; + info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size; + extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT); + info.ptr += extra; + info.bytes += extra; + } else { + info.ptr = 0; + } + + DPRINTK ("EXIT, returning bytes=%d, blocks=%d, ptr=%d\n", + info.bytes, + info.blocks, + info.ptr); + + return copy_to_user (arg, &info, sizeof (info)); +} + + +static int via_dsp_ioctl_trigger (struct via_channel *chan, int val) +{ + int enable, do_something; + + if (chan->is_record) + enable = (val & PCM_ENABLE_INPUT); + else + enable = (val & PCM_ENABLE_OUTPUT); + + if (!chan->is_enabled && enable) { + do_something = 1; + } else if (chan->is_enabled && !enable) { + do_something = -1; + } else { + do_something = 0; + } + + DPRINTK ("enable=%d, do_something=%d\n", + enable, do_something); + + if (chan->is_active && do_something) + return -EINVAL; + + if (do_something == 1) { + chan->is_enabled = 1; + via_chan_maybe_start (chan); + DPRINTK ("Triggering input\n"); + } + + else if (do_something == -1) { + chan->is_enabled = 0; + DPRINTK ("Setup input trigger\n"); + } + + return 0; +} + + +static int via_dsp_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc, rd=0, wr=0, val=0; + struct via_info *card; + struct via_channel *chan; + int nonblock = (file->f_flags & O_NONBLOCK); + + assert (file != NULL); + card = file->private_data; + assert (card != NULL); + + if (file->f_mode & FMODE_WRITE) + wr = 1; + if (file->f_mode & FMODE_READ) + rd = 1; + + rc = via_syscall_down (card, nonblock); + if (rc) + return rc; + rc = -EINVAL; + + switch (cmd) { + + /* OSS API version. XXX unverified */ + case OSS_GETVERSION: + DPRINTK ("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n"); + rc = put_user (SOUND_VERSION, (int *)arg); + break; + + /* list of supported PCM data formats */ + case SNDCTL_DSP_GETFMTS: + DPRINTK ("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n"); + rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg); + break; + + /* query or set current channel's PCM data format */ + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SETFMT, val==%d\n", val); + if (val != AFMT_QUERY) { + rc = 0; + + if (rd) + rc = via_chan_set_fmt (card, &card->ch_in, val); + + if (rc >= 0 && wr) + rc = via_chan_set_fmt (card, &card->ch_out, val); + + if (rc < 0) + break; + + val = rc; + } else { + if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) || + (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_16BIT))) + val = AFMT_S16_LE; + else + val = AFMT_U8; + } + DPRINTK ("SETFMT EXIT, returning %d\n", val); + rc = put_user (val, (int *)arg); + break; + + /* query or set number of channels (1=mono, 2=stereo) */ + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_CHANNELS, val==%d\n", val); + if (val != 0) { + rc = 0; + + if (rd) + rc = via_chan_set_stereo (card, &card->ch_in, val); + + if (rc >= 0 && wr) + rc = via_chan_set_stereo (card, &card->ch_out, val); + + if (rc < 0) + break; + + val = rc; + } else { + if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) || + (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO))) + val = 2; + else + val = 1; + } + DPRINTK ("CHANNELS EXIT, returning %d\n", val); + rc = put_user (val, (int *)arg); + break; + + /* enable (val is not zero) or disable (val == 0) stereo */ + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_STEREO, val==%d\n", val); + rc = 0; + + if (rd) + rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1); + if (rc >= 0 && wr) + rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1); + + if (rc < 0) + break; + + val = rc - 1; + + DPRINTK ("STEREO EXIT, returning %d\n", val); + rc = put_user(val, (int *) arg); + break; + + /* query or set sampling rate */ + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SPEED, val==%d\n", val); + if (val < 0) { + rc = -EINVAL; + break; + } + if (val > 0) { + rc = 0; + + if (rd) + rc = via_chan_set_speed (card, &card->ch_in, val); + if (rc >= 0 && wr) + rc = via_chan_set_speed (card, &card->ch_out, val); + + if (rc < 0) + break; + + val = rc; + } else { + if (rd) + val = card->ch_in.rate; + else if (wr) + val = card->ch_out.rate; + else + val = 0; + } + DPRINTK ("SPEED EXIT, returning %d\n", val); + rc = put_user (val, (int *)arg); + break; + + /* wait until all buffers have been played, and then stop device */ + case SNDCTL_DSP_SYNC: + DPRINTK ("DSP_SYNC\n"); + rc = 0; + if (wr) { + DPRINTK ("SYNC EXIT (after calling via_dsp_drain_playback)\n"); + rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); + } + break; + + /* stop recording/playback immediately */ + case SNDCTL_DSP_RESET: + DPRINTK ("DSP_RESET\n"); + if (rd) { + via_chan_clear (card, &card->ch_in); + card->ch_in.frag_number = 0; + card->ch_in.frag_size = 0; + atomic_set(&card->ch_in.n_frags, 0); + } + + if (wr) { + via_chan_clear (card, &card->ch_out); + card->ch_out.frag_number = 0; + card->ch_out.frag_size = 0; + atomic_set(&card->ch_out.n_frags, 0); + } + + rc = 0; + break; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + rc = 0; + break; + + /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */ + case SNDCTL_DSP_GETCAPS: + DPRINTK ("DSP_GETCAPS\n"); + rc = put_user(VIA_DSP_CAP, (int *)arg); + break; + + /* obtain buffer fragment size */ + case SNDCTL_DSP_GETBLKSIZE: + DPRINTK ("DSP_GETBLKSIZE\n"); + + if (rd) { + via_chan_set_buffering(card, &card->ch_in, -1); + rc = put_user(card->ch_in.frag_size, (int *)arg); + } else if (wr) { + via_chan_set_buffering(card, &card->ch_out, -1); + rc = put_user(card->ch_out.frag_size, (int *)arg); + } + break; + + /* obtain information about input buffering */ + case SNDCTL_DSP_GETISPACE: + DPRINTK ("DSP_GETISPACE\n"); + if (rd) + rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg); + break; + + /* obtain information about output buffering */ + case SNDCTL_DSP_GETOSPACE: + DPRINTK ("DSP_GETOSPACE\n"); + if (wr) + rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg); + break; + + /* obtain information about input hardware pointer */ + case SNDCTL_DSP_GETIPTR: + DPRINTK ("DSP_GETIPTR\n"); + if (rd) + rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg); + break; + + /* obtain information about output hardware pointer */ + case SNDCTL_DSP_GETOPTR: + DPRINTK ("DSP_GETOPTR\n"); + if (wr) + rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg); + break; + + /* return number of bytes remaining to be played by DMA engine */ + case SNDCTL_DSP_GETODELAY: + { + DPRINTK ("DSP_GETODELAY\n"); + + chan = &card->ch_out; + + if (!wr) + break; + + if (chan->is_active) { + + val = chan->frag_number - atomic_read (&chan->n_frags); + + if (val > 0) { + val *= chan->frag_size; + val -= chan->frag_size - + inl (chan->iobase + VIA_PCM_BLOCK_COUNT); + } + val += chan->slop_len % chan->frag_size; + } else + val = 0; + + assert (val <= (chan->frag_size * chan->frag_number)); + + DPRINTK ("GETODELAY EXIT, val = %d bytes\n", val); + rc = put_user (val, (int *)arg); + break; + } + + /* handle the quick-start of a channel, + * or the notification that a quick-start will + * occur in the future + */ + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n", + rd, wr, card->ch_in.is_active, card->ch_out.is_active, + card->ch_in.is_enabled, card->ch_out.is_enabled); + + rc = 0; + + if (rd) + rc = via_dsp_ioctl_trigger (&card->ch_in, val); + + if (!rc && wr) + rc = via_dsp_ioctl_trigger (&card->ch_out, val); + + break; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && card->ch_in.is_enabled) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && card->ch_out.is_enabled) + val |= PCM_ENABLE_OUTPUT; + rc = put_user(val, (int *)arg); + break; + + /* Enable full duplex. Since we do this as soon as we are opened + * with O_RDWR, this is mainly a no-op that always returns success. + */ + case SNDCTL_DSP_SETDUPLEX: + DPRINTK ("DSP_SETDUPLEX\n"); + if (!rd || !wr) + break; + rc = 0; + break; + + /* set fragment size. implemented as a successful no-op for now */ + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK ("DSP_SETFRAGMENT, val==%d\n", val); + + if (rd) + rc = via_chan_set_buffering(card, &card->ch_in, val); + + if (wr) + rc = via_chan_set_buffering(card, &card->ch_out, val); + + DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n", + val & 0xFFFF, + val & 0xFFFF, + (val >> 16) & 0xFFFF, + (val >> 16) & 0xFFFF); + + rc = 0; + break; + + /* inform device of an upcoming pause in input (or output). */ + case SNDCTL_DSP_POST: + DPRINTK ("DSP_POST\n"); + if (wr) { + if (card->ch_out.slop_len > 0) + via_chan_flush_frag (&card->ch_out); + via_chan_maybe_start (&card->ch_out); + } + + rc = 0; + break; + + /* not implemented */ + default: + DPRINTK ("unhandled ioctl, cmd==%u, arg==%p\n", + cmd, (void*) arg); + break; + } + + up (&card->syscall_sem); + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static int via_dsp_open (struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct via_info *card; + struct pci_dev *pdev; + struct via_channel *chan; + struct pci_driver *drvr; + int nonblock = (file->f_flags & O_NONBLOCK); + + DPRINTK ("ENTER, minor=%d, file->f_mode=0x%x\n", minor, file->f_mode); + + if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) { + DPRINTK ("EXIT, returning -EINVAL\n"); + return -EINVAL; + } + + card = NULL; + pci_for_each_dev(pdev) { + drvr = pci_dev_driver (pdev); + if (drvr == &via_driver) { + assert (pci_get_drvdata (pdev) != NULL); + + card = pci_get_drvdata (pdev); + DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n", + card->dev_dsp, minor, + (card->dev_dsp ^ minor) & ~0xf); + + if (((card->dev_dsp ^ minor) & ~0xf) == 0) + goto match; + } + } + + DPRINTK ("no matching %s found\n", card ? "minor" : "driver"); + return -ENODEV; + +match: + if (nonblock) { + if (down_trylock (&card->open_sem)) { + DPRINTK ("EXIT, returning -EAGAIN\n"); + return -EAGAIN; + } + } else { + if (down_interruptible (&card->open_sem)) { + DPRINTK ("EXIT, returning -ERESTARTSYS\n"); + return -ERESTARTSYS; + } + } + + file->private_data = card; + DPRINTK ("file->f_mode == 0x%x\n", file->f_mode); + + /* handle input from analog source */ + if (file->f_mode & FMODE_READ) { + chan = &card->ch_in; + + via_chan_init (card, chan); + + /* why is this forced to 16-bit stereo in all drivers? */ + chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; + + via_chan_pcm_fmt (chan, 0); + via_set_rate (&card->ac97, chan, 44100); + } + + /* handle output to analog source */ + if (file->f_mode & FMODE_WRITE) { + chan = &card->ch_out; + + via_chan_init (card, chan); + + if (file->f_mode & FMODE_READ) { + /* if in duplex mode make the recording and playback channels + have the same settings */ + chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; + via_chan_pcm_fmt (chan, 0); + via_set_rate (&card->ac97, chan, 44100); + } else { + if ((minor & 0xf) == SND_DEV_DSP16) { + chan->pcm_fmt = VIA_PCM_FMT_16BIT; + via_chan_pcm_fmt (chan, 0); + via_set_rate (&card->ac97, chan, 44100); + } else { + via_chan_pcm_fmt (chan, 1); + via_set_rate (&card->ac97, chan, 8000); + } + } + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static int via_dsp_release(struct inode *inode, struct file *file) +{ + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; + + DPRINTK ("ENTER\n"); + + assert (file != NULL); + card = file->private_data; + assert (card != NULL); + + rc = via_syscall_down (card, nonblock); + if (rc) { + DPRINTK ("EXIT (syscall_down error), rc=%d\n", rc); + return rc; + } + + if (file->f_mode & FMODE_WRITE) { + rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); + if (rc) + printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc); + + via_chan_free (card, &card->ch_out); + via_chan_buffer_free(card, &card->ch_out); + } + + if (file->f_mode & FMODE_READ) { + via_chan_free (card, &card->ch_in); + via_chan_buffer_free (card, &card->ch_in); + } + + up (&card->syscall_sem); + up (&card->open_sem); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/**************************************************************** + * + * Chip setup and kernel registration + * + * + */ + +static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id *id) +{ +#ifdef CONFIG_MIDI_VIA82CXXX + u8 r42; +#endif + int rc; + struct via_info *card; + static int printed_version = 0; + + DPRINTK ("ENTER\n"); + + if (printed_version++ == 0) + printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n"); + + rc = pci_enable_device (pdev); + if (rc) + goto err_out; + + rc = pci_request_regions (pdev, "via82cxxx_audio"); + if (rc) + goto err_out_disable; + + card = kmalloc (sizeof (*card), GFP_KERNEL); + if (!card) { + printk (KERN_ERR PFX "out of memory, aborting\n"); + rc = -ENOMEM; + goto err_out_res; + } + + pci_set_drvdata (pdev, card); + + memset (card, 0, sizeof (*card)); + card->pdev = pdev; + card->baseaddr = pci_resource_start (pdev, 0); + card->card_num = via_num_cards++; + spin_lock_init (&card->lock); + init_MUTEX (&card->syscall_sem); + init_MUTEX (&card->open_sem); + + /* we must init these now, in case the intr handler needs them */ + via_chan_init_defaults (card, &card->ch_out); + via_chan_init_defaults (card, &card->ch_in); + via_chan_init_defaults (card, &card->ch_fm); + + /* if BAR 2 is present, chip is Rev H or later, + * which means it has a few extra features */ + if (pci_resource_start (pdev, 2) > 0) + card->rev_h = 1; + + if (pdev->irq < 1) { + printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq); + rc = -ENODEV; + goto err_out_kfree; + } + + if (!(pci_resource_flags (pdev, 0) & IORESOURCE_IO)) { + printk (KERN_ERR PFX "unable to locate I/O resources, aborting\n"); + rc = -ENODEV; + goto err_out_kfree; + } + + /* + * init AC97 mixer and codec + */ + rc = via_ac97_init (card); + if (rc) { + printk (KERN_ERR PFX "AC97 init failed, aborting\n"); + goto err_out_kfree; + } + + /* + * init DSP device + */ + rc = via_dsp_init (card); + if (rc) { + printk (KERN_ERR PFX "DSP device init failed, aborting\n"); + goto err_out_have_mixer; + } + + /* + * per-card /proc info + */ + rc = via_card_init_proc (card); + if (rc) { + printk (KERN_ERR PFX "card-specific /proc init failed, aborting\n"); + goto err_out_have_dsp; + } + + /* + * init and turn on interrupts, as the last thing we do + */ + rc = via_interrupt_init (card); + if (rc) { + printk (KERN_ERR PFX "interrupt init failed, aborting\n"); + goto err_out_have_proc; + } + + printk (KERN_INFO PFX "board #%d at 0x%04lX, IRQ %d\n", + card->card_num + 1, card->baseaddr, pdev->irq); + +#ifdef CONFIG_MIDI_VIA82CXXX + /* Disable by default */ + card->midi_info.io_base = 0; + + pci_read_config_byte (pdev, 0x42, &r42); + /* Disable MIDI interrupt */ + pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK); + if (r42 & VIA_CR42_MIDI_ENABLE) + { + if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */ + card->midi_info.io_base = pci_resource_start (pdev, 2); + else /* Address selected by byte 0x43 */ + { + u8 r43; + pci_read_config_byte (pdev, 0x43, &r43); + card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2); + } + + card->midi_info.irq = -pdev->irq; + if (probe_uart401(& card->midi_info, THIS_MODULE)) + { + card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc; + pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK); + printk("Enabled Via MIDI\n"); + } + } +#endif + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_have_proc: + via_card_cleanup_proc (card); + +err_out_have_dsp: + via_dsp_cleanup (card); + +err_out_have_mixer: + via_ac97_cleanup (card); + +err_out_kfree: +#ifndef VIA_NDEBUG + memset (card, 0xAB, sizeof (*card)); /* poison memory */ +#endif + kfree (card); + +err_out_res: + pci_release_regions (pdev); + +err_out_disable: + pci_disable_device (pdev); + +err_out: + pci_set_drvdata (pdev, NULL); + DPRINTK ("EXIT - returning %d\n", rc); + return rc; +} + + +static void __devexit via_remove_one (struct pci_dev *pdev) +{ + struct via_info *card; + + DPRINTK ("ENTER\n"); + + assert (pdev != NULL); + card = pci_get_drvdata (pdev); + assert (card != NULL); + +#ifdef CONFIG_MIDI_VIA82CXXX + if (card->midi_info.io_base) + unload_uart401(&card->midi_info); +#endif + + free_irq (card->pdev->irq, card); + via_card_cleanup_proc (card); + via_dsp_cleanup (card); + via_ac97_cleanup (card); + +#ifndef VIA_NDEBUG + memset (card, 0xAB, sizeof (*card)); /* poison memory */ +#endif + kfree (card); + + pci_set_drvdata (pdev, NULL); + + pci_release_regions (pdev); + pci_disable_device (pdev); + pci_set_power_state (pdev, 3); /* ...zzzzzz */ + + DPRINTK ("EXIT\n"); + return; +} + + +/**************************************************************** + * + * Driver initialization and cleanup + * + * + */ + +static int __init init_via82cxxx_audio(void) +{ + int rc; + + DPRINTK ("ENTER\n"); + + rc = via_init_proc (); + if (rc) { + DPRINTK ("EXIT, returning %d\n", rc); + return rc; + } + + rc = pci_register_driver (&via_driver); + if (rc < 1) { + if (rc == 0) + pci_unregister_driver (&via_driver); + via_cleanup_proc (); + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void __exit cleanup_via82cxxx_audio(void) +{ + DPRINTK ("ENTER\n"); + + pci_unregister_driver (&via_driver); + via_cleanup_proc (); + + DPRINTK ("EXIT\n"); +} + + +module_init(init_via82cxxx_audio); +module_exit(cleanup_via82cxxx_audio); + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("DSP audio and mixer driver for Via 82Cxxx audio devices"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; + + + +#ifdef VIA_PROC_FS + +/**************************************************************** + * + * /proc/driver/via/info + * + * + */ + +static int via_info_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ +#define YN(val,bit) (((val) & (bit)) ? "yes" : "no") +#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable") + + int len = 0; + u8 r40, r41, r42, r44; + struct via_info *card = data; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + + len += sprintf (page+len, VIA_CARD_NAME "\n\n"); + + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x44, &r44); + + len += sprintf (page+len, + "Via 82Cxxx PCI registers:\n" + "\n" + "40 Codec Ready: %s\n" + " Codec Low-power: %s\n" + " Secondary Codec Ready: %s\n" + "\n" + "41 Interface Enable: %s\n" + " De-Assert Reset: %s\n" + " Force SYNC high: %s\n" + " Force SDO high: %s\n" + " Variable Sample Rate On-Demand Mode: %s\n" + " SGD Read Channel PCM Data Out: %s\n" + " FM Channel PCM Data Out: %s\n" + " SB PCM Data Out: %s\n" + "\n" + "42 Game port enabled: %s\n" + " SoundBlaster enabled: %s\n" + " FM enabled: %s\n" + " MIDI enabled: %s\n" + "\n" + "44 AC-Link Interface Access: %s\n" + " Secondary Codec Support: %s\n" + + "\n", + + YN (r40, VIA_CR40_AC97_READY), + YN (r40, VIA_CR40_AC97_LOW_POWER), + YN (r40, VIA_CR40_SECONDARY_READY), + + ED (r41, VIA_CR41_AC97_ENABLE), + YN (r41, (1 << 6)), + YN (r41, (1 << 5)), + YN (r41, (1 << 4)), + ED (r41, (1 << 3)), + ED (r41, (1 << 2)), + ED (r41, (1 << 1)), + ED (r41, (1 << 0)), + + YN (r42, VIA_CR42_GAME_ENABLE), + YN (r42, VIA_CR42_SB_ENABLE), + YN (r42, VIA_CR42_FM_ENABLE), + YN (r42, VIA_CR42_MIDI_ENABLE), + + YN (r44, VIA_CR44_AC_LINK_ACCESS), + YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT) + + ); + + DPRINTK ("EXIT, returning %d\n", len); + return len; + +#undef YN +#undef ED +} + + +/**************************************************************** + * + * /proc/driver/via/... setup and cleanup + * + * + */ + +static int __init via_init_proc (void) +{ + DPRINTK ("ENTER\n"); + + if (!proc_mkdir ("driver/via", 0)) + return -EIO; + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void via_cleanup_proc (void) +{ + DPRINTK ("ENTER\n"); + + remove_proc_entry ("driver/via", NULL); + + DPRINTK ("EXIT\n"); +} + + +static int __init via_card_init_proc (struct via_info *card) +{ + char s[32]; + int rc; + + DPRINTK ("ENTER\n"); + + sprintf (s, "driver/via/%d", card->card_num); + if (!proc_mkdir (s, 0)) { + rc = -EIO; + goto err_out_none; + } + + sprintf (s, "driver/via/%d/info", card->card_num); + if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) { + rc = -EIO; + goto err_out_dir; + } + + sprintf (s, "driver/via/%d/ac97", card->card_num); + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + rc = -EIO; + goto err_out_info; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_info: + sprintf (s, "driver/via/%d/info", card->card_num); + remove_proc_entry (s, NULL); + +err_out_dir: + sprintf (s, "driver/via/%d", card->card_num); + remove_proc_entry (s, NULL); + +err_out_none: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static void via_card_cleanup_proc (struct via_info *card) +{ + char s[32]; + + DPRINTK ("ENTER\n"); + + sprintf (s, "driver/via/%d/ac97", card->card_num); + remove_proc_entry (s, NULL); + + sprintf (s, "driver/via/%d/info", card->card_num); + remove_proc_entry (s, NULL); + + sprintf (s, "driver/via/%d", card->card_num); + remove_proc_entry (s, NULL); + + DPRINTK ("EXIT\n"); +} + +#endif /* VIA_PROC_FS */ diff -Nru linux/sound/oss/vidc.c linux-2.4.19-pre5-mjc/sound/oss/vidc.c --- linux/sound/oss/vidc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/vidc.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,549 @@ +/* + * linux/drivers/sound/vidc.c + * + * Copyright (C) 1997-2000 by Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * VIDC20 audio driver. + * + * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA + * engine. The DMA transfers fixed-format (16-bit little-endian linear) + * samples to the VIDC20, which then transfers this data serially to the + * DACs. The samplerate is controlled by the VIDC. + * + * We currently support a mixer device, but it is currently non-functional. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" +#include "vidc.h" + +#ifndef _SIOC_TYPE +#define _SIOC_TYPE(x) _IOC_TYPE(x) +#endif +#ifndef _SIOC_NR +#define _SIOC_NR(x) _IOC_NR(x) +#endif + +#define VIDC_SOUND_CLOCK (250000) + +/* + * When using SERIAL SOUND mode (external DAC), the number of physical + * channels is fixed at 2. + */ +static int vidc_busy; +static int vidc_adev; +static int vidc_audio_rate; +static char vidc_audio_format; +static char vidc_audio_channels; + +static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = { + 85, /* master */ + 50, /* bass */ + 50, /* treble */ + 0, /* synth */ + 75, /* pcm */ + 0, /* speaker */ + 100, /* ext line */ + 0, /* mic */ + 100, /* CD */ + 0, +}; + +static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = { + 85, /* master */ + 50, /* bass */ + 50, /* treble */ + 0, /* synth */ + 75, /* pcm */ + 0, /* speaker */ + 100, /* ext line */ + 0, /* mic */ + 100, /* CD */ + 0, +}; + +static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */ +static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */ + +static void (*old_mksound)(unsigned int hz, unsigned int ticks); +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); +extern void vidc_update_filler(int bits, int channels); +extern int softoss_dev; + +static void +vidc_mksound(unsigned int hz, unsigned int ticks) +{ +// printk("BEEP - %d %d!\n", hz, ticks); +} + +static void +vidc_mixer_set(int mdev, unsigned int level) +{ + unsigned int lev_l = level & 0x007f; + unsigned int lev_r = (level & 0x7f00) >> 8; + unsigned int mlev_l, mlev_r; + + if (lev_l > 100) + lev_l = 100; + if (lev_r > 100) + lev_r = 100; + +#define SCALE(lev,master) ((lev) * (master) * 65536 / 10000) + + mlev_l = vidc_level_l[SOUND_MIXER_VOLUME]; + mlev_r = vidc_level_r[SOUND_MIXER_VOLUME]; + + switch (mdev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_PCM: + vidc_level_l[mdev] = lev_l; + vidc_level_r[mdev] = lev_r; + + vidc_audio_volume_l = SCALE(lev_l, mlev_l); + vidc_audio_volume_r = SCALE(lev_r, mlev_r); +/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/ + break; + } +#undef SCALE +} + +static int vidc_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + unsigned int val; + unsigned int mdev; + + if (_SIOC_TYPE(cmd) != 'M') + return -EINVAL; + + mdev = _SIOC_NR(cmd); + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + if (get_user(val, (unsigned int *)arg)) + return -EFAULT; + + if (mdev < SOUND_MIXER_NRDEVICES) + vidc_mixer_set(mdev, val); + else + return -EINVAL; + } + + /* + * Return parameters + */ + switch (mdev) { + case SOUND_MIXER_RECSRC: + val = 0; + break; + + case SOUND_MIXER_DEVMASK: + val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; + break; + + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; + break; + + case SOUND_MIXER_RECMASK: + val = 0; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + default: + if (mdev < SOUND_MIXER_NRDEVICES) + val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8; + else + return -EINVAL; + } + + return put_user(val, (unsigned int *)arg) ? -EFAULT : 0; +} + +static unsigned int vidc_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + default: + fmt = AFMT_S16_LE; + case AFMT_U8: + case AFMT_S8: + case AFMT_S16_LE: + vidc_audio_format = fmt; + vidc_update_filler(vidc_audio_format, vidc_audio_channels); + case AFMT_QUERY: + break; + } + return vidc_audio_format; +} + +static int vidc_audio_set_speed(int dev, int rate) +{ + if (rate) { + unsigned int hwctrl, hwrate; + unsigned int newsize, new2size; + + /* + * If we have selected 44.1kHz, use the DAC clock. + */ + if (0 && rate == 44100) { + hwctrl = 0x00000002; + hwrate = 3; + } else { + hwctrl = 0x00000003; + + hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; + if (hwrate < 3) + hwrate = 3; + if (hwrate > 255) + hwrate = 255; + + rate = VIDC_SOUND_CLOCK / hwrate; + } + + vidc_writel(0xb0000000 | (hwrate - 2)); + vidc_writel(0xb1000000 | hwctrl); + + newsize = (10000 / hwrate) & ~3; + if (newsize < 208) + newsize = 208; + if (newsize > 4096) + newsize = 4096; + for (new2size = 128; new2size < newsize; new2size <<= 1); + if (new2size - newsize > newsize - (new2size >> 1)) + new2size >>= 1; + if (new2size > 4096) { + printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", + newsize, new2size); + new2size = 4096; + } + dma_bufsize = new2size; + vidc_audio_rate = rate; + } + return vidc_audio_rate; +} + +static short vidc_audio_set_channels(int dev, short channels) +{ + switch (channels) { + default: + channels = 2; + case 1: + case 2: + vidc_audio_channels = channels; + vidc_update_filler(vidc_audio_format, vidc_audio_channels); + case 0: + break; + } + return vidc_audio_channels; +} + +/* + * Open the device + */ +static int vidc_audio_open(int dev, int mode) +{ + /* This audio device does not have recording capability */ + if (mode == OPEN_READ) + return -EPERM; + + if (vidc_busy) + return -EBUSY; + + vidc_busy = 1; + return 0; +} + +/* + * Close the device + */ +static void vidc_audio_close(int dev) +{ + vidc_busy = 0; +} + +/* + * Output a block via DMA to sound device. + * + * We just set the DMA start and count; the DMA interrupt routine + * will take care of formatting the samples (via the appropriate + * vidc_filler routine), and flag via vidc_audio_dma_interrupt when + * more data is required. + */ +static void +vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + unsigned long flags; + + local_irq_save(flags); + dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf; + dma_count = total_count; + local_irq_restore(flags); +} + +static void +vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag) +{ +} + +static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + return -EINVAL; +} + +static void vidc_audio_dma_interrupt(void) +{ + DMAbuf_outputintr(vidc_adev, 1); +} + +/* + * Prepare for outputting samples. + * + * Each buffer that will be passed will be `bsize' bytes long, + * with a total of `bcount' buffers. + */ +static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + struct audio_operations *adev = audio_devs[dev]; + + dma_interrupt = NULL; + adev->dmap_out->flags |= DMA_NODMA; + + return 0; +} + +/* + * Stop our current operation. + */ +static void vidc_audio_reset(int dev) +{ + dma_interrupt = NULL; +} + +static int vidc_audio_local_qlen(int dev) +{ + return /*dma_count !=*/ 0; +} + +static void vidc_audio_trigger(int dev, int enable_bits) +{ + struct audio_operations *adev = audio_devs[dev]; + + if (enable_bits & PCM_ENABLE_OUTPUT) { + if (!(adev->flags & DMA_ACTIVE)) { + unsigned long flags; + + local_irq_save(flags); + + /* prevent recusion */ + adev->flags |= DMA_ACTIVE; + + dma_interrupt = vidc_audio_dma_interrupt; + vidc_sound_dma_irq(0, NULL, NULL); + iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR); + + local_irq_restore(flags); + } + } +} + +static struct audio_driver vidc_audio_driver = +{ + owner: THIS_MODULE, + open: vidc_audio_open, + close: vidc_audio_close, + output_block: vidc_audio_output_block, + start_input: vidc_audio_start_input, + prepare_for_input: vidc_audio_prepare_for_input, + prepare_for_output: vidc_audio_prepare_for_output, + halt_io: vidc_audio_reset, + local_qlen: vidc_audio_local_qlen, + trigger: vidc_audio_trigger, + set_speed: vidc_audio_set_speed, + set_bits: vidc_audio_set_format, + set_channels: vidc_audio_set_channels +}; + +static struct mixer_operations vidc_mixer_operations = { + owner: THIS_MODULE, + id: "VIDC", + name: "VIDCsound", + ioctl: vidc_mixer_ioctl +}; + +void vidc_update_filler(int format, int channels) +{ +#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) + + switch (TYPE(format, channels)) { + default: + case TYPE(AFMT_U8, 1): + vidc_filler = vidc_fill_1x8_u; + break; + + case TYPE(AFMT_U8, 2): + vidc_filler = vidc_fill_2x8_u; + break; + + case TYPE(AFMT_S8, 1): + vidc_filler = vidc_fill_1x8_s; + break; + + case TYPE(AFMT_S8, 2): + vidc_filler = vidc_fill_2x8_s; + break; + + case TYPE(AFMT_S16_LE, 1): + vidc_filler = vidc_fill_1x16_s; + break; + + case TYPE(AFMT_S16_LE, 2): + vidc_filler = vidc_fill_2x16_s; + break; + } +} + +static void __init attach_vidc(struct address_info *hw_config) +{ + char name[32]; + int i, adev; + + sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype); + conf_printf(name, hw_config); + memset(dma_buf, 0, sizeof(dma_buf)); + + adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name, + &vidc_audio_driver, sizeof(vidc_audio_driver), + DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE, + NULL, hw_config->dma, hw_config->dma2); + + if (adev < 0) + goto audio_failed; + + /* + * 1024 bytes => 64 buffers + */ + audio_devs[adev]->min_fragment = 10; + audio_devs[adev]->mixer_dev = num_mixers; + + audio_devs[adev]->mixer_dev = + sound_install_mixer(MIXER_DRIVER_VERSION, + name, &vidc_mixer_operations, + sizeof(vidc_mixer_operations), NULL); + + if (audio_devs[adev]->mixer_dev < 0) + goto mixer_failed; + + for (i = 0; i < 2; i++) { + dma_buf[i] = get_free_page(GFP_KERNEL); + if (!dma_buf[i]) { + printk(KERN_ERR "%s: can't allocate required buffers\n", + name); + goto mem_failed; + } + dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]); + } + + if (sound_alloc_dma(hw_config->dma, hw_config->name)) { + printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma); + goto dma_failed; + } + + if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0, + hw_config->name, &dma_start)) { + printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq); + goto irq_failed; + } + old_mksound = kd_mksound; + kd_mksound = vidc_mksound; + vidc_adev = adev; + vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8)); + +#if defined(CONFIG_SOUND_ALSA_SOFTOSS) || defined(CONFIG_SOUND_ALSA_SOFTOSS_MODULE) + softoss_dev = adev; +#endif + return; + +irq_failed: + sound_free_dma(hw_config->dma); +dma_failed: +mem_failed: + for (i = 0; i < 2; i++) + free_page(dma_buf[i]); + sound_unload_mixerdev(audio_devs[adev]->mixer_dev); +mixer_failed: + sound_unload_audiodev(adev); +audio_failed: + return; +} + +static int __init probe_vidc(struct address_info *hw_config) +{ + hw_config->irq = IRQ_DMAS0; + hw_config->dma = DMA_VIRTUAL_SOUND; + hw_config->dma2 = -1; + hw_config->card_subtype = 16; + hw_config->name = "VIDC20"; + return 1; +} + +static void __exit unload_vidc(struct address_info *hw_config) +{ + int i, adev = vidc_adev; + + vidc_adev = -1; + + if (old_mksound) + kd_mksound = old_mksound; + + free_irq(hw_config->irq, &dma_start); + sound_free_dma(hw_config->dma); + + if (adev >= 0) { + sound_unload_mixerdev(audio_devs[adev]->mixer_dev); + sound_unload_audiodev(adev); + for (i = 0; i < 2; i++) + free_page(dma_buf[i]); + } +} + +static struct address_info cfg; + +static int __init init_vidc(void) +{ + if (probe_vidc(&cfg) == 0) + return -ENODEV; + + attach_vidc(&cfg); + + return 0; +} + +static void __exit cleanup_vidc(void) +{ + unload_vidc(&cfg); +} + +module_init(init_vidc); +module_exit(cleanup_vidc); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("VIDC20 audio driver"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -Nru linux/sound/oss/vidc.h linux-2.4.19-pre5-mjc/sound/oss/vidc.h --- linux/sound/oss/vidc.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/vidc.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,67 @@ +/* + * linux/drivers/sound/vidc.h + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * VIDC sound function prototypes + */ + +/* vidc.c */ + +extern int vidc_busy; + +/* vidc_fill.S */ + +/* + * Filler routines for different channels and sample sizes + */ + +extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); +extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); + +/* + * DMA Interrupt handler + */ + +extern void vidc_sound_dma_irq(int irqnr, void *ref, struct pt_regs *regs); + +/* + * Filler routine pointer + */ + +extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend, + unsigned long obuf, int mask); + +/* + * Virtual DMA buffer exhausted + */ + +extern void (*dma_interrupt) (void); + +/* + * Virtual DMA buffer addresses + */ + +extern unsigned long dma_start, dma_count, dma_bufsize; +extern unsigned long dma_buf[2], dma_pbuf[2]; + +/* vidc_synth.c */ + +extern void vidc_synth_init(struct address_info *hw_config); +extern void vidc_synth_exit(struct address_info *hw_config); +extern int vidc_synth_get_volume(void); +extern int vidc_synth_set_volume(int vol); diff -Nru linux/sound/oss/vidc_fill.S linux-2.4.19-pre5-mjc/sound/oss/vidc_fill.S --- linux/sound/oss/vidc_fill.S Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/vidc_fill.S Mon Apr 8 22:31:23 2002 @@ -0,0 +1,218 @@ +/* + * linux/drivers/sound/vidc_fill.S + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Filler routines for DMA buffers + */ +#define __ASSEMBLY__ +#include +#include +#include +#include + + .text + +ENTRY(vidc_fill_1x8_u) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldrb r4, [r0], #1 + eor r4, r4, #0x80 + and r4, ip, r4, lsl #8 + orr r4, r4, r4, lsl #16 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_2x8_u) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r4, [r0], #2 + and r5, r4, ip + and r4, ip, r4, lsl #8 + orr r4, r4, r5, lsl #16 + orr r4, r4, r4, lsr #8 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_1x8_s) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldrb r4, [r0], #1 + and r4, ip, r4, lsl #8 + orr r4, r4, r4, lsl #16 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_2x8_s) + mov ip, #0xff00 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r4, [r0], #2 + and r5, r4, ip + and r4, ip, r4, lsl #8 + orr r4, r4, r5, lsl #16 + orr r4, r4, r4, lsr #8 + str r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_1x16_s) + mov ip, #0xff00 + orr ip, ip, ip, lsr #8 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r5, [r0], #2 + and r4, r5, ip + orr r4, r4, r4, lsl #16 + str r4, [r2], #4 + cmp r0, r1 + addlt r0, r0, #2 + andlt r4, r5, ip, lsl #16 + orrlt r4, r4, r4, lsr #16 + strlt r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_2x16_s) + mov ip, #0xff00 + orr ip, ip, ip, lsr #8 +1: cmp r0, r1 + bge SYMBOL_NAME(vidc_clear) + ldr r4, [r0], #4 + str r4, [r2], #4 + cmp r0, r1 + ldrlt r4, [r0], #4 + strlt r4, [r2], #4 + cmp r2, r3 + blt 1b + mov pc, lr + +ENTRY(vidc_fill_noaudio) + mov r0, #0 + mov r1, #0 +2: mov r4, #0 + mov r5, #0 +1: cmp r2, r3 + stmltia r2!, {r0, r1, r4, r5} + blt 1b + mov pc, lr + +ENTRY(vidc_clear) + mov r0, #0 + mov r1, #0 + tst r2, #4 + str r0, [r2], #4 + tst r2, #8 + stmia r2!, {r0, r1} + b 2b + +/* + * Call filler routines with: + * r0 = phys address + * r1 = phys end + * r2 = buffer + * Returns: + * r0 = new buffer address + * r2 = new buffer finish + * r4 = corrupted + * r5 = corrupted + * ip = corrupted + */ + +ENTRY(vidc_sound_dma_irq) + stmfd sp!, {r4 - r8, lr} + ldr r8, =SYMBOL_NAME(dma_start) + ldmia r8, {r0, r1, r2, r3, r4, r5} + teq r1, #0 + adreq r4, SYMBOL_NAME(vidc_fill_noaudio) + moveq r7, #1 << 31 + movne r7, #0 + mov ip, #IOMD_BASE & 0xff000000 + orr ip, ip, #IOMD_BASE & 0x00ff0000 + ldrb r6, [ip, #IOMD_SD0ST] + tst r6, #DMA_ST_OFL @ Check for overrun + eorne r6, r6, #DMA_ST_AB + tst r6, #DMA_ST_AB + moveq r2, r3 @ DMAing A, update B + add r3, r2, r5 @ End of DMA buffer + add r1, r1, r0 @ End of virtual DMA buffer + mov lr, pc + mov pc, r4 @ Call fill routine (uses r4, ip) + sub r1, r1, r0 @ Remaining length + stmia r8, {r0, r1} + mov r0, #0 + tst r2, #4 @ Round buffer up to 4 words + strne r0, [r2], #4 + tst r2, #8 + strne r0, [r2], #4 + strne r0, [r2], #4 + sub r2, r2, #16 + mov r2, r2, lsl #20 + movs r2, r2, lsr #20 + orreq r2, r2, #1 << 30 @ Set L bit + orr r2, r2, r7 + ldmdb r8, {r3, r4, r5} + tst r6, #DMA_ST_AB + mov ip, #IOMD_BASE & 0xff000000 + orr ip, ip, #IOMD_BASE & 0x00ff0000 + streq r4, [ip, #IOMD_SD0CURB] + strne r5, [ip, #IOMD_SD0CURA] + streq r2, [ip, #IOMD_SD0ENDB] + strne r2, [ip, #IOMD_SD0ENDA] + ldr lr, [ip, #IOMD_SD0ST] + tst lr, #DMA_ST_OFL + bne 1f + tst r6, #DMA_ST_AB + strne r4, [ip, #IOMD_SD0CURB] + streq r5, [ip, #IOMD_SD0CURA] + strne r2, [ip, #IOMD_SD0ENDB] + streq r2, [ip, #IOMD_SD0ENDA] +1: teq r7, #0 + mov r0, #0x10 + strneb r0, [ip, #IOMD_SD0CR] + ldmfd sp!, {r4 - r8, lr} + teq r1, #0 @ If we have no more + movne pc, lr + teq r3, #0 + movne pc, r3 @ Call interrupt routine + mov pc, lr + + .data + .globl SYMBOL_NAME(dma_interrupt) +SYMBOL_NAME(dma_interrupt): + .long 0 @ r3 + .globl SYMBOL_NAME(dma_pbuf) +SYMBOL_NAME(dma_pbuf): + .long 0 @ r4 + .long 0 @ r5 + .globl SYMBOL_NAME(dma_start) +SYMBOL_NAME(dma_start): + .long 0 @ r0 + .globl SYMBOL_NAME(dma_count) +SYMBOL_NAME(dma_count): + .long 0 @ r1 + .globl SYMBOL_NAME(dma_buf) +SYMBOL_NAME(dma_buf): + .long 0 @ r2 + .long 0 @ r3 + .globl SYMBOL_NAME(vidc_filler) +SYMBOL_NAME(vidc_filler): + .long SYMBOL_NAME(vidc_fill_noaudio) @ r4 + .globl SYMBOL_NAME(dma_bufsize) +SYMBOL_NAME(dma_bufsize): + .long 0x1000 @ r5 diff -Nru linux/sound/oss/vwsnd.c linux-2.4.19-pre5-mjc/sound/oss/vwsnd.c --- linux/sound/oss/vwsnd.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/vwsnd.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,3477 @@ +/* + * Sound driver for Silicon Graphics 320 and 540 Visual Workstations' + * onboard audio. See notes in ../../Documentation/sound/vwsnd . + * + * Copyright 1999 Silicon Graphics, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef VWSND_DEBUG /* define for debugging */ + +/* + * XXX to do - + * + * External sync. + * Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational. + * Bug - if select() called before read(), pcm_setup() not called. + * Bug - output doesn't stop soon enough if process killed. + */ + +/* + * Things to test - + * + * Will readv/writev work? Write a test. + * + * insmod/rmmod 100 million times. + * + * Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT + * rate). + * + * Concurrent threads banging on mixer simultaneously, both UP + * and SMP kernels. Especially, watch for thread A changing + * OUTSRC while thread B changes gain -- both write to the same + * ad1843 register. + * + * What happens if a client opens /dev/audio then forks? + * Do two procs have /dev/audio open? Test. + * + * Pump audio through the CD, MIC and line inputs and verify that + * they mix/mute into the output. + * + * Apps: + * amp + * mpg123 + * x11amp + * mxv + * kmedia + * esound + * need more input apps + * + * Run tests while bombarding with signals. setitimer(2) will do it... */ + +/* + * This driver is organized in nine sections. + * The nine sections are: + * + * debug stuff + * low level lithium access + * high level lithium access + * AD1843 access + * PCM I/O + * audio driver + * mixer driver + * probe/attach/unload + * initialization and loadable kernel module interface + * + * That is roughly the order of increasing abstraction, so forward + * dependencies are minimal. + */ + +/* + * Locking Notes + * + * INC_USE_COUNT and DEC_USE_COUNT keep track of the number of + * open descriptors to this driver. They store it in vwsnd_use_count. + * The global device list, vwsnd_dev_list, is immutable when the IN_USE + * is true. + * + * devc->open_lock is a semaphore that is used to enforce the + * single reader/single writer rule for /dev/audio. The rule is + * that each device may have at most one reader and one writer. + * Open will block until the previous client has closed the + * device, unless O_NONBLOCK is specified. + * + * The semaphore devc->io_sema serializes PCM I/O syscalls. This + * is unnecessary in Linux 2.2, because the kernel lock + * serializes read, write, and ioctl globally, but it's there, + * ready for the brave, new post-kernel-lock world. + * + * Locking between interrupt and baselevel is handled by the + * "lock" spinlock in vwsnd_port (one lock each for read and + * write). Each half holds the lock just long enough to see what + * area it owns and update its pointers. See pcm_output() and + * pcm_input() for most of the gory stuff. + * + * devc->mix_sema serializes all mixer ioctls. This is also + * redundant because of the kernel lock. + * + * The lowest level lock is lith->lithium_lock. It is a + * spinlock which is held during the two-register tango of + * reading/writing an AD1843 register. See + * li_{read,write}_ad1843_reg(). + */ + +/* + * Sample Format Notes + * + * Lithium's DMA engine has two formats: 16-bit 2's complement + * and 8-bit unsigned . 16-bit transfers the data unmodified, 2 + * bytes per sample. 8-bit unsigned transfers 1 byte per sample + * and XORs each byte with 0x80. Lithium can input or output + * either mono or stereo in either format. + * + * The AD1843 has four formats: 16-bit 2's complement, 8-bit + * unsigned, 8-bit mu-Law and 8-bit A-Law. + * + * This driver supports five formats: AFMT_S8, AFMT_U8, + * AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE. + * + * For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and + * rely on Lithium's XOR to translate between U8 and S8. + * + * For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR + * the 0x80 bit in software to compensate for Lithium's XOR. + * This happens in pcm_copy_{in,out}(). + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added some __init/__exit + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" + +/*****************************************************************************/ +/* debug stuff */ + +#ifdef VWSND_DEBUG + +#include /* for in_interrupt() */ + +static int shut_up = 1; + +/* + * dbgassert - called when an assertion fails. + */ + +static void dbgassert(const char *fcn, int line, const char *expr) +{ + if (in_interrupt()) + panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n", + __FILE__, fcn, line, expr); + else { + int x; + printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n", + __FILE__, fcn, line, expr); + x = * (volatile int *) 0; /* force proc to exit */ + } +} + +/* + * Bunch of useful debug macros: + * + * ASSERT - print unless e nonzero (panic if in interrupt) + * DBGDO - include arbitrary code if debugging + * DBGX - debug print raw (w/o function name) + * DBGP - debug print w/ function name + * DBGE - debug print function entry + * DBGC - debug print function call + * DBGR - debug print function return + * DBGXV - debug print raw when verbose + * DBGPV - debug print when verbose + * DBGEV - debug print function entry when verbose + * DBGRV - debug print function return when verbose + */ + +#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e)) +#define DBGDO(x) x +#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args)) +#define DBGP(fmt, args...) (DBGX(__FUNCTION__ ": " fmt, ##args)) +#define DBGE(fmt, args...) (DBGX(__FUNCTION__ fmt, ##args)) +#define DBGC(rtn) (DBGP("calling %s\n", rtn)) +#define DBGR() (DBGP("returning\n")) +#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args)) +#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args)) +#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args)) +#define DBGCV(rtn) (shut_up ? 0 : DBGC(rtn)) +#define DBGRV() (shut_up ? 0 : DBGR()) + +#else /* !VWSND_DEBUG */ + +#define ASSERT(e) ((void) 0) +#define DBGDO(x) /* don't */ +#define DBGX(fmt, args...) ((void) 0) +#define DBGP(fmt, args...) ((void) 0) +#define DBGE(fmt, args...) ((void) 0) +#define DBGC(rtn) ((void) 0) +#define DBGR() ((void) 0) +#define DBGPV(fmt, args...) ((void) 0) +#define DBGXV(fmt, args...) ((void) 0) +#define DBGEV(fmt, args...) ((void) 0) +#define DBGCV(rtn) ((void) 0) +#define DBGRV() ((void) 0) + +#endif /* !VWSND_DEBUG */ + +/*****************************************************************************/ +/* low level lithium access */ + +/* + * We need to talk to Lithium registers on three pages. Here are + * the pages' offsets from the base address (0xFF001000). + */ + +enum { + LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */ + LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */ + LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */ +}; + +/* low-level lithium data */ + +typedef struct lithium { + caddr_t page0; /* virtual addresses */ + caddr_t page1; + caddr_t page2; + spinlock_t lock; /* protects codec and UST/MSC access */ +} lithium_t; + +/* + * li_create initializes the lithium_t structure and sets up vm mappings + * to access the registers. + * Returns 0 on success, -errno on failure. + */ + +static int li_create(lithium_t *lith, unsigned long baseaddr) +{ + static void li_destroy(lithium_t *); + + lith->lock = SPIN_LOCK_UNLOCKED; + lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE); + lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE); + lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE); + if (!lith->page0 || !lith->page1 || !lith->page2) { + li_destroy(lith); + return -ENOMEM; + } + return 0; +} + +/* + * li_destroy destroys the lithium_t structure and vm mappings. + */ + +static void li_destroy(lithium_t *lith) +{ + if (lith->page0) { + iounmap(lith->page0); + lith->page0 = NULL; + } + if (lith->page1) { + iounmap(lith->page1); + lith->page1 = NULL; + } + if (lith->page2) { + iounmap(lith->page2); + lith->page2 = NULL; + } +} + +/* + * basic register accessors - read/write long/byte + */ + +static __inline__ unsigned long li_readl(lithium_t *lith, int off) +{ + return * (volatile unsigned long *) (lith->page0 + off); +} + +static __inline__ unsigned char li_readb(lithium_t *lith, int off) +{ + return * (volatile unsigned char *) (lith->page0 + off); +} + +static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val) +{ + * (volatile unsigned long *) (lith->page0 + off) = val; +} + +static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val) +{ + * (volatile unsigned char *) (lith->page0 + off) = val; +} + +/*****************************************************************************/ +/* High Level Lithium Access */ + +/* + * Lithium DMA Notes + * + * Lithium has two dedicated DMA channels for audio. They are known + * as comm1 and comm2 (communication areas 1 and 2). Comm1 is for + * input, and comm2 is for output. Each is controlled by three + * registers: BASE (base address), CFG (config) and CCTL + * (config/control). + * + * Each DMA channel points to a physically contiguous ring buffer in + * main memory of up to 8 Kbytes. (This driver always uses 8 Kb.) + * There are three pointers into the ring buffer: read, write, and + * trigger. The pointers are 8 bits each. Each pointer points to + * 32-byte "chunks" of data. The DMA engine moves 32 bytes at a time, + * so there is no finer-granularity control. + * + * In comm1, the hardware updates the write ptr, and software updates + * the read ptr. In comm2, it's the opposite: hardware updates the + * read ptr, and software updates the write ptr. I designate the + * hardware-updated ptr as the hwptr, and the software-updated ptr as + * the swptr. + * + * The trigger ptr and trigger mask are used to trigger interrupts. + * From the Lithium spec, section 5.6.8, revision of 12/15/1998: + * + * Trigger Mask Value + * + * A three bit wide field that represents a power of two mask + * that is used whenever the trigger pointer is compared to its + * respective read or write pointer. A value of zero here + * implies a mask of 0xFF and a value of seven implies a mask + * 0x01. This value can be used to sub-divide the ring buffer + * into pie sections so that interrupts monitor the progress of + * hardware from section to section. + * + * My interpretation of that is, whenever the hw ptr is updated, it is + * compared with the trigger ptr, and the result is masked by the + * trigger mask. (Actually, by the complement of the trigger mask.) + * If the result is zero, an interrupt is triggered. I.e., interrupt + * if ((hwptr & ~mask) == (trptr & ~mask)). The mask is formed from + * the trigger register value as mask = (1 << (8 - tmreg)) - 1. + * + * In yet different words, setting tmreg to 0 causes an interrupt after + * every 256 DMA chunks (8192 bytes) or once per traversal of the + * ring buffer. Setting it to 7 caues an interrupt every 2 DMA chunks + * (64 bytes) or 128 times per traversal of the ring buffer. + */ + +/* Lithium register offsets and bit definitions */ + +#define LI_HOST_CONTROLLER 0x000 +# define LI_HC_RESET 0x00008000 +# define LI_HC_LINK_ENABLE 0x00004000 +# define LI_HC_LINK_FAILURE 0x00000004 +# define LI_HC_LINK_CODEC 0x00000002 +# define LI_HC_LINK_READY 0x00000001 + +#define LI_INTR_STATUS 0x010 +#define LI_INTR_MASK 0x014 +# define LI_INTR_LINK_ERR 0x00008000 +# define LI_INTR_COMM2_TRIG 0x00000008 +# define LI_INTR_COMM2_UNDERFLOW 0x00000004 +# define LI_INTR_COMM1_TRIG 0x00000002 +# define LI_INTR_COMM1_OVERFLOW 0x00000001 + +#define LI_CODEC_COMMAND 0x018 +# define LI_CC_BUSY 0x00008000 +# define LI_CC_DIR 0x00000080 +# define LI_CC_DIR_RD LI_CC_DIR +# define LI_CC_DIR_WR (!LI_CC_DIR) +# define LI_CC_ADDR_MASK 0x0000007F + +#define LI_CODEC_DATA 0x01C + +#define LI_COMM1_BASE 0x100 +#define LI_COMM1_CTL 0x104 +# define LI_CCTL_RESET 0x80000000 +# define LI_CCTL_SIZE 0x70000000 +# define LI_CCTL_DMA_ENABLE 0x08000000 +# define LI_CCTL_TMASK 0x07000000 /* trigger mask */ +# define LI_CCTL_TPTR 0x00FF0000 /* trigger pointer */ +# define LI_CCTL_RPTR 0x0000FF00 +# define LI_CCTL_WPTR 0x000000FF +#define LI_COMM1_CFG 0x108 +# define LI_CCFG_LOCK 0x00008000 +# define LI_CCFG_SLOT 0x00000070 +# define LI_CCFG_DIRECTION 0x00000008 +# define LI_CCFG_DIR_IN (!LI_CCFG_DIRECTION) +# define LI_CCFG_DIR_OUT LI_CCFG_DIRECTION +# define LI_CCFG_MODE 0x00000004 +# define LI_CCFG_MODE_MONO (!LI_CCFG_MODE) +# define LI_CCFG_MODE_STEREO LI_CCFG_MODE +# define LI_CCFG_FORMAT 0x00000003 +# define LI_CCFG_FMT_8BIT 0x00000000 +# define LI_CCFG_FMT_16BIT 0x00000001 +#define LI_COMM2_BASE 0x10C +#define LI_COMM2_CTL 0x110 + /* bit definitions are the same as LI_COMM1_CTL */ +#define LI_COMM2_CFG 0x114 + /* bit definitions are the same as LI_COMM1_CFG */ + +#define LI_UST_LOW 0x200 /* 64-bit Unadjusted System Time is */ +#define LI_UST_HIGH 0x204 /* microseconds since boot */ + +#define LI_AUDIO1_UST 0x300 /* UST-MSC pairs */ +#define LI_AUDIO1_MSC 0x304 /* MSC (Media Stream Counter) */ +#define LI_AUDIO2_UST 0x308 /* counts samples actually */ +#define LI_AUDIO2_MSC 0x30C /* processed as of time UST */ + +/* + * Lithium's DMA engine operates on chunks of 32 bytes. We call that + * a DMACHUNK. + */ + +#define DMACHUNK_SHIFT 5 +#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT) +#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT) +#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT) + +/* + * Two convenient macros to shift bitfields into/out of position. + * + * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)). + * As long as mask is constant, we trust the compiler will change the + * multipy and divide into shifts. + */ + +#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask)) +#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask))) + +/* + * dma_chan_desc is invariant information about a Lithium + * DMA channel. There are two instances, li_comm1 and li_comm2. + * + * Note that the CCTL register fields are write ptr and read ptr, but what + * we care about are which pointer is updated by software and which by + * hardware. + */ + +typedef struct dma_chan_desc { + int basereg; + int cfgreg; + int ctlreg; + int hwptrreg; + int swptrreg; + int ustreg; + int mscreg; + unsigned long swptrmask; + int ad1843_slot; + int direction; /* LI_CCTL_DIR_IN/OUT */ +} dma_chan_desc_t; + +static const dma_chan_desc_t li_comm1 = { + LI_COMM1_BASE, /* base register offset */ + LI_COMM1_CFG, /* config register offset */ + LI_COMM1_CTL, /* control register offset */ + LI_COMM1_CTL + 0, /* hw ptr reg offset (write ptr) */ + LI_COMM1_CTL + 1, /* sw ptr reg offset (read ptr) */ + LI_AUDIO1_UST, /* ust reg offset */ + LI_AUDIO1_MSC, /* msc reg offset */ + LI_CCTL_RPTR, /* sw ptr bitmask in ctlval */ + 2, /* ad1843 serial slot */ + LI_CCFG_DIR_IN /* direction */ +}; + +static const dma_chan_desc_t li_comm2 = { + LI_COMM2_BASE, /* base register offset */ + LI_COMM2_CFG, /* config register offset */ + LI_COMM2_CTL, /* control register offset */ + LI_COMM2_CTL + 1, /* hw ptr reg offset (read ptr) */ + LI_COMM2_CTL + 0, /* sw ptr reg offset (writr ptr) */ + LI_AUDIO2_UST, /* ust reg offset */ + LI_AUDIO2_MSC, /* msc reg offset */ + LI_CCTL_WPTR, /* sw ptr bitmask in ctlval */ + 2, /* ad1843 serial slot */ + LI_CCFG_DIR_OUT /* direction */ +}; + +/* + * dma_chan is variable information about a Lithium DMA channel. + * + * The desc field points to invariant information. + * The lith field points to a lithium_t which is passed + * to li_read* and li_write* to access the registers. + * The *val fields shadow the lithium registers' contents. + */ + +typedef struct dma_chan { + const dma_chan_desc_t *desc; + lithium_t *lith; + unsigned long baseval; + unsigned long cfgval; + unsigned long ctlval; +} dma_chan_t; + +/* + * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter). + * UST is time in microseconds since the system booted, and MSC is a + * counter that increments with every audio sample. + */ + +typedef struct ustmsc { + unsigned long long ust; + unsigned long msc; +} ustmsc_t; + +/* + * li_ad1843_wait waits until lithium says the AD1843 register + * exchange is not busy. Returns 0 on success, -EBUSY on timeout. + * + * Locking: must be called with lithium_lock held. + */ + +static int li_ad1843_wait(lithium_t *lith) +{ + unsigned long later = jiffies + 2; + while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY) + if (jiffies >= later) + return -EBUSY; + return 0; +} + +/* + * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register. + * + * Returns unsigned register value on success, -errno on failure. + */ + +static int li_read_ad1843_reg(lithium_t *lith, int reg) +{ + int val; + + ASSERT(!in_interrupt()); + spin_lock(&lith->lock); + { + val = li_ad1843_wait(lith); + if (val == 0) { + li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg); + val = li_ad1843_wait(lith); + } + if (val == 0) + val = li_readl(lith, LI_CODEC_DATA); + } + spin_unlock(&lith->lock); + + DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n", + lith, reg, val); + + return val; +} + +/* + * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register. + */ + +static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval) +{ + spin_lock(&lith->lock); + { + if (li_ad1843_wait(lith) == 0) { + li_writel(lith, LI_CODEC_DATA, newval); + li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg); + } + } + spin_unlock(&lith->lock); +} + +/* + * li_setup_dma calculates all the register settings for DMA in a particular + * mode. It takes too many arguments. + */ + +static void li_setup_dma(dma_chan_t *chan, + const dma_chan_desc_t *desc, + lithium_t *lith, + unsigned long buffer_paddr, + int bufshift, + int fragshift, + int channels, + int sampsize) +{ + unsigned long mode, format; + unsigned long size, tmask; + + DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, " + "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n", + chan, desc, lith, buffer_paddr, + bufshift, fragshift, channels, sampsize); + + /* Reset the channel first. */ + + li_writel(lith, desc->ctlreg, LI_CCTL_RESET); + + ASSERT(channels == 1 || channels == 2); + if (channels == 2) + mode = LI_CCFG_MODE_STEREO; + else + mode = LI_CCFG_MODE_MONO; + ASSERT(sampsize == 1 || sampsize == 2); + if (sampsize == 2) + format = LI_CCFG_FMT_16BIT; + else + format = LI_CCFG_FMT_8BIT; + chan->desc = desc; + chan->lith = lith; + + /* + * Lithium DMA address register takes a 40-bit physical + * address, right-shifted by 8 so it fits in 32 bits. Bit 37 + * must be set -- it enables cache coherence. + */ + + ASSERT(!(buffer_paddr & 0xFF)); + chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8); + + chan->cfgval = (!LI_CCFG_LOCK | + SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) | + desc->direction | + mode | + format); + + size = bufshift - 6; + tmask = 13 - fragshift; /* See Lithium DMA Notes above. */ + ASSERT(size >= 2 && size <= 7); + ASSERT(tmask >= 1 && tmask <= 7); + chan->ctlval = (!LI_CCTL_RESET | + SHIFT_FIELD(size, LI_CCTL_SIZE) | + !LI_CCTL_DMA_ENABLE | + SHIFT_FIELD(tmask, LI_CCTL_TMASK) | + SHIFT_FIELD(0, LI_CCTL_TPTR)); + + DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval); + DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval); + DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval); + + li_writel(lith, desc->basereg, chan->baseval); + li_writel(lith, desc->cfgreg, chan->cfgval); + li_writel(lith, desc->ctlreg, chan->ctlval); + + DBGRV(); +} + +static void li_shutdown_dma(dma_chan_t *chan) +{ + lithium_t *lith = chan->lith; + caddr_t lith1 = lith->page1; + + DBGEV("(chan=0x%p)\n", chan); + + chan->ctlval &= ~LI_CCTL_DMA_ENABLE; + DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); + li_writel(lith, chan->desc->ctlreg, chan->ctlval); + + /* + * Offset 0x500 on Lithium page 1 is an undocumented, + * unsupported register that holds the zero sample value. + * Lithium is supposed to output zero samples when DMA is + * inactive, and repeat the last sample when DMA underflows. + * But it has a bug, where, after underflow occurs, the zero + * sample is not reset. + * + * I expect this to break in a future rev of Lithium. + */ + + if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT) + * (volatile unsigned long *) (lith1 + 0x500) = 0; +} + +/* + * li_activate_dma always starts dma at the beginning of the buffer. + * + * N.B., these may be called from interrupt. + */ + +static __inline__ void li_activate_dma(dma_chan_t *chan) +{ + chan->ctlval |= LI_CCTL_DMA_ENABLE; + DBGPV("ctlval = 0x%lx\n", chan->ctlval); + li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval); +} + +static void li_deactivate_dma(dma_chan_t *chan) +{ + lithium_t *lith = chan->lith; + caddr_t lith2 = lith->page2; + + chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR); + DBGPV("ctlval = 0x%lx\n", chan->ctlval); + DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); + li_writel(lith, chan->desc->ctlreg, chan->ctlval); + + /* + * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented, + * unsupported registers that are internal copies of the DMA + * read and write pointers. Because of a Lithium bug, these + * registers aren't zeroed correctly when DMA is shut off. So + * we whack them directly. + * + * I expect this to break in a future rev of Lithium. + */ + + if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) { + * (volatile unsigned long *) (lith2 + 0x98) = 0; + * (volatile unsigned long *) (lith2 + 0x9C) = 0; + } +} + +/* + * read/write the ring buffer pointers. These routines' arguments and results + * are byte offsets from the beginning of the ring buffer. + */ + +static __inline__ int li_read_swptr(dma_chan_t *chan) +{ + const unsigned long mask = chan->desc->swptrmask; + + return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask)); +} + +static __inline__ int li_read_hwptr(dma_chan_t *chan) +{ + return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg)); +} + +static __inline__ void li_write_swptr(dma_chan_t *chan, int val) +{ + const unsigned long mask = chan->desc->swptrmask; + + ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF))); + val = BYTES_TO_CHUNKS(val); + chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask); + li_writeb(chan->lith, chan->desc->swptrreg, val); +} + +/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */ + +static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc) +{ + lithium_t *lith = chan->lith; + const dma_chan_desc_t *desc = chan->desc; + unsigned long now_low, now_high0, now_high1, chan_ust; + + spin_lock(&lith->lock); + { + /* + * retry until we do all five reads without the + * high word changing. (High word increments + * every 2^32 microseconds, i.e., not often) + */ + do { + now_high0 = li_readl(lith, LI_UST_HIGH); + now_low = li_readl(lith, LI_UST_LOW); + + /* + * Lithium guarantees these two reads will be + * atomic -- ust will not increment after msc + * is read. + */ + + ustmsc->msc = li_readl(lith, desc->mscreg); + chan_ust = li_readl(lith, desc->ustreg); + + now_high1 = li_readl(lith, LI_UST_HIGH); + } while (now_high0 != now_high1); + } + spin_unlock(&lith->lock); + ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust); +} + +static void li_enable_interrupts(lithium_t *lith, unsigned int mask) +{ + DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); + + /* clear any already-pending interrupts. */ + + li_writel(lith, LI_INTR_STATUS, mask); + + /* enable the interrupts. */ + + mask |= li_readl(lith, LI_INTR_MASK); + li_writel(lith, LI_INTR_MASK, mask); +} + +static void li_disable_interrupts(lithium_t *lith, unsigned int mask) +{ + unsigned int keepmask; + + DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); + + /* disable the interrupts */ + + keepmask = li_readl(lith, LI_INTR_MASK) & ~mask; + li_writel(lith, LI_INTR_MASK, keepmask); + + /* clear any pending interrupts. */ + + li_writel(lith, LI_INTR_STATUS, mask); +} + +/* Get the interrupt status and clear all pending interrupts. */ + +static unsigned int li_get_clear_intr_status(lithium_t *lith) +{ + unsigned int status; + + status = li_readl(lith, LI_INTR_STATUS); + li_writel(lith, LI_INTR_STATUS, ~0); + return status & li_readl(lith, LI_INTR_MASK); +} + +static int li_init(lithium_t *lith) +{ + /* 1. System power supplies stabilize. */ + + /* 2. Assert the ~RESET signal. */ + + li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET); + udelay(1); + + /* 3. Deassert the ~RESET signal and enter a wait period to allow + the AD1843 internal clocks and the external crystal oscillator + to stabilize. */ + + li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); + udelay(1); + + return 0; +} + +/*****************************************************************************/ +/* AD1843 access */ + +/* + * AD1843 bitfield definitions. All are named as in the AD1843 data + * sheet, with ad1843_ prepended and individual bit numbers removed. + * + * E.g., bits LSS0 through LSS2 become ad1843_LSS. + * + * Only the bitfields we need are defined. + */ + +typedef struct ad1843_bitfield { + char reg; + char lo_bit; + char nbits; +} ad1843_bitfield_t; + +static const ad1843_bitfield_t + ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */ + ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */ + ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */ + ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */ + ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */ + ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */ + ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */ + ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */ + ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */ + ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */ + ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */ + ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */ + ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */ + ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */ + ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */ + ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */ + ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */ + ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */ + ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */ + ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */ + ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */ + ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */ + ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */ + ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */ + ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */ + ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */ + ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */ + ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */ + ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */ + ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */ + ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */ + ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_C2C = { 20, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */ + ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */ + ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */ + ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */ + ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */ + ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */ + ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */ + ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */ + ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */ + ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */ + ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */ + ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */ + ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */ + ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */ + ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */ + ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */ + ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */ + ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */ + +/* + * The various registers of the AD1843 use three different formats for + * specifying gain. The ad1843_gain structure parameterizes the + * formats. + */ + +typedef struct ad1843_gain { + + int negative; /* nonzero if gain is negative. */ + const ad1843_bitfield_t *lfield; + const ad1843_bitfield_t *rfield; + +} ad1843_gain_t; + +static const ad1843_gain_t ad1843_gain_RECLEV + = { 0, &ad1843_LIG, &ad1843_RIG }; +static const ad1843_gain_t ad1843_gain_LINE + = { 1, &ad1843_LX1M, &ad1843_RX1M }; +static const ad1843_gain_t ad1843_gain_CD + = { 1, &ad1843_LX2M, &ad1843_RX2M }; +static const ad1843_gain_t ad1843_gain_MIC + = { 1, &ad1843_LMCM, &ad1843_RMCM }; +static const ad1843_gain_t ad1843_gain_PCM + = { 1, &ad1843_LDA1G, &ad1843_RDA1G }; + +/* read the current value of an AD1843 bitfield. */ + +static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field) +{ + int w = li_read_ad1843_reg(lith, field->reg); + int val = w >> field->lo_bit & ((1 << field->nbits) - 1); + + DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n", + lith, field->reg, field->lo_bit, field->nbits, val); + + return val; +} + +/* + * write a new value to an AD1843 bitfield and return the old value. + */ + +static int ad1843_write_bits(lithium_t *lith, + const ad1843_bitfield_t *field, + int newval) +{ + int w = li_read_ad1843_reg(lith, field->reg); + int mask = ((1 << field->nbits) - 1) << field->lo_bit; + int oldval = (w & mask) >> field->lo_bit; + int newbits = (newval << field->lo_bit) & mask; + w = (w & ~mask) | newbits; + (void) li_write_ad1843_reg(lith, field->reg, w); + + DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) " + "returns 0x%x\n", + lith, field->reg, field->lo_bit, field->nbits, newval, + oldval); + + return oldval; +} + +/* + * ad1843_read_multi reads multiple bitfields from the same AD1843 + * register. It uses a single read cycle to do it. (Reading the + * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20 + * microseconds.) + * + * Called ike this. + * + * ad1843_read_multi(lith, nfields, + * &ad1843_FIELD1, &val1, + * &ad1843_FIELD2, &val2, ...); + */ + +static void ad1843_read_multi(lithium_t *lith, int argcount, ...) +{ + va_list ap; + const ad1843_bitfield_t *fp; + int w = 0, mask, *value, reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int *); + if (reg == -1) { + reg = fp->reg; + w = li_read_ad1843_reg(lith, reg); + } + ASSERT(reg == fp->reg); + mask = (1 << fp->nbits) - 1; + *value = w >> fp->lo_bit & mask; + } + va_end(ap); +} + +/* + * ad1843_write_multi stores multiple bitfields into the same AD1843 + * register. It uses one read and one write cycle to do it. + * + * Called like this. + * + * ad1843_write_multi(lith, nfields, + * &ad1843_FIELD1, val1, + * &ad1843_FIELF2, val2, ...); + */ + +static void ad1843_write_multi(lithium_t *lith, int argcount, ...) +{ + va_list ap; + int reg; + const ad1843_bitfield_t *fp; + int value; + int w, m, mask, bits; + + mask = 0; + bits = 0; + reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int); + if (reg == -1) + reg = fp->reg; + ASSERT(fp->reg == reg); + m = ((1 << fp->nbits) - 1) << fp->lo_bit; + mask |= m; + bits |= (value << fp->lo_bit) & m; + } + va_end(ap); + ASSERT(!(bits & ~mask)); + if (~mask & 0xFFFF) + w = li_read_ad1843_reg(lith, reg); + else + w = 0; + w = (w & ~mask) | bits; + (void) li_write_ad1843_reg(lith, reg, w); +} + +/* + * ad1843_get_gain reads the specified register and extracts the gain value + * using the supplied gain type. It returns the gain in OSS format. + */ + +static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp) +{ + int lg, rg; + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg); + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + lg = (lg * 100 + (mask >> 1)) / mask; + rg = (rg * 100 + (mask >> 1)) / mask; + return lg << 0 | rg << 8; +} + +/* + * Set an audio channel's gain. Converts from OSS format to AD1843's + * format. + * + * Returns the new gain, which may be lower than the old gain. + */ + +static int ad1843_set_gain(lithium_t *lith, + const ad1843_gain_t *gp, + int newval) +{ + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + int lg = newval >> 0 & 0xFF; + int rg = newval >> 8; + if (lg < 0 || lg > 100 || rg < 0 || rg > 100) + return -EINVAL; + lg = (lg * mask + (mask >> 1)) / 100; + rg = (rg * mask + (mask >> 1)) / 100; + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg); + return ad1843_get_gain(lith, gp); +} + +/* Returns the current recording source, in OSS format. */ + +static int ad1843_get_recsrc(lithium_t *lith) +{ + int ls = ad1843_read_bits(lith, &ad1843_LSS); + + switch (ls) { + case 1: + return SOUND_MASK_MIC; + case 2: + return SOUND_MASK_LINE; + case 3: + return SOUND_MASK_CD; + case 6: + return SOUND_MASK_PCM; + default: + ASSERT(0); + return -1; + } +} + +/* + * Enable/disable digital resample mode in the AD1843. + * + * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down + * while switching modes. So we save DA1's state (DA2's state is not + * interesting), power them down, switch into/out of resample mode, + * power them up, and restore state. + * + * This will cause audible glitches if D/A or A/D is going on, so the + * driver disallows that (in mixer_write_ioctl()). + * + * The open question is, is this worth doing? I'm leaving it in, + * because it's written, but... + */ + +static void ad1843_set_resample_mode(lithium_t *lith, int onoff) +{ + /* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */ + int save_da1 = li_read_ad1843_reg(lith, 9); + + /* Power down A/D and D/A. */ + ad1843_write_multi(lith, 4, + &ad1843_DA1EN, 0, + &ad1843_DA2EN, 0, + &ad1843_ADLEN, 0, + &ad1843_ADREN, 0); + + /* Switch mode */ + ASSERT(onoff == 0 || onoff == 1); + ad1843_write_bits(lith, &ad1843_DRSFLT, onoff); + + /* Power up A/D and D/A. */ + ad1843_write_multi(lith, 3, + &ad1843_DA1EN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* Restore DA1 mute and gain. */ + li_write_ad1843_reg(lith, 9, save_da1); +} + +/* + * Set recording source. Arg newsrc specifies an OSS channel mask. + * + * The complication is that when we switch into/out of loopback mode + * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of + * digital resampling mode. + * + * Returns newsrc on success, -errno on failure. + */ + +static int ad1843_set_recsrc(lithium_t *lith, int newsrc) +{ + int bits; + int oldbits; + + switch (newsrc) { + case SOUND_MASK_PCM: + bits = 6; + break; + + case SOUND_MASK_MIC: + bits = 1; + break; + + case SOUND_MASK_LINE: + bits = 2; + break; + + case SOUND_MASK_CD: + bits = 3; + break; + + default: + return -EINVAL; + } + oldbits = ad1843_read_bits(lith, &ad1843_LSS); + if (newsrc == SOUND_MASK_PCM && oldbits != 6) { + DBGP("enabling digital resample mode\n"); + ad1843_set_resample_mode(lith, 1); + ad1843_write_multi(lith, 2, + &ad1843_DAADL, 2, + &ad1843_DAADR, 2); + } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) { + DBGP("disabling digital resample mode\n"); + ad1843_set_resample_mode(lith, 0); + ad1843_write_multi(lith, 2, + &ad1843_DAADL, 0, + &ad1843_DAADR, 0); + } + ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits); + return newsrc; +} + +/* + * Return current output sources, in OSS format. + */ + +static int ad1843_get_outsrc(lithium_t *lith) +{ + int pcm, line, mic, cd; + + pcm = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM; + line = ad1843_read_bits(lith, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE; + cd = ad1843_read_bits(lith, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD; + mic = ad1843_read_bits(lith, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC; + + return pcm | line | cd | mic; +} + +/* + * Set output sources. Arg is a mask of active sources in OSS format. + * + * Returns source mask on success, -errno on failure. + */ + +static int ad1843_set_outsrc(lithium_t *lith, int mask) +{ + int pcm, line, mic, cd; + + if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_CD | SOUND_MASK_MIC)) + return -EINVAL; + pcm = (mask & SOUND_MASK_PCM) ? 0 : 1; + line = (mask & SOUND_MASK_LINE) ? 0 : 1; + mic = (mask & SOUND_MASK_MIC) ? 0 : 1; + cd = (mask & SOUND_MASK_CD) ? 0 : 1; + + ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm); + ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line); + ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd); + ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic); + + return mask; +} + +/* Setup ad1843 for D/A conversion. */ + +static void ad1843_setup_dac(lithium_t *lith, + int framerate, + int fmt, + int channels) +{ + int ad_fmt = 0, ad_mode = 0; + + DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", + lith, framerate, fmt, channels); + + switch (fmt) { + case AFMT_S8: ad_fmt = 1; break; + case AFMT_U8: ad_fmt = 1; break; + case AFMT_S16_LE: ad_fmt = 1; break; + case AFMT_MU_LAW: ad_fmt = 2; break; + case AFMT_A_LAW: ad_fmt = 3; break; + default: ASSERT(0); + } + + switch (channels) { + case 2: ad_mode = 0; break; + case 1: ad_mode = 1; break; + default: ASSERT(0); + } + + DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt); + ASSERT(framerate >= 4000 && framerate <= 49000); + ad1843_write_bits(lith, &ad1843_C1C, framerate); + ad1843_write_multi(lith, 2, + &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt); +} + +static void ad1843_shutdown_dac(lithium_t *lith) +{ + ad1843_write_bits(lith, &ad1843_DA1F, 1); +} + +static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels) +{ + int da_fmt = 0; + + DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", + lith, framerate, fmt, channels); + + switch (fmt) { + case AFMT_S8: da_fmt = 1; break; + case AFMT_U8: da_fmt = 1; break; + case AFMT_S16_LE: da_fmt = 1; break; + case AFMT_MU_LAW: da_fmt = 2; break; + case AFMT_A_LAW: da_fmt = 3; break; + default: ASSERT(0); + } + + DBGPV("da_fmt = %d\n", da_fmt); + ASSERT(framerate >= 4000 && framerate <= 49000); + ad1843_write_bits(lith, &ad1843_C2C, framerate); + ad1843_write_multi(lith, 2, + &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt); +} + +static void ad1843_shutdown_adc(lithium_t *lith) +{ + /* nothing to do */ +} + +/* + * Fully initialize the ad1843. As described in the AD1843 data + * sheet, section "START-UP SEQUENCE". The numbered comments are + * subsection headings from the data sheet. See the data sheet, pages + * 52-54, for more info. + * + * return 0 on success, -errno on failure. */ + +static int __init ad1843_init(lithium_t *lith) +{ + unsigned long later; + int err; + + err = li_init(lith); + if (err) + return err; + + if (ad1843_read_bits(lith, &ad1843_INIT) != 0) { + printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n"); + return -EIO; + } + + ad1843_write_bits(lith, &ad1843_SCF, 1); + + /* 4. Put the conversion resources into standby. */ + + ad1843_write_bits(lith, &ad1843_PDNI, 0); + later = jiffies + HZ / 2; /* roughly half a second */ + DBGDO(shut_up++); + while (ad1843_read_bits(lith, &ad1843_PDNO)) { + if (jiffies > later) { + printk(KERN_ERR + "vwsnd audio: AD1843 won't power up\n"); + return -EIO; + } + schedule(); + } + DBGDO(shut_up--); + + /* 5. Power up the clock generators and enable clock output pins. */ + + ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1); + + /* 6. Configure conversion resources while they are in standby. */ + + /* DAC1 uses clock 1 as source, ADC uses clock 2. Always. */ + + ad1843_write_multi(lith, 3, + &ad1843_DA1C, 1, + &ad1843_ADLC, 2, + &ad1843_ADRC, 2); + + /* 7. Enable conversion resources. */ + + ad1843_write_bits(lith, &ad1843_ADTLK, 1); + ad1843_write_multi(lith, 5, + &ad1843_ANAEN, 1, + &ad1843_AAMEN, 1, + &ad1843_DA1EN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* 8. Configure conversion resources while they are enabled. */ + + ad1843_write_bits(lith, &ad1843_DA1C, 1); + + /* Unmute all channels. */ + + ad1843_set_outsrc(lith, + (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD)); + ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0); + + /* Set default recording source to Line In and set + * mic gain to +20 dB. + */ + + ad1843_set_recsrc(lith, SOUND_MASK_LINE); + ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1); + + /* Set Speaker Out level to +/- 4V and unmute it. */ + + ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0); + + return 0; +} + +/*****************************************************************************/ +/* PCM I/O */ + +#define READ_INTR_MASK (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW) +#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW) + +typedef enum vwsnd_port_swstate { /* software state */ + SW_OFF, + SW_INITIAL, + SW_RUN, + SW_DRAIN, +} vwsnd_port_swstate_t; + +typedef enum vwsnd_port_hwstate { /* hardware state */ + HW_STOPPED, + HW_RUNNING, +} vwsnd_port_hwstate_t; + +/* + * These flags are read by ISR, but only written at baseline. + */ + +typedef enum vwsnd_port_flags { + DISABLED = 1 << 0, + ERFLOWN = 1 << 1, /* overflown or underflown */ + HW_BUSY = 1 << 2, +} vwsnd_port_flags_t; + +/* + * vwsnd_port is the per-port data structure. Each device has two + * ports, one for input and one for output. + * + * Locking: + * + * port->lock protects: hwstate, flags, swb_[iu]_avail. + * + * devc->io_sema protects: swstate, sw_*, swb_[iu]_idx. + * + * everything else is only written by open/release or + * pcm_{setup,shutdown}(), which are serialized by a + * combination of devc->open_sema and devc->io_sema. + */ + +typedef struct vwsnd_port { + + spinlock_t lock; + wait_queue_head_t queue; + vwsnd_port_swstate_t swstate; + vwsnd_port_hwstate_t hwstate; + vwsnd_port_flags_t flags; + + int sw_channels; + int sw_samplefmt; + int sw_framerate; + int sample_size; + int frame_size; + unsigned int zero_word; /* zero for the sample format */ + + int sw_fragshift; + int sw_fragcount; + int sw_subdivshift; + + unsigned int hw_fragshift; + unsigned int hw_fragsize; + unsigned int hw_fragcount; + + int hwbuf_size; + unsigned long hwbuf_paddr; + unsigned long hwbuf_vaddr; + caddr_t hwbuf; /* hwbuf == hwbuf_vaddr */ + int hwbuf_max; /* max bytes to preload */ + + caddr_t swbuf; + unsigned int swbuf_size; /* size in bytes */ + unsigned int swb_u_idx; /* index of next user byte */ + unsigned int swb_i_idx; /* index of next intr byte */ + unsigned int swb_u_avail; /* # bytes avail to user */ + unsigned int swb_i_avail; /* # bytes avail to intr */ + + dma_chan_t chan; + + /* Accounting */ + + int byte_count; + int frag_count; + int MSC_offset; + +} vwsnd_port_t; + +/* vwsnd_dev is the per-device data structure. */ + +typedef struct vwsnd_dev { + struct vwsnd_dev *next_dev; + int audio_minor; /* minor number of audio device */ + int mixer_minor; /* minor number of mixer device */ + + struct semaphore open_sema; + struct semaphore io_sema; + struct semaphore mix_sema; + mode_t open_mode; + wait_queue_head_t open_wait; + + lithium_t lith; + + vwsnd_port_t rport; + vwsnd_port_t wport; +} vwsnd_dev_t; + +static vwsnd_dev_t *vwsnd_dev_list; /* linked list of all devices */ + +static atomic_t vwsnd_use_count = ATOMIC_INIT(0); + +# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count)) +# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count)) +# define IN_USE (atomic_read(&vwsnd_use_count) != 0) + +/* + * Lithium can only DMA multiples of 32 bytes. Its DMA buffer may + * be up to 8 Kb. This driver always uses 8 Kb. + * + * Memory bug workaround -- I'm not sure what's going on here, but + * somehow pcm_copy_out() was triggering segv's going on to the next + * page of the hw buffer. So, I make the hw buffer one size bigger + * than we actually use. That way, the following page is allocated + * and mapped, and no error. I suspect that something is broken + * in Cobalt, but haven't really investigated. HBO is the actual + * size of the buffer, and HWBUF_ORDER is what we allocate. + */ + +#define HWBUF_SHIFT 13 +#define HWBUF_SIZE (1 << HWBUF_SHIFT) +# define HBO (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0) +# define HWBUF_ORDER (HBO + 1) /* next size bigger */ +#define MIN_SPEED 4000 +#define MAX_SPEED 49000 + +#define MIN_FRAGSHIFT (DMACHUNK_SHIFT + 1) +#define MAX_FRAGSHIFT (PAGE_SHIFT) +#define MIN_FRAGSIZE (1 << MIN_FRAGSHIFT) +#define MAX_FRAGSIZE (1 << MAX_FRAGSHIFT) +#define MIN_FRAGCOUNT(fragsize) 3 +#define MAX_FRAGCOUNT(fragsize) (32 * PAGE_SIZE / (fragsize)) +#define DEFAULT_FRAGSHIFT 12 +#define DEFAULT_FRAGCOUNT 16 +#define DEFAULT_SUBDIVSHIFT 0 + +/* + * The software buffer (swbuf) is a ring buffer shared between user + * level and interrupt level. Each level owns some of the bytes in + * the buffer, and may give bytes away by calling swb_inc_{u,i}(). + * User level calls _u for user, and interrupt level calls _i for + * interrupt. + * + * port->swb_{u,i}_avail is the number of bytes available to that level. + * + * port->swb_{u,i}_idx is the index of the first available byte in the + * buffer. + * + * Each level calls swb_inc_{u,i}() to atomically increment its index, + * recalculate the number of bytes available for both sides, and + * return the number of bytes available. Since each side can only + * give away bytes, the other side can only increase the number of + * bytes available to this side. Each side updates its own index + * variable, swb_{u,i}_idx, so no lock is needed to read it. + * + * To query the number of bytes available, call swb_inc_{u,i} with an + * increment of zero. + */ + +static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc) +{ + if (inc) { + port->swb_u_idx += inc; + port->swb_u_idx %= port->swbuf_size; + port->swb_u_avail -= inc; + port->swb_i_avail += inc; + } + return port->swb_u_avail; +} + +static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + { + ret = __swb_inc_u(port, inc); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc) +{ + if (inc) { + port->swb_i_idx += inc; + port->swb_i_idx %= port->swbuf_size; + port->swb_i_avail -= inc; + port->swb_u_avail += inc; + } + return port->swb_i_avail; +} + +static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + { + ret = __swb_inc_i(port, inc); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +/* + * pcm_setup - this routine initializes all port state after + * mode-setting ioctls have been done, but before the first I/O is + * done. + * + * Locking: called with devc->io_sema held. + * + * Returns 0 on success, -errno on failure. + */ + +static int pcm_setup(vwsnd_dev_t *devc, + vwsnd_port_t *rport, + vwsnd_port_t *wport) +{ + vwsnd_port_t *aport = rport ? rport : wport; + int sample_size; + unsigned int zero_word; + + DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); + + ASSERT(aport != NULL); + if (aport->swbuf != NULL) + return 0; + switch (aport->sw_samplefmt) { + case AFMT_MU_LAW: + sample_size = 1; + zero_word = 0xFFFFFFFF ^ 0x80808080; + break; + + case AFMT_A_LAW: + sample_size = 1; + zero_word = 0xD5D5D5D5 ^ 0x80808080; + break; + + case AFMT_U8: + sample_size = 1; + zero_word = 0x80808080; + break; + + case AFMT_S8: + sample_size = 1; + zero_word = 0x00000000; + break; + + case AFMT_S16_LE: + sample_size = 2; + zero_word = 0x00000000; + break; + + default: + sample_size = 0; /* prevent compiler warning */ + zero_word = 0; + ASSERT(0); + } + aport->sample_size = sample_size; + aport->zero_word = zero_word; + aport->frame_size = aport->sw_channels * aport->sample_size; + aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift; + aport->hw_fragsize = 1 << aport->hw_fragshift; + aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift; + ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE); + ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE); + ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize)); + ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize)); + if (rport) { + int hwfrags, swfrags; + rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; + hwfrags = rport->hwbuf_max >> aport->hw_fragshift; + swfrags = aport->hw_fragcount - hwfrags; + if (swfrags < 2) + swfrags = 2; + rport->swbuf_size = swfrags * aport->hw_fragsize; + DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); + DBGPV("read hwbuf_max = %d, swbuf_size = %d\n", + rport->hwbuf_max, rport->swbuf_size); + } + if (wport) { + int hwfrags, swfrags; + int total_bytes = aport->hw_fragcount * aport->hw_fragsize; + wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; + if (wport->hwbuf_max > total_bytes) + wport->hwbuf_max = total_bytes; + hwfrags = wport->hwbuf_max >> aport->hw_fragshift; + DBGPV("hwfrags = %d\n", hwfrags); + swfrags = aport->hw_fragcount - hwfrags; + if (swfrags < 2) + swfrags = 2; + wport->swbuf_size = swfrags * aport->hw_fragsize; + DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); + DBGPV("write hwbuf_max = %d, swbuf_size = %d\n", + wport->hwbuf_max, wport->swbuf_size); + } + + aport->swb_u_idx = 0; + aport->swb_i_idx = 0; + aport->byte_count = 0; + + /* + * Is this a Cobalt bug? We need to make this buffer extend + * one page further than we actually use -- somehow memcpy + * causes an exceptoin otherwise. I suspect there's a bug in + * Cobalt (or somewhere) where it's generating a fault on a + * speculative load or something. Obviously, I haven't taken + * the time to track it down. + */ + + aport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); + if (!aport->swbuf) + return -ENOMEM; + if (rport && wport) { + ASSERT(aport == rport); + ASSERT(wport->swbuf == NULL); + /* One extra page - see comment above. */ + wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); + if (!wport->swbuf) { + vfree(aport->swbuf); + aport->swbuf = NULL; + return -ENOMEM; + } + wport->sample_size = rport->sample_size; + wport->zero_word = rport->zero_word; + wport->frame_size = rport->frame_size; + wport->hw_fragshift = rport->hw_fragshift; + wport->hw_fragsize = rport->hw_fragsize; + wport->hw_fragcount = rport->hw_fragcount; + wport->swbuf_size = rport->swbuf_size; + wport->hwbuf_max = rport->hwbuf_max; + wport->swb_u_idx = rport->swb_u_idx; + wport->swb_i_idx = rport->swb_i_idx; + wport->byte_count = rport->byte_count; + } + if (rport) { + rport->swb_u_avail = 0; + rport->swb_i_avail = rport->swbuf_size; + rport->swstate = SW_RUN; + li_setup_dma(&rport->chan, + &li_comm1, + &devc->lith, + rport->hwbuf_paddr, + HWBUF_SHIFT, + rport->hw_fragshift, + rport->sw_channels, + rport->sample_size); + ad1843_setup_adc(&devc->lith, + rport->sw_framerate, + rport->sw_samplefmt, + rport->sw_channels); + li_enable_interrupts(&devc->lith, READ_INTR_MASK); + if (!(rport->flags & DISABLED)) { + ustmsc_t ustmsc; + rport->hwstate = HW_RUNNING; + li_activate_dma(&rport->chan); + li_read_USTMSC(&rport->chan, &ustmsc); + rport->MSC_offset = ustmsc.msc; + } + } + if (wport) { + if (wport->hwbuf_max > wport->swbuf_size) + wport->hwbuf_max = wport->swbuf_size; + wport->flags &= ~ERFLOWN; + wport->swb_u_avail = wport->swbuf_size; + wport->swb_i_avail = 0; + wport->swstate = SW_RUN; + li_setup_dma(&wport->chan, + &li_comm2, + &devc->lith, + wport->hwbuf_paddr, + HWBUF_SHIFT, + wport->hw_fragshift, + wport->sw_channels, + wport->sample_size); + ad1843_setup_dac(&devc->lith, + wport->sw_framerate, + wport->sw_samplefmt, + wport->sw_channels); + li_enable_interrupts(&devc->lith, WRITE_INTR_MASK); + } + DBGRV(); + return 0; +} + +/* + * pcm_shutdown_port - shut down one port (direction) for PCM I/O. + * Only called from pcm_shutdown. + */ + +static void pcm_shutdown_port(vwsnd_dev_t *devc, + vwsnd_port_t *aport, + unsigned int mask) +{ + unsigned long flags; + vwsnd_port_hwstate_t hwstate; + DECLARE_WAITQUEUE(wait, current); + + aport->swstate = SW_INITIAL; + add_wait_queue(&aport->queue, &wait); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + spin_lock_irqsave(&aport->lock, flags); + { + hwstate = aport->hwstate; + } + spin_unlock_irqrestore(&aport->lock, flags); + if (hwstate == HW_STOPPED) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&aport->queue, &wait); + li_disable_interrupts(&devc->lith, mask); + if (aport == &devc->rport) + ad1843_shutdown_adc(&devc->lith); + else /* aport == &devc->wport) */ + ad1843_shutdown_dac(&devc->lith); + li_shutdown_dma(&aport->chan); + vfree(aport->swbuf); + aport->swbuf = NULL; + aport->byte_count = 0; +} + +/* + * pcm_shutdown undoes what pcm_setup did. + * Also sets the ports' swstate to newstate. + */ + +static void pcm_shutdown(vwsnd_dev_t *devc, + vwsnd_port_t *rport, + vwsnd_port_t *wport) +{ + DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); + + if (rport && rport->swbuf) { + DBGPV("shutting down rport\n"); + pcm_shutdown_port(devc, rport, READ_INTR_MASK); + } + if (wport && wport->swbuf) { + DBGPV("shutting down wport\n"); + pcm_shutdown_port(devc, wport, WRITE_INTR_MASK); + } + DBGRV(); +} + +static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb) +{ + char *src = rport->hwbuf + hwidx; + char *dst = rport->swbuf + swidx; + int fmt = rport->sw_samplefmt; + + DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx); + ASSERT(rport->hwbuf != NULL); + ASSERT(rport->swbuf != NULL); + ASSERT(nb > 0 && (nb % 32) == 0); + ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); + ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size); + ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size); + + if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { + + /* See Sample Format Notes above. */ + + char *end = src + nb; + while (src < end) + *dst++ = *src++ ^ 0x80; + } else + memcpy(dst, src, nb); +} + +static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb) +{ + char *src = wport->swbuf + swidx; + char *dst = wport->hwbuf + hwidx; + int fmt = wport->sw_samplefmt; + + ASSERT(nb > 0 && (nb % 32) == 0); + ASSERT(wport->hwbuf != NULL); + ASSERT(wport->swbuf != NULL); + ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); + ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size); + ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size); + if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { + + /* See Sample Format Notes above. */ + + char *end = src + nb; + while (src < end) + *dst++ = *src++ ^ 0x80; + } else + memcpy(dst, src, nb); +} + +/* + * pcm_output() is called both from baselevel and from interrupt level. + * This is where audio frames are copied into the hardware-accessible + * ring buffer. + * + * Locking note: The part of this routine that figures out what to do + * holds wport->lock. The longer part releases wport->lock, but sets + * wport->flags & HW_BUSY. Afterward, it reacquires wport->lock, and + * checks for more work to do. + * + * If another thread calls pcm_output() while HW_BUSY is set, it + * returns immediately, knowing that the thread that set HW_BUSY will + * look for more work to do before returning. + * + * This has the advantage that port->lock is held for several short + * periods instead of one long period. Also, when pcm_output is + * called from base level, it reenables interrupts. + */ + +static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb) +{ + vwsnd_port_t *wport = &devc->wport; + const int hwmax = wport->hwbuf_max; + const int hwsize = wport->hwbuf_size; + const int swsize = wport->swbuf_size; + const int fragsize = wport->hw_fragsize; + unsigned long iflags; + + DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); + spin_lock_irqsave(&wport->lock, iflags); + if (erflown) + wport->flags |= ERFLOWN; + (void) __swb_inc_u(wport, nb); + if (wport->flags & HW_BUSY) { + spin_unlock_irqrestore(&wport->lock, iflags); + DBGPV("returning: HW BUSY\n"); + return; + } + if (wport->flags & DISABLED) { + spin_unlock_irqrestore(&wport->lock, iflags); + DBGPV("returning: DISABLED\n"); + return; + } + wport->flags |= HW_BUSY; + while (1) { + int swptr, hwptr, hw_avail, sw_avail, swidx; + vwsnd_port_hwstate_t hwstate = wport->hwstate; + vwsnd_port_swstate_t swstate = wport->swstate; + int hw_unavail; + ustmsc_t ustmsc; + + hwptr = li_read_hwptr(&wport->chan); + swptr = li_read_swptr(&wport->chan); + hw_unavail = (swptr - hwptr + hwsize) % hwsize; + hw_avail = (hwmax - hw_unavail) & -fragsize; + sw_avail = wport->swb_i_avail & -fragsize; + if (sw_avail && swstate == SW_RUN) { + if (wport->flags & ERFLOWN) { + wport->flags &= ~ERFLOWN; + } + } else if (swstate == SW_INITIAL || + swstate == SW_OFF || + (swstate == SW_DRAIN && + !sw_avail && + (wport->flags & ERFLOWN))) { + DBGP("stopping. hwstate = %d\n", hwstate); + if (hwstate != HW_STOPPED) { + li_deactivate_dma(&wport->chan); + wport->hwstate = HW_STOPPED; + } + wake_up(&wport->queue); + break; + } + if (!sw_avail || !hw_avail) + break; + spin_unlock_irqrestore(&wport->lock, iflags); + + /* + * We gave up the port lock, but we have the HW_BUSY flag. + * Proceed without accessing any nonlocal state. + * Do not exit the loop -- must check for more work. + */ + + swidx = wport->swb_i_idx; + nb = hw_avail; + if (nb > sw_avail) + nb = sw_avail; + if (nb > hwsize - swptr) + nb = hwsize - swptr; /* don't overflow hwbuf */ + if (nb > swsize - swidx) + nb = swsize - swidx; /* don't overflow swbuf */ + ASSERT(nb > 0); + if (nb % fragsize) { + DBGP("nb = %d, fragsize = %d\n", nb, fragsize); + DBGP("hw_avail = %d\n", hw_avail); + DBGP("sw_avail = %d\n", sw_avail); + DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); + DBGP("swsize = %d, swidx = %d\n", swsize, swidx); + } + ASSERT(!(nb % fragsize)); + DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n", + swidx, swidx + nb, swptr, swptr + nb); + pcm_copy_out(wport, swidx, swptr, nb); + li_write_swptr(&wport->chan, (swptr + nb) % hwsize); + spin_lock_irqsave(&wport->lock, iflags); + if (hwstate == HW_STOPPED) { + DBGPV("starting\n"); + li_activate_dma(&wport->chan); + wport->hwstate = HW_RUNNING; + li_read_USTMSC(&wport->chan, &ustmsc); + ASSERT(wport->byte_count % wport->frame_size == 0); + wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size; + } + __swb_inc_i(wport, nb); + wport->byte_count += nb; + wport->frag_count += nb / fragsize; + ASSERT(nb % fragsize == 0); + wake_up(&wport->queue); + } + wport->flags &= ~HW_BUSY; + spin_unlock_irqrestore(&wport->lock, iflags); + DBGRV(); +} + +/* + * pcm_input() is called both from baselevel and from interrupt level. + * This is where audio frames are copied out of the hardware-accessible + * ring buffer. + * + * Locking note: The part of this routine that figures out what to do + * holds rport->lock. The longer part releases rport->lock, but sets + * rport->flags & HW_BUSY. Afterward, it reacquires rport->lock, and + * checks for more work to do. + * + * If another thread calls pcm_input() while HW_BUSY is set, it + * returns immediately, knowing that the thread that set HW_BUSY will + * look for more work to do before returning. + * + * This has the advantage that port->lock is held for several short + * periods instead of one long period. Also, when pcm_input is + * called from base level, it reenables interrupts. + */ + +static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb) +{ + vwsnd_port_t *rport = &devc->rport; + const int hwmax = rport->hwbuf_max; + const int hwsize = rport->hwbuf_size; + const int swsize = rport->swbuf_size; + const int fragsize = rport->hw_fragsize; + unsigned long iflags; + + DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); + + spin_lock_irqsave(&rport->lock, iflags); + if (erflown) + rport->flags |= ERFLOWN; + (void) __swb_inc_u(rport, nb); + if (rport->flags & HW_BUSY || !rport->swbuf) { + spin_unlock_irqrestore(&rport->lock, iflags); + DBGPV("returning: HW BUSY or !swbuf\n"); + return; + } + if (rport->flags & DISABLED) { + spin_unlock_irqrestore(&rport->lock, iflags); + DBGPV("returning: DISABLED\n"); + return; + } + rport->flags |= HW_BUSY; + while (1) { + int swptr, hwptr, hw_avail, sw_avail, swidx; + vwsnd_port_hwstate_t hwstate = rport->hwstate; + vwsnd_port_swstate_t swstate = rport->swstate; + + hwptr = li_read_hwptr(&rport->chan); + swptr = li_read_swptr(&rport->chan); + hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize; + if (hw_avail > hwmax) + hw_avail = hwmax; + sw_avail = rport->swb_i_avail & -fragsize; + if (swstate != SW_RUN) { + DBGP("stopping. hwstate = %d\n", hwstate); + if (hwstate != HW_STOPPED) { + li_deactivate_dma(&rport->chan); + rport->hwstate = HW_STOPPED; + } + wake_up(&rport->queue); + break; + } + if (!sw_avail || !hw_avail) + break; + spin_unlock_irqrestore(&rport->lock, iflags); + + /* + * We gave up the port lock, but we have the HW_BUSY flag. + * Proceed without accessing any nonlocal state. + * Do not exit the loop -- must check for more work. + */ + + swidx = rport->swb_i_idx; + nb = hw_avail; + if (nb > sw_avail) + nb = sw_avail; + if (nb > hwsize - swptr) + nb = hwsize - swptr; /* don't overflow hwbuf */ + if (nb > swsize - swidx) + nb = swsize - swidx; /* don't overflow swbuf */ + ASSERT(nb > 0); + if (nb % fragsize) { + DBGP("nb = %d, fragsize = %d\n", nb, fragsize); + DBGP("hw_avail = %d\n", hw_avail); + DBGP("sw_avail = %d\n", sw_avail); + DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); + DBGP("swsize = %d, swidx = %d\n", swsize, swidx); + } + ASSERT(!(nb % fragsize)); + DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n", + swptr, swptr + nb, swidx, swidx + nb); + pcm_copy_in(rport, swidx, swptr, nb); + li_write_swptr(&rport->chan, (swptr + nb) % hwsize); + spin_lock_irqsave(&rport->lock, iflags); + __swb_inc_i(rport, nb); + rport->byte_count += nb; + rport->frag_count += nb / fragsize; + ASSERT(nb % fragsize == 0); + wake_up(&rport->queue); + } + rport->flags &= ~HW_BUSY; + spin_unlock_irqrestore(&rport->lock, iflags); + DBGRV(); +} + +/* + * pcm_flush_frag() writes zero samples to fill the current fragment, + * then flushes it to the hardware. + * + * It is only meaningful to flush output, not input. + */ + +static void pcm_flush_frag(vwsnd_dev_t *devc) +{ + vwsnd_port_t *wport = &devc->wport; + + DBGPV("swstate = %d\n", wport->swstate); + if (wport->swstate == SW_RUN) { + int idx = wport->swb_u_idx; + int end = (idx + wport->hw_fragsize - 1) + >> wport->hw_fragshift + << wport->hw_fragshift; + int nb = end - idx; + DBGPV("clearing %d bytes\n", nb); + if (nb) + memset(wport->swbuf + idx, + (char) wport->zero_word, + nb); + wport->swstate = SW_DRAIN; + pcm_output(devc, 0, nb); + } + DBGRV(); +} + +/* + * Wait for output to drain. This sleeps uninterruptibly because + * there is nothing intelligent we can do if interrupted. This + * means the process will be delayed in responding to the signal. + */ + +static void pcm_write_sync(vwsnd_dev_t *devc) +{ + vwsnd_port_t *wport = &devc->wport; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + vwsnd_port_hwstate_t hwstate; + + DBGEV("(devc=0x%p)\n", devc); + add_wait_queue(&wport->queue, &wait); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + spin_lock_irqsave(&wport->lock, flags); + { + hwstate = wport->hwstate; + } + spin_unlock_irqrestore(&wport->lock, flags); + if (hwstate == HW_STOPPED) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate); + DBGRV(); +} + +/*****************************************************************************/ +/* audio driver */ + +/* + * seek on an audio device always fails. + */ + +static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status) +{ + int overflown = status & LI_INTR_COMM1_OVERFLOW; + + if (status & READ_INTR_MASK) + pcm_input(devc, overflown, 0); +} + +static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status) +{ + int underflown = status & LI_INTR_COMM2_UNDERFLOW; + + if (status & WRITE_INTR_MASK) + pcm_output(devc, underflown, 0); +} + +static void vwsnd_audio_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) dev_id; + unsigned int status; + + DBGEV("(irq=%d, dev_id=0x%p, regs=0x%p)\n", irq, dev_id, regs); + + status = li_get_clear_intr_status(&devc->lith); + vwsnd_audio_read_intr(devc, status); + vwsnd_audio_write_intr(devc, status); +} + +static ssize_t vwsnd_audio_do_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ? + &devc->rport : NULL); + int ret, nb; + + DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", + file, buffer, count, ppos); + + if (!rport) + return -EINVAL; + + if (rport->swbuf == NULL) { + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + ret = pcm_setup(devc, rport, wport); + if (ret < 0) + return ret; + } + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count) { + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(&rport->queue, &wait); + while ((nb = swb_inc_u(rport, 0)) == 0) { + DBGPV("blocking\n"); + set_current_state(TASK_INTERRUPTIBLE); + if (rport->flags & DISABLED || + file->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + return ret ? ret : -EAGAIN; + } + schedule(); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + return ret ? ret : -ERESTARTSYS; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + pcm_input(devc, 0, 0); + /* nb bytes are available in userbuf. */ + if (nb > count) + nb = count; + DBGPV("nb = %d\n", nb); + copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb); + (void) swb_inc_u(rport, nb); + buffer += nb; + count -= nb; + ret += nb; + } + DBGPV("returning %d\n", ret); + return ret; +} + +static ssize_t vwsnd_audio_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + ssize_t ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_read(file, buffer, count, ppos); + up(&devc->io_sema); + return ret; +} + +static ssize_t vwsnd_audio_do_write(struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL); + int ret, nb; + + DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", + file, buffer, count, ppos); + + if (!wport) + return -EINVAL; + + if (wport->swbuf == NULL) { + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + ret = pcm_setup(devc, rport, wport); + if (ret < 0) + return ret; + } + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count) { + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(&wport->queue, &wait); + while ((nb = swb_inc_u(wport, 0)) == 0) { + set_current_state(TASK_INTERRUPTIBLE); + if (wport->flags & DISABLED || + file->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + return ret ? ret : -EAGAIN; + } + schedule(); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + return ret ? ret : -ERESTARTSYS; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + /* nb bytes are available in userbuf. */ + if (nb > count) + nb = count; + DBGPV("nb = %d\n", nb); + copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb); + pcm_output(devc, 0, nb); + buffer += nb; + count -= nb; + ret += nb; + } + DBGPV("returning %d\n", ret); + return ret; +} + +static ssize_t vwsnd_audio_write(struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + ssize_t ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_write(file, buffer, count, ppos); + up(&devc->io_sema); + return ret; +} + +/* No kernel lock - fine */ +static unsigned int vwsnd_audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + unsigned int mask = 0; + + DBGEV("(file=0x%p, wait=0x%p)\n", file, wait); + + ASSERT(rport || wport); + if (rport) { + poll_wait(file, &rport->queue, wait); + if (swb_inc_u(rport, 0)) + mask |= (POLLIN | POLLRDNORM); + } + if (wport) { + poll_wait(file, &wport->queue, wait); + if (wport->swbuf == NULL || swb_inc_u(wport, 0)) + mask |= (POLLOUT | POLLWRNORM); + } + + DBGPV("returning 0x%x\n", mask); + return mask; +} + +static int vwsnd_audio_do_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + vwsnd_port_t *aport = rport ? rport : wport; + struct audio_buf_info buf_info; + struct count_info info; + unsigned long flags; + int ival; + + + DBGEV("(inode=0x%p, file=0x%p, cmd=0x%x, arg=0x%lx)\n", + inode, file, cmd, arg); + switch (cmd) { + case OSS_GETVERSION: /* _SIOR ('M', 118, int) */ + DBGX("OSS_GETVERSION\n"); + ival = SOUND_VERSION; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */ + DBGX("SNDCTL_DSP_GETCAPS\n"); + ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */ + DBGX("SNDCTL_DSP_GETFMTS\n"); + ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | + AFMT_U8 | AFMT_S8); + return put_user(ival, (int *) arg); + break; + + case SOUND_PCM_READ_RATE: /* _SIOR ('P', 2, int) */ + DBGX("SOUND_PCM_READ_RATE\n"); + ival = aport->sw_framerate; + return put_user(ival, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: /* _SIOR ('P', 6, int) */ + DBGX("SOUND_PCM_READ_CHANNELS\n"); + ival = aport->sw_channels; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SPEED: /* _SIOWR('P', 2, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SPEED %d\n", ival); + if (ival) { + if (aport->swstate != SW_INITIAL) { + DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n", + aport->swstate); + return -EINVAL; + } + if (ival < MIN_SPEED) + ival = MIN_SPEED; + if (ival > MAX_SPEED) + ival = MAX_SPEED; + if (rport) + rport->sw_framerate = ival; + if (wport) + wport->sw_framerate = ival; + } else + ival = aport->sw_framerate; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_STEREO %d\n", ival); + if (ival != 0 && ival != 1) + return -EINVAL; + if (aport->swstate != SW_INITIAL) + return -EINVAL; + if (rport) + rport->sw_channels = ival + 1; + if (wport) + wport->sw_channels = ival + 1; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_CHANNELS: /* _SIOWR('P', 6, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_CHANNELS %d\n", ival); + if (ival != 1 && ival != 2) + return -EINVAL; + if (aport->swstate != SW_INITIAL) + return -EINVAL; + if (rport) + rport->sw_channels = ival; + if (wport) + wport->sw_channels = ival; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */ + ival = pcm_setup(devc, rport, wport); + if (ival < 0) { + DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival); + return ival; + } + ival = 1 << aport->sw_fragshift; + DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n", + ival >> 16, ival & 0xFFFF); + if (aport->swstate != SW_INITIAL) + return -EINVAL; + { + int sw_fragshift = ival & 0xFFFF; + int sw_subdivshift = aport->sw_subdivshift; + int hw_fragshift = sw_fragshift - sw_subdivshift; + int sw_fragcount = (ival >> 16) & 0xFFFF; + int hw_fragsize; + if (hw_fragshift < MIN_FRAGSHIFT) + hw_fragshift = MIN_FRAGSHIFT; + if (hw_fragshift > MAX_FRAGSHIFT) + hw_fragshift = MAX_FRAGSHIFT; + sw_fragshift = hw_fragshift + aport->sw_subdivshift; + hw_fragsize = 1 << hw_fragshift; + if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize)) + sw_fragcount = MIN_FRAGCOUNT(hw_fragsize); + if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) + sw_fragcount = MAX_FRAGCOUNT(hw_fragsize); + DBGPV("sw_fragshift = %d\n", sw_fragshift); + DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport); + if (rport) { + rport->sw_fragshift = sw_fragshift; + rport->sw_fragcount = sw_fragcount; + } + if (wport) { + wport->sw_fragshift = sw_fragshift; + wport->sw_fragcount = sw_fragcount; + } + ival = sw_fragcount << 16 | sw_fragshift; + } + DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n", + ival >> 16, ival & 0xFFFF); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SUBDIVIDE: /* _SIOWR('P', 9, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival); + if (aport->swstate != SW_INITIAL) + return -EINVAL; + { + int subdivshift; + int hw_fragshift, hw_fragsize, hw_fragcount; + switch (ival) { + case 1: subdivshift = 0; break; + case 2: subdivshift = 1; break; + case 4: subdivshift = 2; break; + default: return -EINVAL; + } + hw_fragshift = aport->sw_fragshift - subdivshift; + if (hw_fragshift < MIN_FRAGSHIFT || + hw_fragshift > MAX_FRAGSHIFT) + return -EINVAL; + hw_fragsize = 1 << hw_fragshift; + hw_fragcount = aport->sw_fragcount >> subdivshift; + if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) || + hw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) + return -EINVAL; + if (rport) + rport->sw_subdivshift = subdivshift; + if (wport) + wport->sw_subdivshift = subdivshift; + } + return 0; + + case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SETFMT %d\n", ival); + if (ival != AFMT_QUERY) { + if (aport->swstate != SW_INITIAL) { + DBGP("SETFMT failed, swstate = %d\n", + aport->swstate); + return -EINVAL; + } + switch (ival) { + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + case AFMT_S16_LE: + if (rport) + rport->sw_samplefmt = ival; + if (wport) + wport->sw_samplefmt = ival; + break; + default: + return -EINVAL; + } + } + ival = aport->sw_samplefmt; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETOSPACE: /* _SIOR ('P',12, audio_buf_info) */ + DBGXV("SNDCTL_DSP_GETOSPACE\n"); + if (!wport) + return -EINVAL; + ival = pcm_setup(devc, rport, wport); + if (ival < 0) + return ival; + ival = swb_inc_u(wport, 0); + buf_info.fragments = ival >> wport->sw_fragshift; + buf_info.fragstotal = wport->sw_fragcount; + buf_info.fragsize = 1 << wport->sw_fragshift; + buf_info.bytes = ival; + DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n", + buf_info.fragments, buf_info.fragstotal, + buf_info.fragsize, buf_info.bytes); + return copy_to_user((void *) arg, &buf_info, sizeof buf_info); + + case SNDCTL_DSP_GETISPACE: /* _SIOR ('P',13, audio_buf_info) */ + DBGX("SNDCTL_DSP_GETISPACE\n"); + if (!rport) + return -EINVAL; + ival = pcm_setup(devc, rport, wport); + if (ival < 0) + return ival; + ival = swb_inc_u(rport, 0); + buf_info.fragments = ival >> rport->sw_fragshift; + buf_info.fragstotal = rport->sw_fragcount; + buf_info.fragsize = 1 << rport->sw_fragshift; + buf_info.bytes = ival; + DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n", + buf_info.fragments, buf_info.fragstotal, + buf_info.fragsize, buf_info.bytes); + return copy_to_user((void *) arg, &buf_info, sizeof buf_info); + + case SNDCTL_DSP_NONBLOCK: /* _SIO ('P',14) */ + DBGX("SNDCTL_DSP_NONBLOCK\n"); + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: /* _SIO ('P', 0) */ + DBGX("SNDCTL_DSP_RESET\n"); + /* + * Nothing special needs to be done for input. Input + * samples sit in swbuf, but it will be reinitialized + * to empty when pcm_setup() is called. + */ + if (wport && wport->swbuf) { + wport->swstate = SW_INITIAL; + pcm_output(devc, 0, 0); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + return 0; + + case SNDCTL_DSP_SYNC: /* _SIO ('P', 1) */ + DBGX("SNDCTL_DSP_SYNC\n"); + if (wport) { + pcm_flush_frag(devc); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + return 0; + + case SNDCTL_DSP_POST: /* _SIO ('P', 8) */ + DBGX("SNDCTL_DSP_POST\n"); + if (!wport) + return -EINVAL; + pcm_flush_frag(devc); + return 0; + + case SNDCTL_DSP_GETIPTR: /* _SIOR ('P', 17, count_info) */ + DBGX("SNDCTL_DSP_GETIPTR\n"); + if (!rport) + return -EINVAL; + spin_lock_irqsave(&rport->lock, flags); + { + ustmsc_t ustmsc; + if (rport->hwstate == HW_RUNNING) { + ASSERT(rport->swstate == SW_RUN); + li_read_USTMSC(&rport->chan, &ustmsc); + info.bytes = ustmsc.msc - rport->MSC_offset; + info.bytes *= rport->frame_size; + } else { + info.bytes = rport->byte_count; + } + info.blocks = rport->frag_count; + info.ptr = 0; /* not implemented */ + rport->frag_count = 0; + } + spin_unlock_irqrestore(&rport->lock, flags); + return copy_to_user((void *) arg, &info, sizeof info); + + case SNDCTL_DSP_GETOPTR: /* _SIOR ('P',18, count_info) */ + DBGX("SNDCTL_DSP_GETOPTR\n"); + if (!wport) + return -EINVAL; + spin_lock_irqsave(&wport->lock, flags); + { + ustmsc_t ustmsc; + if (wport->hwstate == HW_RUNNING) { + ASSERT(wport->swstate == SW_RUN); + li_read_USTMSC(&wport->chan, &ustmsc); + info.bytes = ustmsc.msc - wport->MSC_offset; + info.bytes *= wport->frame_size; + } else { + info.bytes = wport->byte_count; + } + info.blocks = wport->frag_count; + info.ptr = 0; /* not implemented */ + wport->frag_count = 0; + } + spin_unlock_irqrestore(&wport->lock, flags); + return copy_to_user((void *) arg, &info, sizeof info); + + case SNDCTL_DSP_GETODELAY: /* _SIOR ('P', 23, int) */ + DBGX("SNDCTL_DSP_GETODELAY\n"); + if (!wport) + return -EINVAL; + spin_lock_irqsave(&wport->lock, flags); + { + int fsize = wport->frame_size; + ival = wport->swb_i_avail / fsize; + if (wport->hwstate == HW_RUNNING) { + int swptr, hwptr, hwframes, hwbytes, hwsize; + int totalhwbytes; + ustmsc_t ustmsc; + + hwsize = wport->hwbuf_size; + swptr = li_read_swptr(&wport->chan); + li_read_USTMSC(&wport->chan, &ustmsc); + hwframes = ustmsc.msc - wport->MSC_offset; + totalhwbytes = hwframes * fsize; + hwptr = totalhwbytes % hwsize; + hwbytes = (swptr - hwptr + hwsize) % hwsize; + ival += hwbytes / fsize; + } + } + spin_unlock_irqrestore(&wport->lock, flags); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_PROFILE: /* _SIOW ('P', 23, int) */ + DBGX("SNDCTL_DSP_PROFILE\n"); + + /* + * Thomas Sailer explains SNDCTL_DSP_PROFILE + * (private email, March 24, 1999): + * + * This gives the sound driver a hint on what it + * should do with partial fragments + * (i.e. fragments partially filled with write). + * This can direct the driver to zero them or + * leave them alone. But don't ask me what this + * is good for, my driver just zeroes the last + * fragment before the receiver stops, no idea + * what good for any other behaviour could + * be. Implementing it as NOP seems safe. + */ + + break; + + case SNDCTL_DSP_GETTRIGGER: /* _SIOR ('P',16, int) */ + DBGX("SNDCTL_DSP_GETTRIGGER\n"); + ival = 0; + if (rport) { + spin_lock_irqsave(&rport->lock, flags); + { + if (!(rport->flags & DISABLED)) + ival |= PCM_ENABLE_INPUT; + } + spin_unlock_irqrestore(&rport->lock, flags); + } + if (wport) { + spin_lock_irqsave(&wport->lock, flags); + { + if (!(wport->flags & DISABLED)) + ival |= PCM_ENABLE_OUTPUT; + } + spin_unlock_irqrestore(&wport->lock, flags); + } + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: /* _SIOW ('P',16, int) */ + if (get_user(ival, (int *) arg)) + return -EFAULT; + DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival); + + /* + * If user is disabling I/O and port is not in initial + * state, fail with EINVAL. + */ + + if (((rport && !(ival & PCM_ENABLE_INPUT)) || + (wport && !(ival & PCM_ENABLE_OUTPUT))) && + aport->swstate != SW_INITIAL) + return -EINVAL; + + if (rport) { + vwsnd_port_hwstate_t hwstate; + spin_lock_irqsave(&rport->lock, flags); + { + hwstate = rport->hwstate; + if (ival & PCM_ENABLE_INPUT) + rport->flags &= ~DISABLED; + else + rport->flags |= DISABLED; + } + spin_unlock_irqrestore(&rport->lock, flags); + if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) { + + if (rport->swstate == SW_INITIAL) + pcm_setup(devc, rport, wport); + else + li_activate_dma(&rport->chan); + } + } + if (wport) { + vwsnd_port_flags_t pflags; + spin_lock_irqsave(&wport->lock, flags); + { + pflags = wport->flags; + if (ival & PCM_ENABLE_OUTPUT) + wport->flags &= ~DISABLED; + else + wport->flags |= DISABLED; + } + spin_unlock_irqrestore(&wport->lock, flags); + if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) { + if (wport->swstate == SW_RUN) + pcm_output(devc, 0, 0); + } + } + return 0; + + default: + DBGP("unknown ioctl 0x%x\n", cmd); + return -EINVAL; + } + DBGP("unimplemented ioctl 0x%x\n", cmd); + return -EINVAL; +} + +static int vwsnd_audio_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + int ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_ioctl(inode, file, cmd, arg); + up(&devc->io_sema); + return ret; +} + +/* No mmap. */ + +static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + DBGE("(file=0x%p, vma=0x%p)\n", file, vma); + return -ENODEV; +} + +/* + * Open the audio device for read and/or write. + * + * Returns 0 on success, -errno on failure. + */ + +static int vwsnd_audio_open(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc; + dev_t minor = MINOR(inode->i_rdev); + int sw_samplefmt; + + DBGE("(inode=0x%p, file=0x%p)\n", inode, file); + + INC_USE_COUNT; + for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) + if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F)) + break; + + if (devc == NULL) { + DEC_USE_COUNT; + return -ENODEV; + } + + down(&devc->open_sema); + while (devc->open_mode & file->f_mode) { + up(&devc->open_sema); + if (file->f_flags & O_NONBLOCK) { + DEC_USE_COUNT; + return -EBUSY; + } + interruptible_sleep_on(&devc->open_wait); + if (signal_pending(current)) { + DEC_USE_COUNT; + return -ERESTARTSYS; + } + down(&devc->open_sema); + } + devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&devc->open_sema); + + /* get default sample format from minor number. */ + + sw_samplefmt = 0; + if ((minor & 0xF) == SND_DEV_DSP) + sw_samplefmt = AFMT_U8; + else if ((minor & 0xF) == SND_DEV_AUDIO) + sw_samplefmt = AFMT_MU_LAW; + else if ((minor & 0xF) == SND_DEV_DSP16) + sw_samplefmt = AFMT_S16_LE; + else + ASSERT(0); + + /* Initialize vwsnd_ports. */ + + down(&devc->io_sema); + { + if (file->f_mode & FMODE_READ) { + devc->rport.swstate = SW_INITIAL; + devc->rport.flags = 0; + devc->rport.sw_channels = 1; + devc->rport.sw_samplefmt = sw_samplefmt; + devc->rport.sw_framerate = 8000; + devc->rport.sw_fragshift = DEFAULT_FRAGSHIFT; + devc->rport.sw_fragcount = DEFAULT_FRAGCOUNT; + devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; + devc->rport.byte_count = 0; + devc->rport.frag_count = 0; + } + if (file->f_mode & FMODE_WRITE) { + devc->wport.swstate = SW_INITIAL; + devc->wport.flags = 0; + devc->wport.sw_channels = 1; + devc->wport.sw_samplefmt = sw_samplefmt; + devc->wport.sw_framerate = 8000; + devc->wport.sw_fragshift = DEFAULT_FRAGSHIFT; + devc->wport.sw_fragcount = DEFAULT_FRAGCOUNT; + devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; + devc->wport.byte_count = 0; + devc->wport.frag_count = 0; + } + } + up(&devc->io_sema); + + file->private_data = devc; + DBGRV(); + return 0; +} + +/* + * Release (close) the audio device. + */ + +static int vwsnd_audio_release(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *wport = NULL, *rport = NULL; + int err = 0; + + lock_kernel(); + down(&devc->io_sema); + { + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + + if (file->f_mode & FMODE_READ) + rport = &devc->rport; + if (file->f_mode & FMODE_WRITE) { + wport = &devc->wport; + pcm_flush_frag(devc); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + if (rport) + rport->swstate = SW_OFF; + if (wport) + wport->swstate = SW_OFF; + } + up(&devc->io_sema); + + down(&devc->open_sema); + { + devc->open_mode &= ~file->f_mode; + } + up(&devc->open_sema); + wake_up(&devc->open_wait); + DEC_USE_COUNT; + DBGR(); + unlock_kernel(); + return err; +} + +static struct file_operations vwsnd_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: vwsnd_audio_read, + write: vwsnd_audio_write, + poll: vwsnd_audio_poll, + ioctl: vwsnd_audio_ioctl, + mmap: vwsnd_audio_mmap, + open: vwsnd_audio_open, + release: vwsnd_audio_release, +}; + +/*****************************************************************************/ +/* mixer driver */ + +/* open the mixer device. */ + +static int vwsnd_mixer_open(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc; + + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + + INC_USE_COUNT; + for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) + if (devc->mixer_minor == MINOR(inode->i_rdev)) + break; + + if (devc == NULL) { + DEC_USE_COUNT; + return -ENODEV; + } + file->private_data = devc; + return 0; +} + +/* release (close) the mixer device. */ + +static int vwsnd_mixer_release(struct inode *inode, struct file *file) +{ + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + DEC_USE_COUNT; + return 0; +} + +/* mixer_read_ioctl handles all read ioctls on the mixer device. */ + +static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) +{ + int val = -1; + + DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); + + switch (nr) { + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + case SOUND_MIXER_DEVMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); + break; + + case SOUND_MIXER_STEREODEVS: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); + break; + + case SOUND_MIXER_OUTMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD); + break; + + case SOUND_MIXER_RECMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD); + break; + + case SOUND_MIXER_PCM: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM); + break; + + case SOUND_MIXER_LINE: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE); + break; + + case SOUND_MIXER_MIC: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC); + break; + + case SOUND_MIXER_CD: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD); + break; + + case SOUND_MIXER_RECLEV: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV); + break; + + case SOUND_MIXER_RECSRC: + val = ad1843_get_recsrc(&devc->lith); + break; + + case SOUND_MIXER_OUTSRC: + val = ad1843_get_outsrc(&devc->lith); + break; + + default: + return -EINVAL; + } + return put_user(val, (int *) arg); +} + +/* mixer_write_ioctl handles all write ioctls on the mixer device. */ + +static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) +{ + int val; + int err; + + DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); + + err = get_user(val, (int *) arg); + if (err) + return -EFAULT; + switch (nr) { + case SOUND_MIXER_PCM: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val); + break; + + case SOUND_MIXER_LINE: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val); + break; + + case SOUND_MIXER_MIC: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val); + break; + + case SOUND_MIXER_CD: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val); + break; + + case SOUND_MIXER_RECLEV: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val); + break; + + case SOUND_MIXER_RECSRC: + if (devc->rport.swbuf || devc->wport.swbuf) + return -EBUSY; /* can't change recsrc while running */ + val = ad1843_set_recsrc(&devc->lith, val); + break; + + case SOUND_MIXER_OUTSRC: + val = ad1843_set_outsrc(&devc->lith, val); + break; + + default: + return -EINVAL; + } + if (val < 0) + return val; + return put_user(val, (int *) arg); +} + +/* This is the ioctl entry to the mixer driver. */ + +static int vwsnd_mixer_ioctl(struct inode *ioctl, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT; + const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT; + int retval; + + DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg); + + down(&devc->mix_sema); + { + if ((cmd & ~nrmask) == MIXER_READ(0)) + retval = mixer_read_ioctl(devc, nr, (caddr_t) arg); + else if ((cmd & ~nrmask) == MIXER_WRITE(0)) + retval = mixer_write_ioctl(devc, nr, (caddr_t) arg); + else + retval = -EINVAL; + } + up(&devc->mix_sema); + return retval; +} + +static struct file_operations vwsnd_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: vwsnd_mixer_ioctl, + open: vwsnd_mixer_open, + release: vwsnd_mixer_release, +}; + +/*****************************************************************************/ +/* probe/attach/unload */ + +/* driver probe routine. Return nonzero if hardware is found. */ + +static int __init probe_vwsnd(struct address_info *hw_config) +{ + lithium_t lith; + int w; + unsigned long later; + + DBGEV("(hw_config=0x%p)\n", hw_config); + + /* XXX verify lithium present (to prevent crash on non-vw) */ + + if (li_create(&lith, hw_config->io_base) != 0) { + printk(KERN_WARNING "probe_vwsnd: can't map lithium\n"); + return 0; + } + later = jiffies + 2; + li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); + do { + w = li_readl(&lith, LI_HOST_CONTROLLER); + } while (w == LI_HC_LINK_ENABLE && jiffies < later); + + li_destroy(&lith); + + DBGPV("HC = 0x%04x\n", w); + + if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) { + + /* This may indicate a beta machine with no audio, + * or a future machine with different audio. + * On beta-release 320 w/ no audio, HC == 0x4000 */ + + printk(KERN_WARNING "probe_vwsnd: audio codec not found\n"); + return 0; + } + + if (w & LI_HC_LINK_FAILURE) { + printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n"); + return 0; + } + + printk(KERN_INFO "probe_vwsnd: lithium audio found\n"); + + return 1; +} + +/* + * driver attach routine. Initialize driver data structures and + * initialize hardware. A new vwsnd_dev_t is allocated and put + * onto the global list, vwsnd_dev_list. + * + * Return +minor_dev on success, -errno on failure. + */ + +static int __init attach_vwsnd(struct address_info *hw_config) +{ + vwsnd_dev_t *devc = NULL; + int err = -ENOMEM; + + DBGEV("(hw_config=0x%p)\n", hw_config); + + devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL); + if (devc == NULL) + goto fail0; + + err = li_create(&devc->lith, hw_config->io_base); + if (err) + goto fail1; + + init_waitqueue(&devc->open_wait); + + devc->rport.hwbuf_size = HWBUF_SIZE; + devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); + if (!devc->rport.hwbuf_vaddr) + goto fail2; + devc->rport.hwbuf = (caddr_t) devc->rport.hwbuf_vaddr; + devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf); + + /* + * Quote from the NT driver: + * + * // WARNING!!! HACK to setup output dma!!! + * // This is required because even on output there is some data + * // trickling into the input DMA channel. This is a bug in the + * // Lithium microcode. + * // --sde + * + * We set the input side's DMA base address here. It will remain + * valid until the driver is unloaded. + */ + + li_writel(&devc->lith, LI_COMM1_BASE, + devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8)); + + devc->wport.hwbuf_size = HWBUF_SIZE; + devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); + if (!devc->wport.hwbuf_vaddr) + goto fail3; + devc->wport.hwbuf = (caddr_t) devc->wport.hwbuf_vaddr; + devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf); + DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf); + + DBGDO(shut_up++); + err = ad1843_init(&devc->lith); + DBGDO(shut_up--); + if (err) + goto fail4; + + /* install interrupt handler */ + + err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc); + if (err) + goto fail5; + + /* register this device's drivers. */ + + devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1); + if ((err = devc->audio_minor) < 0) { + DBGDO(printk(KERN_WARNING + "attach_vwsnd: register_sound_dsp error %d\n", + err)); + goto fail6; + } + devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops, + devc->audio_minor >> 4); + if ((err = devc->mixer_minor) < 0) { + DBGDO(printk(KERN_WARNING + "attach_vwsnd: register_sound_mixer error %d\n", + err)); + goto fail7; + } + + /* Squirrel away device indices for unload routine. */ + + hw_config->slots[0] = devc->audio_minor; + + /* Initialize as much of *devc as possible */ + + devc->open_sema = MUTEX; + devc->io_sema = MUTEX; + devc->mix_sema = MUTEX; + devc->open_mode = 0; + devc->rport.lock = SPIN_LOCK_UNLOCKED; + init_waitqueue(&devc->rport.queue); + devc->rport.swstate = SW_OFF; + devc->rport.hwstate = HW_STOPPED; + devc->rport.flags = 0; + devc->rport.swbuf = NULL; + devc->wport.lock = SPIN_LOCK_UNLOCKED; + init_waitqueue(&devc->wport.queue); + devc->wport.swstate = SW_OFF; + devc->wport.hwstate = HW_STOPPED; + devc->wport.flags = 0; + devc->wport.swbuf = NULL; + + /* Success. Link us onto the local device list. */ + + devc->next_dev = vwsnd_dev_list; + vwsnd_dev_list = devc; + return devc->audio_minor; + + /* So many ways to fail. Undo what we did. */ + + fail7: + unregister_sound_dsp(devc->audio_minor); + fail6: + free_irq(hw_config->irq, devc); + fail5: + fail4: + free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); + fail3: + free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); + fail2: + li_destroy(&devc->lith); + fail1: + kfree(devc); + fail0: + return err; +} + +static int __exit unload_vwsnd(struct address_info *hw_config) +{ + vwsnd_dev_t *devc, **devcp; + + DBGE("()\n"); + + devcp = &vwsnd_dev_list; + while ((devc = *devcp)) { + if (devc->audio_minor == hw_config->slots[0]) { + *devcp = devc->next_dev; + break; + } + devcp = &devc->next_dev; + } + + if (!devc) + return -ENODEV; + + unregister_sound_mixer(devc->mixer_minor); + unregister_sound_dsp(devc->audio_minor); + free_irq(hw_config->irq, devc); + free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); + free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); + li_destroy(&devc->lith); + kfree(devc); + + return 0; +} + +/*****************************************************************************/ +/* initialization and loadable kernel module interface */ + +static struct address_info the_hw_config = { + 0xFF001000, /* lithium phys addr */ + CO_IRQ(CO_APIC_LI_AUDIO) /* irq */ +}; + +MODULE_DESCRIPTION("SGI Visual Workstation sound module"); +MODULE_AUTHOR("Bob Miller "); +MODULE_LICENSE("GPL"); + +static int __init init_vwsnd(void) +{ + int err; + + DBGXV("\n"); + DBGXV("sound::vwsnd::init_module()\n"); + + if(!probe_vwsnd(&the_hw_config)) + return -ENODEV; + err = attach_vwsnd(&the_hw_config); + if (err < 0) + return err; + return 0; +} + +static void __exit cleanup_vwsnd(void) +{ + DBGX("sound::vwsnd::cleanup_module()\n"); + + unload_vwsnd(&the_hw_config); +} + +module_init(init_vwsnd); +module_exit(cleanup_vwsnd); diff -Nru linux/sound/oss/waveartist.c linux-2.4.19-pre5-mjc/sound/oss/waveartist.c --- linux/sound/oss/waveartist.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/waveartist.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2033 @@ +/* + * linux/drivers/sound/waveartist.c + * + * The low level driver for the RWA010 Rockwell Wave Artist + * codec chip used in the Rebel.com NetWinder. + * + * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk) + * and Pat Beirne (patb@corel.ca) + * + * + * Copyright (C) by Rebel.com 1998-1999 + * + * RWA010 specs received under NDA from Rockwell + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to waveartist_init() + */ + +/* Debugging */ +#define DEBUG_CMD 1 +#define DEBUG_OUT 2 +#define DEBUG_IN 4 +#define DEBUG_INTR 8 +#define DEBUG_MIXER 16 +#define DEBUG_TRIGGER 32 + +#define debug_flg (0) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sound_config.h" +#include "waveartist.h" + +#ifdef CONFIG_ARM +#include +#include +#endif + +#ifndef NO_DMA +#define NO_DMA 255 +#endif + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ + SOUND_MASK_PCM |\ + SOUND_MASK_LINE |\ + SOUND_MASK_MIC |\ + SOUND_MASK_LINE1 |\ + SOUND_MASK_RECLEV |\ + SOUND_MASK_VOLUME |\ + SOUND_MASK_IMIX) + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = { + 0x5555, /* Master Volume */ + 0x0000, /* Bass */ + 0x0000, /* Treble */ + 0x2323, /* Synth (FM) */ + 0x4b4b, /* PCM */ + 0x6464, /* PC Speaker */ + 0x0000, /* Ext Line */ + 0x0000, /* Mic */ + 0x0000, /* CD */ + 0x6464, /* Recording monitor */ + 0x0000, /* SB PCM (ALT PCM) */ + 0x0000, /* Recording level */ + 0x6464, /* Input gain */ + 0x6464, /* Output gain */ + 0x0000, /* Line1 (Aux1) */ + 0x0000, /* Line2 (Aux2) */ + 0x0000, /* Line3 (Aux3) */ + 0x0000, /* Digital1 */ + 0x0000, /* Digital2 */ + 0x0000, /* Digital3 */ + 0x0000, /* Phone In */ + 0x6464, /* Phone Out */ + 0x0000, /* Video */ + 0x0000, /* Radio */ + 0x0000 /* Monitor */ +}; + +typedef struct { + struct address_info hw; /* hardware */ + char *chip_name; + + int xfer_count; + int audio_mode; + int open_mode; + int audio_flags; + int record_dev; + int playback_dev; + int dev_no; + + /* Mixer parameters */ + const struct waveartist_mixer_info *mix; + + unsigned short *levels; /* cache of volume settings */ + int recmask; /* currently enabled recording device! */ + +#ifdef CONFIG_ARCH_NETWINDER + signed int slider_vol; /* hardware slider volume */ + unsigned int handset_detect :1; + unsigned int telephone_detect:1; + unsigned int no_autoselect :1;/* handset/telephone autoselects a path */ + unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */ + unsigned int line_mute_state :1;/* set by ioctl or autoselect */ + unsigned int use_slider :1;/* use slider setting for o/p vol */ +#endif +} wavnc_info; + +/* + * This is the implementation specific mixer information. + */ +struct waveartist_mixer_info { + unsigned int supported_devs; /* Supported devices */ + unsigned int recording_devs; /* Recordable devies */ + unsigned int stereo_devs; /* Stereo devices */ + + unsigned int (*select_input)(wavnc_info *, unsigned int, + unsigned char *, unsigned char *); + int (*decode_mixer)(wavnc_info *, int, + unsigned char, unsigned char); + int (*get_mixer)(wavnc_info *, int); +}; + +typedef struct wavnc_port_info { + int open_mode; + int speed; + int channels; + int audio_format; +} wavnc_port_info; + +static int nr_waveartist_devs; +static wavnc_info adev_info[MAX_AUDIO_DEV]; +static spinlock_t waveartist_lock = SPIN_LOCK_UNLOCKED; + +#ifndef CONFIG_ARCH_NETWINDER +#define machine_is_netwinder() 0 +#else +static struct timer_list vnc_timer; +static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask); +static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg); +static void vnc_slider_tick(unsigned long data); +#endif + +static inline void +waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) +{ + unsigned int ctlr_port = hw->io_base + CTLR; + + clear = ~clear & inb(ctlr_port); + + outb(clear | set, ctlr_port); +} + +/* Toggle IRQ acknowledge line + */ +static inline void +waveartist_iack(wavnc_info *devc) +{ + unsigned int ctlr_port = devc->hw.io_base + CTLR; + int old_ctlr; + + old_ctlr = inb(ctlr_port) & ~IRQ_ACK; + + outb(old_ctlr | IRQ_ACK, ctlr_port); + outb(old_ctlr, ctlr_port); +} + +static inline int +waveartist_sleep(int timeout_ms) +{ + unsigned int timeout = timeout_ms * 10 * HZ / 100; + + do { + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(timeout); + } while (timeout); + + return 0; +} + +static int +waveartist_reset(wavnc_info *devc) +{ + struct address_info *hw = &devc->hw; + unsigned int timeout, res = -1; + + waveartist_set_ctlr(hw, -1, RESET); + waveartist_sleep(2); + waveartist_set_ctlr(hw, RESET, 0); + + timeout = 500; + do { + mdelay(2); + + if (inb(hw->io_base + STATR) & CMD_RF) { + res = inw(hw->io_base + CMDR); + if (res == 0x55aa) + break; + } + } while (--timeout); + + if (timeout == 0) { + printk(KERN_WARNING "WaveArtist: reset timeout "); + if (res != (unsigned int)-1) + printk("(res=%04X)", res); + printk("\n"); + return 1; + } + return 0; +} + +/* Helper function to send and receive words + * from WaveArtist. It handles all the handshaking + * and can send or receive multiple words. + */ +static int +waveartist_cmd(wavnc_info *devc, + int nr_cmd, unsigned int *cmd, + int nr_resp, unsigned int *resp) +{ + unsigned int io_base = devc->hw.io_base; + unsigned int timed_out = 0; + unsigned int i; + + if (debug_flg & DEBUG_CMD) { + printk("waveartist_cmd: cmd="); + + for (i = 0; i < nr_cmd; i++) + printk("%04X ", cmd[i]); + + printk("\n"); + } + + if (inb(io_base + STATR) & CMD_RF) { + int old_data; + + /* flush the port + */ + + old_data = inw(io_base + CMDR); + + if (debug_flg & DEBUG_CMD) + printk("flushed %04X...", old_data); + + udelay(10); + } + + for (i = 0; !timed_out && i < nr_cmd; i++) { + int count; + + for (count = 5000; count; count--) + if (inb(io_base + STATR) & CMD_WE) + break; + + if (!count) + timed_out = 1; + else + outw(cmd[i], io_base + CMDR); + } + + for (i = 0; !timed_out && i < nr_resp; i++) { + int count; + + for (count = 5000; count; count--) + if (inb(io_base + STATR) & CMD_RF) + break; + + if (!count) + timed_out = 1; + else + resp[i] = inw(io_base + CMDR); + } + + if (debug_flg & DEBUG_CMD) { + if (!timed_out) { + printk("waveartist_cmd: resp="); + + for (i = 0; i < nr_resp; i++) + printk("%04X ", resp[i]); + + printk("\n"); + } else + printk("waveartist_cmd: timed out\n"); + } + + return timed_out ? 1 : 0; +} + +/* + * Send one command word + */ +static inline int +waveartist_cmd1(wavnc_info *devc, unsigned int cmd) +{ + return waveartist_cmd(devc, 1, &cmd, 0, NULL); +} + +/* + * Send one command, receive one word + */ +static inline unsigned int +waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd) +{ + unsigned int ret; + + waveartist_cmd(devc, 1, &cmd, 1, &ret); + + return ret; +} + +/* + * Send a double command, receive one + * word (and throw it away) + */ +static inline int +waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg) +{ + unsigned int vals[2]; + + vals[0] = cmd; + vals[1] = arg; + + return waveartist_cmd(devc, 2, vals, 1, vals); +} + +/* + * Send a triple command + */ +static inline int +waveartist_cmd3(wavnc_info *devc, unsigned int cmd, + unsigned int arg1, unsigned int arg2) +{ + unsigned int vals[3]; + + vals[0] = cmd; + vals[1] = arg1; + vals[2] = arg2; + + return waveartist_cmd(devc, 3, vals, 0, NULL); +} + +static int +waveartist_getrev(wavnc_info *devc, char *rev) +{ + unsigned int temp[2]; + unsigned int cmd = WACMD_GETREV; + + waveartist_cmd(devc, 1, &cmd, 2, temp); + + rev[0] = temp[0] >> 8; + rev[1] = temp[0] & 255; + rev[2] = '\0'; + + return temp[0]; +} + +static void waveartist_halt_output(int dev); +static void waveartist_halt_input(int dev); +static void waveartist_halt(int dev); +static void waveartist_trigger(int dev, int state); + +static int +waveartist_open(int dev, int mode) +{ + wavnc_info *devc; + wavnc_port_info *portc; + unsigned long flags; + + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; + + devc = (wavnc_info *) audio_devs[dev]->devc; + portc = (wavnc_port_info *) audio_devs[dev]->portc; + + spin_lock_irqsave(&waveartist_lock, flags); + if (portc->open_mode || (devc->open_mode & mode)) { + spin_unlock_irqrestore(&waveartist_lock, flags); + return -EBUSY; + } + + devc->audio_mode = 0; + devc->open_mode |= mode; + portc->open_mode = mode; + waveartist_trigger(dev, 0); + + if (mode & OPEN_READ) + devc->record_dev = dev; + if (mode & OPEN_WRITE) + devc->playback_dev = dev; + spin_unlock_irqrestore(&waveartist_lock, flags); + + return 0; +} + +static void +waveartist_close(int dev) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned long flags; + + spin_lock_irqsave(&waveartist_lock, flags); + + waveartist_halt(dev); + + devc->audio_mode = 0; + devc->open_mode &= ~portc->open_mode; + portc->open_mode = 0; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + unsigned int count = __count; + + if (debug_flg & DEBUG_OUT) + printk("waveartist: output block, buf=0x%lx, count=0x%x...\n", + buf, count); + /* + * 16 bit data + */ + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) + count >>= 1; + + if (portc->channels > 1) + count >>= 1; + + count -= 1; + + if (devc->audio_mode & PCM_ENABLE_OUTPUT && + audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + count == devc->xfer_count) { + devc->audio_mode |= PCM_ENABLE_OUTPUT; + return; /* + * Auto DMA mode on. No need to react + */ + } + + spin_lock_irqsave(&waveartist_lock, flags); + + /* + * set sample count + */ + waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count); + + devc->xfer_count = count; + devc->audio_mode |= PCM_ENABLE_OUTPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + unsigned int count = __count; + + if (debug_flg & DEBUG_IN) + printk("waveartist: start input, buf=0x%lx, count=0x%x...\n", + buf, count); + + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + count >>= 1; + + if (portc->channels > 1) + count >>= 1; + + count -= 1; + + if (devc->audio_mode & PCM_ENABLE_INPUT && + audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + count == devc->xfer_count) { + devc->audio_mode |= PCM_ENABLE_INPUT; + return; /* + * Auto DMA mode on. No need to react + */ + } + + spin_lock_irqsave(&waveartist_lock, flags); + + /* + * set sample count + */ + waveartist_cmd2(devc, WACMD_INPUTSIZE, count); + + devc->xfer_count = count; + devc->audio_mode |= PCM_ENABLE_INPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static int +waveartist_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + return -EINVAL; +} + +static unsigned int +waveartist_get_speed(wavnc_port_info *portc) +{ + unsigned int speed; + + /* + * program the speed, channels, bits + */ + if (portc->speed == 8000) + speed = 0x2E71; + else if (portc->speed == 11025) + speed = 0x4000; + else if (portc->speed == 22050) + speed = 0x8000; + else if (portc->speed == 44100) + speed = 0x0; + else { + /* + * non-standard - just calculate + */ + speed = portc->speed << 16; + + speed = (speed / 44100) & 65535; + } + + return speed; +} + +static unsigned int +waveartist_get_bits(wavnc_port_info *portc) +{ + unsigned int bits; + + if (portc->audio_format == AFMT_S16_LE) + bits = 1; + else if (portc->audio_format == AFMT_S8) + bits = 0; + else + bits = 2; //default AFMT_U8 + + return bits; +} + +static int +waveartist_prepare_for_input(int dev, int bsize, int bcount) +{ + unsigned long flags; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned int speed, bits; + + if (devc->audio_mode) + return 0; + + speed = waveartist_get_speed(portc); + bits = waveartist_get_bits(portc); + + spin_lock_irqsave(&waveartist_lock, flags); + + if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) + printk(KERN_WARNING "waveartist: error setting the " + "record format to %d\n", portc->audio_format); + + if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels)) + printk(KERN_WARNING "waveartist: error setting record " + "to %d channels\n", portc->channels); + + /* + * write cmd SetSampleSpeedTimeConstant + */ + if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed)) + printk(KERN_WARNING "waveartist: error setting the record " + "speed to %dHz.\n", portc->speed); + + if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1)) + printk(KERN_WARNING "waveartist: error setting the record " + "data path to 0x%X\n", 1); + + if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) + printk(KERN_WARNING "waveartist: error setting the record " + "format to %d\n", portc->audio_format); + + devc->xfer_count = 0; + spin_unlock_irqrestore(&waveartist_lock, flags); + waveartist_halt_input(dev); + + if (debug_flg & DEBUG_INTR) { + printk("WA CTLR reg: 0x%02X.\n", + inb(devc->hw.io_base + CTLR)); + printk("WA STAT reg: 0x%02X.\n", + inb(devc->hw.io_base + STATR)); + printk("WA IRQS reg: 0x%02X.\n", + inb(devc->hw.io_base + IRQSTAT)); + } + + return 0; +} + +static int +waveartist_prepare_for_output(int dev, int bsize, int bcount) +{ + unsigned long flags; + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned int speed, bits; + + /* + * program the speed, channels, bits + */ + speed = waveartist_get_speed(portc); + bits = waveartist_get_bits(portc); + + spin_lock_irqsave(&waveartist_lock, flags); + + if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) && + waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed)) + printk(KERN_WARNING "waveartist: error setting the playback " + "speed to %dHz.\n", portc->speed); + + if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels)) + printk(KERN_WARNING "waveartist: error setting the playback " + "to %d channels\n", portc->channels); + + if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0)) + printk(KERN_WARNING "waveartist: error setting the playback " + "data path to 0x%X\n", 0); + + if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits)) + printk(KERN_WARNING "waveartist: error setting the playback " + "format to %d\n", portc->audio_format); + + devc->xfer_count = 0; + spin_unlock_irqrestore(&waveartist_lock, flags); + waveartist_halt_output(dev); + + if (debug_flg & DEBUG_INTR) { + printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR)); + printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR)); + printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT)); + } + + return 0; +} + +static void +waveartist_halt(int dev) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + wavnc_info *devc; + + if (portc->open_mode & OPEN_WRITE) + waveartist_halt_output(dev); + + if (portc->open_mode & OPEN_READ) + waveartist_halt_input(dev); + + devc = (wavnc_info *) audio_devs[dev]->devc; + devc->audio_mode = 0; +} + +static void +waveartist_halt_input(int dev) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&waveartist_lock, flags); + + /* + * Stop capture + */ + waveartist_cmd1(devc, WACMD_INPUTSTOP); + + devc->audio_mode &= ~PCM_ENABLE_INPUT; + + /* + * Clear interrupt by toggling + * the IRQ_ACK bit in CTRL + */ + if (inb(devc->hw.io_base + STATR) & IRQ_REQ) + waveartist_iack(devc); + +// devc->audio_mode &= ~PCM_ENABLE_INPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_halt_output(int dev) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + unsigned long flags; + + spin_lock_irqsave(&waveartist_lock, flags); + + waveartist_cmd1(devc, WACMD_OUTPUTSTOP); + + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + + /* + * Clear interrupt by toggling + * the IRQ_ACK bit in CTRL + */ + if (inb(devc->hw.io_base + STATR) & IRQ_REQ) + waveartist_iack(devc); + +// devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static void +waveartist_trigger(int dev, int state) +{ + wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned long flags; + + if (debug_flg & DEBUG_TRIGGER) { + printk("wavnc: audio trigger "); + if (state & PCM_ENABLE_INPUT) + printk("in "); + if (state & PCM_ENABLE_OUTPUT) + printk("out"); + printk("\n"); + } + + spin_lock_irqsave(&waveartist_lock, flags); + + state &= devc->audio_mode; + + if (portc->open_mode & OPEN_READ && + state & PCM_ENABLE_INPUT) + /* + * enable ADC Data Transfer to PC + */ + waveartist_cmd1(devc, WACMD_INPUTSTART); + + if (portc->open_mode & OPEN_WRITE && + state & PCM_ENABLE_OUTPUT) + /* + * enable DAC data transfer from PC + */ + waveartist_cmd1(devc, WACMD_OUTPUTSTART); + + spin_unlock_irqrestore(&waveartist_lock, flags); +} + +static int +waveartist_set_speed(int dev, int arg) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + + if (arg <= 0) + return portc->speed; + + if (arg < 5000) + arg = 5000; + if (arg > 44100) + arg = 44100; + + portc->speed = arg; + return portc->speed; + +} + +static short +waveartist_set_channels(int dev, short arg) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + + if (arg != 1 && arg != 2) + return portc->channels; + + portc->channels = arg; + return arg; +} + +static unsigned int +waveartist_set_bits(int dev, unsigned int arg) +{ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + + if (arg == 0) + return portc->audio_format; + + if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8)) + arg = AFMT_U8; + + portc->audio_format = arg; + + return arg; +} + +static struct audio_driver waveartist_audio_driver = { + owner: THIS_MODULE, + open: waveartist_open, + close: waveartist_close, + output_block: waveartist_output_block, + start_input: waveartist_start_input, + ioctl: waveartist_ioctl, + prepare_for_input: waveartist_prepare_for_input, + prepare_for_output: waveartist_prepare_for_output, + halt_io: waveartist_halt, + halt_input: waveartist_halt_input, + halt_output: waveartist_halt_output, + trigger: waveartist_trigger, + set_speed: waveartist_set_speed, + set_bits: waveartist_set_bits, + set_channels: waveartist_set_channels +}; + + +static void +waveartist_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + wavnc_info *devc = (wavnc_info *)dev_id; + int irqstatus, status; + + irqstatus = inb(devc->hw.io_base + IRQSTAT); + status = inb(devc->hw.io_base + STATR); + + if (debug_flg & DEBUG_INTR) + printk("waveartist_intr: stat=%02x, irqstat=%02x\n", + status, irqstatus); + + if (status & IRQ_REQ) /* Clear interrupt */ + waveartist_iack(devc); + else + printk(KERN_WARNING "waveartist: unexpected interrupt\n"); + + if (irqstatus & 0x01) { + int temp = 1; + + /* PCM buffer done + */ + if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) { + DMAbuf_outputintr(devc->playback_dev, 1); + temp = 0; + } + if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) { + DMAbuf_inputintr(devc->record_dev); + temp = 0; + } + if (temp) //default: + printk(KERN_WARNING "waveartist: Unknown interrupt\n"); + } + if (irqstatus & 0x2) + // We do not use SB mode natively... + printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n"); +} + +/* ------------------------------------------------------------------------- + * Mixer stuff + */ +struct mix_ent { + unsigned char reg_l; + unsigned char reg_r; + unsigned char shift; + unsigned char max; +}; + +static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = { + { 2, 6, 1, 7 }, /* SOUND_MIXER_VOLUME */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_BASS */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_TREBLE */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_SYNTH */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_PCM */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_SPEAKER */ + { 0, 4, 6, 31 }, /* SOUND_MIXER_LINE */ + { 2, 6, 4, 3 }, /* SOUND_MIXER_MIC */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_CD */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_IMIX */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_ALTPCM */ +#if 0 + { 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_IGAIN */ +#else + { 0, 0, 0, 0 }, /* SOUND_MIXER_RECLEV */ + { 3, 7, 0, 7 }, /* SOUND_MIXER_IGAIN */ +#endif + { 0, 0, 0, 0 }, /* SOUND_MIXER_OGAIN */ + { 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1 */ + { 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_LINE3 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL1 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL2 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL3 */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEIN */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEOUT */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_VIDEO */ + { 0, 0, 0, 0 }, /* SOUND_MIXER_RADIO */ + { 0, 0, 0, 0 } /* SOUND_MIXER_MONITOR */ +}; + +static void +waveartist_mixer_update(wavnc_info *devc, int whichDev) +{ + unsigned int lev_left, lev_right; + + lev_left = devc->levels[whichDev] & 0xff; + lev_right = devc->levels[whichDev] >> 8; + + if (lev_left > 100) + lev_left = 100; + if (lev_right > 100) + lev_right = 100; + +#define SCALE(lev,max) ((lev) * (max) / 100) + + if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT) + whichDev = SOUND_MIXER_VOLUME; + + if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) { + const struct mix_ent *mix = mix_devs + whichDev; + unsigned int mask, left, right; + + mask = mix->max << mix->shift; + lev_left = SCALE(lev_left, mix->max) << mix->shift; + lev_right = SCALE(lev_right, mix->max) << mix->shift; + + /* read left setting */ + left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | + mix->reg_l << 8); + + /* read right setting */ + right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | + mix->reg_r << 8); + + left = (left & ~mask) | (lev_left & mask); + right = (right & ~mask) | (lev_right & mask); + + /* write left,right back */ + waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); + } else { + switch(whichDev) { + case SOUND_MIXER_PCM: + waveartist_cmd3(devc, WACMD_SET_LEVEL, + SCALE(lev_left, 32767), + SCALE(lev_right, 32767)); + break; + + case SOUND_MIXER_SYNTH: + waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL, + SCALE(lev_left, 32767), + SCALE(lev_right, 32767)); + break; + } + } +} + +/* + * Set the ADC MUX to the specified values. We do NOT do any + * checking of the values passed, since we assume that the + * relevant *_select_input function has done that for us. + */ +static void +waveartist_set_adc_mux(wavnc_info *devc, char left_dev, char right_dev) +{ + unsigned int reg_08, reg_09; + + reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800); + reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900); + + reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev; + + waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09); +} + +/* + * Decode a recording mask into a mixer selection as follows: + * + * OSS Source WA Source Actual source + * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) + * SOUND_MASK_LINE Line Line in + * SOUND_MASK_LINE1 Aux 1 Aux 1 in + * SOUND_MASK_LINE2 Aux 2 Aux 2 in + * SOUND_MASK_MIC Mic Microphone + */ +static unsigned int +waveartist_select_input(wavnc_info *devc, unsigned int recmask, + unsigned char *dev_l, unsigned char *dev_r) +{ + unsigned int recdev = ADC_MUX_NONE; + + if (recmask & SOUND_MASK_IMIX) { + recmask = SOUND_MASK_IMIX; + recdev = ADC_MUX_MIXER; + } else if (recmask & SOUND_MASK_LINE2) { + recmask = SOUND_MASK_LINE2; + recdev = ADC_MUX_AUX2; + } else if (recmask & SOUND_MASK_LINE1) { + recmask = SOUND_MASK_LINE1; + recdev = ADC_MUX_AUX1; + } else if (recmask & SOUND_MASK_LINE) { + recmask = SOUND_MASK_LINE; + recdev = ADC_MUX_LINE; + } else if (recmask & SOUND_MASK_MIC) { + recmask = SOUND_MASK_MIC; + recdev = ADC_MUX_MIC; + } + + *dev_l = *dev_r = recdev; + + return recmask; +} + +static int +waveartist_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l, + unsigned char lev_r) +{ + switch (dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_MIC: + case SOUND_MIXER_IGAIN: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_LINE2: + devc->levels[dev] = lev_l | lev_r << 8; + break; + + case SOUND_MIXER_IMIX: + break; + + default: + dev = -EINVAL; + break; + } + + return dev; +} + +static int waveartist_get_mixer(wavnc_info *devc, int dev) +{ + return devc->levels[dev]; +} + +static const struct waveartist_mixer_info waveartist_mixer = { + supported_devs: SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN, + recording_devs: SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | + SOUND_MASK_IMIX, + stereo_devs: (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~ + (SOUND_MASK_SPEAKER | SOUND_MASK_IMIX), + select_input: waveartist_select_input, + decode_mixer: waveartist_decode_mixer, + get_mixer: waveartist_get_mixer, +}; + +static void +waveartist_set_recmask(wavnc_info *devc, unsigned int recmask) +{ + unsigned char dev_l, dev_r; + + recmask &= devc->mix->recording_devs; + + /* + * If more than one recording device selected, + * disable the device that is currently in use. + */ + if (hweight32(recmask) > 1) + recmask &= ~devc->recmask; + + /* + * Translate the recording device mask into + * the ADC multiplexer settings. + */ + devc->recmask = devc->mix->select_input(devc, recmask, + &dev_l, &dev_r); + + waveartist_set_adc_mux(devc, dev_l, dev_r); +} + +static int +waveartist_set_mixer(wavnc_info *devc, int dev, unsigned int level) +{ + unsigned int lev_left = level & 0x00ff; + unsigned int lev_right = (level & 0xff00) >> 8; + + if (lev_left > 100) + lev_left = 100; + if (lev_right > 100) + lev_right = 100; + + /* + * Mono devices have their right volume forced to their + * left volume. (from ALSA driver OSS emulation). + */ + if (!(devc->mix->stereo_devs & (1 << dev))) + lev_right = lev_left; + + dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right); + + if (dev >= 0) + waveartist_mixer_update(devc, dev); + + return dev < 0 ? dev : 0; +} + +static int +waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int ret = 0, val, nr; + + /* + * All SOUND_MIXER_* ioctls use type 'M' + */ + if (((cmd >> 8) & 255) != 'M') + return -ENOIOCTLCMD; + +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) { + ret = vnc_private_ioctl(dev, cmd, arg); + if (ret != -ENOIOCTLCMD) + return ret; + else + ret = 0; + } +#endif + + nr = cmd & 0xff; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (nr) { + case SOUND_MIXER_RECSRC: + waveartist_set_recmask(devc, val); + break; + + default: + ret = -EINVAL; + if (nr < SOUND_MIXER_NRDEVICES && + devc->mix->supported_devs & (1 << nr)) + ret = waveartist_set_mixer(devc, nr, val); + } + } + + if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) { + ret = -EINVAL; + + switch (nr) { + case SOUND_MIXER_RECSRC: + ret = devc->recmask; + break; + + case SOUND_MIXER_DEVMASK: + ret = devc->mix->supported_devs; + break; + + case SOUND_MIXER_STEREODEVS: + ret = devc->mix->stereo_devs; + break; + + case SOUND_MIXER_RECMASK: + ret = devc->mix->recording_devs; + break; + + case SOUND_MIXER_CAPS: + ret = SOUND_CAP_EXCL_INPUT; + break; + + default: + if (nr < SOUND_MIXER_NRDEVICES) + ret = devc->mix->get_mixer(devc, nr); + break; + } + + if (ret >= 0) + ret = put_user(ret, (int *)arg) ? -EFAULT : 0; + } + + return ret; +} + +static struct mixer_operations waveartist_mixer_operations = +{ + owner: THIS_MODULE, + id: "WaveArtist", + name: "WaveArtist", + ioctl: waveartist_mixer_ioctl +}; + +static void +waveartist_mixer_reset(wavnc_info *devc) +{ + int i; + + if (debug_flg & DEBUG_MIXER) + printk("%s: mixer_reset\n", devc->hw.name); + + /* + * reset mixer cmd + */ + waveartist_cmd1(devc, WACMD_RST_MIXER); + + /* + * set input for ADC to come from 'quiet' + * turn on default modes + */ + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836); + + /* + * set mixer input select to none, RX filter gains 0 dB + */ + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00); + + /* + * set bit 0 reg 2 to 1 - unmute MonoOut + */ + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800); + + /* set default input device = internal mic + * current recording device = none + */ + waveartist_set_recmask(devc, 0); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + waveartist_mixer_update(devc, i); +} + +static int __init waveartist_init(wavnc_info *devc) +{ + wavnc_port_info *portc; + char rev[3], dev_name[64]; + int my_dev; + + if (waveartist_reset(devc)) + return -ENODEV; + + sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name); + + if (waveartist_getrev(devc, rev)) { + strcat(dev_name, " rev. "); + strcat(dev_name, rev); + } + strcat(dev_name, ")"); + + conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq, + devc->hw.dma, devc->hw.dma2); + + portc = (wavnc_port_info *)kmalloc(sizeof(wavnc_port_info), GFP_KERNEL); + if (portc == NULL) + goto nomem; + + memset(portc, 0, sizeof(wavnc_port_info)); + + my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name, + &waveartist_audio_driver, sizeof(struct audio_driver), + devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8, + devc, devc->hw.dma, devc->hw.dma2); + + if (my_dev < 0) + goto free; + + audio_devs[my_dev]->portc = portc; + + waveartist_mixer_reset(devc); + + /* + * clear any pending interrupt + */ + waveartist_iack(devc); + + if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) { + printk(KERN_ERR "%s: IRQ %d in use\n", + devc->hw.name, devc->hw.irq); + goto uninstall; + } + + if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) { + printk(KERN_ERR "%s: Can't allocate DMA%d\n", + devc->hw.name, devc->hw.dma); + goto uninstall_irq; + } + + if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA) + if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) { + printk(KERN_ERR "%s: can't allocate DMA%d\n", + devc->hw.name, devc->hw.dma2); + goto uninstall_dma; + } + + waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE); + + audio_devs[my_dev]->mixer_dev = + sound_install_mixer(MIXER_DRIVER_VERSION, + dev_name, + &waveartist_mixer_operations, + sizeof(struct mixer_operations), + devc); + + return my_dev; + +uninstall_dma: + sound_free_dma(devc->hw.dma); + +uninstall_irq: + free_irq(devc->hw.irq, devc); + +uninstall: + sound_unload_audiodev(my_dev); + +free: + kfree(portc); + +nomem: + return -1; +} + +static int __init probe_waveartist(struct address_info *hw_config) +{ + wavnc_info *devc = &adev_info[nr_waveartist_devs]; + + if (nr_waveartist_devs >= MAX_AUDIO_DEV) { + printk(KERN_WARNING "waveartist: too many audio devices\n"); + return 0; + } + + if (check_region(hw_config->io_base, 15)) { + printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); + return 0; + } + + if (hw_config->irq > 15 || hw_config->irq < 0) { + printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", + hw_config->irq); + return 0; + } + + if (hw_config->dma != 3) { + printk(KERN_WARNING "WaveArtist: Bad DMA %d\n", + hw_config->dma); + return 0; + } + + hw_config->name = "WaveArtist"; + devc->hw = *hw_config; + devc->open_mode = 0; + devc->chip_name = "RWA-010"; + + return 1; +} + +static void __init +attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix) +{ + wavnc_info *devc = &adev_info[nr_waveartist_devs]; + + /* + * NOTE! If irq < 0, there is another driver which has allocated the + * IRQ so that this driver doesn't need to allocate/deallocate it. + * The actually used IRQ is ABS(irq). + */ + devc->hw = *hw; + devc->hw.irq = (hw->irq > 0) ? hw->irq : 0; + devc->open_mode = 0; + devc->playback_dev = 0; + devc->record_dev = 0; + devc->audio_flags = DMA_AUTOMODE; + devc->levels = levels; + + if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA) + devc->audio_flags |= DMA_DUPLEX; + + request_region(hw->io_base, 15, devc->hw.name); + + devc->mix = mix; + devc->dev_no = waveartist_init(devc); + + if (devc->dev_no < 0) + release_region(hw->io_base, 15); + else { +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) { + init_timer(&vnc_timer); + vnc_timer.function = vnc_slider_tick; + vnc_timer.expires = jiffies; + vnc_timer.data = nr_waveartist_devs; + add_timer(&vnc_timer); + + vnc_configure_mixer(devc, 0); + + devc->no_autoselect = 1; + } +#endif + nr_waveartist_devs += 1; + } +} + +static void __exit unload_waveartist(struct address_info *hw) +{ + wavnc_info *devc = NULL; + int i; + + for (i = 0; i < nr_waveartist_devs; i++) + if (hw->io_base == adev_info[i].hw.io_base) { + devc = adev_info + i; + break; + } + + if (devc != NULL) { + int mixer; + +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) + del_timer(&vnc_timer); +#endif + + release_region(devc->hw.io_base, 15); + + waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0); + + if (devc->hw.irq >= 0) + free_irq(devc->hw.irq, devc); + + sound_free_dma(devc->hw.dma); + + if (devc->hw.dma != devc->hw.dma2 && + devc->hw.dma2 != NO_DMA) + sound_free_dma(devc->hw.dma2); + + mixer = audio_devs[devc->dev_no]->mixer_dev; + + if (mixer >= 0) + sound_unload_mixerdev(mixer); + + if (devc->dev_no >= 0) + sound_unload_audiodev(devc->dev_no); + + nr_waveartist_devs -= 1; + + for (; i < nr_waveartist_devs; i++) + adev_info[i] = adev_info[i + 1]; + } else + printk(KERN_WARNING "waveartist: can't find device " + "to unload\n"); +} + +#ifdef CONFIG_ARCH_NETWINDER + +/* + * Rebel.com Netwinder specifics... + */ + +#include + +#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec + +#define MIXER_PRIVATE3_RESET 0x53570000 +#define MIXER_PRIVATE3_READ 0x53570001 +#define MIXER_PRIVATE3_WRITE 0x53570002 + +#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit +#define VNC_MUTE_LINE_OUT 0x10 +#define VNC_PHONE_DETECT 0x20 +#define VNC_HANDSET_DETECT 0x40 +#define VNC_DISABLE_AUTOSWITCH 0x80 + +extern spinlock_t gpio_lock; + +static inline void +vnc_mute_spkr(wavnc_info *devc) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE); + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static void +vnc_mute_lout(wavnc_info *devc) +{ + unsigned int left, right; + + left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL); + right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400); + + if (devc->line_mute_state) { + left &= ~1; + right &= ~1; + } else { + left |= 1; + right |= 1; + } + waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); + +} + +static int +vnc_volume_slider(wavnc_info *devc) +{ + static signed int old_slider_volume; + unsigned long flags; + signed int volume = 255; + + *CSR_TIMER1_LOAD = 0x00ffffff; + + save_flags(flags); + cli(); + + outb(0xFF, 0x201); + *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; + + while (volume && (inb(0x201) & 0x01)) + volume--; + + *CSR_TIMER1_CNTL = 0; + + restore_flags(flags); + + volume = 0x00ffffff - *CSR_TIMER1_VALUE; + + +#ifndef REVERSE + volume = 150 - (volume >> 5); +#else + volume = (volume >> 6) - 25; +#endif + + if (volume < 0) + volume = 0; + + if (volume > 100) + volume = 100; + + /* + * slider quite often reads +-8, so debounce this random noise + */ + if (abs(volume - old_slider_volume) > 7) { + old_slider_volume = volume; + + if (debug_flg & DEBUG_MIXER) + printk(KERN_DEBUG "Slider volume: %d.\n", volume); + } + + return old_slider_volume; +} + +/* + * Decode a recording mask into a mixer selection on the NetWinder + * as follows: + * + * OSS Source WA Source Actual source + * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) + * SOUND_MASK_LINE Line Line in + * SOUND_MASK_LINE1 Left Mic Handset + * SOUND_MASK_PHONEIN Left Aux Telephone microphone + * SOUND_MASK_MIC Right Mic Builtin microphone + */ +static unsigned int +netwinder_select_input(wavnc_info *devc, unsigned int recmask, + unsigned char *dev_l, unsigned char *dev_r) +{ + unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE; + + if (recmask & SOUND_MASK_IMIX) { + recmask = SOUND_MASK_IMIX; + recdev_l = ADC_MUX_MIXER; + recdev_r = ADC_MUX_MIXER; + } else if (recmask & SOUND_MASK_LINE) { + recmask = SOUND_MASK_LINE; + recdev_l = ADC_MUX_LINE; + recdev_r = ADC_MUX_LINE; + } else if (recmask & SOUND_MASK_LINE1) { + recmask = SOUND_MASK_LINE1; + waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ + recdev_l = ADC_MUX_MIC; + recdev_r = ADC_MUX_NONE; + } else if (recmask & SOUND_MASK_PHONEIN) { + recmask = SOUND_MASK_PHONEIN; + waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ + recdev_l = ADC_MUX_AUX1; + recdev_r = ADC_MUX_NONE; + } else if (recmask & SOUND_MASK_MIC) { + recmask = SOUND_MASK_MIC; + waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */ + recdev_l = ADC_MUX_NONE; + recdev_r = ADC_MUX_MIC; + } + + *dev_l = recdev_l; + *dev_r = recdev_r; + + return recmask; +} + +static int +netwinder_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l, + unsigned char lev_r) +{ + switch (dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_IGAIN: + devc->levels[dev] = lev_l | lev_r << 8; + break; + + case SOUND_MIXER_MIC: /* right mic only */ + devc->levels[SOUND_MIXER_MIC] &= 0xff; + devc->levels[SOUND_MIXER_MIC] |= lev_l << 8; + break; + + case SOUND_MIXER_LINE1: /* left mic only */ + devc->levels[SOUND_MIXER_MIC] &= 0xff00; + devc->levels[SOUND_MIXER_MIC] |= lev_l; + dev = SOUND_MIXER_MIC; + break; + + case SOUND_MIXER_PHONEIN: /* left aux only */ + devc->levels[SOUND_MIXER_LINE1] = lev_l; + dev = SOUND_MIXER_LINE1; + break; + + case SOUND_MIXER_IMIX: + case SOUND_MIXER_PHONEOUT: + break; + + default: + dev = -EINVAL; + break; + } + return dev; +} + +static int netwinder_get_mixer(wavnc_info *devc, int dev) +{ + int levels; + + switch (dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_IGAIN: + levels = devc->levels[dev]; + break; + + case SOUND_MIXER_MIC: /* builtin mic: right mic only */ + levels = devc->levels[SOUND_MIXER_MIC] >> 8; + levels |= levels << 8; + break; + + case SOUND_MIXER_LINE1: /* handset mic: left mic only */ + levels = devc->levels[SOUND_MIXER_MIC] & 0xff; + levels |= levels << 8; + break; + + case SOUND_MIXER_PHONEIN: /* phone mic: left aux1 only */ + levels = devc->levels[SOUND_MIXER_LINE1] & 0xff; + levels |= levels << 8; + break; + + default: + levels = 0; + } + + return levels; +} + +/* + * Waveartist specific mixer information. + */ +static const struct waveartist_mixer_info netwinder_mixer = { + supported_devs: SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | + SOUND_MASK_PCM | SOUND_MASK_SPEAKER | + SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_IMIX | SOUND_MASK_LINE1 | + SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT| + SOUND_MASK_IGAIN, + + recording_devs: SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_IMIX | SOUND_MASK_LINE1 | + SOUND_MASK_PHONEIN, + + stereo_devs: SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | + SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_IMIX | SOUND_MASK_IGAIN, + + select_input: netwinder_select_input, + decode_mixer: netwinder_decode_mixer, + get_mixer: netwinder_get_mixer, +}; + +static void +vnc_configure_mixer(wavnc_info *devc, unsigned int recmask) +{ + if (!devc->no_autoselect) { + if (devc->handset_detect) { + recmask = SOUND_MASK_LINE1; + devc->spkr_mute_state = devc->line_mute_state = 1; + } else if (devc->telephone_detect) { + recmask = SOUND_MASK_PHONEIN; + devc->spkr_mute_state = devc->line_mute_state = 1; + } else { + /* unless someone has asked for LINE-IN, + * we default to MIC + */ + if ((devc->recmask & SOUND_MASK_LINE) == 0) + devc->recmask = SOUND_MASK_MIC; + devc->spkr_mute_state = devc->line_mute_state = 0; + } + vnc_mute_spkr(devc); + vnc_mute_lout(devc); + + if (recmask != devc->recmask) + waveartist_set_recmask(devc, recmask); + } +} + +static int +vnc_slider(wavnc_info *devc) +{ + signed int slider_volume; + unsigned int temp, old_hs, old_td; + + /* + * read the "buttons" state. + * Bit 4 = 0 means handset present + * Bit 5 = 1 means phone offhook + */ + temp = inb(0x201); + + old_hs = devc->handset_detect; + old_td = devc->telephone_detect; + + devc->handset_detect = !(temp & 0x10); + devc->telephone_detect = !!(temp & 0x20); + + if (!devc->no_autoselect && + (old_hs != devc->handset_detect || + old_td != devc->telephone_detect)) + vnc_configure_mixer(devc, devc->recmask); + + slider_volume = vnc_volume_slider(devc); + + /* + * If we're using software controlled volume, and + * the slider moves by more than 20%, then we + * switch back to slider controlled volume. + */ + if (abs(devc->slider_vol - slider_volume) > 20) + devc->use_slider = 1; + + /* + * use only left channel + */ + temp = levels[SOUND_MIXER_VOLUME] & 0xFF; + + if (slider_volume != temp && devc->use_slider) { + devc->slider_vol = slider_volume; + + waveartist_set_mixer(devc, SOUND_MIXER_VOLUME, + slider_volume | slider_volume << 8); + + return 1; + } + + return 0; +} + +static void +vnc_slider_tick(unsigned long data) +{ + int next_timeout; + + if (vnc_slider(adev_info + data)) + next_timeout = 5; // mixer reported change + else + next_timeout = VNC_TIMER_PERIOD; + + mod_timer(&vnc_timer, jiffies + next_timeout); +} + +static int +vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int val; + + switch (cmd) { + case SOUND_MIXER_PRIVATE1: + { + u_int prev_spkr_mute, prev_line_mute, prev_auto_state; + int val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* check if parameter is logical */ + if (val & ~(VNC_MUTE_INTERNAL_SPKR | + VNC_MUTE_LINE_OUT | + VNC_DISABLE_AUTOSWITCH)) + return -EINVAL; + + prev_auto_state = devc->no_autoselect; + prev_spkr_mute = devc->spkr_mute_state; + prev_line_mute = devc->line_mute_state; + + devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0; + devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0; + devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0; + + if (prev_spkr_mute != devc->spkr_mute_state) + vnc_mute_spkr(devc); + + if (prev_line_mute != devc->line_mute_state) + vnc_mute_lout(devc); + + if (prev_auto_state != devc->no_autoselect) + vnc_configure_mixer(devc, devc->recmask); + + return 0; + } + + case SOUND_MIXER_PRIVATE2: + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { +#define VNC_SOUND_PAUSE 0x53 //to pause the DSP +#define VNC_SOUND_RESUME 0x57 //to unpause the DSP + case VNC_SOUND_PAUSE: + waveartist_cmd1(devc, 0x16); + break; + + case VNC_SOUND_RESUME: + waveartist_cmd1(devc, 0x18); + break; + + default: + return -EINVAL; + } + return 0; + + /* private ioctl to allow bulk access to waveartist */ + case SOUND_MIXER_PRIVATE3: + { + unsigned long flags; + int mixer_reg[15], i, val; + + if (get_user(val, (int *)arg)) + return -EFAULT; + if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg))) + return -EFAULT; + + switch (mixer_reg[14]) { + case MIXER_PRIVATE3_RESET: + waveartist_mixer_reset(devc); + break; + + case MIXER_PRIVATE3_WRITE: + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]); + + waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]); + waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]); + break; + + case MIXER_PRIVATE3_READ: + spin_lock_irqsave(&waveartist_lock, flags); + + for (i = 0x30; i < 14 << 8; i += 1 << 8) + waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8)); + + spin_unlock_irqrestore(&waveartist_lock, flags); + + if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg))) + return -EFAULT; + break; + + default: + return -EINVAL; + } + return 0; + } + + /* read back the state from PRIVATE1 */ + case SOUND_MIXER_PRIVATE4: + val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) | + (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) | + (devc->handset_detect ? VNC_HANDSET_DETECT : 0) | + (devc->telephone_detect ? VNC_PHONE_DETECT : 0) | + (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0); + + return put_user(val, (int *)arg) ? -EFAULT : 0; + } + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + /* + * special case for master volume: if we + * received this call - switch from hw + * volume control to a software volume + * control, till the hw volume is modified + * to signal that user wants to be back in + * hardware... + */ + if ((cmd & 0xff) == SOUND_MIXER_VOLUME) + devc->use_slider = 0; + + /* speaker output */ + if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) { + unsigned int val, l, r; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + l = val & 0x7f; + r = (val & 0x7f00) >> 8; + val = (l + r) / 2; + devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8); + devc->spkr_mute_state = (val <= 50); + vnc_mute_spkr(devc); + return 0; + } + } + + return -ENOIOCTLCMD; +} + +#endif + +static struct address_info cfg; + +static int attached; + +static int __initdata io = 0; +static int __initdata irq = 0; +static int __initdata dma = 0; +static int __initdata dma2 = 0; + + +static int __init init_waveartist(void) +{ + const struct waveartist_mixer_info *mix; + + if (!io && machine_is_netwinder()) { + /* + * The NetWinder WaveArtist is at a fixed address. + * If the user does not supply an address, use the + * well-known parameters. + */ + io = 0x250; + irq = 12; + dma = 3; + dma2 = 7; + } + + mix = &waveartist_mixer; +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) + mix = &netwinder_mixer; +#endif + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + if (!probe_waveartist(&cfg)) + return -ENODEV; + + attach_waveartist(&cfg, mix); + attached = 1; + + return 0; +} + +static void __exit cleanup_waveartist(void) +{ + if (attached) + unload_waveartist(&cfg); +} + +module_init(init_waveartist); +module_exit(cleanup_waveartist); + +#ifndef MODULE +static int __init setup_waveartist(char *str) +{ + /* io, irq, dma, dma2 */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + + return 1; +} +__setup("waveartist=", setup_waveartist); +#endif + +MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver"); +MODULE_PARM(io, "i"); /* IO base */ +MODULE_PARM(irq, "i"); /* IRQ */ +MODULE_PARM(dma, "i"); /* DMA */ +MODULE_PARM(dma2, "i"); /* DMA2 */ +MODULE_LICENSE("GPL"); diff -Nru linux/sound/oss/waveartist.h linux-2.4.19-pre5-mjc/sound/oss/waveartist.h --- linux/sound/oss/waveartist.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/waveartist.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,92 @@ +/* + * linux/drivers/sound/waveartist.h + * + * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder + */ + +//registers +#define CMDR 0 +#define DATR 2 +#define CTLR 4 +#define STATR 5 +#define IRQSTAT 12 + +//bit defs +//reg STATR +#define CMD_WE 0x80 +#define CMD_RF 0x40 +#define DAT_WE 0x20 +#define DAT_RF 0x10 + +#define IRQ_REQ 0x08 +#define DMA1 0x04 +#define DMA0 0x02 + +//bit defs +//reg CTLR +#define CMD_WEIE 0x80 +#define CMD_RFIE 0x40 +#define DAT_WEIE 0x20 +#define DAT_RFIE 0x10 + +#define RESET 0x08 +#define DMA1_IE 0x04 +#define DMA0_IE 0x02 +#define IRQ_ACK 0x01 + +//commands + +#define WACMD_SYSTEMID 0x00 +#define WACMD_GETREV 0x00 +#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U +#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo +#define WACMD_INPUTSPEED 0x12 //sampling rate +#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO +#define WACMD_INPUTSIZE 0x14 //samples to interrupt +#define WACMD_INPUTSTART 0x15 //start ADC +#define WACMD_INPUTPAUSE 0x16 //pause ADC +#define WACMD_INPUTSTOP 0x17 //stop ADC +#define WACMD_INPUTRESUME 0x18 //resume ADC +#define WACMD_INPUTPIO 0x19 //PIO ADC + +#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U +#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo +#define WACMD_OUTPUTSPEED 0x22 //sampling rate +#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO +#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt +#define WACMD_OUTPUTSTART 0x25 //start ADC +#define WACMD_OUTPUTPAUSE 0x26 //pause ADC +#define WACMD_OUTPUTSTOP 0x27 //stop ADC +#define WACMD_OUTPUTRESUME 0x28 //resume ADC +#define WACMD_OUTPUTPIO 0x29 //PIO ADC + +#define WACMD_GET_LEVEL 0x30 +#define WACMD_SET_LEVEL 0x31 +#define WACMD_SET_MIXER 0x32 +#define WACMD_RST_MIXER 0x33 +#define WACMD_SET_MONO 0x34 + +/* + * Definitions for left/right recording input mux + */ +#define ADC_MUX_NONE 0 +#define ADC_MUX_MIXER 1 +#define ADC_MUX_LINE 2 +#define ADC_MUX_AUX2 3 +#define ADC_MUX_AUX1 4 +#define ADC_MUX_MIC 5 + +/* + * Definitions for mixer gain settings + */ +#define MIX_GAIN_LINE 0 /* line in */ +#define MIX_GAIN_AUX1 1 /* aux1 */ +#define MIX_GAIN_AUX2 2 /* aux2 */ +#define MIX_GAIN_XMIC 3 /* crossover mic */ +#define MIX_GAIN_MIC 4 /* normal mic */ +#define MIX_GAIN_PREMIC 5 /* preamp mic */ +#define MIX_GAIN_OUT 6 /* output */ +#define MIX_GAIN_MONO 7 /* mono in */ + +int wa_sendcmd(unsigned int cmd); +int wa_writecmd(unsigned int cmd, unsigned int arg); diff -Nru linux/sound/oss/wavfront.c linux-2.4.19-pre5-mjc/sound/oss/wavfront.c --- linux/sound/oss/wavfront.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/wavfront.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,3529 @@ +/* -*- linux-c -*- + * + * sound/wavfront.c + * + * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + * It also provides support for the ICS emulation of an MPU-401. Full + * support for the ICS emulation's "virtual MIDI mode" is provided in + * wf_midi.c. + * + * Support is also provided for the Tropez Plus' onboard FX processor, + * a Yamaha YSS225. Currently, code exists to configure the YSS225, + * and there is an interface allowing tweaking of any of its memory + * addresses. However, I have been unable to decipher the logical + * positioning of the configuration info for various effects, so for + * now, you just get the YSS225 in the same state as Turtle Beach's + * "SETUPSND.EXE" utility leaves it. + * + * The boards' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co], + * This chip also controls the configuration of the card: the wavefront + * synth is logical unit 4. + * + * + * Supported devices: + * + * /dev/dsp - using cs4232+ad1848 modules, OSS compatible + * /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible + * /dev/synth00 - raw synth interface + * + ********************************************************************** + * + * Copyright (C) by Paul Barton-Davis 1998 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * Although the relevant code here is all new, the handling of + * sample/alias/multi- samples is entirely based on a driver by Matt + * Martin and Rutger Nijlunsing which demonstrated how to get things + * to work correctly. The GUS patch loading code has been almost + * unaltered by me, except to fit formatting and function names in the + * rest of the file. Many thanks to them. + * + * Appreciation and thanks to Hannu Savolainen for his early work on the Maui + * driver, and answering a few questions while this one was developed. + * + * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their + * complete lack of help in developing this driver, and in particular + * for their utter silence in response to questions about undocumented + * aspects of configuring a WaveFront soundcard, particularly the + * effects processor. + * + * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $ + * + * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added some __init and __initdata to entries in yss225.c + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "sound_config.h" + +#include + +#define _MIDI_SYNTH_C_ +#define MIDI_SYNTH_NAME "WaveFront MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +/* Compile-time control of the extent to which OSS is supported. + + I consider /dev/sequencer to be an anachronism, but given its + widespread usage by various Linux MIDI software, it seems worth + offering support to it if its not too painful. Instead of using + /dev/sequencer, I recommend: + + for synth programming and patch loading: /dev/synthNN + for kernel-synchronized MIDI sequencing: the ALSA sequencer + for direct MIDI control: /dev/midiNN + + I have never tried static compilation into the kernel. The #if's + for this are really just notes to myself about what the code is + for. +*/ + +#define OSS_SUPPORT_SEQ 0x1 /* use of /dev/sequencer */ +#define OSS_SUPPORT_STATIC_INSTALL 0x2 /* static compilation into kernel */ + +#define OSS_SUPPORT_LEVEL 0x1 /* just /dev/sequencer for now */ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ +static int (*midi_load_patch) (int devno, int format, const char *addr, + int offs, int count, int pmgr_flag) = NULL; +#endif /* OSS_SUPPORT_SEQ */ + +/* if WF_DEBUG not defined, no run-time debugging messages will + be available via the debug flag setting. Given the current + beta state of the driver, this will remain set until a future + version. +*/ + +#define WF_DEBUG 1 + +#ifdef WF_DEBUG + +/* Thank goodness for gcc's preprocessor ... */ + +#define DPRINT(cond, format, args...) \ + if ((dev.debug & (cond)) == (cond)) { \ + printk (KERN_DEBUG LOGNAME format, ## args); \ + } +#else +#define DPRINT(cond, format, args...) +#endif + +#define LOGNAME "WaveFront: " + +/* bitmasks for WaveFront status port value */ + +#define STAT_RINTR_ENABLED 0x01 +#define STAT_CAN_READ 0x02 +#define STAT_INTR_READ 0x04 +#define STAT_WINTR_ENABLED 0x10 +#define STAT_CAN_WRITE 0x20 +#define STAT_INTR_WRITE 0x40 + +/*** Module-accessible parameters ***************************************/ + +int wf_raw; /* we normally check for "raw state" to firmware + loading. if set, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +int debug_default; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +int wait_polls = 2000; /* This is a number of tries we poll the status register + before resorting to sleeping. WaveFront being an ISA + card each poll takes about 1.2us. So before going to + sleep we wait up to 2.4ms in a loop. + */ + +int sleep_length = HZ/100; /* This says how long we're going to sleep between polls. + 10ms sounds reasonable for fast response. + */ + +int sleep_tries = 50; /* Wait for status 0.5 seconds total. */ + +int reset_time = 2; /* hundreths of a second we wait after a HW reset for + the expected interrupt. + */ + +int ramcheck_time = 20; /* time in seconds to wait while ROM code + checks on-board RAM. + */ + +int osrun_time = 10; /* time in seconds we wait for the OS to + start running. + */ + +MODULE_PARM(wf_raw,"i"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM(debug_default,"i"); +MODULE_PARM(wait_polls,"i"); +MODULE_PARM(sleep_length,"i"); +MODULE_PARM(sleep_tries,"i"); +MODULE_PARM(ospath,"s"); +MODULE_PARM(reset_time,"i"); +MODULE_PARM(ramcheck_time,"i"); +MODULE_PARM(osrun_time,"i"); + +/***************************************************************************/ + +/* Note: because this module doesn't export any symbols, this really isn't + a global variable, even if it looks like one. I was quite confused by + this when I started writing this as a (newer) module -- pbd. +*/ + +struct wf_config { + int devno; /* device number from kernel */ + int irq; /* "you were one, one of the few ..." */ + int base; /* low i/o port address */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + volatile int irq_cnt; /* ditto */ + int opened; /* flag, holds open(2) mode */ + char debug; /* debugging flags */ + int freemem; /* installed RAM, in bytes */ + + int synth_dev; /* devno for "raw" synth */ + int mididev; /* devno for internal MIDI */ + int ext_mididev; /* devno for external MIDI */ + int fx_mididev; /* devno for FX MIDI interface */ +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + int oss_dev; /* devno for OSS sequencer synth */ +#endif /* OSS_SUPPORT_SEQ */ + + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char has_fx; /* has FX processor (Tropez+) */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_on; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ + wait_queue_head_t interrupt_sleeper; +} dev; + +static int detect_wffx(void); +static int wffx_ioctl (wavefront_fx_info *); +static int wffx_init (void); + +static int wavefront_delete_sample (int sampnum); +static int wavefront_find_free_sample (void); + +/* From wf_midi.c */ + +extern int virtual_midi_enable (void); +extern int virtual_midi_disable (void); +extern int detect_wf_mpu (int, int); +extern int install_wf_mpu (void); +extern int uninstall_wf_mpu (void); + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0x0, 0x0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, + { 0x00 } +}; + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static inline int +wavefront_status (void) + +{ + return inb (dev.status_port); +} + +static int +wavefront_wait (int mask) + +{ + int i; + + for (i = 0; i < wait_polls; i++) + if (wavefront_status() & mask) + return 1; + + for (i = 0; i < sleep_tries; i++) { + + if (wavefront_status() & mask) { + set_current_state(TASK_RUNNING); + return 1; + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(sleep_length); + if (signal_pending(current)) + break; + } + + set_current_state(TASK_RUNNING); + return 0; +} + +static int +wavefront_read (void) + +{ + if (wavefront_wait (STAT_CAN_READ)) + return inb (dev.data_port); + + DPRINT (WF_DEBUG_DATA, "read timeout.\n"); + + return -1; +} + +static int +wavefront_write (unsigned char data) + +{ + if (wavefront_wait (STAT_CAN_WRITE)) { + outb (data, dev.data_port); + return 0; + } + + DPRINT (WF_DEBUG_DATA, "write timeout.\n"); + + return -1; +} + +static int +wavefront_cmd (int cmd, unsigned char *rbuf, unsigned char *wbuf) + +{ + int ack; + int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + printk (KERN_WARNING LOGNAME "command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned int) rbuf; + rbuf = 0; + } + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + + if (wavefront_write (cmd)) { + DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + } + + if (wfcmd->write_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (wavefront_write (wbuf[i])) { + DPRINT (WF_DEBUG_IO, "bad write for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + + if (wfcmd->read_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read()) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read ()) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for " + "error byte at " + "read byte %d " + "of 0x%x [%s].\n", + i, cmd, + wfcmd->action); + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return (0); + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + + DPRINT (WF_DEBUG_IO, "error %d (%s) " + "during " + "read for byte " + "%d of 0x%x " + "[%s].\n", + c, + wavefront_errorstr (c), + i, cmd, + wfcmd->action); + return 1; + + } + + } else { + rbuf[i] = c; + } + + DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read()) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + DPRINT (WF_DEBUG_IO, "cannot read ack for " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read ()) == -1) { + DPRINT (WF_DEBUG_DATA, + "cannot read err " + "for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + + DPRINT (WF_DEBUG_IO, "0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + + return -err; + } + } + + DPRINT (WF_DEBUG_DATA, "ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } else { + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + } + + return 0; + +} + +/*********************************************************************** +WaveFront: data munging + +Things here are weird. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8-32 bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +weird casting and worrying about bit-level offsets. + +**********************************************************************/ + +static +unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + int i; + + for (i = 0;i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = wavefront_cmd (WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + dev.sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (wavefront_cmd (WFC_GET_NSAMPLES, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME "cannot request sample count.\n"); + return -1; + } + + sc_real = sc_alias = sc_multi = dev.samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (wavefront_cmd (WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot identify sample " + "type of slot %d\n", i); + dev.sample_status[i] = WF_ST_EMPTY; + continue; + } + + dev.sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + dev.sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + printk (KERN_WARNING LOGNAME "unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + dev.samples_used++; + } + } + + printk (KERN_INFO LOGNAME + "%d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", dev.samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - dev.samples_used); + + + return (0); + +} + +static int +wavefront_get_patch_status (void) + +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = wavefront_cmd (WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + dev.patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + dev.sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + dev.patch_status[i] = 0; + } else { + printk (KERN_ERR LOGNAME "upload patch " + "error 0x%x\n", x); + dev.patch_status[i] = 0; + return 1; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (dev.patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (dev.patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + printk (KERN_INFO LOGNAME + "%d patch slots filled, %d in use\n", cnt, cnt2); + + return (0); +} + +static int +wavefront_get_program_status (void) + +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = wavefront_cmd (WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + dev.prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + dev.patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + dev.prog_status[i] = 0; + } else { + printk (KERN_ERR LOGNAME "upload program " + "error 0x%x\n", x); + dev.prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (dev.prog_status[i]) { + cnt++; + } + } + + printk (KERN_INFO LOGNAME "%d programs slots in use\n", cnt); + + return (0); +} + +static int +wavefront_send_patch (wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", + header->number); + + dev.patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (wavefront_cmd (WFC_DOWNLOAD_PATCH, 0, buf)) { + printk (KERN_ERR LOGNAME "download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_send_program (wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", + header->number); + + dev.prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + dev.patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (wavefront_cmd (WFC_DOWNLOAD_PROGRAM, 0, buf)) { + printk (KERN_WARNING LOGNAME "download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_freemem (void) + +{ + char rbuf[8]; + + if (wavefront_cmd (WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + printk (KERN_WARNING LOGNAME "can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (wavefront_patch_info *header, + UINT16 *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + UINT16 sample_short; + UINT32 length; + UINT16 *data_end = 0; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + + DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%x\n", + header->size ? "" : "header ", + header->number, header->subkey, + header->size, + (int) header->dataptr); + + if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { + int x; + + if ((x = wavefront_find_free_sample ()) < 0) { + return -ENOMEM; + } + printk (KERN_DEBUG LOGNAME "unspecified sample => %d\n", x); + header->number = x; + } + + if (header->size) { + + /* XXX its a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little + crazy: we'd need 158K of kernel space just to hold + a copy of the patch/program/sample header data. + */ + + if (dev.rom_samples_rdonly) { + if (dev.sample_status[header->number] & WF_SLOT_ROM) { + printk (KERN_ERR LOGNAME "sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (header->number); + } + + if (header->size) { + dev.freemem = wavefront_freemem (); + + if (dev.freemem < header->size) { + printk (KERN_ERR LOGNAME + "insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { + printk (KERN_ERR LOGNAME "channel selection only " + "possible on 16-bit samples"); + return -(EINVAL); + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), + initial_skip, skip); + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly weird. What kind of weirdo decided that in + a system dominated by 16 and 32 bit integers, they would use + a just 12 bits ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero ? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (wavefront_cmd (header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + printk (KERN_WARNING LOGNAME "sample %sdownload refused.\n", + header->size ? "" : "header "); + return -(EIO); + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (wavefront_cmd (WFC_DOWNLOAD_BLOCK, 0, 0)) { + printk (KERN_WARNING LOGNAME "download block " + "request refused.\n"); + return -(EIO); + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + __get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { /* GUS ? */ + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, dev.block_port); + } else { + outw (sample_short, dev.last_block_port); + } + } + + /* Get "DMA page acknowledge", even though its really + nothing to do with DMA at all. + */ + + if ((dma_ack = wavefront_read ()) != WF_DMA_ACK) { + if (dma_ack == -1) { + printk (KERN_ERR LOGNAME "upload sample " + "DMA ack timeout\n"); + return -(EIO); + } else { + printk (KERN_ERR LOGNAME "upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -(EIO); + } + } + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return (0); +} + +static int +wavefront_send_alias (wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (wavefront_cmd (WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + printk (KERN_ERR LOGNAME "download alias failed.\n"); + return -(EIO); + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return (0); +} + +static int +wavefront_send_multisample (wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + + DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", + header->number, + header->hdr.ms.NumberOfSamples, + num_samples); + + for (i = 0; i < num_samples; i++) { + DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (wavefront_cmd (WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) ((num_samples*2)+3), + msample_hdr)) { + printk (KERN_ERR LOGNAME "download of multisample failed.\n"); + return -(EIO); + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return (0); +} + +static int +wavefront_fetch_multisample (wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (wavefront_cmd (WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + printk (KERN_ERR LOGNAME "upload multisample failed.\n"); + return -(EIO); + } + + DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", + header->number, log_ns[0]); + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + char d[2]; + + if ((d[0] = wavefront_read ()) == -1) { + printk (KERN_ERR LOGNAME "upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + if ((d[1] = wavefront_read ()) == -1) { + printk (KERN_ERR LOGNAME "upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + + return (0); +} + + +static int +wavefront_send_drum (wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (wavefront_cmd (WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + printk (KERN_ERR LOGNAME "download drum failed.\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_find_free_sample (void) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(dev.sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING LOGNAME "no free sample slots!\n"); + return -1; +} + +static int +wavefront_find_free_patch (void) + +{ + int i; + + for (i = 0; i < WF_MAX_PATCH; i++) { + if (!(dev.patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING LOGNAME "no free patch slots!\n"); + return -1; +} + +static int +log2_2048(int n) + +{ + int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, + 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, + 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, + 9510, 9626, 9738, 9845, 9949, 10049, 10146}; + int i; + + /* Returns 2048*log2(n) */ + + /* FIXME: this is like doing integer math + on quantum particles (RuN) */ + + i=0; + while(n>=32*256) { + n>>=8; + i+=2048*8; + } + while(n>=32) { + n>>=1; + i+=2048; + } + i+=tbl[n]; + return(i); +} + +static int +wavefront_load_gus_patch (int devno, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info guspatch; + wavefront_patch_info samp, pat, prog; + wavefront_patch *patp; + wavefront_sample *sampp; + wavefront_program *progp; + + int i,base_note; + long sizeof_patch; + + /* Copy in the header of the GUS patch */ + + sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; + copy_from_user (&((char *) &guspatch)[offs], + &(addr)[offs], sizeof_patch - offs); + + if ((i = wavefront_find_free_patch ()) == -1) { + return -EBUSY; + } + pat.number = i; + pat.subkey = WF_ST_PATCH; + patp = &pat.hdr.p; + + if ((i = wavefront_find_free_sample ()) == -1) { + return -EBUSY; + } + samp.number = i; + samp.subkey = WF_ST_SAMPLE; + samp.size = guspatch.len; + sampp = &samp.hdr.s; + + prog.number = guspatch.instr_no; + progp = &prog.hdr.pr; + + /* Setup the patch structure */ + + patp->amplitude_bias=guspatch.volume; + patp->portamento=0; + patp->sample_number= samp.number & 0xff; + patp->sample_msb= samp.number>>8; + patp->pitch_bend= /*12*/ 0; + patp->mono=1; + patp->retrigger=1; + patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; + patp->frequency_bias=0; + patp->restart=0; + patp->reuse=0; + patp->reset_lfo=1; + patp->fm_src2=0; + patp->fm_src1=WF_MOD_MOD_WHEEL; + patp->am_src=WF_MOD_PRESSURE; + patp->am_amount=127; + patp->fc1_mod_amount=0; + patp->fc2_mod_amount=0; + patp->fm_amount1=0; + patp->fm_amount2=0; + patp->envelope1.attack_level=127; + patp->envelope1.decay1_level=127; + patp->envelope1.decay2_level=127; + patp->envelope1.sustain_level=127; + patp->envelope1.release_level=0; + patp->envelope2.attack_velocity=127; + patp->envelope2.attack_level=127; + patp->envelope2.decay1_level=127; + patp->envelope2.decay2_level=127; + patp->envelope2.sustain_level=127; + patp->envelope2.release_level=0; + patp->envelope2.attack_velocity=127; + patp->randomizer=0; + + /* Program for this patch */ + + progp->layer[0].patch_number= pat.number; /* XXX is this right ? */ + progp->layer[0].mute=1; + progp->layer[0].pan_or_mod=1; + progp->layer[0].pan=7; + progp->layer[0].mix_level=127 /* guspatch.volume */; + progp->layer[0].split_type=0; + progp->layer[0].split_point=0; + progp->layer[0].play_below=0; + + for (i = 1; i < 4; i++) { + progp->layer[i].mute=0; + } + + /* Sample data */ + + sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); + + for (base_note=0; + note_to_freq (base_note) < guspatch.base_note; + base_note++); + + if ((guspatch.base_note-note_to_freq(base_note)) + >(note_to_freq(base_note)-guspatch.base_note)) + base_note++; + + printk(KERN_DEBUG "ref freq=%d,base note=%d\n", + guspatch.base_freq, + base_note); + + sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) + + base_note*171); + printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); + sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; + sampp->sampleStartOffset.Fraction=0; + sampp->sampleStartOffset.Integer=0; + sampp->loopStartOffset.Fraction=0; + sampp->loopStartOffset.Integer=guspatch.loop_start + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->loopEndOffset.Fraction=0; + sampp->loopEndOffset.Integer=guspatch.loop_end + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->sampleEndOffset.Fraction=0; + sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); + sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; + sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; + + /* Now ship it down */ + + wavefront_send_sample (&samp, + (unsigned short *) &(addr)[sizeof_patch], + (guspatch.mode & WAVE_UNSIGNED) ? 1:0); + wavefront_send_patch (&pat); + wavefront_send_program (&prog); + + /* Now pan as best we can ... use the slave/internal MIDI device + number if it exists (since it talks to the WaveFront), or the + master otherwise. + */ + + if (dev.mididev > 0) { + midi_synth_controller (dev.mididev, guspatch.instr_no, 10, + ((guspatch.panning << 4) > 127) ? + 127 : (guspatch.panning << 4)); + } + + return(0); +} + +static int +wavefront_load_patch (const char *addr) + + +{ + wavefront_patch_info header; + + if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - + sizeof(wavefront_any))) { + printk (KERN_WARNING LOGNAME "bad address for load patch.\n"); + return -(EINVAL); + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample)); + + return wavefront_send_sample (&header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample)); + + return wavefront_send_multisample (&header); + + + case WF_ST_ALIAS: + + copy_from_user ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias)); + + return wavefront_send_alias (&header); + + case WF_ST_DRUM: + copy_from_user ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum)); + + return wavefront_send_drum (&header); + + case WF_ST_PATCH: + copy_from_user ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch)); + + return wavefront_send_patch (&header); + + case WF_ST_PROGRAM: + copy_from_user ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program)); + + return wavefront_send_program (&header); + + default: + printk (KERN_ERR LOGNAME "unknown patch type %d.\n", + header.subkey); + return -(EINVAL); + } + + return 0; +} + +/*********************************************************************** +WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces +***********************************************************************/ + +static void +process_sample_hdr (UCHAR8 *buf) + +{ + wavefront_sample s; + UCHAR8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (int cmd, wavefront_control *wc) + +{ + unsigned char patchnumbuf[2]; + int i; + + DPRINT (WF_DEBUG_CMD, "synth control with " + "cmd 0x%x\n", wc->cmd); + + /* Pre-handling of or for various commands */ + + switch (wc->cmd) { + case WFC_DISABLE_INTERRUPTS: + printk (KERN_INFO LOGNAME "interrupts disabled.\n"); + outb (0x80|0x20, dev.control_port); + dev.interrupts_on = 0; + return 0; + + case WFC_ENABLE_INTERRUPTS: + printk (KERN_INFO LOGNAME "interrupts enabled.\n"); + outb (0x80|0x40|0x20, dev.control_port); + dev.interrupts_on = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc->rbuf[0] = dev.interrupts_on; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + dev.rom_samples_rdonly = wc->wbuf[0]; + wc->status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc->wbuf[0] | (wc->wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + printk (KERN_WARNING LOGNAME "invalid slot ID %d\n", + i); + wc->status = EINVAL; + return 0; + } + wc->rbuf[0] = dev.sample_status[i]; + wc->status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + dev.debug = wc->wbuf[0]; + printk (KERN_INFO LOGNAME "debug = 0x%x\n", dev.debug); + return 0; + + case WFC_FX_IOCTL: + wffx_ioctl ((wavefront_fx_info *) &wc->wbuf[0]); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2); + memcpy (wc->wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + /* multisamples have to be handled differently, and + cannot be dealt with properly by wavefront_cmd() alone. + */ + wc->status = wavefront_fetch_multisample + ((wavefront_patch_info *) wc->rbuf); + return 0; + + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO LOGNAME "support for sample alias upload " + "being considered.\n"); + wc->status = EINVAL; + return -EINVAL; + } + + wc->status = wavefront_cmd (wc->cmd, wc->rbuf, wc->wbuf); + + /* Post-handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc->status == 0) { + switch (wc->cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + dev.freemem = demunge_int32 (wc->rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc->rbuf); + break; + + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO LOGNAME "support for " + "sample aliases still " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + if (virtual_midi_disable () < 0) { + return -(EIO); + } + break; + + case WFC_VMIDI_ON: + if (virtual_midi_enable () < 0) { + return -(EIO); + } + break; + } + } + + return 0; +} + + +/***********************************************************************/ +/* WaveFront: Linux file system interface (for access via raw synth) */ +/***********************************************************************/ + +static int +wavefront_open (struct inode *inode, struct file *file) +{ + /* XXX fix me */ + dev.opened = file->f_flags; + return 0; +} + +static int +wavefront_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + dev.opened = 0; + dev.debug = 0; + unlock_kernel(); + return 0; +} + +static int +wavefront_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + wavefront_control wc; + int err; + + switch (cmd) { + + case WFCTL_WFCMD: + copy_from_user (&wc, (void *) arg, sizeof (wc)); + + if ((err = wavefront_synth_control (cmd, &wc)) == 0) { + copy_to_user ((void *) arg, &wc, sizeof (wc)); + } + + return err; + + case WFCTL_LOAD_SPP: + return wavefront_load_patch ((const char *) arg); + + default: + printk (KERN_WARNING LOGNAME "invalid ioctl %#x\n", cmd); + return -(EINVAL); + + } + return 0; +} + +static /*const*/ struct file_operations wavefront_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: wavefront_ioctl, + open: wavefront_open, + release: wavefront_release, +}; + + +/***********************************************************************/ +/* WaveFront: OSS installation and support interface */ +/***********************************************************************/ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + +static struct synth_info wavefront_info = +{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, + 0, 32, 0, 0, SYNTH_CAP_INPUT}; + +static int +wavefront_oss_open (int devno, int mode) + +{ + dev.opened = mode; + return 0; +} + +static void +wavefront_oss_close (int devno) + +{ + dev.opened = 0; + dev.debug = 0; + return; +} + +static int +wavefront_oss_ioctl (int devno, unsigned int cmd, caddr_t arg) + +{ + wavefront_control wc; + int err; + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + if(copy_to_user(&((char *) arg)[0], &wavefront_info, + sizeof (wavefront_info))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_RESETSAMPLES: +// printk (KERN_WARNING LOGNAME "driver cannot reset samples.\n"); + return 0; /* don't force an error */ + + case SNDCTL_SEQ_PERCMODE: + return 0; /* don't force an error */ + + case SNDCTL_SYNTH_MEMAVL: + if ((dev.freemem = wavefront_freemem ()) < 0) { + printk (KERN_ERR LOGNAME "cannot get memory size\n"); + return -EIO; + } else { + return dev.freemem; + } + break; + + case SNDCTL_SYNTH_CONTROL: + if(copy_from_user (&wc, arg, sizeof (wc))) + err = -EFAULT; + else if ((err = wavefront_synth_control (cmd, &wc)) == 0) { + if(copy_to_user (arg, &wc, sizeof (wc))) + err = -EFAULT; + } + + return err; + + default: + return -(EINVAL); + } +} + +int +wavefront_oss_load_patch (int devno, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + + if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ + if (midi_load_patch == NULL) { + printk (KERN_ERR LOGNAME + "SYSEX not loadable: " + "no midi patch loader!\n"); + return -(EINVAL); + } + + return midi_load_patch (devno, format, addr, + offs, count, pmgr_flag); + + } else if (format == GUS_PATCH) { + return wavefront_load_gus_patch (devno, format, + addr, offs, count, pmgr_flag); + + } else if (format != WAVEFRONT_PATCH) { + printk (KERN_ERR LOGNAME "unknown patch format %d\n", format); + return -(EINVAL); + } + + if (count < sizeof (wavefront_patch_info)) { + printk (KERN_ERR LOGNAME "sample header too short\n"); + return -(EINVAL); + } + + /* "addr" points to a user-space wavefront_patch_info */ + + return wavefront_load_patch (addr); +} + +static struct synth_operations wavefront_operations = +{ + owner: THIS_MODULE, + id: "WaveFront", + info: &wavefront_info, + midi_dev: 0, + synth_type: SYNTH_TYPE_SAMPLE, + synth_subtype: SAMPLE_TYPE_WAVEFRONT, + open: wavefront_oss_open, + close: wavefront_oss_close, + ioctl: wavefront_oss_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice +}; +#endif /* OSS_SUPPORT_SEQ */ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_STATIC_INSTALL + +static void __init attach_wavefront (struct address_info *hw_config) +{ + (void) install_wavefront (); +} + +static int __init probe_wavefront (struct address_info *hw_config) +{ + return !detect_wavefront (hw_config->irq, hw_config->io_base); +} + +static void __exit unload_wavefront (struct address_info *hw_config) +{ + (void) uninstall_wavefront (); +} + +#endif /* OSS_SUPPORT_STATIC_INSTALL */ + +/***********************************************************************/ +/* WaveFront: Linux modular sound kernel installation interface */ +/***********************************************************************/ + +void +wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct wf_config *hw = dev_id; + + /* + Some comments on interrupts. I attempted a version of this + driver that used interrupts throughout the code instead of + doing busy and/or sleep-waiting. Alas, it appears that once + the Motorola firmware is downloaded, the card *never* + generates an RX interrupt. These are successfully generated + during firmware loading, and after that wavefront_status() + reports that an interrupt is pending on the card from time + to time, but it never seems to be delivered to this + driver. Note also that wavefront_status() continues to + report that RX interrupts are enabled, suggesting that I + didn't goof up and disable them by mistake. + + Thus, I stepped back to a prior version of + wavefront_wait(), the only place where this really + matters. Its sad, but I've looked through the code to check + on things, and I really feel certain that the Motorola + firmware prevents RX-ready interrupts. + */ + + if ((wavefront_status() & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { + return; + } + + hw->irq_ok = 1; + hw->irq_cnt++; + wake_up_interruptible (&hw->interrupt_sleeper); +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused +*/ + +int +wavefront_interrupt_bits (int irq) + +{ + int bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk (KERN_WARNING LOGNAME "invalid IRQ %d\n", irq); + bits = -1; + } + + return bits; +} + +void +wavefront_should_cause_interrupt (int val, int port, int timeout) + +{ + unsigned long flags; + + save_flags (flags); + cli(); + dev.irq_ok = 0; + outb (val,port); + interruptible_sleep_on_timeout (&dev.interrupt_sleeper, timeout); + restore_flags (flags); +} + +static int __init wavefront_hw_reset (void) +{ + int bits; + int hwv[2]; + unsigned long irq_mask; + short reported_irq; + + /* IRQ already checked in init_module() */ + + bits = wavefront_interrupt_bits (dev.irq); + + printk (KERN_DEBUG LOGNAME "autodetecting WaveFront IRQ\n"); + + sti (); + + irq_mask = probe_irq_on (); + + outb (0x0, dev.control_port); + outb (0x80 | 0x40 | bits, dev.data_port); + wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, + dev.control_port, + (reset_time*HZ)/100); + + reported_irq = probe_irq_off (irq_mask); + + if (reported_irq != dev.irq) { + if (reported_irq == 0) { + printk (KERN_ERR LOGNAME + "No unassigned interrupts detected " + "after h/w reset\n"); + } else if (reported_irq < 0) { + printk (KERN_ERR LOGNAME + "Multiple unassigned interrupts detected " + "after h/w reset\n"); + } else { + printk (KERN_ERR LOGNAME "autodetected IRQ %d not the " + "value provided (%d)\n", reported_irq, + dev.irq); + } + dev.irq = -1; + return 1; + } else { + printk (KERN_INFO LOGNAME "autodetected IRQ at %d\n", + reported_irq); + } + + if (request_irq (dev.irq, wavefrontintr, + SA_INTERRUPT|SA_SHIRQ, + "wavefront synth", &dev) < 0) { + printk (KERN_WARNING LOGNAME "IRQ %d not available!\n", + dev.irq); + return 1; + } + + /* try reset of port */ + + outb (0x0, dev.control_port); + + /* At this point, the board is in reset, and the H/W initialization + register is accessed at the same address as the data port. + + Bit 7 - Enable IRQ Driver + 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs + 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. + + Bit 6 - MIDI Interface Select + + 0 - Use the MIDI Input from the 26-pin WaveBlaster + compatible header as the serial MIDI source + 1 - Use the MIDI Input from the 9-pin D connector as the + serial MIDI source. + + Bits 5:3 - IRQ Selection + 0 0 0 - IRQ 2/9 + 0 0 1 - IRQ 5 + 0 1 0 - IRQ 12 + 0 1 1 - IRQ 15 + 1 0 0 - Reserved + 1 0 1 - Reserved + 1 1 0 - Reserved + 1 1 1 - Reserved + + Bits 2:1 - Reserved + Bit 0 - Disable Boot ROM + 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM + 1 - memory accesses to 03FC30-03FFFFH are directed to external + storage. + + */ + + /* configure hardware: IRQ, enable interrupts, + plus external 9-pin MIDI interface selected + */ + + outb (0x80 | 0x40 | bits, dev.data_port); + + /* CONTROL REGISTER + + 0 Host Rx Interrupt Enable (1=Enabled) 0x1 + 1 Unused 0x2 + 2 Unused 0x4 + 3 Unused 0x8 + 4 Host Tx Interrupt Enable 0x10 + 5 Mute (0=Mute; 1=Play) 0x20 + 6 Master Interrupt Enable (1=Enabled) 0x40 + 7 Master Reset (0=Reset; 1=Run) 0x80 + + Take us out of reset, mute output, master + TX + RX interrupts on. + + We'll get an interrupt presumably to tell us that the TX + register is clear. + */ + + wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, + dev.control_port, + (reset_time*HZ)/100); + + /* Note: data port is now the data port, not the h/w initialization + port. + */ + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "intr not received after h/w un-reset.\n"); + goto gone_bad; + } + + dev.interrupts_on = 1; + + /* Note: data port is now the data port, not the h/w initialization + port. + + At this point, only "HW VERSION" or "DOWNLOAD OS" commands + will work. So, issue one of them, and wait for TX + interrupt. This can take a *long* time after a cold boot, + while the ISC ROM does its RAM test. The SDK says up to 4 + seconds - with 12MB of RAM on a Tropez+, it takes a lot + longer than that (~16secs). Note that the card understands + the difference between a warm and a cold boot, so + subsequent ISC2115 reboots (say, caused by module + reloading) will get through this much faster. + + XXX Interesting question: why is no RX interrupt received first ? + */ + + wavefront_should_cause_interrupt(WFC_HARDWARE_VERSION, + dev.data_port, ramcheck_time*HZ); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "post-RAM-check interrupt not received.\n"); + goto gone_bad; + } + + if (!wavefront_wait (STAT_CAN_READ)) { + printk (KERN_WARNING LOGNAME + "no response to HW version cmd.\n"); + goto gone_bad; + } + + if ((hwv[0] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME + "board not responding correctly.\n"); + goto gone_bad; + } + + if (hwv[0] == 0xFF) { /* NAK */ + + /* Board's RAM test failed. Try to read error code, + and tell us about it either way. + */ + + if ((hwv[0] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME "on-board RAM test failed " + "(bad error code).\n"); + } else { + printk (KERN_WARNING LOGNAME "on-board RAM test failed " + "(error code: 0x%x).\n", + hwv[0]); + } + goto gone_bad; + } + + /* We're OK, just get the next byte of the HW version response */ + + if ((hwv[1] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME "incorrect h/w response.\n"); + goto gone_bad; + } + + printk (KERN_INFO LOGNAME "hardware version %d.%d\n", + hwv[0], hwv[1]); + + return 0; + + + gone_bad: + if (dev.irq >= 0) { + free_irq (dev.irq, &dev); + dev.irq = -1; + } + return (1); +} + +static int __init detect_wavefront (int irq, int io_base) +{ + unsigned char rbuf[4], wbuf[4]; + + /* TB docs say the device takes up 8 ports, but we know that + if there is an FX device present (i.e. a Tropez+) it really + consumes 16. + */ + + if (check_region (io_base, 16)) { + printk (KERN_ERR LOGNAME "IO address range 0x%x - 0x%x " + "already in use - ignored\n", dev.base, + dev.base+15); + return -1; + } + + dev.irq = irq; + dev.base = io_base; + dev.israw = 0; + dev.debug = debug_default; + dev.interrupts_on = 0; + dev.irq_cnt = 0; + dev.rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ + + if (wavefront_cmd (WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + + dev.fw_version[0] = rbuf[0]; + dev.fw_version[1] = rbuf[1]; + printk (KERN_INFO LOGNAME + "firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + /* check that a command actually works */ + + if (wavefront_cmd (WFC_HARDWARE_VERSION, + rbuf, wbuf) == 0) { + dev.hw_version[0] = rbuf[0]; + dev.hw_version[1] = rbuf[1]; + } else { + printk (KERN_WARNING LOGNAME "not raw, but no " + "hardware version!\n"); + return 0; + } + + if (!wf_raw) { + return 1; + } else { + printk (KERN_INFO LOGNAME + "reloading firmware anyway.\n"); + dev.israw = 1; + } + + } else { + + dev.israw = 1; + printk (KERN_INFO LOGNAME + "no response to firmware probe, assume raw.\n"); + + } + + init_waitqueue_head (&dev.interrupt_sleeper); + + if (wavefront_hw_reset ()) { + printk (KERN_WARNING LOGNAME "hardware reset failed\n"); + return 0; + } + + /* Check for FX device, present only on Tropez+ */ + + dev.has_fx = (detect_wffx () == 0); + + return 1; +} + +#include "os.h" +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include + +static int errno; + +static int +wavefront_download_firmware (char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ + int section_cnt_downloaded = 0; + int fd; + int c; + int i; + mm_segment_t fs; + + /* This tries to be a bit cleverer than the stuff Alan Cox did for + the generic sound firmware, in that it actually knows + something about the structure of the Motorola firmware. In + particular, it uses a version that has been stripped of the + 20K of useless header information, and had section lengths + added, making it possible to load the entire OS without any + [kv]malloc() activity, since the longest entity we ever read is + 42 bytes (well, WF_SECTION_MAX) long. + */ + + fs = get_fs(); + set_fs (get_ds()); + + if ((fd = open (path, 0, 0)) < 0) { + printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n", + path); + return 1; + } + + while (1) { + int x; + + if ((x = read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + printk (KERN_ERR LOGNAME "firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (read (fd, section, section_length) != section_length) { + printk (KERN_ERR LOGNAME "firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (wavefront_write (WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (wavefront_write (section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (STAT_CAN_READ)) { + + if ((c = inb (dev.data_port)) != WF_ACK) { + + printk (KERN_ERR LOGNAME "download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } + + } else { + printk (KERN_ERR LOGNAME "time out for firmware ACK.\n"); + goto failure; + } + + } + + close (fd); + set_fs (fs); + return 0; + + failure: + close (fd); + set_fs (fs); + printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); + return 1; +} + +static int __init wavefront_config_midi (void) +{ + unsigned char rbuf[4], wbuf[4]; + + if (detect_wf_mpu (dev.irq, dev.base) < 0) { + printk (KERN_WARNING LOGNAME + "could not find working MIDI device\n"); + return -1; + } + + if ((dev.mididev = install_wf_mpu ()) < 0) { + printk (KERN_WARNING LOGNAME + "MIDI interfaces not configured\n"); + return -1; + } + + /* Route external MIDI to WaveFront synth (by default) */ + + if (wavefront_cmd (WFC_MISYNTH_ON, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot enable MIDI-IN to synth routing.\n"); + /* XXX error ? */ + } + + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + /* Get the regular MIDI patch loading function, so we can + use it if we ever get handed a SYSEX patch. This is + unlikely, because its so damn slow, but we may as well + leave this functionality from maui.c behind, since it + could be useful for sequencer applications that can + only use MIDI to do patch loading. + */ + + if (midi_devs[dev.mididev]->converter != NULL) { + midi_load_patch = midi_devs[dev.mididev]->converter->load_patch; + midi_devs[dev.mididev]->converter->load_patch = + &wavefront_oss_load_patch; + } + +#endif /* OSS_SUPPORT_SEQ */ + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (wavefront_cmd (WFC_VMIDI_OFF, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "virtual MIDI mode not disabled\n"); + return 0; /* We're OK, but missing the external MIDI dev */ + } + + if ((dev.ext_mididev = virtual_midi_enable ()) < 0) { + printk (KERN_WARNING LOGNAME "no virtual MIDI access.\n"); + } else { + if (wavefront_cmd (WFC_VMIDI_ON, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot enable virtual MIDI mode.\n"); + virtual_midi_disable (); + } + } + + return 0; +} + +static int __init wavefront_do_reset (int atboot) +{ + char voices[1]; + + if (!atboot && wavefront_hw_reset ()) { + printk (KERN_WARNING LOGNAME "hw reset failed.\n"); + goto gone_bad; + } + + if (dev.israw) { + if (wavefront_download_firmware (ospath)) { + goto gone_bad; + } + + dev.israw = 0; + + /* Wait for the OS to get running. The protocol for + this is non-obvious, and was determined by + using port-IO tracing in DOSemu and some + experimentation here. + + Rather than using timed waits, use interrupts creatively. + */ + + wavefront_should_cause_interrupt (WFC_NOOP, + dev.data_port, + (osrun_time*HZ)); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "no post-OS interrupt.\n"); + goto gone_bad; + } + + /* Now, do it again ! */ + + wavefront_should_cause_interrupt (WFC_NOOP, + dev.data_port, (10*HZ)); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "no post-OS interrupt(2).\n"); + goto gone_bad; + } + + /* OK, no (RX/TX) interrupts any more, but leave mute + in effect. + */ + + outb (0x80|0x40, dev.control_port); + + /* No need for the IRQ anymore */ + + free_irq (dev.irq, &dev); + + } + + if (dev.has_fx && fx_raw) { + wffx_init (); + } + + /* SETUPSND.EXE asks for sample memory config here, but since i + have no idea how to interpret the result, we'll forget + about it. + */ + + if ((dev.freemem = wavefront_freemem ()) < 0) { + goto gone_bad; + } + + printk (KERN_INFO LOGNAME "available DRAM %dk\n", dev.freemem / 1024); + + if (wavefront_write (0xf0) || + wavefront_write (1) || + (wavefront_read () < 0)) { + dev.debug = 0; + printk (KERN_WARNING LOGNAME "MPU emulation mode not set.\n"); + goto gone_bad; + } + + voices[0] = 32; + + if (wavefront_cmd (WFC_SET_NVOICES, 0, voices)) { + printk (KERN_WARNING LOGNAME + "cannot set number of voices to 32.\n"); + goto gone_bad; + } + + + return 0; + + gone_bad: + /* reset that sucker so that it doesn't bother us. */ + + outb (0x0, dev.control_port); + dev.interrupts_on = 0; + if (dev.irq >= 0) { + free_irq (dev.irq, &dev); + } + return 1; +} + +static int __init wavefront_init (int atboot) +{ + int samples_are_from_rom; + + if (dev.israw) { + samples_are_from_rom = 1; + } else { + /* XXX is this always true ? */ + samples_are_from_rom = 0; + } + + if (dev.israw || fx_raw) { + if (wavefront_do_reset (atboot)) { + return -1; + } + } + + wavefront_get_sample_status (samples_are_from_rom); + wavefront_get_program_status (); + wavefront_get_patch_status (); + + /* Start normal operation: unreset, master interrupt enabled, no mute + */ + + outb (0x80|0x40|0x20, dev.control_port); + + return (0); +} + +static int __init install_wavefront (void) + +{ + if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) { + printk (KERN_ERR LOGNAME "cannot register raw synth\n"); + return -1; + } + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + if ((dev.oss_dev = sound_alloc_synthdev()) == -1) { + printk (KERN_ERR LOGNAME "Too many sequencers\n"); + return -1; + } else { + synth_devs[dev.oss_dev] = &wavefront_operations; + } +#endif /* OSS_SUPPORT_SEQ */ + + if (wavefront_init (1) < 0) { + printk (KERN_WARNING LOGNAME "initialization failed.\n"); + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + sound_unload_synthdev (dev.oss_dev); +#endif /* OSS_SUPPORT_SEQ */ + + return -1; + } + + request_region (dev.base+2, 6, "wavefront synth"); + + if (dev.has_fx) { + request_region (dev.base+8, 8, "wavefront fx"); + } + + if (wavefront_config_midi ()) { + printk (KERN_WARNING LOGNAME "could not initialize MIDI.\n"); + } + + return dev.oss_dev; +} + +static void __exit uninstall_wavefront (void) +{ + /* the first two i/o addresses are freed by the wf_mpu code */ + release_region (dev.base+2, 6); + + if (dev.has_fx) { + release_region (dev.base+8, 8); + } + + unregister_sound_synth (dev.synth_dev); + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + sound_unload_synthdev (dev.oss_dev); +#endif /* OSS_SUPPORT_SEQ */ + uninstall_wf_mpu (); +} + +/***********************************************************************/ +/* WaveFront FX control */ +/***********************************************************************/ + +#include "yss225.h" + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static int +wffx_idle (void) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (dev.fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + printk (KERN_ERR LOGNAME "FX device never idle.\n"); + return 0; + } + + return (1); +} + +int __init detect_wffx (void) +{ + /* This is a crude check, but its the best one I have for now. + Certainly on the Maui and the Tropez, wffx_idle() will + report "never idle", which suggests that this test should + work OK. + */ + + if (inb (dev.fx_status) & 0x80) { + printk (KERN_INFO LOGNAME "Hmm, probably a Maui or Tropez.\n"); + return -1; + } + + return 0; +} + +int __init attach_wffx (void) +{ + if ((dev.fx_mididev = sound_alloc_mididev ()) < 0) { + printk (KERN_WARNING LOGNAME "cannot install FX Midi driver\n"); + return -1; + } + + return 0; +} + +void +wffx_mute (int onoff) + +{ + if (!wffx_idle()) { + return; + } + + outb (onoff ? 0x02 : 0x00, dev.fx_op); +} + +static int +wffx_memset (int page, + int addr, int cnt, unsigned short *data) +{ + if (page < 0 || page > 7) { + printk (KERN_ERR LOGNAME "FX memset: " + "page must be >= 0 and <= 7\n"); + return -(EINVAL); + } + + if (addr < 0 || addr > 0x7f) { + printk (KERN_ERR LOGNAME "FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -(EINVAL); + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (page, dev.fx_dsp_page); + outb (addr, dev.fx_dsp_addr); + outb ((data[0] >> 8), dev.fx_dsp_msb); + outb ((data[0] & 0xff), dev.fx_dsp_lsb); + + printk (KERN_INFO LOGNAME "FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (page, dev.fx_dsp_page); + outb (addr, dev.fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), dev.fx_dsp_msb); + outb ((data[i] & 0xff), dev.fx_dsp_lsb); + if (!wffx_idle ()) { + break; + } + } + + if (i != cnt) { + printk (KERN_WARNING LOGNAME + "FX memset " + "(0x%x, 0x%x, 0x%x, %d) incomplete\n", + page, addr, (int) data, cnt); + return -(EIO); + } + } + + return 0; +} + +static int +wffx_ioctl (wavefront_fx_info *r) + +{ + unsigned short page_data[256]; + unsigned short *pd; + + switch (r->request) { + case WFFX_MUTE: + wffx_mute (r->data[0]); + return 0; + + case WFFX_MEMSET: + + if (r->data[2] <= 0) { + printk (KERN_ERR LOGNAME "cannot write " + "<= 0 bytes to FX\n"); + return -(EINVAL); + } else if (r->data[2] == 1) { + pd = (unsigned short *) &r->data[3]; + } else { + if (r->data[2] > sizeof (page_data)) { + printk (KERN_ERR LOGNAME "cannot write " + "> 255 bytes to FX\n"); + return -(EINVAL); + } + copy_from_user (page_data, (unsigned char *) r->data[3], + r->data[2]); + pd = page_data; + } + + return wffx_memset (r->data[0], /* page */ + r->data[1], /* addr */ + r->data[2], /* cnt */ + pd); + + default: + printk (KERN_WARNING LOGNAME + "FX: ioctl %d not yet supported\n", + r->request); + return -(EINVAL); + } +} + +/* YSS225 initialization. + + This code was developed using DOSEMU. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEMU enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. Its really pretty weird. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + +static int __init wffx_init (void) +{ + int i; + int j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle ()) { + return (-1); + } + + outb (i, dev.fx_mod_addr); + outb (0x0, dev.fx_mod_data); + } + } + + if (!wffx_idle()) return (-1); + outb (0x02, dev.fx_op); /* mute on */ + + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x44, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x42, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x43, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x7c, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x7e, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x46, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x49, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x47, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x4a, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle ()) { + return (-1); + } + + outb (i, dev.fx_mod_addr); + outb (0x0, dev.fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x00, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], dev.fx_dsp_msb); + outb (page_zero[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x01, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], dev.fx_dsp_msb); + outb (page_one[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x02, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x03, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x04, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x06, dev.fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], dev.fx_dsp_addr); + outb (page_six[i+1], dev.fx_dsp_msb); + outb (page_six[i+2], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x07, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], dev.fx_dsp_msb); + outb (page_seven[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev.fx_mod_addr); + outb (i, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0x02, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, dev.fx_mod_addr); + outb (0xff, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x1e, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, dev.fx_mod_addr); + outb (0xff, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x2e, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x3f, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x4e, dev.fx_mod_addr); + outb (0x0e, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0x4f, dev.fx_mod_addr); + outb (0x0e, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x6c, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6d, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6e, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6f, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, dev.fx_mod_addr); + outb (0xc0, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0xde, dev.fx_mod_addr); + outb (0x10, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xdf, dev.fx_mod_addr); + outb (0x10, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev.fx_mod_addr); + outb (i, dev.fx_mod_data); + outb (0x02, dev.fx_mod_addr); + outb (0x01, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x02, dev.fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], dev.fx_dsp_page); + outb (coefficients[i+1], dev.fx_dsp_addr); + outb (coefficients[i+2], dev.fx_dsp_msb); + outb (coefficients[i+3], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wffx_idle()) return (-1); + outb (0x1e, dev.fx_mod_addr); + outb (0x14, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xde, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xdf, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + + /* some more coefficients */ + + if (!wffx_idle()) return (-1); + outb (0x06, dev.fx_dsp_page); + outb (0x78, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x40, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x03, dev.fx_dsp_addr); + outb (0x0f, dev.fx_dsp_msb); + outb (0xff, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x0b, dev.fx_dsp_addr); + outb (0x0f, dev.fx_dsp_msb); + outb (0xff, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x02, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x0a, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x46, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x49, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x00, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], dev.fx_dsp_msb); + outb (page_zero_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x01, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], dev.fx_dsp_msb); + outb (page_one_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + if (!wffx_idle()) return (-1); + if (!wffx_idle()) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x02, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x03, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x04, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x06, dev.fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x07, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], dev.fx_dsp_msb); + outb (page_seven_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], dev.fx_mod_addr); + outb (mod_v2[i+1], dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], dev.fx_dsp_page); + outb (coefficients2[i+1], dev.fx_dsp_addr); + outb (coefficients2[i+2], dev.fx_dsp_msb); + outb (coefficients2[i+3], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, dev.fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, dev.fx_dsp_addr); + outb (coefficients3[i], dev.fx_dsp_msb); + outb (coefficients3[i+1], dev.fx_dsp_lsb); + } + + outb (0x00, dev.fx_op); /* mute off */ + if (!wffx_idle()) return (-1); + + return (0); +} + +static int io = -1; +static int irq = -1; + +MODULE_AUTHOR ("Paul Barton-Davis "); +MODULE_DESCRIPTION ("Turtle Beach WaveFront Linux Driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM (io,"i"); +MODULE_PARM (irq,"i"); + +static int __init init_wavfront (void) +{ + printk ("Turtle Beach WaveFront Driver\n" + "Copyright (C) by Hannu Solvainen, " + "Paul Barton-Davis 1993-1998.\n"); + + /* XXX t'would be lovely to ask the CS4232 for these values, eh ? */ + + if (io == -1 || irq == -1) { + printk (KERN_INFO LOGNAME "irq and io options must be set.\n"); + return -EINVAL; + } + + if (wavefront_interrupt_bits (irq) < 0) { + printk (KERN_INFO LOGNAME + "IRQ must be 9, 5, 12 or 15 (not %d)\n", irq); + return -ENODEV; + } + + if (detect_wavefront (irq, io) < 0) { + return -ENODEV; + } + + if (install_wavefront () < 0) { + return -EIO; + } + + return 0; +} + +static void __exit cleanup_wavfront (void) +{ + uninstall_wavefront (); +} + +module_init(init_wavfront); +module_exit(cleanup_wavfront); diff -Nru linux/sound/oss/wf_midi.c linux-2.4.19-pre5-mjc/sound/oss/wf_midi.c --- linux/sound/oss/wf_midi.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/wf_midi.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,880 @@ +/* + * sound/wf_midi.c + * + * The low level driver for the WaveFront ICS2115 MIDI interface(s) + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez Plus. This code has nothing + * to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct + * midi devices (/dev/midiNN and /dev/midiNN+1) to be used + * completely independently, giving 32 channels of MIDI routing, + * 16 to the WaveFront synth and 16 to the external MIDI bus. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +/* + * Copyright (C) by Paul Barton-Davis 1998 + * Some portions of this file are derived from work that is: + * + * CopyriGht (C) by Hannu Savolainen 1993-1996 + * + * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include +#include "sound_config.h" + +#include + +#ifdef MODULE + +struct wf_mpu_config { + int base; +#define DATAPORT(d) (d)->base +#define COMDPORT(d) (d)->base+1 +#define STATPORT(d) (d)->base+1 + + int irq; + int opened; + int devno; + int synthno; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + + void (*inputintr) (int dev, unsigned char data); + char isvirtual; /* do virtual I/O stuff */ +}; + +static struct wf_mpu_config devs[2]; +static struct wf_mpu_config *phys_dev = &devs[0]; +static struct wf_mpu_config *virt_dev = &devs[1]; + +static void start_uart_mode (void); + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define UART_MODE_ON 0x3F + +static inline int wf_mpu_status (void) +{ + return inb (STATPORT (phys_dev)); +} + +static inline int input_avail (void) +{ + return !(wf_mpu_status() & INPUT_AVAIL); +} + +static inline int output_ready (void) +{ + return !(wf_mpu_status() & OUTPUT_READY); +} + +static inline int read_data (void) +{ + return inb (DATAPORT (phys_dev)); +} + +static inline void write_data (unsigned char byte) +{ + outb (byte, DATAPORT (phys_dev)); +} + +/* + * States for the input scanner (should be in dev_table.h) + */ + +#define MST_SYSMSG 100 /* System message (sysx etc). */ +#define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define MST_SONGSEL 103 /* Song select */ +#define MST_SONGPOS 104 /* Song position pointer */ +#define MST_TIMED 105 /* Leading timing byte rcvd */ + +/* buffer space check for input scanner */ + +#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ +{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ + mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} + +static unsigned char len_tab[] = /* # of data bytes following a status + */ +{ + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ +}; + +static int +wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic) + +{ + struct midi_input_info *mi = &midi_devs[devno]->in_info; + + switch (mi->m_state) { + case MST_INIT: + switch (midic) { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + break; + + case 0xfd: + /* XXX do something useful with this. If there is + an external MIDI timer (e.g. a hardware sequencer, + a useful timer can be derived ... + + For now, no timer support. + */ + break; + + case 0xfe: + return MPU_ACK; + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + break; + + case 0xf9: + break; + + case 0xff: + mi->m_state = MST_SYSMSG; + break; + + default: + if (midic <= 0xef) { + mi->m_state = MST_TIMED; + } + else + printk (KERN_ERR " ", + midic); + } + break; + + case MST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + mi->m_state = MST_DATA; + + if (msg < 8) { /* Data byte */ + + msg = ((int) (mi->m_prev_status & 0xf0) >> 4); + msg -= 8; + mi->m_left = len_tab[msg] - 1; + + mi->m_ptr = 2; + mi->m_buf[0] = mi->m_prev_status; + mi->m_buf[1] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + } else if (msg == 0xf) { /* MPU MARK */ + + mi->m_state = MST_INIT; + + switch (midic) { + case 0xf8: + break; + + case 0xf9: + break; + + case 0xfc: + break; + + default: + break; + } + } else { + mi->m_prev_status = midic; + msg -= 8; + mi->m_left = len_tab[msg]; + + mi->m_ptr = 1; + mi->m_buf[0] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + } + } + break; + + case MST_SYSMSG: + switch (midic) { + case 0xf0: + mi->m_state = MST_SYSEX; + break; + + case 0xf1: + mi->m_state = MST_MTC; + break; + + case 0xf2: + mi->m_state = MST_SONGPOS; + mi->m_ptr = 0; + break; + + case 0xf3: + mi->m_state = MST_SONGSEL; + break; + + case 0xf6: + mi->m_state = MST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xfA: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFB: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFC: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFE: + /* active sensing */ + mi->m_state = MST_INIT; + break; + + case 0xff: + mi->m_state = MST_INIT; + break; + + default: + printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); + mi->m_state = MST_INIT; + } + break; + + case MST_MTC: + mi->m_state = MST_INIT; + break; + + case MST_SYSEX: + if (midic == 0xf7) { + mi->m_state = MST_INIT; + } else { + /* XXX fix me */ + } + break; + + case MST_SONGPOS: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if (mi->m_ptr == 2) { + mi->m_state = MST_INIT; + mi->m_ptr = 0; + /* XXX need ext MIDI timer support */ + } + break; + + case MST_DATA: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if ((--mi->m_left) <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + break; + + default: + printk (KERN_ERR "Bad state %d ", mi->m_state); + mi->m_state = MST_INIT; + } + + return 1; +} + +void +wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy) + +{ + struct wf_mpu_config *physical_dev = dev_id; + static struct wf_mpu_config *input_dev = 0; + struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info; + int n; + + if (!input_avail()) { /* not for us */ + return; + } + + if (mi->m_busy) return; + mi->m_busy = 1; + sti (); + + if (!input_dev) { + input_dev = physical_dev; + } + + n = 50; /* XXX why ? */ + + do { + unsigned char c = read_data (); + + if (phys_dev->isvirtual) { + + if (c == WF_EXTERNAL_SWITCH) { + input_dev = virt_dev; + continue; + } else if (c == WF_INTERNAL_SWITCH) { + input_dev = phys_dev; + continue; + } /* else just leave it as it is */ + + } else { + input_dev = phys_dev; + } + + if (input_dev->mode == MODE_SYNTH) { + + wf_mpu_input_scanner (input_dev->devno, + input_dev->synthno, c); + + } else if (input_dev->opened & OPEN_READ) { + + if (input_dev->inputintr) { + input_dev->inputintr (input_dev->devno, c); + } + } + + } while (input_avail() && n-- > 0); + + mi->m_busy = 0; +} + +static int +wf_mpu_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) + ) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return -(ENXIO); + + if (phys_dev->devno == dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return -(EINVAL); + } + + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_MIDI; + devc->opened = mode; + devc->synthno = 0; + + devc->inputintr = input; + return 0; +} + +static void +wf_mpu_close (int dev) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return; + + if (phys_dev->devno == dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return; + } + + devc->mode = 0; + devc->inputintr = NULL; + devc->opened = 0; +} + +static int +wf_mpu_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + static int lastoutdev = -1; + unsigned char switchch; + + if (phys_dev->isvirtual && lastoutdev != dev) { + + if (dev == phys_dev->devno) { + switchch = WF_INTERNAL_SWITCH; + } else if (dev == virt_dev->devno) { + switchch = WF_EXTERNAL_SWITCH; + } else { + printk (KERN_ERR "WF-MPU: bad device number %d", dev); + return (0); + } + + /* XXX fix me */ + + for (timeout = 30000; timeout > 0 && !output_ready (); + timeout--); + + save_flags (flags); + cli (); + + if (!output_ready ()) { + printk (KERN_WARNING "WF-MPU: Send switch " + "byte timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (switchch); + restore_flags (flags); + } + + lastoutdev = dev; + + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + /* XXX fix me */ + + for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); + + save_flags (flags); + cli (); + if (!output_ready ()) { + printk (KERN_WARNING "WF-MPU: Send data timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (midi_byte); + restore_flags (flags); + + return 1; +} + +static inline int wf_mpu_start_read (int dev) { + return 0; +} + +static inline int wf_mpu_end_read (int dev) { + return 0; +} + +static int wf_mpu_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + printk (KERN_WARNING + "WF-MPU: Intelligent mode not supported by hardware.\n"); + return -(EINVAL); +} + +static int wf_mpu_buffer_status (int dev) +{ + return 0; +} + +static struct synth_operations wf_mpu_synth_operations[2]; +static struct midi_operations wf_mpu_midi_operations[2]; + +static struct midi_operations wf_mpu_midi_proto = +{ + owner: THIS_MODULE, + info: {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + in_info: {0}, /* in_info */ + open: wf_mpu_open, + close: wf_mpu_close, + ioctl: wf_mpu_ioctl, + outputc: wf_mpu_out, + start_read: wf_mpu_start_read, + end_read: wf_mpu_end_read, + buffer_status: wf_mpu_buffer_status, +}; + +static struct synth_info wf_mpu_synth_info_proto = +{"WaveFront MPU-401 interface", 0, + SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; + +static struct synth_info wf_mpu_synth_info[2]; + +static int +wf_mpu_synth_ioctl (int dev, + unsigned int cmd, caddr_t arg) +{ + int midi_dev; + int index; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) + return -(ENXIO); + + if (midi_dev == phys_dev->devno) { + index = 0; + } else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) { + index = 1; + } else { + return -(EINVAL); + } + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + if(copy_to_user (&((char *) arg)[0], + &wf_mpu_synth_info[index], + sizeof (struct synth_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + default: + return -EINVAL; + } +} + +static int +wf_mpu_synth_open (int dev, int mode) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) { + return -(ENXIO); + } + + if (phys_dev->devno == midi_dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return -(EINVAL); + } + + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_SYNTH; + devc->synthno = dev; + devc->opened = mode; + devc->inputintr = NULL; + return 0; +} + +static void +wf_mpu_synth_close (int dev) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (phys_dev->devno == midi_dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return; + } + + devc->inputintr = NULL; + devc->opened = 0; + devc->mode = 0; +} + +#define _MIDI_SYNTH_C_ +#define MIDI_SYNTH_NAME "WaveFront (MIDI)" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations wf_mpu_synth_proto = +{ + owner: THIS_MODULE, + id: "WaveFront (ICS2115)", + info: NULL, /* info field, filled in during configuration */ + midi_dev: 0, /* MIDI dev XXX should this be -1 ? */ + synth_type: SYNTH_TYPE_MIDI, + synth_subtype: SAMPLE_TYPE_WAVEFRONT, + open: wf_mpu_synth_open, + close: wf_mpu_synth_close, + ioctl: wf_mpu_synth_ioctl, + kill_note: midi_synth_kill_note, + start_note: midi_synth_start_note, + set_instr: midi_synth_set_instr, + reset: midi_synth_reset, + hw_control: midi_synth_hw_control, + load_patch: midi_synth_load_patch, + aftertouch: midi_synth_aftertouch, + controller: midi_synth_controller, + panning: midi_synth_panning, + bender: midi_synth_bender, + setup_voice: midi_synth_setup_voice, + send_sysex: midi_synth_send_sysex +}; + +static int +config_wf_mpu (struct wf_mpu_config *dev) + +{ + int is_external; + char *name; + int index; + + if (dev == phys_dev) { + name = "WaveFront internal MIDI"; + is_external = 0; + index = 0; + memcpy ((char *) &wf_mpu_synth_operations[index], + (char *) &wf_mpu_synth_proto, + sizeof (struct synth_operations)); + } else { + name = "WaveFront external MIDI"; + is_external = 1; + index = 1; + /* no synth operations for an external MIDI interface */ + } + + memcpy ((char *) &wf_mpu_synth_info[dev->devno], + (char *) &wf_mpu_synth_info_proto, + sizeof (struct synth_info)); + + strcpy (wf_mpu_synth_info[index].name, name); + + wf_mpu_synth_operations[index].midi_dev = dev->devno; + wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index]; + + memcpy ((char *) &wf_mpu_midi_operations[index], + (char *) &wf_mpu_midi_proto, + sizeof (struct midi_operations)); + + if (is_external) { + wf_mpu_midi_operations[index].converter = NULL; + } else { + wf_mpu_midi_operations[index].converter = + &wf_mpu_synth_operations[index]; + } + + strcpy (wf_mpu_midi_operations[index].info.name, name); + + midi_devs[dev->devno] = &wf_mpu_midi_operations[index]; + midi_devs[dev->devno]->in_info.m_busy = 0; + midi_devs[dev->devno]->in_info.m_state = MST_INIT; + midi_devs[dev->devno]->in_info.m_ptr = 0; + midi_devs[dev->devno]->in_info.m_left = 0; + midi_devs[dev->devno]->in_info.m_prev_status = 0; + + devs[index].opened = 0; + devs[index].mode = 0; + + return (0); +} + +int virtual_midi_enable (void) + +{ + if ((virt_dev->devno < 0) && + (virt_dev->devno = sound_alloc_mididev()) == -1) { + printk (KERN_ERR + "WF-MPU: too many midi devices detected\n"); + return -1; + } + + config_wf_mpu (virt_dev); + + phys_dev->isvirtual = 1; + return virt_dev->devno; +} + +int +virtual_midi_disable (void) + +{ + unsigned long flags; + + save_flags (flags); + cli(); + + wf_mpu_close (virt_dev->devno); + /* no synth on virt_dev, so no need to call wf_mpu_synth_close() */ + phys_dev->isvirtual = 0; + + restore_flags (flags); + + return 0; +} + +int __init detect_wf_mpu (int irq, int io_base) +{ + if (check_region (io_base, 2)) { + printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n", + io_base); + return -1; + } + + phys_dev->base = io_base; + phys_dev->irq = irq; + phys_dev->devno = -1; + virt_dev->devno = -1; + + return 0; +} + +int __init install_wf_mpu (void) +{ + if ((phys_dev->devno = sound_alloc_mididev()) < 0){ + + printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); + return -1; + + } + + request_region (phys_dev->base, 2, "wavefront midi"); + phys_dev->isvirtual = 0; + + if (config_wf_mpu (phys_dev)) { + + printk (KERN_WARNING + "WF-MPU: configuration for MIDI device %d failed\n", + phys_dev->devno); + sound_unload_mididev (phys_dev->devno); + + } + + /* OK, now we're configured to handle an interrupt ... */ + + if (request_irq (phys_dev->irq, wf_mpuintr, SA_INTERRUPT|SA_SHIRQ, + "wavefront midi", phys_dev) < 0) { + + printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", + phys_dev->irq); + return -1; + + } + + /* This being a WaveFront (ICS-2115) emulated MPU-401, we have + to switch it into UART (dumb) mode, because otherwise, it + won't do anything at all. + */ + + start_uart_mode (); + + return phys_dev->devno; +} + +void +uninstall_wf_mpu (void) + +{ + release_region (phys_dev->base, 2); + free_irq (phys_dev->irq, phys_dev); + sound_unload_mididev (phys_dev->devno); + + if (virt_dev->devno >= 0) { + sound_unload_mididev (virt_dev->devno); + } +} + +static void +start_uart_mode (void) + +{ + int ok, i; + unsigned long flags; + + save_flags (flags); + cli (); + + /* XXX fix me */ + + for (i = 0; i < 30000 && !output_ready (); i++); + + outb (UART_MODE_ON, COMDPORT(phys_dev)); + + for (ok = 0, i = 50000; i > 0 && !ok; i--) { + if (input_avail ()) { + if (read_data () == MPU_ACK) { + ok = 1; + } + } + } + + restore_flags (flags); +} +#endif diff -Nru linux/sound/oss/ymfpci.c linux-2.4.19-pre5-mjc/sound/oss/ymfpci.c --- linux/sound/oss/ymfpci.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ymfpci.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2671 @@ +/* + * Copyright 1999 Jaroslav Kysela + * Copyright 2000 Alan Cox + * Copyright 2001 Kai Germaschewski + * Copyright 2002 Pete Zaitcev + * + * Yamaha YMF7xx driver. + * + * This code is a result of high-speed collision + * between ymfpci.c of ALSA and cs46xx.c of Linux. + * -- Pete Zaitcev ; 2000/09/18 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + * + * TODO: + * - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). + * - 96KHz playback for DVD - use pitch of 2.0. + * - Retain DMA buffer on close, do not wait the end of frame. + * - Resolve XXX tagged questions. + * - Cannot play 5133Hz. + * - 2001/01/07 Consider if we can remove voice_lock, like so: + * : Allocate/deallocate voices in open/close under semafore. + * : We access voices in interrupt, that only for pcms that open. + * voice_lock around playback_prepare closes interrupts for insane duration. + * - Revisit the way voice_alloc is done - too confusing, overcomplicated. + * Should support various channel types, however. + * - Remove prog_dmabuf from read/write, leave it in open. + * - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_ALSA_YMFPCI_LEGACY code with + * native synthesizer through a playback slot. + * - 2001/11/29 ac97_save_state + * Talk to Kai to remove ac97_save_state before it's too late! + * - Second AC97 + * - Restore S/PDIF - Toshibas have it. + * + * Kai used pci_alloc_consistent for DMA buffer, which sounds a little + * unconventional. However, given how small our fragments can be, + * a little uncached access is perhaps better than endless flushing. + * On i386 and other I/O-coherent architectures pci_alloc_consistent + * is entirely harmless. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY +# include "sound_config.h" +# include "mpu401.h" +#endif +#include "ymfpci.h" + +/* + * I do not believe in debug levels as I never can guess what + * part of the code is going to be problematic in the future. + * Don't forget to run your klogd with -c 8. + * + * Example (do not remove): + * #define YMFDBG(fmt, arg...) do{ printk(KERN_DEBUG fmt, ##arg); }while(0) + */ +#define YMFDBGW(fmt, arg...) /* */ /* write counts */ +#define YMFDBGI(fmt, arg...) /* */ /* interrupts */ +#define YMFDBGX(fmt, arg...) /* */ /* ioctl */ + +static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); +static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice); +static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank); +static int ymf_playback_prepare(struct ymf_state *state); +static int ymf_capture_prepare(struct ymf_state *state); +static struct ymf_state *ymf_state_alloc(ymfpci_t *unit); + +static void ymfpci_aclink_reset(struct pci_dev * pci); +static void ymfpci_disable_dsp(ymfpci_t *unit); +static void ymfpci_download_image(ymfpci_t *codec); +static void ymf_memload(ymfpci_t *unit); + +static LIST_HEAD(ymf_devs); + +/* + * constants + */ + +static struct pci_device_id ymf_id_tbl[] __devinitdata = { +#define DEV(v, d, data) \ + { PCI_VENDOR_ID_##v, PCI_DEVICE_ID_##v##_##d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)data } + DEV (YAMAHA, 724, "YMF724"), + DEV (YAMAHA, 724F, "YMF724F"), + DEV (YAMAHA, 740, "YMF740"), + DEV (YAMAHA, 740C, "YMF740C"), + DEV (YAMAHA, 744, "YMF744"), + DEV (YAMAHA, 754, "YMF754"), +#undef DEV + { } +}; +MODULE_DEVICE_TABLE(pci, ymf_id_tbl); + +/* + * common I/O routines + */ + +static inline u8 ymfpci_readb(ymfpci_t *codec, u32 offset) +{ + return readb(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val) +{ + writeb(val, codec->reg_area_virt + offset); +} + +static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset) +{ + return readw(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val) +{ + writew(val, codec->reg_area_virt + offset); +} + +static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset) +{ + return readl(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val) +{ + writel(val, codec->reg_area_virt + offset); +} + +static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched) +{ + signed long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = jiffies + 3 * (HZ / 4); + do { + if ((ymfpci_readw(codec, reg) & 0x8000) == 0) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n", + secondary, ymfpci_readw(codec, reg)); + return -EBUSY; +} + +static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val) +{ + ymfpci_t *codec = dev->private_data; + u32 cmd; + + /* XXX Do make use of dev->id */ + ymfpci_codec_ready(codec, 0, 0); + cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; + ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd); +} + +static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +{ + ymfpci_t *unit = dev->private_data; + int i; + + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) { + for (i = 0; i < 600; i++) + ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); + } + return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); +} + +/* + * Misc routines + */ + +/* + * Calculate the actual sampling rate relatetively to the base clock (48kHz). + */ +static u32 ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 48000) << 12; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymf_calc_lend(u32 rate) +{ + return (rate * YMF_SAMPF) / 48000; +} + +/* + * We ever allow only a few formats, but let's be generic, for smaller surprise. + */ +static int ymf_pcm_format_width(int format) +{ + static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE; + + if ((format & (format-1)) != 0) { + printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format); + return 8; + } + + if (format == AFMT_IMA_ADPCM) return 4; + if ((format & mask16) != 0) return 16; + return 8; +} + +static void ymf_pcm_update_shift(struct ymf_pcm_format *f) +{ + f->shift = 0; + if (f->voices == 2) + f->shift++; + if (ymf_pcm_format_width(f->format) == 16) + f->shift++; +} + +/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */ +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* + * Allocate DMA buffer + */ +static int alloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + void *rawbuf = NULL; + dma_addr_t dma_addr; + int order; + struct page *map, *mapend; + + /* alloc as big a chunk as we can */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + rawbuf = pci_alloc_consistent(unit->pci, PAGE_SIZE << order, &dma_addr); + if (rawbuf) + break; + } + if (!rawbuf) + return -ENOMEM; + +#if 0 + printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->dma_addr = dma_addr; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (map = virt_to_page(rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &map->flags); + + return 0; +} + +/* + * Free DMA buffer + */ +static void dealloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + struct page *map, *mapend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &map->flags); + + pci_free_consistent(unit->pci, PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_addr); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct ymf_state *state, int rec) +{ + struct ymf_dmabuf *dmabuf; + int w_16; + unsigned bufsize; + unsigned long flags; + int redzone, redfrags; + int ret; + + w_16 = ymf_pcm_format_width(state->format.format) == 16; + dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf; + + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) + if ((ret = alloc_dmabuf(state->unit, dmabuf))) + return ret; + + /* + * Create fake fragment sizes and numbers for OSS ioctls. + * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT. + */ + bufsize = PAGE_SIZE << dmabuf->buforder; + /* By default we give 4 big buffers. */ + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2); + if (dmabuf->ossfragshift > 3 && + dmabuf->ossfragshift < dmabuf->fragshift) { + /* If OSS set smaller fragments, give more smaller buffers. */ + dmabuf->fragshift = dmabuf->ossfragshift; + } + dmabuf->fragsize = 1 << dmabuf->fragshift; + + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + + if (dmabuf->ossmaxfrags >= 2) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift; + + if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) { + dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + } + } + + memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize); + + /* + * Now set up the ring + */ + + /* XXX ret = rec? cap_pre(): pbk_pre(); */ + spin_lock_irqsave(&state->unit->voice_lock, flags); + if (rec) { + if ((ret = ymf_capture_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } else { + if ((ret = ymf_playback_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + + /* set the ready flag for the dma buffer (this comment is not stupid) */ + dmabuf->ready = 1; + +#if 0 + printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x," + " numfrag %d fragsize %d dmasize %d\n", + state->format.rate, state->format.format, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + + return 0; +} + +static void ymf_start_dac(struct ymf_state *state) +{ + ymf_playback_trigger(state->unit, &state->wpcm, 1); +} + +// static void ymf_start_adc(struct ymf_state *state) +// { +// ymf_capture_trigger(state->unit, &state->rpcm, 1); +// } + +/* + * Wait until output is drained. + * This does not kill the hardware for the sake of ioctls. + */ +static void ymf_wait_dac(struct ymf_state *state) +{ + struct ymf_unit *unit = state->unit; + struct ymf_pcm *ypcm = &state->wpcm; + DECLARE_WAITQUEUE(waita, current); + unsigned long flags; + + add_wait_queue(&ypcm->dmabuf.wait, &waita); + + spin_lock_irqsave(&unit->reg_lock, flags); + if (ypcm->dmabuf.count != 0 && !ypcm->running) { + ymf_playback_trigger(unit, ypcm, 1); + } + +#if 0 + if (file->f_flags & O_NONBLOCK) { + /* + * XXX Our mistake is to attach DMA buffer to state + * rather than to some per-device structure. + * Cannot skip waiting, can only make it shorter. + */ + } +#endif + + set_current_state(TASK_UNINTERRUPTIBLE); + while (ypcm->running) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + spin_lock_irqsave(&unit->reg_lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&ypcm->dmabuf.wait, &waita); + + /* + * This function may take up to 4 seconds to reach this point + * (32K circular buffer, 8000 Hz). User notices. + */ +} + +/* Can just stop, without wait. Or can we? */ +static void ymf_stop_adc(struct ymf_state *state) +{ + struct ymf_unit *unit = state->unit; + unsigned long flags; + + spin_lock_irqsave(&unit->reg_lock, flags); + ymf_capture_trigger(unit, &state->rpcm, 0); + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* + * Hardware start management + */ + +static void ymfpci_hw_start(ymfpci_t *unit) +{ + unsigned long flags; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->start_count++ == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) | 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +static void ymfpci_hw_stop(ymfpci_t *unit) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (--unit->start_count == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0) + break; + } + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* + * Playback voice management + */ + +static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[]) +{ + ymfpci_voice_t *voice, *voice2; + int idx; + + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &codec->voices[idx]; + voice2 = pair ? &codec->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + ymfpci_hw_start(codec); + rvoice[0] = voice; + if (voice2) { + ymfpci_hw_start(codec); + rvoice[1] = voice2; + } + return 0; + } + return -EBUSY; /* Your audio channel is open by someone else. */ +} + +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice) +{ + ymfpci_hw_stop(unit); + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; +} + +/* + */ + +static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) +{ + struct ymf_pcm *ypcm; + int redzone; + int pos, delta, swptr; + int played, distance; + struct ymf_state *state; + struct ymf_dmabuf *dmabuf; + char silence; + + if ((ypcm = voice->ypcm) == NULL) { + return; + } + if ((state = ypcm->state) == NULL) { + ypcm->running = 0; // lock it + return; + } + dmabuf = &ypcm->dmabuf; + spin_lock(&codec->reg_lock); + if (ypcm->running) { + YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n", + voice->number, codec->active_bank, dmabuf->count, + le32_to_cpu(voice->bank[0].start), + le32_to_cpu(voice->bank[1].start)); + silence = (ymf_pcm_format_width(state->format.format) == 16) ? + 0 : 0x80; + /* We need actual left-hand-side redzone size here. */ + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + swptr = dmabuf->swptr; + + pos = le32_to_cpu(voice->bank[codec->active_bank].start); + pos <<= state->format.shift; + if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ + printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n", + codec->dev_audio, voice->number, + dmabuf->hwptr, pos, dmabuf->dmasize); + pos = 0; + } + if (pos < dmabuf->hwptr) { + delta = dmabuf->dmasize - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + delta += pos; + memset(dmabuf->rawbuf, silence, pos); + } else { + delta = pos - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + } + dmabuf->hwptr = pos; + + if (dmabuf->count == 0) { + printk(KERN_ERR "ymfpci%d: %d: strain: hwptr %d\n", + codec->dev_audio, voice->number, dmabuf->hwptr); + ymf_playback_trigger(codec, ypcm, 0); + } + + if (swptr <= pos) { + distance = pos - swptr; + } else { + distance = dmabuf->dmasize - (swptr - pos); + } + if (distance < redzone) { + /* + * hwptr inside redzone => DMA ran out of samples. + */ + if (delta < dmabuf->count) { + /* + * Lost interrupt or other screwage. + */ + printk(KERN_ERR "ymfpci%d: %d: lost: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } else { + /* + * Normal end of DMA. + */ + YMFDBGI("ymfpci%d: %d: done: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } + played = dmabuf->count; + if (ypcm->running) { + ymf_playback_trigger(codec, ypcm, 0); + } + } else { + /* + * hwptr is chipping away towards a remote swptr. + * Calculate other distance and apply it to count. + */ + if (swptr >= pos) { + distance = swptr - pos; + } else { + distance = dmabuf->dmasize - (pos - swptr); + } + if (distance < dmabuf->count) { + played = dmabuf->count - distance; + } else { + played = 0; + } + } + + dmabuf->total_bytes += played; + dmabuf->count -= played; + if (dmabuf->count < dmabuf->dmasize / 2) { + wake_up(&dmabuf->wait); + } + } + spin_unlock(&codec->reg_lock); +} + +static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap) +{ + struct ymf_pcm *ypcm; + int redzone; + struct ymf_state *state; + struct ymf_dmabuf *dmabuf; + int pos, delta; + int cnt; + + if ((ypcm = cap->ypcm) == NULL) { + return; + } + if ((state = ypcm->state) == NULL) { + ypcm->running = 0; // lock it + return; + } + dmabuf = &ypcm->dmabuf; + spin_lock(&unit->reg_lock); + if (ypcm->running) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + + pos = le32_to_cpu(cap->bank[unit->active_bank].start); + // pos <<= state->format.shift; + if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ + printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n", + unit->dev_audio, ypcm->capture_bank_number, + dmabuf->hwptr, pos, dmabuf->dmasize); + pos = 0; + } + if (pos < dmabuf->hwptr) { + delta = dmabuf->dmasize - dmabuf->hwptr; + delta += pos; + } else { + delta = pos - dmabuf->hwptr; + } + dmabuf->hwptr = pos; + + cnt = dmabuf->count; + cnt += delta; + if (cnt + redzone > dmabuf->dmasize) { + /* Overflow - bump swptr */ + dmabuf->count = dmabuf->dmasize - redzone; + dmabuf->swptr = dmabuf->hwptr + redzone; + if (dmabuf->swptr >= dmabuf->dmasize) { + dmabuf->swptr -= dmabuf->dmasize; + } + } else { + dmabuf->count = cnt; + } + + dmabuf->total_bytes += delta; + if (dmabuf->count) { /* && is_sleeping XXX */ + wake_up(&dmabuf->wait); + } + } + spin_unlock(&unit->reg_lock); +} + +static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + + if (ypcm->voices[0] == NULL) { + return -EINVAL; + } + if (cmd != 0) { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = + cpu_to_le32(ypcm->voices[0]->bank_ba); + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = + cpu_to_le32(ypcm->voices[1]->bank_ba); + ypcm->running = 1; + } else { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + } + return 0; +} + +static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + u32 tmp; + + if (cmd != 0) { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + } else { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + } +} + +static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices) +{ + struct ymf_unit *unit; + int err; + + unit = ypcm->state->unit; + if (ypcm->voices[1] != NULL && voices < 2) { + ymfpci_voice_free(unit, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + ymfpci_voice_free(unit, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + ypcm->voices[1]->ypcm = ypcm; + } else { + if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + } + return 0; +} + +static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo, + int rate, int w_16, unsigned long addr, unsigned int end, int spdif) +{ + u32 format; + u32 delta = ymfpci_calc_delta(rate); + u32 lpfQ = ymfpci_calc_lpfQ(rate); + u32 lpfK = ymfpci_calc_lpfK(rate); + ymfpci_playback_bank_t *bank; + int nbank; + + /* + * The gain is a floating point number. According to the manual, + * bit 31 indicates a sign bit, bit 30 indicates an integer part, + * and bits [29:15] indicate a decimal fraction part. Thus, + * for a gain of 1.0 the constant of 0x40000000 is loaded. + */ + unsigned default_gain = cpu_to_le32(0x40000000); + + format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + if (stereo) + end >>= 1; + if (w_16) + end >>= 1; + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + bank->format = cpu_to_le32(format); + bank->loop_default = 0; /* 0-loops forever, otherwise count */ + bank->base = cpu_to_le32(addr); + bank->loop_start = 0; + bank->loop_end = cpu_to_le32(end); + bank->loop_frac = 0; + bank->eg_gain_end = default_gain; + bank->lpfQ = cpu_to_le32(lpfQ); + bank->status = 0; + bank->num_of_frames = 0; + bank->loop_count = 0; + bank->start = 0; + bank->start_frac = 0; + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = default_gain; + bank->lpfD1 = + bank->lpfD2 = 0; + + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = + bank->eff1_gain = + bank->eff2_gain = + bank->eff3_gain = + bank->eff1_gain_end = + bank->eff2_gain_end = + bank->eff3_gain_end = 0; + + if (!stereo) { + if (!spdif) { + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = default_gain; + } else { + bank->eff2_gain = + bank->eff2_gain_end = + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } else { + if (!spdif) { + if ((voice->number & 1) == 0) { + bank->left_gain = + bank->left_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->right_gain = + bank->right_gain_end = default_gain; + } + } else { + if ((voice->number & 1) == 0) { + bank->eff2_gain = + bank->eff2_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } + } + } +} + +/* + * XXX Capture channel allocation is entirely fake at the moment. + * We use only one channel and mark it busy as required. + */ +static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank) +{ + struct ymf_capture *cap; + int cbank; + + cbank = 1; /* Only ADC slot is used for now. */ + cap = &unit->capture[cbank]; + if (cap->use) + return -EBUSY; + cap->use = 1; + *pbank = cbank; + return 0; +} + +static int ymf_playback_prepare(struct ymf_state *state) +{ + struct ymf_pcm *ypcm = &state->wpcm; + int err, nvoice; + + if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { + /* Somebody started 32 mpg123's in parallel? */ + printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", + state->unit->dev_audio); + return err; + } + + for (nvoice = 0; nvoice < state->format.voices; nvoice++) { + ymf_pcm_init_voice(ypcm->voices[nvoice], + state->format.voices == 2, state->format.rate, + ymf_pcm_format_width(state->format.format) == 16, + ypcm->dmabuf.dma_addr, ypcm->dmabuf.dmasize, + ypcm->spdif); + } + return 0; +} + +static int ymf_capture_prepare(struct ymf_state *state) +{ + ymfpci_t *unit = state->unit; + struct ymf_pcm *ypcm = &state->rpcm; + ymfpci_capture_bank_t * bank; + /* XXX This is confusing, gotta rename one of them banks... */ + int nbank; /* flip-flop bank */ + int cbank; /* input [super-]bank */ + struct ymf_capture *cap; + u32 rate, format; + + if (ypcm->capture_bank_number == -1) { + if (ymf_capture_alloc(unit, &cbank) != 0) + return -EBUSY; + + ypcm->capture_bank_number = cbank; + + cap = &unit->capture[cbank]; + cap->bank = unit->bank_capture[cbank][0]; + cap->ypcm = ypcm; + ymfpci_hw_start(unit); + } + + // ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream); + // frag_size is replaced with nonfragged byte-aligned rolling buffer + rate = ((48000 * 4096) / state->format.rate) - 1; + format = 0; + if (state->format.voices == 2) + format |= 2; + if (ymf_pcm_format_width(state->format.format) == 8) + format |= 1; + switch (ypcm->capture_bank_number) { + case 0: + ymfpci_writel(unit, YDSXGR_RECFORMAT, format); + ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate); + break; + case 1: + ymfpci_writel(unit, YDSXGR_ADCFORMAT, format); + ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = unit->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(ypcm->dmabuf.dma_addr); + // bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift; + bank->loop_end = cpu_to_le32(ypcm->dmabuf.dmasize); + bank->start = 0; + bank->num_of_loops = 0; + } +#if 0 /* s/pdif */ + if (state->digital.dig_valid) + /*state->digital.type == SND_PCM_DIG_AES_IEC958*/ + ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, + state->digital.dig_status[0] | (state->digital.dig_status[1] << 8)); +#endif + return 0; +} + +void ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ymfpci_t *codec = dev_id; + u32 status, nvoice, mode; + struct ymf_voice *voice; + struct ymf_capture *cap; + + status = ymfpci_readl(codec, YDSXGR_STATUS); + if (status & 0x80000000) { + codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1; + spin_lock(&codec->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &codec->voices[nvoice]; + if (voice->use) + ymf_pcm_interrupt(codec, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + cap = &codec->capture[nvoice]; + if (cap->use) + ymf_cap_interrupt(codec, cap); + } + spin_unlock(&codec->voice_lock); + spin_lock(&codec->reg_lock); + ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000); + mode = ymfpci_readl(codec, YDSXGR_MODE) | 2; + ymfpci_writel(codec, YDSXGR_MODE, mode); + spin_unlock(&codec->reg_lock); + } + + status = ymfpci_readl(codec, YDSXGR_INTFLAG); + if (status & 1) { + /* timer handler */ + ymfpci_writel(codec, YDSXGR_INTFLAG, ~0); + } +} + +static void ymf_pcm_free_substream(struct ymf_pcm *ypcm) +{ + unsigned long flags; + struct ymf_unit *unit; + + unit = ypcm->state->unit; + + if (ypcm->type == PLAYBACK_VOICE) { + spin_lock_irqsave(&unit->voice_lock, flags); + if (ypcm->voices[1]) + ymfpci_voice_free(unit, ypcm->voices[1]); + if (ypcm->voices[0]) + ymfpci_voice_free(unit, ypcm->voices[0]); + spin_unlock_irqrestore(&unit->voice_lock, flags); + } else { + if (ypcm->capture_bank_number != -1) { + unit->capture[ypcm->capture_bank_number].use = 0; + ypcm->capture_bank_number = -1; + ymfpci_hw_stop(unit); + } + } +} + +static struct ymf_state *ymf_state_alloc(ymfpci_t *unit) +{ + struct ymf_pcm *ypcm; + struct ymf_state *state; + + if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) { + goto out0; + } + memset(state, 0, sizeof(struct ymf_state)); + + ypcm = &state->wpcm; + ypcm->state = state; + ypcm->type = PLAYBACK_VOICE; + ypcm->capture_bank_number = -1; + init_waitqueue_head(&ypcm->dmabuf.wait); + + ypcm = &state->rpcm; + ypcm->state = state; + ypcm->type = CAPTURE_AC97; + ypcm->capture_bank_number = -1; + init_waitqueue_head(&ypcm->dmabuf.wait); + + state->unit = unit; + + state->format.format = AFMT_U8; + state->format.rate = 8000; + state->format.voices = 1; + ymf_pcm_update_shift(&state->format); + + return state; + +out0: + return NULL; +} + +/* AES/IEC958 channel status bits */ +#define SND_PCM_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ +#define SND_PCM_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ +#define SND_PCM_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ +#define SND_PCM_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ +#define SND_PCM_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ +#define SND_PCM_AES0_PRO_FS (3<<6) /* mask - sample frequency */ +#define SND_PCM_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ +#define SND_PCM_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ +#define SND_PCM_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ +#define SND_PCM_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ +#define SND_PCM_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ +#define SND_PCM_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ +#define SND_PCM_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ +#define SND_PCM_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ +#define SND_PCM_AES0_CON_MODE (3<<6) /* mask - mode */ +#define SND_PCM_AES1_PRO_MODE (15<<0) /* mask - channel mode */ +#define SND_PCM_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ +#define SND_PCM_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ +#define SND_PCM_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ +#define SND_PCM_AES1_PRO_MODE_TWO (8<<0) /* two channels */ +#define SND_PCM_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ +#define SND_PCM_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ +#define SND_PCM_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ +#define SND_PCM_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ +#define SND_PCM_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ +#define SND_PCM_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ +#define SND_PCM_AES1_CON_CATEGORY 0x7f +#define SND_PCM_AES1_CON_GENERAL 0x00 +#define SND_PCM_AES1_CON_EXPERIMENTAL 0x40 +#define SND_PCM_AES1_CON_SOLIDMEM_MASK 0x0f +#define SND_PCM_AES1_CON_SOLIDMEM_ID 0x08 +#define SND_PCM_AES1_CON_BROADCAST1_MASK 0x07 +#define SND_PCM_AES1_CON_BROADCAST1_ID 0x04 +#define SND_PCM_AES1_CON_DIGDIGCONV_MASK 0x07 +#define SND_PCM_AES1_CON_DIGDIGCONV_ID 0x02 +#define SND_PCM_AES1_CON_ADC_COPYRIGHT_MASK 0x1f +#define SND_PCM_AES1_CON_ADC_COPYRIGHT_ID 0x06 +#define SND_PCM_AES1_CON_ADC_MASK 0x1f +#define SND_PCM_AES1_CON_ADC_ID 0x16 +#define SND_PCM_AES1_CON_BROADCAST2_MASK 0x0f +#define SND_PCM_AES1_CON_BROADCAST2_ID 0x0e +#define SND_PCM_AES1_CON_LASEROPT_MASK 0x07 +#define SND_PCM_AES1_CON_LASEROPT_ID 0x01 +#define SND_PCM_AES1_CON_MUSICAL_MASK 0x07 +#define SND_PCM_AES1_CON_MUSICAL_ID 0x05 +#define SND_PCM_AES1_CON_MAGNETIC_MASK 0x07 +#define SND_PCM_AES1_CON_MAGNETIC_ID 0x03 +#define SND_PCM_AES1_CON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x00) +#define SND_PCM_AES1_CON_NON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x08) +#define SND_PCM_AES1_CON_PCM_CODER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x00) +#define SND_PCM_AES1_CON_SAMPLER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x20) +#define SND_PCM_AES1_CON_MIXER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x10) +#define SND_PCM_AES1_CON_RATE_CONVERTER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x18) +#define SND_PCM_AES1_CON_SYNTHESIZER (SND_PCM_AES1_CON_MUSICAL_ID|0x00) +#define SND_PCM_AES1_CON_MICROPHONE (SND_PCM_AES1_CON_MUSICAL_ID|0x08) +#define SND_PCM_AES1_CON_DAT (SND_PCM_AES1_CON_MAGNETIC_ID|0x00) +#define SND_PCM_AES1_CON_VCR (SND_PCM_AES1_CON_MAGNETIC_ID|0x08) +#define SND_PCM_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ +#define SND_PCM_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ +#define SND_PCM_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ +#define SND_PCM_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ +#define SND_PCM_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ +#define SND_PCM_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ +#define SND_PCM_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ +#define SND_PCM_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ +#define SND_PCM_AES2_CON_SOURCE (15<<0) /* mask - source number */ +#define SND_PCM_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ +#define SND_PCM_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ +#define SND_PCM_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ +#define SND_PCM_AES3_CON_FS (15<<0) /* mask - sample frequency */ +#define SND_PCM_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ +#define SND_PCM_AES3_CON_FS_48000 (2<<0) /* 48kHz */ +#define SND_PCM_AES3_CON_FS_32000 (3<<0) /* 32kHz */ +#define SND_PCM_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ +#define SND_PCM_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ +#define SND_PCM_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ +#define SND_PCM_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ + +/* + * User interface + */ + +/* + * in this loop, dmabuf.count signifies the amount of data that is + * waiting to be copied to the user's buffer. it is filled by the dma + * machine and drained by this loop. + */ +static ssize_t +ymf_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf; + struct ymf_unit *unit = state->unit; + DECLARE_WAITQUEUE(waita, current); + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; /* This many to go in this revolution */ + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + ret = 0; + + add_wait_queue(&dmabuf->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while (count > 0) { + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + spin_unlock_irqrestore(&unit->reg_lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (!state->rpcm.running) { + ymf_capture_trigger(state->unit, &state->rpcm, 1); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + break; + } + /* This isnt strictly right for the 810 but it'll do */ + tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2); + tmo >>= state->format.shift; + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + tmo = schedule_timeout(tmo); + spin_lock_irqsave(&state->unit->reg_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tmo == 0 && dmabuf->count == 0) { + printk(KERN_ERR "ymfpci%d: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + state->unit->dev_audio, + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + break; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) ret = -EFAULT; + break; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } + + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + // spin_unlock_irqrestore(&unit->reg_lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + // spin_lock_irqsave(&unit->reg_lock, flags); + if (!state->rpcm.running) { + ymf_capture_trigger(unit, &state->rpcm, 1); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + return ret; +} + +static ssize_t +ymf_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; + struct ymf_unit *unit = state->unit; + DECLARE_WAITQUEUE(waita, current); + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; /* This many to go in this revolution */ + int redzone; + int delay; + + YMFDBGW("ymf_write: count %d\n", count); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + ret = 0; + + /* + * Alan's cs46xx works without a red zone - marvel of ingenuity. + * We are not so brilliant... Red zone does two things: + * 1. allows for safe start after a pause as we have no way + * to know what the actual, relentlessly advancing, hwptr is. + * 2. makes computations in ymf_pcm_interrupt simpler. + */ + redzone = ymf_calc_lend(state->format.rate) << state->format.shift; + redzone *= 3; /* 2 redzone + 1 possible uncertainty reserve. */ + + add_wait_queue(&dmabuf->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while (count > 0) { + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + if (dmabuf->count < 0) { + printk(KERN_ERR + "ymf_write: count %d, was legal in cs46xx\n", + dmabuf->count); + dmabuf->count = 0; + } + if (dmabuf->count == 0) { + swptr = dmabuf->hwptr; + if (state->wpcm.running) { + /* + * Add uncertainty reserve. + */ + cnt = ymf_calc_lend(state->format.rate); + cnt <<= state->format.shift; + if ((swptr += cnt) >= dmabuf->dmasize) { + swptr -= dmabuf->dmasize; + } + } + dmabuf->swptr = swptr; + } else { + /* + * XXX This is not right if dmabuf->count is small - + * about 2*x frame size or less. We cannot count on + * on appending and not causing an artefact. + * Should use a variation of the count==0 case above. + */ + swptr = dmabuf->swptr; + } + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize - redzone) + cnt = (dmabuf->dmasize - redzone) - dmabuf->count; + spin_unlock_irqrestore(&unit->reg_lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + YMFDBGW("ymf_write: full, count %d swptr %d\n", + dmabuf->count, dmabuf->swptr); + /* + * buffer is full, start the dma machine and + * wait for data to be played + */ + spin_lock_irqsave(&unit->reg_lock, flags); + if (!state->wpcm.running) { + ymf_playback_trigger(unit, &state->wpcm, 1); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + break; + } + + if ((swptr += cnt) >= dmabuf->dmasize) { + swptr -= dmabuf->dmasize; + } + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } + dmabuf->swptr = swptr; + dmabuf->count += cnt; + + /* + * Start here is a bad idea - may cause startup click + * in /bin/play when dmabuf is not full yet. + * However, some broken applications do not make + * any use of SNDCTL_DSP_SYNC (Doom is the worst). + * One frame is about 5.3ms, Doom write size is 46ms. + */ + delay = state->format.rate / 20; /* 50ms */ + delay <<= state->format.shift; + if (dmabuf->count >= delay && !state->wpcm.running) { + ymf_playback_trigger(unit, &state->wpcm, 1); + } + + spin_unlock_irqrestore(&unit->reg_lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + YMFDBGW("ymf_write: ret %d dmabuf.count %d\n", ret, dmabuf->count); + return ret; +} + +static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf; + int redzone; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &state->wpcm.dmabuf.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &state->rpcm.dmabuf.wait, wait); + + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (file->f_mode & FMODE_READ) { + dmabuf = &state->rpcm.dmabuf; + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + + dmabuf = &state->wpcm.dmabuf; + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + /* + * Don't select unless a full fragment is available. + * Otherwise artsd does GETOSPACE, sees 0, and loops. + */ + if (dmabuf->count + redzone + dmabuf->fragsize + <= dmabuf->dmasize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + + return mask; +} + +static int ymf_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; + int ret; + unsigned long size; + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(state, 0)) != 0) + return ret; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(state, 1)) != 0) + return ret; + } else + return -EINVAL; + + if (vma->vm_pgoff != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) + return -EAGAIN; + dmabuf->mapped = 1; + +/* P3 */ printk(KERN_INFO "ymfpci: using memory mapped sound, untested!\n"); + return 0; +} + +static int ymf_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int redzone; + int val; + + switch (cmd) { + case OSS_GETVERSION: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETVER) arg 0x%lx\n", cmd, arg); + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + YMFDBGX("ymf_ioctl: cmd 0x%x(RESET)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = dmabuf->total_bytes = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = dmabuf->total_bytes = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + return 0; + + case SNDCTL_DSP_SYNC: + YMFDBGX("ymf_ioctl: cmd 0x%x(SYNC)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + dmabuf = &state->wpcm.dmabuf; + if (file->f_flags & O_NONBLOCK) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (dmabuf->count != 0 && !state->wpcm.running) { + ymf_start_dac(state); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } else { + ymf_wait_dac(state); + } + } + /* XXX What does this do for reading? dmabuf->count=0; ? */ + return 0; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SPEED) sp %d\n", cmd, val); + if (val >= 8000 && val <= 48000) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.rate = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.rate = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + return put_user(state->format.rate, (int *)arg); + + /* + * OSS manual does not mention SNDCTL_DSP_STEREO at all. + * All channels are mono and if you want stereo, you + * play into two channels with SNDCTL_DSP_CHANNELS. + * However, mpg123 calls it. I wonder, why Michael Hipp used it. + */ + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(STEREO) st %d\n", cmd, val); + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.voices = val ? 2 : 1; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.voices = val ? 2 : 1; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETBLK)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(state, 0))) + return val; + val = state->wpcm.dmabuf.fragsize; + YMFDBGX("ymf_ioctl: GETBLK w %d\n", val); + return put_user(val, (int *)arg); + } + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + return val; + val = state->rpcm.dmabuf.fragsize; + YMFDBGX("ymf_ioctl: GETBLK r %d\n", val); + return put_user(val, (int *)arg); + } + return -EINVAL; + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + YMFDBGX("ymf_ioctl: cmd 0x%x(GETFMTS)\n", cmd); + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SETFMT) fmt %d\n", cmd, val); + if (val == AFMT_S16_LE || val == AFMT_U8) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.format = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.format = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + return put_user(state->format.format, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(CHAN) ch %d\n", cmd, val); + if (val != 0) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + if (val == 1 || val == 2) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf = &state->wpcm.dmabuf; + dmabuf->ready = 0; + state->format.voices = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + if (val == 1 || val == 2) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf = &state->rpcm.dmabuf; + dmabuf->ready = 0; + state->format.voices = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + } + return put_user(state->format.voices, (int *)arg); + + case SNDCTL_DSP_POST: + YMFDBGX("ymf_ioctl: cmd 0x%x(POST)\n", cmd); + /* + * Quoting OSS PG: + * The ioctl SNDCTL_DSP_POST is a lightweight version of + * SNDCTL_DSP_SYNC. It just tells to the driver that there + * is likely to be a pause in the output. This makes it + * possible for the device to handle the pause more + * intelligently. This ioctl doesn't block the application. + * + * The paragraph above is a clumsy way to say "flush ioctl". + * This ioctl is used by mpg123. + */ + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) { + ymf_start_dac(state); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SETFRAG) fr 0x%04x:%04x(%d:%d)\n", + cmd, + (val >> 16) & 0xFFFF, val & 0xFFFF, + (val >> 16) & 0xFFFF, val & 0xFFFF); + dmabuf = &state->wpcm.dmabuf; + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (dmabuf->ossfragshift < 4) + dmabuf->ossfragshift = 4; + if (dmabuf->ossfragshift > 15) + dmabuf->ossfragshift = 15; + return 0; + + case SNDCTL_DSP_GETOSPACE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETOSPACE)\n", cmd); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + dmabuf = &state->wpcm.dmabuf; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + spin_lock_irqsave(&state->unit->reg_lock, flags); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->dmasize - dmabuf->count - redzone; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETISPACE)\n", cmd); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + dmabuf = &state->rpcm.dmabuf; + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->unit->reg_lock, flags); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + YMFDBGX("ymf_ioctl: cmd 0x%x(NONBLOCK)\n", cmd); + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETCAPS)\n", cmd); + /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, + (int *)arg); */ + return put_user(0, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETIPTR)\n", cmd); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + YMFDBGX("ymf_ioctl: GETIPTR ptr %d bytes %d\n", + cinfo.ptr, cinfo.bytes); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETOPTR)\n", cmd); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + YMFDBGX("ymf_ioctl: GETOPTR ptr %d bytes %d\n", + cinfo.ptr, cinfo.bytes); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_SETDUPLEX: + YMFDBGX("ymf_ioctl: cmd 0x%x(SETDUPLEX)\n", cmd); + return 0; /* Always duplex */ + + case SOUND_PCM_READ_RATE: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_RATE)\n", cmd); + return put_user(state->format.rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_CH)\n", cmd); + return put_user(state->format.voices, (int *)arg); + + case SOUND_PCM_READ_BITS: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_BITS)\n", cmd); + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + YMFDBGX("ymf_ioctl: cmd 0x%x unsupported\n", cmd); + return -ENOTTY; + + default: + /* + * Some programs mix up audio devices and ioctls + * or perhaps they expect "universal" ioctls, + * for instance we get SNDCTL_TMR_CONTINUE here. + * (mpg123 -g 100 ends here too - to be fixed.) + */ + YMFDBGX("ymf_ioctl: cmd 0x%x unknown\n", cmd); + break; + } + return -ENOTTY; +} + +/* + * open(2) + * We use upper part of the minor to distinguish between soundcards. + * Channels are opened with a clone open. + */ +static int ymf_open(struct inode *inode, struct file *file) +{ + struct list_head *list; + ymfpci_t *unit = NULL; + int minor; + struct ymf_state *state; + int err; + + minor = minor(inode->i_rdev); + if ((minor & 0x0F) == 3) { /* /dev/dspN */ + ; + } else { + return -ENXIO; + } + + unit = NULL; /* gcc warns */ + list_for_each(list, &ymf_devs) { + unit = list_entry(list, ymfpci_t, ymf_devs); + if (((unit->dev_audio ^ minor) & ~0x0F) == 0) + break; + } + if (list == &ymf_devs) + return -ENODEV; + + down(&unit->open_sem); + + if ((state = ymf_state_alloc(unit)) == NULL) { + up(&unit->open_sem); + return -ENOMEM; + } + list_add_tail(&state->chain, &unit->states); + + file->private_data = state; + + /* + * ymf_read and ymf_write that we borrowed from cs46xx + * allocate buffers with prog_dmabuf(). We call prog_dmabuf + * here so that in case of DMA memory exhaustion open + * fails rather than write. + * + * XXX prog_dmabuf allocates voice. Should allocate explicitly, above. + */ + if (file->f_mode & FMODE_WRITE) { + if (!state->wpcm.dmabuf.ready) { + if ((err = prog_dmabuf(state, 0)) != 0) { + goto out_nodma; + } + } + } + if (file->f_mode & FMODE_READ) { + if (!state->rpcm.dmabuf.ready) { + if ((err = prog_dmabuf(state, 1)) != 0) { + goto out_nodma; + } + } + } + +#if 0 /* test if interrupts work */ + ymfpci_writew(unit, YDSXGR_TIMERCOUNT, 0xfffe); /* ~ 680ms */ + ymfpci_writeb(unit, YDSXGR_TIMERCTRL, + (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN)); +#endif + up(&unit->open_sem); + + return 0; + +out_nodma: + /* + * XXX Broken custom: "goto out_xxx" in other place is + * a nestable exception, but here it is not nestable due to semaphore. + * XXX Doubtful technique of self-describing objects.... + */ + dealloc_dmabuf(unit, &state->wpcm.dmabuf); + dealloc_dmabuf(unit, &state->rpcm.dmabuf); + ymf_pcm_free_substream(&state->wpcm); + ymf_pcm_free_substream(&state->rpcm); + + list_del(&state->chain); + kfree(state); + + up(&unit->open_sem); + return err; +} + +static int ymf_release(struct inode *inode, struct file *file) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + ymfpci_t *unit = state->unit; + +#if 0 /* test if interrupts work */ + ymfpci_writeb(unit, YDSXGR_TIMERCTRL, 0); +#endif + + down(&unit->open_sem); + + /* + * XXX Solve the case of O_NONBLOCK close - don't deallocate here. + * Deallocate when unloading the driver and we can wait. + */ + ymf_wait_dac(state); + ymf_stop_adc(state); /* fortunately, it's immediate */ + dealloc_dmabuf(unit, &state->wpcm.dmabuf); + dealloc_dmabuf(unit, &state->rpcm.dmabuf); + ymf_pcm_free_substream(&state->wpcm); + ymf_pcm_free_substream(&state->rpcm); + + list_del(&state->chain); + file->private_data = NULL; /* Can you tell I programmed Solaris */ + kfree(state); + + up(&unit->open_sem); + + return 0; +} + +/* + * Mixer operations are based on cs46xx. + */ +static int ymf_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = minor(inode->i_rdev); + struct list_head *list; + ymfpci_t *unit; + int i; + + list_for_each(list, &ymf_devs) { + unit = list_entry(list, ymfpci_t, ymf_devs); + for (i = 0; i < NR_AC97; i++) { + if (unit->ac97_codec[i] != NULL && + unit->ac97_codec[i]->dev_mixer == minor) { + goto match; + } + } + } + return -ENODEV; + + match: + file->private_data = unit->ac97_codec[i]; + + return 0; +} + +static int ymf_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int ymf_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static /*const*/ struct file_operations ymf_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: ymf_read, + write: ymf_write, + poll: ymf_poll, + ioctl: ymf_ioctl, + mmap: ymf_mmap, + open: ymf_open, + release: ymf_release, +}; + +static /*const*/ struct file_operations ymf_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: ymf_ioctl_mixdev, + open: ymf_open_mixdev, + release: ymf_release_mixdev, +}; + +/* + */ + +static int ymf_suspend(struct pci_dev *pcidev, u32 unused) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct ymf_dmabuf *dmabuf; + struct list_head *p; + struct ymf_state *state; + struct ac97_codec *codec; + int i; + + spin_lock_irqsave(&unit->reg_lock, flags); + + unit->suspended = 1; + + for (i = 0; i < NR_AC97; i++) { + if ((codec = unit->ac97_codec[i]) != NULL) + ac97_save_state(codec); + } + + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + + dmabuf = &state->wpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + + dmabuf = &state->rpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + } + + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0); + ymfpci_disable_dsp(unit); + + spin_unlock_irqrestore(&unit->reg_lock, flags); + + return 0; +} + +static int ymf_resume(struct pci_dev *pcidev) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct list_head *p; + struct ymf_state *state; + struct ac97_codec *codec; + int i; + + ymfpci_aclink_reset(unit->pci); + ymfpci_codec_ready(unit, 0, 1); /* prints diag if not ready. */ + +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY + /* XXX At this time the legacy registers are probably deprogrammed. */ +#endif + + ymfpci_download_image(unit); + + ymf_memload(unit); + + spin_lock_irqsave(&unit->reg_lock, flags); + + if (unit->start_count) { + ymfpci_writel(unit, YDSXGR_MODE, 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + + for (i = 0; i < NR_AC97; i++) { + if ((codec = unit->ac97_codec[i]) != NULL) + ac97_restore_state(codec); + } + + unit->suspended = 0; + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + wake_up(&state->wpcm.dmabuf.wait); + wake_up(&state->rpcm.dmabuf.wait); + } + + spin_unlock_irqrestore(&unit->reg_lock, flags); + return 0; +} + +/* + * initialization routines + */ + +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY + +static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev) +{ + int v; + int mpuio = -1, oplio = -1; + + switch (unit->iomidi) { + case 0x330: + mpuio = 0; + break; + case 0x300: + mpuio = 1; + break; + case 0x332: + mpuio = 2; + break; + case 0x334: + mpuio = 3; + break; + default: ; + } + + switch (unit->iosynth) { + case 0x388: + oplio = 0; + break; + case 0x398: + oplio = 1; + break; + case 0x3a0: + oplio = 2; + break; + case 0x3a8: + oplio = 3; + break; + default: ; + } + + if (mpuio >= 0 || oplio >= 0) { + /* 0x0020: 1 - 10 bits of I/O address decoded, 0 - 16 bits. */ + v = 0x001e; + pci_write_config_word(pcidev, PCIR_LEGCTRL, v); + + switch (pcidev->device) { + case PCI_DEVICE_ID_YAMAHA_724: + case PCI_DEVICE_ID_YAMAHA_740: + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + v = 0x8800; + if (mpuio >= 0) { v |= mpuio<<4; } + if (oplio >= 0) { v |= oplio; } + pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); + break; + + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + v = 0x8800; + pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); + if (oplio >= 0) { + pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth); + } + if (mpuio >= 0) { + pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi); + } + break; + + default: + printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n", + pcidev->device); + return -EINVAL; + } + } + + return 0; +} +#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */ + +static void ymfpci_aclink_reset(struct pci_dev * pci) +{ + u8 cmd; + + /* + * In the 744, 754 only 0x01 exists, 0x02 is undefined. + * It does not seem to hurt to trip both regardless of revision. + */ + pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); + + pci_write_config_word(pci, PCIR_DSXPWRCTRL1, 0); + pci_write_config_word(pci, PCIR_DSXPWRCTRL2, 0); +} + +static void ymfpci_enable_dsp(ymfpci_t *codec) +{ + ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001); +} + +static void ymfpci_disable_dsp(ymfpci_t *codec) +{ + u32 val; + int timeout = 1000; + + val = ymfpci_readl(codec, YDSXGR_CONFIG); + if (val) + ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000); + while (timeout-- > 0) { + val = ymfpci_readl(codec, YDSXGR_STATUS); + if ((val & 0x00000002) == 0) + break; + } +} + +#include "ymfpci_image.h" + +static void ymfpci_download_image(ymfpci_t *codec) +{ + int i, ver_1e; + u16 ctrl; + + ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000); + ymfpci_disable_dsp(codec); + ymfpci_writel(codec, YDSXGR_MODE, 0x00010000); + ymfpci_writel(codec, YDSXGR_MODE, 0x00000000); + ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000); + ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000); + ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000); + ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000); + ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + + /* setup DSP instruction code */ + for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); + + switch (codec->pci->device) { + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + ver_1e = 1; + break; + default: + ver_1e = 0; + } + + if (ver_1e) { + /* setup control instruction code */ + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst1E[i]); + } else { + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst[i]); + } + + ymfpci_enable_dsp(codec); + + /* 0.02s sounds not too bad, we may do schedule_timeout() later. */ + mdelay(20); /* seems we need some delay after downloading image.. */ +} + +static int ymfpci_memalloc(ymfpci_t *codec) +{ + unsigned int playback_ctrl_size; + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int size; + unsigned int off; + char *ptr; + dma_addr_t pba; + int voice, bank; + + playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; + bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2; + bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2; + bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2; + codec->work_size = YDSXG_DEFAULT_WORK_SIZE; + + size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + + ((bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) + + ((bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) + + ((bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) + + codec->work_size; + + ptr = pci_alloc_consistent(codec->pci, size + 0xff, &pba); + if (ptr == NULL) + return -ENOMEM; + codec->dma_area_va = ptr; + codec->dma_area_ba = pba; + codec->dma_area_size = size + 0xff; + + if ((off = ((uint) ptr) & 0xff) != 0) { + ptr += 0x100 - off; + pba += 0x100 - off; + } + + /* + * Hardware requires only ptr[playback_ctrl_size] zeroed, + * but in our judgement it is a wrong kind of savings, so clear it all. + */ + memset(ptr, 0, size); + + codec->ctrl_playback = (u32 *)ptr; + codec->ctrl_playback_ba = pba; + codec->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); + ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + pba += (playback_ctrl_size + 0x00ff) & ~0x00ff; + + off = 0; + for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { + codec->voices[voice].number = voice; + codec->voices[voice].bank = + (ymfpci_playback_bank_t *) (ptr + off); + codec->voices[voice].bank_ba = pba + off; + off += 2 * bank_size_playback; /* 2 banks */ + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + off = 0; + codec->bank_base_capture = pba; + for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + codec->bank_capture[voice][bank] = + (ymfpci_capture_bank_t *) (ptr + off); + off += bank_size_capture; + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + off = 0; + codec->bank_base_effect = pba; + for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + codec->bank_effect[voice][bank] = + (ymfpci_effect_bank_t *) (ptr + off); + off += bank_size_effect; + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + codec->work_base = pba; + + return 0; +} + +static void ymfpci_memfree(ymfpci_t *codec) +{ + ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_WORKBASE, 0); + ymfpci_writel(codec, YDSXGR_WORKSIZE, 0); + pci_free_consistent(codec->pci, + codec->dma_area_size, codec->dma_area_va, codec->dma_area_ba); +} + +static void ymf_memload(ymfpci_t *unit) +{ + + ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, unit->ctrl_playback_ba); + ymfpci_writel(unit, YDSXGR_RECCTRLBASE, unit->bank_base_capture); + ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, unit->bank_base_effect); + ymfpci_writel(unit, YDSXGR_WORKBASE, unit->work_base); + ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2); + + /* S/PDIF output initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0); + ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS, + SND_PCM_AES0_CON_EMPHASIS_NONE | + (SND_PCM_AES1_CON_ORIGINAL << 8) | + (SND_PCM_AES1_CON_PCM_CODER << 8)); + + /* S/PDIF input initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0); + + /* move this volume setup to mixer */ + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0); + ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); +} + +static int ymf_ac97_init(ymfpci_t *unit, int num_ac97) +{ + struct ac97_codec *codec; + u16 eid; + + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = unit; + codec->id = num_ac97; + + codec->codec_read = ymfpci_codec_read; + codec->codec_write = ymfpci_codec_write; + + if (ac97_probe_codec(codec) == 0) { + printk(KERN_ERR "ymfpci: ac97_probe_codec failed\n"); + goto out_kfree; + } + + eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); + if (eid==0xFFFFFF) { + printk(KERN_WARNING "ymfpci: no codec attached ?\n"); + goto out_kfree; + } + + unit->ac97_features = eid; + + if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) { + printk(KERN_ERR "ymfpci: couldn't register mixer!\n"); + goto out_kfree; + } + + unit->ac97_codec[num_ac97] = codec; + + return 0; + out_kfree: + kfree(codec); + return -ENODEV; +} + +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY +# ifdef MODULE +static int mpu_io = 0; +static int synth_io = 0; +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(synth_io, "i"); +# else +static int mpu_io = 0x330; +static int synth_io = 0x388; +# endif +static int assigned; +#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */ + +static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + u16 ctrl; + unsigned long base; + ymfpci_t *codec; + + int err; + + if ((err = pci_enable_device(pcidev)) != 0) { + printk(KERN_ERR "ymfpci: pci_enable_device failed\n"); + return err; + } + base = pci_resource_start(pcidev, 0); + + if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "ymfpci: no core\n"); + return -ENOMEM; + } + memset(codec, 0, sizeof(*codec)); + + spin_lock_init(&codec->reg_lock); + spin_lock_init(&codec->voice_lock); + init_MUTEX(&codec->open_sem); + INIT_LIST_HEAD(&codec->states); + codec->pci = pcidev; + + pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev); + + if (request_mem_region(base, 0x8000, "ymfpci") == NULL) { + printk(KERN_ERR "ymfpci: unable to request mem region\n"); + goto out_free; + } + + if ((codec->reg_area_virt = ioremap(base, 0x8000)) == NULL) { + printk(KERN_ERR "ymfpci: unable to map registers\n"); + goto out_release_region; + } + + pci_set_master(pcidev); + + printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n", + (char *)ent->driver_data, base, pcidev->irq); + + ymfpci_aclink_reset(pcidev); + if (ymfpci_codec_ready(codec, 0, 1) < 0) + goto out_unmap; + +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY + if (assigned == 0) { + codec->iomidi = mpu_io; + codec->iosynth = synth_io; + if (ymfpci_setup_legacy(codec, pcidev) < 0) + goto out_unmap; + assigned = 1; + } +#endif + + ymfpci_download_image(codec); + + if (ymfpci_memalloc(codec) < 0) + goto out_disable_dsp; + ymf_memload(codec); + + if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) { + printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", + pcidev->irq); + goto out_memfree; + } + + /* register /dev/dsp */ + if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) { + printk(KERN_ERR "ymfpci: unable to register dsp\n"); + goto out_free_irq; + } + + /* + * Poke just the primary for the moment. + */ + if ((err = ymf_ac97_init(codec, 0)) != 0) + goto out_unregister_sound_dsp; + +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY + codec->opl3_data.name = "ymfpci"; + codec->mpu_data.name = "ymfpci"; + + codec->opl3_data.io_base = codec->iosynth; + codec->opl3_data.irq = -1; + + codec->mpu_data.io_base = codec->iomidi; + codec->mpu_data.irq = -1; /* May be different from our PCI IRQ. */ + + if (codec->iomidi) { + if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) { + codec->iomidi = 0; /* XXX kludge */ + } + } +#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */ + + /* put it into driver list */ + list_add_tail(&codec->ymf_devs, &ymf_devs); + pci_set_drvdata(pcidev, codec); + + return 0; + + out_unregister_sound_dsp: + unregister_sound_dsp(codec->dev_audio); + out_free_irq: + free_irq(pcidev->irq, codec); + out_memfree: + ymfpci_memfree(codec); + out_disable_dsp: + ymfpci_disable_dsp(codec); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + ymfpci_writel(codec, YDSXGR_STATUS, ~0); + out_unmap: + iounmap(codec->reg_area_virt); + out_release_region: + release_mem_region(pci_resource_start(pcidev, 0), 0x8000); + out_free: + kfree(codec); + return -ENODEV; +} + +static void __devexit ymf_remove_one(struct pci_dev *pcidev) +{ + __u16 ctrl; + ymfpci_t *codec = pci_get_drvdata(pcidev); + + /* remove from list of devices */ + list_del(&codec->ymf_devs); + + unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer); + kfree(codec->ac97_codec[0]); + unregister_sound_dsp(codec->dev_audio); + free_irq(pcidev->irq, codec); + ymfpci_memfree(codec); + ymfpci_writel(codec, YDSXGR_STATUS, ~0); + ymfpci_disable_dsp(codec); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + iounmap(codec->reg_area_virt); + release_mem_region(pci_resource_start(pcidev, 0), 0x8000); +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY + if (codec->iomidi) { + unload_uart401(&codec->mpu_data); + } +#endif /* CONFIG_SOUND_ALSA_YMFPCI_LEGACY */ + kfree(codec); +} + +MODULE_AUTHOR("Jaroslav Kysela"); +MODULE_DESCRIPTION("Yamaha YMF7xx PCI Audio"); +MODULE_LICENSE("GPL"); + +static struct pci_driver ymfpci_driver = { + name: "ymfpci", + id_table: ymf_id_tbl, + probe: ymf_probe_one, + remove: __devexit_p(ymf_remove_one), + suspend: ymf_suspend, + resume: ymf_resume +}; + +static int __init ymf_init_module(void) +{ + return pci_module_init(&ymfpci_driver); +} + +static void __exit ymf_cleanup_module (void) +{ + pci_unregister_driver(&ymfpci_driver); +} + +module_init(ymf_init_module); +module_exit(ymf_cleanup_module); diff -Nru linux/sound/oss/ymfpci.h linux-2.4.19-pre5-mjc/sound/oss/ymfpci.h --- linux/sound/oss/ymfpci.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ymfpci.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,359 @@ +#ifndef __YMFPCI_H +#define __YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include + +/* + * Direct registers + */ + +/* #define YMFREG(codec, reg) (codec->port + YDSXGR_##reg) */ + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCTRL_TEN 0x0001 +#define YDSXGR_TIMERCTRL_TIEN 0x0002 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_SPDIFOUTVOL 0x0088 +#define YDSXGR_SPDIFOUTVOLL 0x0088 +#define YDSXGR_SPDIFOUTVOLR 0x008A +#define YDSXGR_AC3OUTVOL 0x008C +#define YDSXGR_AC3OUTVOLL 0x008C +#define YDSXGR_AC3OUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_SPDIFLOOPVOL 0x009C +#define YDSXGR_SPDIFLOOPVOLL 0x009E +#define YDSXGR_SPDIFLOOPVOLR 0x009E +#define YDSXGR_AC3LOOPVOL 0x00A0 +#define YDSXGR_AC3LOOPVOLL 0x00A0 +#define YDSXGR_AC3LOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL2 0x00B8 +#define YDSXGR_SPDIFOUTVOL2L 0x00B8 +#define YDSXGR_SPDIFOUTVOL2R 0x00BA +#define YDSXGR_SPDIFLOOPVOL2 0x00BC +#define YDSXGR_SPDIFLOOPVOL2L 0x00BC +#define YDSXGR_SPDIFLOOPVOL2R 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_LEGCTRL 0x40 +#define PCIR_ELEGCTRL 0x42 +#define PCIR_DSXGCTRL 0x48 +#define PCIR_DSXPWRCTRL1 0x4a +#define PCIR_DSXPWRCTRL2 0x4e +#define PCIR_OPLADR 0x60 +#define PCIR_SBADR 0x62 +#define PCIR_MPUADR 0x64 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +#define YMF_SAMPF 256 /* Samples per frame @48000 */ + +/* + * The slot/voice control bank (2 of these per voice) + */ + +typedef struct stru_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; /* P3: Always 0 for some reason. */ + u32 num_of_frames; + u32 loop_count; + u32 start; /* P3: J. reads this to know where chip is. */ + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; +} ymfpci_playback_bank_t; + +typedef struct stru_ymfpci_capture_bank { + u32 base; /* 32-bit address (aligned at 4) */ + u32 loop_end; /* size in BYTES (aligned at 4) */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +} ymfpci_capture_bank_t; + +typedef struct stru_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +} ymfpci_effect_bank_t; + +typedef struct ymf_voice ymfpci_voice_t; +/* + * Throughout the code Yaroslav names YMF unit pointer "codec" + * even though it does not correspond to any codec. Must be historic. + * We replace it with "unit" over time. + * AC97 parts use "codec" to denote a codec, naturally. + */ +typedef struct ymf_unit ymfpci_t; + +typedef enum { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +} ymfpci_voice_type_t; + +struct ymf_voice { + // ymfpci_t *codec; + int number; + char use, pcm, synth, midi; // bool + ymfpci_playback_bank_t *bank; + struct ymf_pcm *ypcm; + dma_addr_t bank_ba; +}; + +struct ymf_capture { + // struct ymf_unit *unit; + int use; + ymfpci_capture_bank_t *bank; + struct ymf_pcm *ypcm; +}; + +struct ymf_unit { + u8 rev; /* PCI revision */ + void *reg_area_virt; + void *dma_area_va; + dma_addr_t dma_area_ba; + unsigned int dma_area_size; + + dma_addr_t bank_base_capture; + dma_addr_t bank_base_effect; + dma_addr_t work_base; + unsigned int work_size; + + u32 *ctrl_playback; + dma_addr_t ctrl_playback_ba; + ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; + ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + int suspended; + + u32 active_bank; + struct ymf_voice voices[YDSXG_PLAYBACK_VOICES]; + struct ymf_capture capture[YDSXG_CAPTURE_VOICES]; + + struct ac97_codec *ac97_codec[NR_AC97]; + u16 ac97_features; + + struct pci_dev *pci; + +#ifdef CONFIG_SOUND_ALSA_YMFPCI_LEGACY + /* legacy hardware resources */ + unsigned int iosynth, iomidi; + struct address_info opl3_data, mpu_data; +#endif + + spinlock_t reg_lock; + spinlock_t voice_lock; + + /* soundcore stuff */ + int dev_audio; + struct semaphore open_sem; + + struct list_head ymf_devs; + struct list_head states; /* List of states for this unit */ +}; + +struct ymf_dmabuf { + dma_addr_t dma_addr; + void *rawbuf; + unsigned buforder; + + /* OSS buffer management stuff */ + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started */ + unsigned swptr; /* where driver last clear/filled */ + int count; /* fill count */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; /* Total rawbuf[] size */ + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +}; + +struct ymf_pcm_format { + int format; /* OSS format */ + int rate; /* rate in Hz */ + int voices; /* number of voices */ + int shift; /* redundant, computed from the above */ +}; + +typedef enum { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +} ymfpci_pcm_type_t; + +/* This is variant record, but we hate unions. Little waste on pointers []. */ +struct ymf_pcm { + ymfpci_pcm_type_t type; + struct ymf_state *state; + + ymfpci_voice_t *voices[2]; + int capture_bank_number; + + struct ymf_dmabuf dmabuf; + int running; + int spdif; +}; + +/* + * "Software" or virtual channel, an instance of opened /dev/dsp. + * It may have two physical channels (pcms) for duplex operations. + */ + +struct ymf_state { + struct list_head chain; + struct ymf_unit *unit; /* backpointer */ + struct ymf_pcm rpcm, wpcm; + struct ymf_pcm_format format; +}; + +#endif /* __YMFPCI_H */ diff -Nru linux/sound/oss/ymfpci_image.h linux-2.4.19-pre5-mjc/sound/oss/ymfpci_image.h --- linux/sound/oss/ymfpci_image.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/ymfpci_image.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1565 @@ +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static u32 DspInst[YDSXG_DSPLENGTH / 4] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09 creat +// 04/12 stop nise fix +// 06/21 WorkingOff timming +static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ diff -Nru linux/sound/oss/yss225.c linux-2.4.19-pre5-mjc/sound/oss/yss225.c --- linux/sound/oss/yss225.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/yss225.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,319 @@ +#include + +unsigned char page_zero[] __initdata = { +0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, +0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, +0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, +0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, +0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, +0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, +0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, +0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, +0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, +0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, +0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, +0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, +0x1d, 0x02, 0xdf +}; + +unsigned char page_one[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, +0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, +0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, +0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, +0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, +0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, +0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, +0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, +0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, +0x60, 0x00, 0x1b +}; + +unsigned char page_two[] __initdata = { +0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, +0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, +0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, +0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, +0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, +0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, +0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, +0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 +}; + +unsigned char page_three[] __initdata = { +0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, +0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, +0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, +0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, +0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 +}; + +unsigned char page_four[] __initdata = { +0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, +0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, +0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, +0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, +0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 +}; + +unsigned char page_six[] __initdata = { +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, +0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, +0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, +0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, +0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, +0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, +0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, +0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, +0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, +0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, +0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, +0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, +0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, +0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, +0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, +0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, +0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, +0x80, 0x00, 0x7e, 0x80, 0x80 +}; + +unsigned char page_seven[] __initdata = { +0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, +0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, +0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, +0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, +0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, +0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, +0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, +0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, +0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, +0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, +0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, +0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x00 +}; + +unsigned char page_zero_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_one_v2[] __initdata = { +0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_two_v2[] __initdata = { +0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_three_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_four_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_seven_v2[] __initdata = { +0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char mod_v2[] __initdata = { +0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, +0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, +0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, +0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, +0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, +0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, +0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, +0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, +0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, +0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, +0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, +0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, +0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, +0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, +0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, +0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, +0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, +0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, +0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, +0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, +0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, +0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, +0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, +0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 +}; +unsigned char coefficients[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, +0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, +0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, +0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, +0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, +0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, +0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, +0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, +0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, +0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, +0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, +0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, +0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, +0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, +0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, +0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, +0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, +0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, +0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, +0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, +0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, +0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, +0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, +0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, +0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, +0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, +0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, +0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, +0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, +0xba +}; +unsigned char coefficients2[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, +0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, +0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, +0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, +0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 +}; +unsigned char coefficients3[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, +0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, +0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, +0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, +0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, +0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, +0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, +0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, +0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, +0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, +0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, +0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, +0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, +0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, +0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, +0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, +0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, +0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, +0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, +0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, +0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, +0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, +0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, +0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, +0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, +0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, +0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, +0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, +0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, +0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, +0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, +0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, +0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, +0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, +0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, +0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, +0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff +}; + diff -Nru linux/sound/oss/yss225.h linux-2.4.19-pre5-mjc/sound/oss/yss225.h --- linux/sound/oss/yss225.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/oss/yss225.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,24 @@ +#ifndef __yss255_h__ +#define __yss255_h__ + +extern unsigned char page_zero[256]; +extern unsigned char page_one[256]; +extern unsigned char page_two[128]; +extern unsigned char page_three[128]; +extern unsigned char page_four[128]; +extern unsigned char page_six[192]; +extern unsigned char page_seven[256]; +extern unsigned char page_zero_v2[96]; +extern unsigned char page_one_v2[96]; +extern unsigned char page_two_v2[48]; +extern unsigned char page_three_v2[48]; +extern unsigned char page_four_v2[48]; +extern unsigned char page_seven_v2[96]; +extern unsigned char mod_v2[304]; +extern unsigned char coefficients[364]; +extern unsigned char coefficients2[56]; +extern unsigned char coefficients3[404]; + + +#endif /* __ys225_h__ */ + diff -Nru linux/sound/pci/Config.help linux-2.4.19-pre5-mjc/sound/pci/Config.help --- linux/sound/pci/Config.help Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/Config.help Mon Apr 8 22:31:24 2002 @@ -0,0 +1,75 @@ +CONFIG_SND_ALI5451 + Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core. + +CONFIG_SND_CS46XX + Say 'Y' or 'M' to include support for Cirrus Logic CS4610 / CS4612 / + CS4614 / CS4615 / CS4622 / CS4624 / CS4630 / CS4280 chips. + +CONFIG_SND_CS46XX_ACCEPT_VALID + Say 'Y' to allow sample resolution for mmap() transfers. + +CONFIG_SND_EMU10K1 + Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!, + Audigy and E-mu APS (partially supported). + +CONFIG_SND_KORG1212 + Say 'Y' or 'M' to include support for Korg 1212IO. + +CONFIG_SND_NM256 + Say 'Y' or 'M' to include support for NeoMagic NM256AV/ZX chips. + +CONFIG_SND_RME96 + Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and + Digi96/8 PRO/PAD/PST. + +CONFIG_SND_RME9652 + Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 / + Digi9636) soundcards. + +CONFIG_SND_TRIDENT + +CONFIG_SND_YMFPCI + Say 'Y' or 'M' to include support for Yamaha PCI audio chips - + YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754. + +CONFIG_SND_ALS4000 + Say 'Y' or 'M' to include support for Avance Logic ALS4000. + +CONFIG_SND_CMIPCI + Say 'Y' or 'M' to include support for C-Media CMI8338 and 8738 PCI + soundcards. + +CONFIG_SND_ENS1370 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1370. + +CONFIG_SND_ENS1371 + Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1371 and + Sound Blaster PCI 64 or 128 soundcards. + +CONFIG_SND_ES1938 + Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946) + soundcard. + +CONFIG_SND_ES1968 + Say 'Y' or 'M' to include support for ESS Maestro 1/2/2E. + +CONFIG_SND_MAESTRO3 + Say 'Y' or 'M' to include support for ESS Maestro 3 (Allegro) soundcard. + +CONFIG_SND_FM801 + Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards. + +CONFIG_SND_ICE1712 + Say 'Y' or 'M' to include support for ICE1712 (Envy24) based soundcards. + +CONFIG_SND_INTEL8X0 + Say 'Y' or 'M' to include support for Intel8x0 based soundcards. + +CONFIG_SND_SONICVIBES + Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards. + +CONFIG_SND_VIA686 + Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge. + +CONFIG_SND_VIA8233 + Say 'Y' or 'M' to include support for VIA VT8233 South Bridge. diff -Nru linux/sound/pci/Config.in linux-2.4.19-pre5-mjc/sound/pci/Config.in --- linux/sound/pci/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/Config.in Mon Apr 8 22:31:24 2002 @@ -0,0 +1,32 @@ +# ALSA PCI drivers + +mainmenu_option next_comment +comment 'PCI devices' + +dep_tristate 'ALi PCI Audio M5451' CONFIG_SND_ALI5451 $CONFIG_SND +dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SND_CS46XX $CONFIG_SND +if [ "$CONFIG_SND_CS46XX" != "n" ]; then + bool ' Cirrus Logic (Sound Fusion) MMAP support for OSS' CONFIG_SND_CS46XX_ACCEPT_VALID +fi +dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND +dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND +dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND +dep_tristate 'RME Digi96, 96/8, 96/8 PRO' CONFIG_SND_RME96 $CONFIG_SND +dep_tristate 'RME Digi9652 (Hammerfall)' CONFIG_SND_RME9652 $CONFIG_SND +dep_tristate 'Trident 4D-Wave DX/NX; SiS 7018' CONFIG_SND_TRIDENT $CONFIG_SND +dep_tristate 'Yamaha YMF724/740/744/754' CONFIG_SND_YMFPCI $CONFIG_SND +dep_tristate 'Avance Logic ALS4000' CONFIG_SND_ALS4000 $CONFIG_SND +dep_tristate 'C-Media 8738, 8338' CONFIG_SND_CMIPCI $CONFIG_SND +dep_tristate '(Creative) Ensoniq AudioPCI 1370' CONFIG_SND_ENS1370 $CONFIG_SND +dep_tristate '(Creative) Ensoniq AudioPCI 1371/1373' CONFIG_SND_ENS1371 $CONFIG_SND +dep_tristate 'ESS ES1938/1946 (Solo-1)' CONFIG_SND_ES1938 $CONFIG_SND +dep_tristate 'ESS ES1968/1978 (Maestro-1/2/2E)' CONFIG_SND_ES1968 $CONFIG_SND +dep_tristate 'ESS Allegro/Maestro3' CONFIG_SND_MAESTRO3 $CONFIG_SND +dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND +dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND +dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND +dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND +dep_tristate 'VIA 82C686A/B South Bridge' CONFIG_SND_VIA686 $CONFIG_SND +dep_tristate 'VIA 8233 South Bridge' CONFIG_SND_VIA8233 $CONFIG_SND + +endmenu diff -Nru linux/sound/pci/Makefile linux-2.4.19-pre5-mjc/sound/pci/Makefile --- linux/sound/pci/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,94 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := pci.o + +subdir-y := ac97 ali5451 cs46xx emu10k1 korg1212 nm256 rme9652 trident ymfpci +subdir-m := $(subdir-y) + +list-multi := snd-als4000.o snd-cmipci.o snd-cs4281.o snd-ens1370.o \ + snd-ens1371.o snd-es1938.o snd-es1968.o snd-fm801.o \ + snd-ice1712.o snd-intel8x0.o snd-maestro3.o snd-rme96.o \ + snd-sonicvibes.o snd-via686.o snd-via8233.o + +snd-als4000-objs := als4000.o +snd-cmipci-objs := cmipci.o +snd-cs4281-objs := cs4281.o +snd-ens1370-objs := ens1370.o +snd-ens1371-objs := ens1371.o +snd-es1938-objs := es1938.o +snd-es1968-objs := es1968.o +snd-fm801-objs := fm801.o +snd-ice1712-objs := ice1712.o +snd-intel8x0-objs := intel8x0.o +snd-maestro3-objs := maestro3.o +snd-rme96-objs := rme96.o +snd-sonicvibes-objs := sonicvibes.o +snd-via686-objs := via686.o +snd-via8233-objs := via8233.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALS4000) += snd-als4000.o +obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o +obj-$(CONFIG_SND_CS4281) += snd-cs4281.o +obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o +obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o +obj-$(CONFIG_SND_ES1938) += snd-es1938.o +obj-$(CONFIG_SND_ES1968) += snd-es1968.o +obj-$(CONFIG_SND_FM801) += snd-fm801.o +obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o +obj-$(CONFIG_SND_INTEL8X0) += snd-intel8x0.o +obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o +obj-$(CONFIG_SND_RME96) += snd-rme96.o +obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o +obj-$(CONFIG_SND_VIA686) += snd-via686.o +obj-$(CONFIG_SND_VIA8233) += snd-via8233.o + +include $(TOPDIR)/Rules.make + +snd-als4000.o: $(snd-als4000-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-als4000-objs) + +snd-cmipci.o: $(snd-cmipci-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cmipci-objs) + +snd-cs4281.o: $(snd-cs4281-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs4281-objs) + +snd-ens1370.o: $(snd-ens1370-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1370-objs) + +snd-ens1371.o: $(snd-ens1371-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ens1371-objs) + +snd-es1938.o: $(snd-es1938-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1938-objs) + +snd-es1968.o: $(snd-es1968-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-es1968-objs) + +snd-fm801.o: $(snd-fm801-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-fm801-objs) + +snd-ice1712.o: $(snd-ice1712-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ice1712-objs) + +snd-intel8x0.o: $(snd-intel8x0-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-intel8x0-objs) + +snd-maestro3.o: $(snd-maestro3-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-maestro3-objs) + +snd-rme96.o: $(snd-rme96-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme96-objs) + +snd-sonicvibes.o: $(snd-sonicvibes-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-sonicvibes-objs) + +snd-via686.o: $(snd-via686-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-via686-objs) + +snd-via8233.o: $(snd-via8233-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-via8233-objs) diff -Nru linux/sound/pci/ac97/Makefile linux-2.4.19-pre5-mjc/sound/pci/ac97/Makefile --- linux/sound/pci/ac97/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ac97/Makefile Mon Apr 8 22:31:24 2002 @@ -0,0 +1,39 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ac97.o + +list-multi := snd-ac97-codec.o snd-ak4531-codec.o + +export-objs := ac97_codec.o ak4531_codec.o + +snd-ac97-codec-objs := ac97_codec.o +snd-ak4531-codec-objs := ak4531_codec.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CS4281) += snd-ac97-codec.o +obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o +obj-$(CONFIG_SND_ENS1371) += snd-ac97-codec.o +obj-$(CONFIG_SND_ES1968) += snd-ac97-codec.o +obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o +obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o +obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o +obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o +obj-$(CONFIG_SND_VIA686) += snd-ac97-codec.o +obj-$(CONFIG_SND_VIA8233) += snd-ac97-codec.o +obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o +obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o +obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o +obj-$(CONFIG_SND_NM256) += snd-ac97-codec.o +obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o +obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o + +include $(TOPDIR)/Rules.make + +snd-ac97-codec.o: $(snd-ac97-codec-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ac97-codec-objs) + +snd-ak4531-codec.o: $(snd-ak4531-codec-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ak4531-codec-objs) diff -Nru linux/sound/pci/ac97/ac97_codec.c linux-2.4.19-pre5-mjc/sound/pci/ac97/ac97_codec.c --- linux/sound/pci/ac97/ac97_codec.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ac97/ac97_codec.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2085 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.2 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Universal interface for Audio Codec '97"); +MODULE_LICENSE("GPL"); + +static int enable_loopback = 0; + +MODULE_PARM(enable_loopback, "i"); +MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); +MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC); + +#define chip_t ac97_t + +/* + + */ + +static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97); +static void snd_ac97_proc_done(ac97_t * ac97); + +static int patch_wolfson(ac97_t * ac97); + +static int patch_tritech_tr28028(ac97_t * ac97); +static int patch_sigmatel_stac9708(ac97_t * ac97); +static int patch_sigmatel_stac9721(ac97_t * ac97); +static int patch_sigmatel_stac9744(ac97_t * ac97); +static int patch_sigmatel_stac9756(ac97_t * ac97); +static int patch_cirrus_cs4299(ac97_t * ac97); +static int patch_ad1819(ac97_t * ac97); +static int patch_ad1881(ac97_t * ac97); + +typedef struct { + unsigned int id; + unsigned int mask; + char *name; + int (*patch)(ac97_t *ac97); +} ac97_codec_id_t; + +static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = { +{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL }, +{ 0x41445300, 0xffffff00, "Analog Devices", NULL }, +{ 0x414c4300, 0xffffff00, "Realtek", NULL }, +{ 0x414c4700, 0xffffff00, "Avance Logic", NULL }, +{ 0x43525900, 0xffffff00, "Cirrus Logic", NULL }, +{ 0x48525300, 0xffffff00, "Intersil", NULL }, +{ 0x49434500, 0xffffff00, "ICEnsemble", NULL }, +{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL }, +{ 0x53494c00, 0xffffff00, "Silicon Laboratory", NULL }, +{ 0x54524100, 0xffffff00, "TriTech", NULL }, +{ 0x54584e00, 0xffffff00, "Texas Instruments", NULL }, +{ 0x57454300, 0xffffff00, "Winbond", NULL }, +{ 0x574d4c00, 0xffffff00, "Wolfson", patch_wolfson }, +{ 0x594d4800, 0xffffff00, "Yamaha", NULL }, +{ 0x83847600, 0xffffff00, "SigmaTel", NULL }, +{ 0x45838300, 0xffffff00, "ESS Technology", NULL }, +{ 0, 0, NULL, NULL } +}; + +static const ac97_codec_id_t snd_ac97_codec_ids[] = { +{ 0x414b4d00, 0xffffffff, "AK4540", NULL }, +{ 0x414b4d01, 0xffffffff, "AK4542", NULL }, +{ 0x414b4d02, 0xffffffff, "AK4543", NULL }, +{ 0x414b4d06, 0xffffffff, "AK4544A", NULL }, +{ 0x414b4d07, 0xffffffff, "AK4545", NULL }, +{ 0x41445303, 0xffffffff, "AD1819", patch_ad1819 }, +{ 0x41445340, 0xffffffff, "AD1881", patch_ad1881 }, +{ 0x41445348, 0xffffffff, "AD1881A", patch_ad1881 }, +{ 0x41445360, 0xffffffff, "AD1885", patch_ad1881 }, +{ 0x41445361, 0xffffffff, "AD1886", patch_ad1881 }, +{ 0x41445362, 0xffffffff, "AD1887", patch_ad1881 }, +{ 0x414c4300, 0xfffffff0, "RL5306", NULL }, +{ 0x414c4310, 0xfffffff0, "RL5382", NULL }, +{ 0x414c4320, 0xfffffff0, "RL5383", NULL }, +{ 0x414c4710, 0xffffffff, "ALC200/200P", NULL }, +{ 0x43525900, 0xfffffff8, "CS4297", NULL }, +{ 0x43525910, 0xfffffff8, "CS4297A", NULL }, +{ 0x42525920, 0xfffffff8, "CS4294/4298", NULL }, +{ 0x42525928, 0xfffffff8, "CS4294", NULL }, +{ 0x43525930, 0xfffffff8, "CS4299", patch_cirrus_cs4299 }, +{ 0x43525948, 0xfffffff8, "CS4201", NULL }, +{ 0x43525958, 0xfffffff8, "CS4205", NULL }, +{ 0x43525960, 0xfffffff8, "CS4291", NULL }, +{ 0x48525300, 0xffffff00, "HMP9701", NULL }, +{ 0x49434501, 0xffffffff, "ICE1230", NULL }, +{ 0x49434511, 0xffffffff, "ICE1232", NULL }, // alias VIA VT1611A? +{ 0x4e534300, 0xffffffff, "LM4540/43/45/46/48", NULL }, // only guess --jk +{ 0x4e534331, 0xffffffff, "LM4549", NULL }, +{ 0x53494c22, 0xffffffff, "Si3036", NULL }, +{ 0x53494c23, 0xffffffff, "Si3038", NULL }, +{ 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028 }, // added by xin jin [07/09/99] +{ 0x54524123, 0xffffffff, "TR28602", NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] +{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL }, +{ 0x57454301, 0xffffffff, "W83971D", NULL }, +{ 0x574d4c00, 0xffffffff, "WM9701A", NULL }, +{ 0x574d4c03, 0xffffffff, "WM9703/9704", NULL }, +{ 0x574d4c04, 0xffffffff, "WM9704 (quad)", NULL }, +{ 0x594d4800, 0xffffffff, "YMF743", NULL }, +{ 0x83847600, 0xffffffff, "STAC9700/83/84", NULL }, +{ 0x83847604, 0xffffffff, "STAC9701/3/4/5", NULL }, +{ 0x83847605, 0xffffffff, "STAC9704", NULL }, +{ 0x83847608, 0xffffffff, "STAC9708/11", patch_sigmatel_stac9708 }, +{ 0x83847609, 0xffffffff, "STAC9721/23", patch_sigmatel_stac9721 }, +{ 0x83847644, 0xffffffff, "STAC9744", patch_sigmatel_stac9744 }, +{ 0x83847656, 0xffffffff, "STAC9756/57", patch_sigmatel_stac9756 }, +{ 0x45838308, 0xffffffff, "ESS1988", NULL }, +{ 0, 0, NULL, NULL } +}; + +#define AC97_ID_AK4540 0x414b4d00 +#define AC97_ID_AK4542 0x414b4d01 +#define AC97_ID_AD1819 0x41445303 +#define AC97_ID_AD1881 0x41445340 +#define AC97_ID_AD1881A 0x41445348 +#define AC97_ID_AD1885 0x41445360 +#define AC97_ID_AD1886 0x41445361 +#define AC97_ID_AD1887 0x41445362 +#define AC97_ID_TR28028 0x54524108 +#define AC97_ID_STAC9700 0x83847600 +#define AC97_ID_STAC9704 0x83847604 +#define AC97_ID_STAC9705 0x83847605 +#define AC97_ID_STAC9708 0x83847608 +#define AC97_ID_STAC9721 0x83847609 +#define AC97_ID_STAC9744 0x83847644 +#define AC97_ID_STAC9756 0x83847656 + +static const char *snd_ac97_stereo_enhancements[] = +{ + /* 0 */ "No 3D Stereo Enhancement", + /* 1 */ "Analog Devices Phat Stereo", + /* 2 */ "Creative Stereo Enhancement", + /* 3 */ "National Semi 3D Stereo Enhancement", + /* 4 */ "YAMAHA Ymersion", + /* 5 */ "BBE 3D Stereo Enhancement", + /* 6 */ "Crystal Semi 3D Stereo Enhancement", + /* 7 */ "Qsound QXpander", + /* 8 */ "Spatializer 3D Stereo Enhancement", + /* 9 */ "SRS 3D Stereo Enhancement", + /* 10 */ "Platform Tech 3D Stereo Enhancement", + /* 11 */ "AKM 3D Audio", + /* 12 */ "Aureal Stereo Enhancement", + /* 13 */ "Aztech 3D Enhancement", + /* 14 */ "Binaura 3D Audio Enhancement", + /* 15 */ "ESS Technology Stereo Enhancement", + /* 16 */ "Harman International VMAx", + /* 17 */ "Nvidea 3D Stereo Enhancement", + /* 18 */ "Philips Incredible Sound", + /* 19 */ "Texas Instruments 3D Stereo Enhancement", + /* 20 */ "VLSI Technology 3D Stereo Enhancement", + /* 21 */ "TriTech 3D Stereo Enhancement", + /* 22 */ "Realtek 3D Stereo Enhancement", + /* 23 */ "Samsung 3D Stereo Enhancement", + /* 24 */ "Wolfson Microelectronics 3D Enhancement", + /* 25 */ "Delta Integration 3D Enhancement", + /* 26 */ "SigmaTel 3D Enhancement", + /* 27 */ "Reserved 27", + /* 28 */ "Rockwell 3D Stereo Enhancement", + /* 29 */ "Reserved 29", + /* 30 */ "Reserved 30", + /* 31 */ "Reserved 31" +}; + +/* + * I/O routines + */ + +static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg) +{ + /* filter some registers for buggy codecs */ + switch (ac97->id) { + case AC97_ID_AK4540: + case AC97_ID_AK4542: + if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c) + return 1; + return 0; + case AC97_ID_AD1819: /* AD1819 */ + case AC97_ID_AD1881: /* AD1881 */ + case AC97_ID_AD1881A: /* AD1881A */ + if (reg >= 0x3a && reg <= 0x6e) /* 0x59 */ + return 0; + return 1; + case AC97_ID_AD1885: /* AD1885 */ + case AC97_ID_AD1886: /* AD1886 */ + case AC97_ID_AD1887: /* AD1887 - !!verify!! --jk */ + if (reg == 0x5a) + return 1; + if (reg >= 0x3c && reg <= 0x6e) /* 0x59 */ + return 0; + return 1; + case AC97_ID_STAC9700: + case AC97_ID_STAC9704: + case AC97_ID_STAC9705: + case AC97_ID_STAC9708: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + if (reg <= 0x3a || reg >= 0x5a) + return 1; + return 0; + } + return 1; +} + +void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return; + ac97->write(ac97, reg, value); +} + +unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return 0; + return ac97->read(ac97, reg); +} + +void snd_ac97_write_cache(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + if (!snd_ac97_valid_reg(ac97, reg)) + return; + spin_lock(&ac97->reg_lock); + ac97->write(ac97, reg, ac97->regs[reg] = value); + spin_unlock(&ac97->reg_lock); + set_bit(reg, ac97->reg_accessed); +} + +#ifndef CONFIG_SND_DEBUG +#define snd_ac97_write_cache_test snd_ac97_write_cache +#else +static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + return snd_ac97_write_cache(ac97, reg, value); + if (!snd_ac97_valid_reg(ac97, reg)) + return; + spin_lock(&ac97->reg_lock); + ac97->write(ac97, reg, value); + ac97->regs[reg] = ac97->read(ac97, reg); + if (value != ac97->regs[reg]) + snd_printk("AC97 reg=%02x val=%04x real=%04x\n", reg, value, ac97->regs[reg]); + spin_unlock(&ac97->reg_lock); +} +#endif + +int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) +{ + int change; + + if (!snd_ac97_valid_reg(ac97, reg)) + return -EINVAL; + spin_lock(&ac97->reg_lock); + change = ac97->regs[reg] != value; + if (change) { + ac97->write(ac97, reg, value); + ac97->regs[reg] = value; + } + spin_unlock(&ac97->reg_lock); + return change; +} + +int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + if (!snd_ac97_valid_reg(ac97, reg)) + return -EINVAL; + spin_lock(&ac97->reg_lock); + old = ac97->regs[reg]; + new = (old & ~mask) | value; + change = old != new; + if (change) { + ac97->write(ac97, reg, new); + ac97->regs[reg] = new; + } + spin_unlock(&ac97->reg_lock); + return change; +} + +int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + down(&ac97->spec.ad18xx.mutex); + spin_lock(&ac97->reg_lock); + old = ac97->spec.ad18xx.pcmreg[codec]; + new = (old & ~mask) | value; + change = old != new; + if (change) { + /* select single codec */ + ac97->write(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); + /* update PCM bits */ + ac97->write(ac97, AC97_PCM, new); + /* select all codecs */ + ac97->write(ac97, AC97_AD_SERIAL_CFG, 0x7000); + ac97->spec.ad18xx.pcmreg[codec] = new; + } + spin_unlock(&ac97->reg_lock); + up(&ac97->spec.ad18xx.mutex); + return change; +} + +/* + * + */ + +static int snd_ac97_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "CD", "Video", "Aux", "Line", + "Mix", "Mix Mono", "Phone" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = ac97->regs[AC97_REC_SEL]; + ucontrol->value.enumerated.item[0] = (val >> 8) & 7; + ucontrol->value.enumerated.item[1] = (val >> 0) & 7; + return 0; +} + +static int snd_ac97_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if (ucontrol->value.enumerated.item[0] > 7 || + ucontrol->value.enumerated.item[1] > 7) + return -EINVAL; + val = (ucontrol->value.enumerated.item[0] << 8) | + (ucontrol->value.enumerated.item[1] << 0); + return snd_ac97_update(ac97, AC97_REC_SEL, val); +} + +#define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_enum_double, \ + get: snd_ac97_get_enum_double, put: snd_ac97_put_enum_double, \ + private_value: reg | (shift << 8) | (invert << 24) } + +static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts1[2] = { "pre 3D", "post 3D" }; + static char *texts2[2] = { "Mix", "Mic" }; + static char *texts3[2] = { "Mic1", "Mic2" }; + char **texts = NULL; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + + switch (reg) { + case AC97_GENERAL_PURPOSE: + switch (shift) { + case 15: texts = texts1; break; + case 9: texts = texts2; break; + case 8: texts = texts3; break; + } + } + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + val = (ac97->regs[reg] >> shift) & 1; + if (invert) + val ^= 1; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short val; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; + val = !!ucontrol->value.enumerated.item[0]; + if (invert) + val = !val; + return snd_ac97_update_bits(ac97, reg, 1 << shift, val << shift); +} + +#define AC97_SINGLE(xname, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_single, \ + get: snd_ac97_get_single, put: snd_ac97_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_ac97_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ac97_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + return snd_ac97_update_bits(ac97, reg, mask << shift, val << shift); +} + +#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: (xname), info: snd_ac97_info_double, \ + get: snd_ac97_get_double, put: snd_ac97_put_double, \ + private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_ac97_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock(&ac97->reg_lock); + ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = (ac97->regs[reg] >> shift_right) & mask; + spin_unlock(&ac97->reg_lock); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ac97_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + return snd_ac97_update_bits(ac97, reg, + (mask << shift_left) | (mask << shift_right), + (val1 << shift_left) | (val2 << shift_right)); +} + +static const snd_kcontrol_new_t snd_ac97_controls_master[2] = { +AC97_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), +AC97_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_headphone[2] = { +AC97_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), +AC97_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = { +AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), +AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_tone[2] = { +AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1), +AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_pc_beep[2] = { +AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1), +AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_phone[2] = { +AC97_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1), +AC97_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 15, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_mic[3] = { +AC97_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1), +AC97_SINGLE("Mic Playback Volume", AC97_MIC, 0, 15, 1), +AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_line[2] = { +AC97_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1), +AC97_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_cd[2] = { +AC97_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1), +AC97_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_video[2] = { +AC97_SINGLE("Video Playback Switch", AC97_VIDEO, 15, 1, 1), +AC97_DOUBLE("Video Playback Volume", AC97_VIDEO, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_aux[2] = { +AC97_SINGLE("Aux Playback Switch", AC97_AUX, 15, 1, 1), +AC97_DOUBLE("Aux Playback Volume", AC97_AUX, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_pcm[2] = { +AC97_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), +AC97_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_capture[3] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_ac97_info_mux, + get: snd_ac97_get_mux, + put: snd_ac97_put_mux, +}, +AC97_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1), +AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_mic_capture[2] = { +AC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1), +AC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0) +}; + +typedef enum { + AC97_GENERAL_PCM_OUT = 0, + AC97_GENERAL_STEREO_ENHANCEMENT, + AC97_GENERAL_3D, + AC97_GENERAL_LOUDNESS, + AC97_GENERAL_MONO, + AC97_GENERAL_MIC, + AC97_GENERAL_LOOPBACK +} ac97_general_index_t; + +static const snd_kcontrol_new_t snd_ac97_controls_general[7] = { +AC97_ENUM_DOUBLE("PCM Out Path & Mute", AC97_GENERAL_PURPOSE, 15, 0), +AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0), +AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), +AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0), +AC97_ENUM_DOUBLE("Mono Output Select", AC97_GENERAL_PURPOSE, 9, 0), +AC97_ENUM_DOUBLE("Mic Select", AC97_GENERAL_PURPOSE, 8, 0), +AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_3d[2] = { +AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0), +AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_center[2] = { +AC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1), +AC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_lfe[2] = { +AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1), +AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = { +AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), +AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), +}; + +static const snd_kcontrol_new_t snd_ac97_sigmatel_controls[] = { +AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0), +AC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0) +}; + +static const snd_kcontrol_new_t snd_ac97_control_eapd = +AC97_SINGLE("External Amplifier Power Down", AC97_POWERDOWN, 15, 1, 0); + +static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ac97_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_CON_EMPHASIS_5015 | + IEC958_AES0_CON_NOT_COPYRIGHT; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | + IEC958_AES1_CON_ORIGINAL; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; + return 0; +} + +static int snd_ac97_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + /* FIXME: AC'97 spec doesn't say which bits are used for what */ + ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | + IEC958_AES0_NONAUDIO | + IEC958_AES0_PRO_FS | + IEC958_AES0_PRO_EMPHASIS_5015; + return 0; +} + +static int snd_ac97_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + + spin_lock(&ac97->reg_lock); + ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff; + ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff; + spin_unlock(&ac97->reg_lock); + return 0; +} + +static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned int new = 0; + unsigned short val = 0; + int change = 0; + + spin_lock(&ac97->reg_lock); + new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO); + if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) { + new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015); + switch (new & IEC958_AES0_PRO_FS) { + case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break; + case IEC958_AES0_PRO_FS_32000: val |= 2<<12; break; + case IEC958_AES0_PRO_FS_48000: val |= 1<<12; break; + default: val |= 1<<12; break; + } + if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015) + val |= 1<<3; + } else { + new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT); + new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8); + new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 8); + if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015) + val |= 1<<3; + if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT)) + val |= 1<<2; + val |= ((new >> 8) & 0xff) << 4; // category + original + switch ((new >> 24) & 0xff) { + case IEC958_AES3_CON_FS_44100: val |= 0<<12; break; + case IEC958_AES3_CON_FS_48000: val |= 2<<12; break; + case IEC958_AES3_CON_FS_32000: val |= 3<<12; break; + default: val |= 1<<12; break; + } + } + change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val); + change |= ac97->spdif_status != new; + ac97->spdif_status = new; + spin_unlock(&ac97->reg_lock); + return change; +} + +static const snd_kcontrol_new_t snd_ac97_controls_spdif[5] = { + { + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_ac97_spdif_mask_info, + get: snd_ac97_spdif_cmask_get, + }, + { + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_ac97_spdif_mask_info, + get: snd_ac97_spdif_pmask_get, + }, + { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_ac97_spdif_mask_info, + get: snd_ac97_spdif_default_get, + put: snd_ac97_spdif_default_put, + }, + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0), + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",AC97_EXTENDED_STATUS, 4, 3, 0) +}; + +#define AD18XX_PCM_BITS(xname, codec, shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_bits, \ + get: snd_ac97_ad18xx_pcm_get_bits, put: snd_ac97_ad18xx_pcm_put_bits, \ + private_value: codec | (shift << 8) | (mask << 16) } + +static int snd_ac97_ad18xx_pcm_info_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ac97_ad18xx_pcm_get_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + + ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> shift) & mask); + return 0; +} + +static int snd_ac97_ad18xx_pcm_put_bits(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + unsigned short val; + + val = mask - (ucontrol->value.integer.value[0] & mask); + return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, mask << shift, val << shift); +} + +#define AD18XX_PCM_VOLUME(xname, codec) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_volume, \ + get: snd_ac97_ad18xx_pcm_get_volume, put: snd_ac97_ad18xx_pcm_put_volume, \ + private_value: codec } + +static int snd_ac97_ad18xx_pcm_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_ac97_ad18xx_pcm_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + + spin_lock(&ac97->reg_lock); + ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31); + ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31); + spin_unlock(&ac97->reg_lock); + return 0; +} + +static int snd_ac97_ad18xx_pcm_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int codec = kcontrol->private_value & 3; + unsigned short val1, val2; + + val1 = 31 - (ucontrol->value.integer.value[0] & 31); + val2 = 31 - (ucontrol->value.integer.value[1] & 31); + return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2); +} + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_pcm[2] = { +AD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 1), +AD18XX_PCM_VOLUME("PCM Playback Volume", 0) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_surround[2] = { +AD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 1), +AD18XX_PCM_VOLUME("Surround Playback Volume", 1) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_center[2] = { +AD18XX_PCM_BITS("Center Playback Switch", 2, 15, 1), +AD18XX_PCM_BITS("Center Playback Volume", 2, 8, 31) +}; + +static const snd_kcontrol_new_t snd_ac97_controls_ad18xx_lfe[1] = { +AD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 31) +}; + +/* + * + */ + +static int snd_ac97_free(ac97_t *ac97) +{ + if (ac97) { + snd_ac97_proc_done(ac97); + if (ac97->private_free) + ac97->private_free(ac97); + snd_magic_kfree(ac97); + } + return 0; +} + +static int snd_ac97_dev_free(snd_device_t *device) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, device->device_data, return -ENXIO); + return snd_ac97_free(ac97); +} + +static int snd_ac97_try_volume_mix(ac97_t * ac97, int reg) +{ + unsigned short val, mask = 0x8000; + + switch (reg) { + case AC97_MASTER_TONE: + return ac97->caps & 0x04 ? 1 : 0; + case AC97_HEADPHONE: + return ac97->caps & 0x10 ? 1 : 0; + case AC97_REC_GAIN_MIC: + return ac97->caps & 0x01 ? 1 : 0; + case AC97_3D_CONTROL: + if (ac97->caps & 0x7c00) { + val = snd_ac97_read(ac97, reg); + /* if nonzero - fixed and we can't set it */ + return val == 0; + } + return 0; + case AC97_CENTER_LFE_MASTER: /* center */ + if ((ac97->ext_id & 0x40) == 0) + return 0; + break; + case AC97_CENTER_LFE_MASTER+1: /* lfe */ + if ((ac97->ext_id & 0x100) == 0) + return 0; + reg = AC97_CENTER_LFE_MASTER; + mask = 0x0080; + break; + case AC97_SURROUND_MASTER: + if ((ac97->ext_id & 0x80) == 0) + return 0; + break; + } + val = snd_ac97_read(ac97, reg); + if (!(val & mask)) { + /* nothing seems to be here - mute flag is not set */ + /* try another test */ + snd_ac97_write_cache_test(ac97, reg, val | mask); + val = snd_ac97_read(ac97, reg); + if (!(val & mask)) + return 0; /* nothing here */ + } + return 1; /* success, useable */ +} + +static int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit) +{ + unsigned short mask, val, orig, res; + + mask = 1 << bit; + orig = snd_ac97_read(ac97, reg); + val = orig ^ mask; + snd_ac97_write(ac97, reg, val); + res = snd_ac97_read(ac97, reg); + snd_ac97_write_cache(ac97, reg, orig); + return res == val; +} + +static void snd_ac97_change_volume_params1(ac97_t * ac97, int reg, unsigned char *max) +{ + unsigned short val, val1; + + *max = 63; + val = 0x8000 | 0x0020; + snd_ac97_write(ac97, reg, val); + val1 = snd_ac97_read(ac97, reg); + if (val != val1) { + *max = 31; + } + /* reset volume to zero */ + snd_ac97_write_cache(ac97, reg, 0x8000); +} + +static void snd_ac97_change_volume_params2(ac97_t * ac97, int reg, int shift, unsigned char *max) +{ + unsigned short val, val1; + + *max = 63; + val = 0x8080 | (0x20 << shift); + snd_ac97_write(ac97, reg, val); + val1 = snd_ac97_read(ac97, reg); + if (val != val1) { + *max = 31; + } + /* reset volume to zero */ + snd_ac97_write_cache(ac97, reg, 0x8080); +} + +static inline int printable(unsigned int x) +{ + x &= 0xff; + if (x < ' ' || x >= 0x71) { + if (x <= 0x89) + return x - 0x71 + 'A'; + return '?'; + } + return x; +} + +static snd_kcontrol_t *snd_ac97_cnew(const snd_kcontrol_new_t *_template, ac97_t * ac97) +{ + snd_kcontrol_new_t template; + memcpy(&template, _template, sizeof(template)); + snd_runtime_check(!template.index, return NULL); + template.index = ac97->num; + return snd_ctl_new1(&template, ac97); +} + +static int snd_ac97_mixer_build(snd_card_t * card, ac97_t * ac97) +{ + snd_kcontrol_t *kctl; + int err, idx; + unsigned char max; + + /* build master controls */ + /* AD claims to remove this control from AD1887, although spec v2.2 don't allow this */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_MASTER, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000 | max | (max << 8)); + } + + ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; + + /* build center controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max); + } + + /* build LFE controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8); + } + + /* build surround controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_surround[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_surround[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params2(ac97, AC97_SURROUND_MASTER, 0, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x8080 | max | (max << 8)); + } + + /* build headphone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_headphone[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_headphone[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_HEADPHONE, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_HEADPHONE, 0x8000 | max | (max << 8)); + } + + /* build master mono controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_master_mono[0], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_master_mono[1], ac97))) < 0) + return err; + snd_ac97_change_volume_params1(ac97, AC97_MASTER_MONO, &max); + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + snd_ac97_write_cache(ac97, AC97_MASTER_MONO, 0x8000 | max); + } + + /* build master tone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f); + } + + /* build PC Speaker controls */ + if ((ac97->flags & AC97_HAS_PC_BEEP) || + snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e); + } + + /* build Phone controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_phone[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_PHONE, 0x801f); + } + + /* build MIC controls */ + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_MIC, 0x801f); + + /* build Line controls */ + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_line[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_LINE, 0x9f1f); + + /* build CD controls */ + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_cd[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_CD, 0x9f1f); + + /* build Video controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_video[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_VIDEO, 0x9f1f); + } + + /* build Aux controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_aux[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_AUX, 0x9f1f); + } + + /* build PCM controls */ + if (ac97->flags & AC97_AD_MULTI) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[0] = 0x9f1f; + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[1] = 0x9f1f; + } + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[0], ac97))) < 0) + return err; + ac97->spec.ad18xx.pcmreg[2] = 0x9f1f; + } + } else { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pcm[idx], ac97))) < 0) + return err; + } + snd_ac97_write_cache(ac97, AC97_PCM, 0x9f1f); + + /* build Capture controls */ + for (idx = 0; idx < 3; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_capture[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000); + + /* build MIC Capture controls */ + if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) { + for (idx = 0; idx < 2; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0) + return err; + snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000); + } + + /* build PCM out path & mute control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0) + return err; + } + + /* build Simulated Stereo Enhancement control */ + if (ac97->caps & 0x0008) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) + return err; + } + + /* build 3D Stereo Enhancement control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0) + return err; + } + + /* build Loudness control */ + if (ac97->caps & 0x0020) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) + return err; + } + + /* build Mono output select control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0) + return err; + } + + /* build Mic select control */ + if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0) + return err; + } + + /* build ADC/DAC loopback control */ + if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0) + return err; + } + + snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x0000); + + /* build 3D controls */ + switch (ac97->id) { + case AC97_ID_STAC9708: + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + kctl->private_value = AC97_3D_CONTROL | (3 << 16); + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); + kctl->private_value = AC97_3D_CONTROL | (2 << 8) | (3 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + break; + case AC97_ID_STAC9700: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + kctl->private_value = AC97_3D_CONTROL | (3 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + break; + default: + if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) { + unsigned short val; + val = 0x0707; + snd_ac97_write(ac97, AC97_3D_CONTROL, val); + val = snd_ac97_read(ac97, AC97_3D_CONTROL); + val = val == 0x0606; + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) + return err; + if (val) + kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0) + return err; + if (val) + kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16); + snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); + } + } + + /* build S/PDIF controls */ + if (ac97->ext_id & 0x0004) { + for (idx = 0; idx < 5; idx++) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0) + return err; + /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original */ + snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20); + ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + } + + /* build Sigmatel specific controls */ + switch (ac97->id) { + case AC97_ID_STAC9700: + case AC97_ID_STAC9708: + case AC97_ID_STAC9721: + case AC97_ID_STAC9744: + case AC97_ID_STAC9756: + snd_ac97_write_cache_test(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003); + if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1)) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[0], ac97))) < 0) + return err; + if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0)) + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_sigmatel_controls[1], ac97))) < 0) + return err; + default: + /* nothing */ + break; + } + + if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) { + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_eapd, ac97))) < 0) + return err; + } + + return 0; +} + +static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate) +{ + unsigned short val; + unsigned int tmp; + + tmp = ((unsigned int)rate * ac97->clock) / 48000; + snd_ac97_write_cache_test(ac97, reg, tmp & 0xffff); + val = snd_ac97_read(ac97, reg); + return val == (tmp & 0xffff); +} + +static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result) +{ + unsigned int result = 0; + + /* test a non-standard rate */ + if (snd_ac97_test_rate(ac97, reg, 11000)) + result |= SNDRV_PCM_RATE_CONTINUOUS; + /* let's try to obtain standard rates */ + if (snd_ac97_test_rate(ac97, reg, 8000)) + result |= SNDRV_PCM_RATE_8000; + if (snd_ac97_test_rate(ac97, reg, 11025)) + result |= SNDRV_PCM_RATE_11025; + if (snd_ac97_test_rate(ac97, reg, 16000)) + result |= SNDRV_PCM_RATE_16000; + if (snd_ac97_test_rate(ac97, reg, 22050)) + result |= SNDRV_PCM_RATE_22050; + if (snd_ac97_test_rate(ac97, reg, 32000)) + result |= SNDRV_PCM_RATE_32000; + if (snd_ac97_test_rate(ac97, reg, 44100)) + result |= SNDRV_PCM_RATE_44100; + if (snd_ac97_test_rate(ac97, reg, 48000)) + result |= SNDRV_PCM_RATE_48000; + *r_result = result; +} + +static void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name) +{ + const ac97_codec_id_t *pid; + + sprintf(name, "0x%x %c%c%c", id, + printable(id >> 24), + printable(id >> 16), + printable(id >> 8)); + for (pid = snd_ac97_codec_id_vendors; pid->id; pid++) + if (pid->id == (id & pid->mask)) { + strcpy(name, pid->name); + if (ac97 && pid->patch) + pid->patch(ac97); + goto __vendor_ok; + } + return; + + __vendor_ok: + for (pid = snd_ac97_codec_ids; pid->id; pid++) + if (pid->id == (id & pid->mask)) { + strcat(name, " "); + strcat(name, pid->name); + if (pid->mask != 0xffffffff) + sprintf(name + strlen(name), " rev %d", id & ~pid->mask); + if (ac97 && pid->patch) + pid->patch(ac97); + return; + } + sprintf(name + strlen(name), " (%x)", id & 0xff); +} + +int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97) +{ + int err; + ac97_t *ac97; + char name[64]; + signed long end_time; + static snd_device_ops_t ops = { + dev_free: snd_ac97_dev_free, + }; + + snd_assert(rac97 != NULL, return -EINVAL); + *rac97 = NULL; + snd_assert(card != NULL && _ac97 != NULL, return -EINVAL); + ac97 = snd_magic_kcalloc(ac97_t, 0, GFP_KERNEL); + if (ac97 == NULL) + return -ENOMEM; + *ac97 = *_ac97; + ac97->card = card; + spin_lock_init(&ac97->reg_lock); + snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ + if (ac97->wait) + ac97->wait(ac97); + else { + udelay(50); + + /* it's necessary to wait awhile until registers are accessible after RESET */ + /* because the PCM or MASTER volume registers can be modified, */ + /* the REC_GAIN register is used for tests */ + end_time = jiffies + HZ; + do { + /* use preliminary reads to settle the communication */ + snd_ac97_read(ac97, AC97_RESET); + snd_ac97_read(ac97, AC97_VENDOR_ID1); + snd_ac97_read(ac97, AC97_VENDOR_ID2); + /* test if we can write to the PCM volume register */ + snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); + if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) + goto __access_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/100); + } while (end_time - (signed long)jiffies >= 0); + snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err); + snd_ac97_free(ac97); + return -ENXIO; + } + __access_ok: + ac97->caps = snd_ac97_read(ac97, AC97_RESET); + ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; + ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); + ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->ext_id == 0xffff) /* invalid combination */ + ac97->ext_id = 0; + if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) { + snd_printk("AC'97 %d:%d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->addr, ac97->id); + snd_ac97_free(ac97); + return -EIO; + } + /* FIXME: add powerdown control */ + /* nothing should be in powerdown mode */ + snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); + snd_ac97_write_cache_test(ac97, AC97_RESET, 0); /* reset to defaults */ + udelay(100); + /* nothing should be in powerdown mode */ + snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0); + snd_ac97_write_cache_test(ac97, AC97_GENERAL_PURPOSE, 0); + end_time = jiffies + (HZ / 10); + do { + if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) + goto __ready_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr); + __ready_ok: + if (ac97->clock == 0) + ac97->clock = 48000; /* standard value */ + if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */ + snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189); + if (ac97->ext_id & 0x0001) { /* VRA support */ + snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates_front_dac); + snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates_adc); + } else { + ac97->rates_front_dac = SNDRV_PCM_RATE_48000; + ac97->rates_adc = SNDRV_PCM_RATE_48000; + } + if (ac97->ext_id & 0x0008) { /* MIC VRA support */ + snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates_mic_adc); + } else { + ac97->rates_mic_adc = SNDRV_PCM_RATE_48000; + } + if (ac97->ext_id & 0x0080) { /* SDAC support */ + snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates_surr_dac); + ac97->scaps |= AC97_SCAP_SURROUND_DAC; + } + if (ac97->ext_id & 0x0100) { /* LDAC support */ + snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates_lfe_dac); + ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; + } + if (ac97->init) + ac97->init(ac97); + snd_ac97_get_name(ac97, ac97->id, name); + snd_ac97_get_name(NULL, ac97->id, name); // ac97->id might be changed in the special setup code + if (card->mixername[0] == '\0') { + strcpy(card->mixername, name); + } else { + if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { + strcat(card->mixername, ","); + strcat(card->mixername, name); + } + } + if ((err = snd_component_add(card, "AC97")) < 0) { + snd_ac97_free(ac97); + return err; + } + if (snd_ac97_mixer_build(card, ac97) < 0) { + snd_ac97_free(ac97); + return -ENOMEM; + } + snd_ac97_proc_init(card, ac97); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { + snd_ac97_free(ac97); + return err; + } + *rac97 = ac97; + return 0; +} + +/* + + */ + +static void snd_ac97_proc_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) +{ + char name[64]; + unsigned int id; + unsigned short val, tmp, ext; + static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=res" }; + static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" }; + + id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16; + id |= snd_ac97_read(ac97, AC97_VENDOR_ID2); + snd_ac97_get_name(NULL, id, name); + snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name); + val = snd_ac97_read(ac97, AC97_RESET); + snd_iprintf(buffer, "Capabilities :%s%s%s%s%s%s\n", + val & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", + val & 0x0002 ? " -reserved1-" : "", + val & 0x0004 ? " -bass & treble-" : "", + val & 0x0008 ? " -simulated stereo-" : "", + val & 0x0010 ? " -headphone out-" : "", + val & 0x0020 ? " -loudness-" : ""); + tmp = ac97->caps & 0x00c0; + snd_iprintf(buffer, "DAC resolution : %s%s%s%s\n", + tmp == 0x0000 ? "16-bit" : "", + tmp == 0x0040 ? "18-bit" : "", + tmp == 0x0080 ? "20-bit" : "", + tmp == 0x00c0 ? "???" : ""); + tmp = ac97->caps & 0x0300; + snd_iprintf(buffer, "ADC resolution : %s%s%s%s\n", + tmp == 0x0000 ? "16-bit" : "", + tmp == 0x0100 ? "18-bit" : "", + tmp == 0x0200 ? "20-bit" : "", + tmp == 0x0300 ? "???" : ""); + snd_iprintf(buffer, "3D enhancement : %s\n", + snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]); + snd_iprintf(buffer, "\nCurrent setup\n"); + val = snd_ac97_read(ac97, AC97_MIC); + snd_iprintf(buffer, "Mic gain : %s [%s]\n", val & 0x0040 ? "+20dB" : "+0dB", ac97->regs[AC97_MIC] & 0x0040 ? "+20dB" : "+0dB"); + val = snd_ac97_read(ac97, AC97_GENERAL_PURPOSE); + snd_iprintf(buffer, "POP path : %s 3D\n" + "Sim. stereo : %s\n" + "3D enhancement : %s\n" + "Loudness : %s\n" + "Mono output : %s\n" + "Mic select : %s\n" + "ADC/DAC loopback : %s\n", + val & 0x8000 ? "post" : "pre", + val & 0x4000 ? "on" : "off", + val & 0x2000 ? "on" : "off", + val & 0x1000 ? "on" : "off", + val & 0x0200 ? "Mic" : "MIX", + val & 0x0100 ? "Mic2" : "Mic1", + val & 0x0080 ? "on" : "off"); + ext = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ext == 0) + return; + snd_iprintf(buffer, "Extended ID : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n", + (ext >> 14) & 3, + (ext >> 10) & 3, + ext & 0x0200 ? " AMAP" : "", + ext & 0x0100 ? " LDAC" : "", + ext & 0x0080 ? " SDAC" : "", + ext & 0x0040 ? " CDAC" : "", + (ext >> 4) & 3, + ext & 0x0008 ? " VRM" : "", + ext & 0x0004 ? " SPDIF" : "", + ext & 0x0002 ? " DRA" : "", + ext & 0x0001 ? " VRA" : ""); + val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); + snd_iprintf(buffer, "Extended status :%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + val & 0x4000 ? " PRL" : "", + val & 0x2000 ? " PRK" : "", + val & 0x1000 ? " PRJ" : "", + val & 0x0800 ? " PRI" : "", + val & 0x0400 ? " SPCV" : "", + val & 0x0200 ? " MADC" : "", + val & 0x0100 ? " LDAC" : "", + val & 0x0080 ? " SDAC" : "", + val & 0x0040 ? " CDAC" : "", + ext & 0x0004 ? spdif_slots[(val & 0x0030) >> 4] : "", + val & 0x0008 ? " VRM" : "", + val & 0x0004 ? " SPDIF" : "", + val & 0x0002 ? " DRA" : "", + val & 0x0001 ? " VRA" : ""); + if (ext & 1) { /* VRA */ + val = snd_ac97_read(ac97, AC97_PCM_FRONT_DAC_RATE); + snd_iprintf(buffer, "PCM front DAC : %iHz\n", val); + if (ext & 0x0080) { + val = snd_ac97_read(ac97, AC97_PCM_SURR_DAC_RATE); + snd_iprintf(buffer, "PCM Surr DAC : %iHz\n", val); + } + if (ext & 0x0100) { + val = snd_ac97_read(ac97, AC97_PCM_LFE_DAC_RATE); + snd_iprintf(buffer, "PCM LFE DAC : %iHz\n", val); + } + val = snd_ac97_read(ac97, AC97_PCM_LR_ADC_RATE); + snd_iprintf(buffer, "PCM ADC : %iHz\n", val); + } + if (ext & 0x0008) { + val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE); + snd_iprintf(buffer, "PCM MIC ADC : %iHz\n", val); + } + if (ext & 0x0004) { + val = snd_ac97_read(ac97, AC97_SPDIF); + snd_iprintf(buffer, "SPDIF Control :%s%s%s%s Category=0x%x Generation=%i%s%s%s\n", + val & 0x0001 ? " PRO" : " Consumer", + val & 0x0002 ? " Non-audio" : " PCM", + val & 0x0004 ? " Copyright" : "", + val & 0x0008 ? " Preemph50/15" : "", + (val & 0x07f0) >> 4, + (val & 0x0800) >> 11, + spdif_rates[(val & 0x3000) >> 12], + val & 0x4000 ? " DRS" : "", + val & 0x8000 ? " Validity" : ""); + } +} + +static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + + if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 + int idx; + down(&ac97->spec.ad18xx.mutex); + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) { + /* select single codec */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]); + snd_ac97_proc_read_main(ac97, buffer, idx); + snd_iprintf(buffer, "\n\n"); + } + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + up(&ac97->spec.ad18xx.mutex); + + snd_iprintf(buffer, "\nAD18XX configuration\n"); + snd_iprintf(buffer, "Unchained : 0x%04x,0x%04x,0x%04x\n", + ac97->spec.ad18xx.unchained[0], + ac97->spec.ad18xx.unchained[1], + ac97->spec.ad18xx.unchained[2]); + snd_iprintf(buffer, "Chained : 0x%04x,0x%04x,0x%04x\n", + ac97->spec.ad18xx.chained[0], + ac97->spec.ad18xx.chained[1], + ac97->spec.ad18xx.chained[2]); + } else { + snd_ac97_proc_read_main(ac97, buffer, 0); + } +} + +static void snd_ac97_proc_regs_read_main(ac97_t *ac97, snd_info_buffer_t * buffer, int subidx) +{ + int reg, val; + + for (reg = 0; reg < 0x80; reg += 2) { + val = snd_ac97_read(ac97, reg); + snd_iprintf(buffer, "%i:%02x = %04x\n", subidx, reg, val); + } +} + +static void snd_ac97_proc_regs_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + + if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 + int idx; + down(&ac97->spec.ad18xx.mutex); + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) { + /* select single codec */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]); + snd_ac97_proc_regs_read_main(ac97, buffer, idx); + } + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + up(&ac97->spec.ad18xx.mutex); + } else { + snd_ac97_proc_regs_read_main(ac97, buffer, 0); + } +} + +static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97) +{ + snd_info_entry_t *entry; + char name[12]; + + sprintf(name, "ac97#%d", ac97->addr); + if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ac97; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 512; + entry->c.text.read = snd_ac97_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ac97->proc_entry = entry; + sprintf(name, "ac97#%dregs", ac97->addr); + if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ac97; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_ac97_proc_regs_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ac97->proc_regs_entry = entry; +} + +static void snd_ac97_proc_done(ac97_t * ac97) +{ + if (ac97->proc_regs_entry) { + snd_info_unregister(ac97->proc_regs_entry); + ac97->proc_regs_entry = NULL; + } + if (ac97->proc_entry) { + snd_info_unregister(ac97->proc_entry); + ac97->proc_entry = NULL; + } +} + +/* + * Chip specific initialization + */ + +static int patch_wolfson(ac97_t * ac97) +{ + // for all wolfson codecs (is this correct? --jk) + snd_ac97_write_cache(ac97, 0x72, 0x0808); + snd_ac97_write_cache(ac97, 0x74, 0x0808); + + // patch for DVD noise + snd_ac97_write_cache(ac97, 0x5a, 0x0200); + + // init vol + snd_ac97_write_cache(ac97, 0x70, 0x0808); + + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); + return 0; +} + +static int patch_tritech_tr28028(ac97_t * ac97) +{ + snd_ac97_write_cache(ac97, 0x26, 0x0300); + snd_ac97_write_cache(ac97, 0x26, 0x0000); + snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); + snd_ac97_write_cache(ac97, AC97_SPDIF, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9708(ac97_t * ac97) +{ + unsigned int codec72, codec6c; + + codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000; + codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG); + + if ((codec72==0) && (codec6c==0)) { + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1000); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0007); + } else if ((codec72==0x8000) && (codec6c==0)) { + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1001); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_DAC2INVERT, 0x0008); + } else if ((codec72==0x8000) && (codec6c==0x0080)) { + /* nothing */ + } + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9721(ac97_t * ac97) +{ + if (snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) == 0) { + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x4000); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + } + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9744(ac97_t * ac97) +{ + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_sigmatel_stac9756(ac97_t * ac97) +{ + // patch for SigmaTel + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000); /* is this correct? --jk */ + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002); + snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000); + return 0; +} + +static int patch_cirrus_cs4299(ac97_t * ac97) +{ + ac97->flags |= AC97_HAS_PC_BEEP; /* force the detection of PC Beep */ + return 0; +} + +static int patch_ad1819(ac97_t * ac97) +{ + // patch for Analog Devices + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); /* select all codecs */ + return 0; +} + +static unsigned short patch_ad1881_unchained(ac97_t * ac97, int idx, unsigned short mask) +{ + unsigned short val; + + // test for unchained codec + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, mask); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); /* ID0C, ID1C, SDIE = off */ + val = snd_ac97_read(ac97, AC97_VENDOR_ID2); + if ((val & 0xff40) != 0x5340) + return 0; + ac97->spec.ad18xx.unchained[idx] = mask; + ac97->spec.ad18xx.id[idx] = val; + return mask; +} + +static int patch_ad1881_chained1(ac97_t * ac97, int idx, unsigned short codec_bits) +{ + static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 }; + unsigned short val; + + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, cfg_bits[idx]); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004); // SDIE + val = snd_ac97_read(ac97, AC97_VENDOR_ID2); + if ((val & 0xff40) != 0x5340) + return 0; + if (codec_bits) + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits); + ac97->spec.ad18xx.chained[idx] = cfg_bits[idx]; + ac97->spec.ad18xx.id[idx] = val; + return 1; +} + +static void patch_ad1881_chained(ac97_t * ac97, int unchained_idx, int cidx1, int cidx2) +{ + // already detected? + if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1]) + cidx1 = -1; + if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2]) + cidx2 = -1; + if (cidx1 < 0 && cidx2 < 0) + return; + // test for chained codecs + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, ac97->spec.ad18xx.unchained[unchained_idx]); + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002); // ID1C + if (cidx1 >= 0) { + if (patch_ad1881_chained1(ac97, cidx1, 0x0006)) // SDIE | ID1C + patch_ad1881_chained1(ac97, cidx2, 0); + else if (patch_ad1881_chained1(ac97, cidx2, 0x0006)) // SDIE | ID1C + patch_ad1881_chained1(ac97, cidx1, 0); + } else if (cidx2 >= 0) { + patch_ad1881_chained1(ac97, cidx2, 0); + } +} + +static int patch_ad1881(ac97_t * ac97) +{ + static const char cfg_idxs[3][2] = { + {2, 1}, + {0, 2}, + {0, 1} + }; + + // patch for Analog Devices + unsigned short codecs[3]; + int idx, num; + + init_MUTEX(&ac97->spec.ad18xx.mutex); + + codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12)); + codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14)); + codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13)); + + snd_runtime_check(codecs[0] | codecs[1] | codecs[2], goto __end); + + for (idx = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.unchained[idx]) + patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]); + + if (ac97->spec.ad18xx.id[1]) { + ac97->flags |= AC97_AD_MULTI; + ac97->scaps |= AC97_SCAP_SURROUND_DAC; + } + if (ac97->spec.ad18xx.id[2]) { + ac97->flags |= AC97_AD_MULTI; + ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; + } + + __end: + /* select all codecs */ + snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, 0x7000); + /* check if only one codec is present */ + for (idx = num = 0; idx < 3; idx++) + if (ac97->spec.ad18xx.id[idx]) + num++; + if (num == 1) { + /* ok, deselect all ID bits */ + snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000); + } + /* required for AD1886/AD1885 combination */ + ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->spec.ad18xx.id[0]) { + ac97->id &= 0xffff0000; + ac97->id |= ac97->spec.ad18xx.id[0]; + } + return 0; +} + +/* + * PCM support + */ + +int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate) +{ + unsigned short mask; + unsigned int tmp; + signed long end_time; + + switch (reg) { + case AC97_PCM_MIC_ADC_RATE: + mask = 0x0000; + if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0008) == 0) /* MIC VRA */ + if (rate != 48000) + return -EINVAL; + break; + case AC97_PCM_FRONT_DAC_RATE: + mask = 0x0200; + if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0) /* VRA */ + if (rate != 48000) + return -EINVAL; + break; + case AC97_PCM_LR_ADC_RATE: + mask = 0x0100; + if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0) /* VRA */ + if (rate != 48000) + return -EINVAL; + break; + default: return -EINVAL; + } + tmp = ((unsigned int)rate * ac97->clock) / 48000; + if (tmp > 65535) + return -EINVAL; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, mask); + end_time = jiffies + (HZ / 50); + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_ac97_update(ac97, reg, tmp & 0xffff); + udelay(10); + snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, 0); + end_time = jiffies + (HZ / 50); + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + end_time = jiffies + (HZ / 10); + do { + if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0003) == 0x0003) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + return 0; +} + + +#ifdef CONFIG_PM +/* + * general suspend procedure + */ +void snd_ac97_suspend(ac97_t *ac97) +{ + unsigned short power = (ac97->regs[AC97_POWERDOWN] ^ 0x8000) & ~0x8000; /* invert EAPD */ + + power |= 0x4000; /* Headphone amplifier powerdown */ + power |= 0x0300; /* ADC & DAC powerdown */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); + udelay(100); + power |= 0x0400; /* Analog Mixer powerdown (Vref on) */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); + udelay(100); + power |= 0x3800; /* AC-link powerdown, internal Clk disable */ + snd_ac97_write(ac97, AC97_POWERDOWN, power); +} + +/* + * general resume procedure + */ +void snd_ac97_resume(ac97_t *ac97) +{ + int i; + + snd_ac97_write(ac97, AC97_POWERDOWN, 0); + snd_ac97_write(ac97, AC97_RESET, 0); + udelay(100); + snd_ac97_write(ac97, AC97_POWERDOWN, 0); + snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); + + snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); + snd_ac97_write(ac97, AC97_MASTER, 0x8000); + for (i = 0; i < 10; i++) { + if (snd_ac97_read(ac97, AC97_MASTER) == 0x8000) + break; + mdelay(1); + } + + if (ac97->init) + ac97->init(ac97); + + /* restore ac97 status */ + for (i = 2; i < 0x7c ; i += 2) { + if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID) + continue; + /* restore only accessible registers + * some chip (e.g. nm256) may hang up when unsupported registers + * are accessed..! + */ + if (test_bit(i, ac97->reg_accessed)) + snd_ac97_write(ac97, i, ac97->regs[i]); + } +} +#endif + + +/* + * Exported symbols + */ + +EXPORT_SYMBOL(snd_ac97_write); +EXPORT_SYMBOL(snd_ac97_read); +EXPORT_SYMBOL(snd_ac97_write_cache); +EXPORT_SYMBOL(snd_ac97_update); +EXPORT_SYMBOL(snd_ac97_update_bits); +EXPORT_SYMBOL(snd_ac97_mixer); +EXPORT_SYMBOL(snd_ac97_set_rate); +#ifdef CONFIG_PM +EXPORT_SYMBOL(snd_ac97_resume); +#endif + +/* + * INIT part + */ + +static int __init alsa_ac97_init(void) +{ + return 0; +} + +static void __exit alsa_ac97_exit(void) +{ +} + +module_init(alsa_ac97_init) +module_exit(alsa_ac97_exit) diff -Nru linux/sound/pci/ac97/ak4531_codec.c linux-2.4.19-pre5-mjc/sound/pci/ac97/ak4531_codec.c --- linux/sound/pci/ac97/ak4531_codec.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ac97/ak4531_codec.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,466 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal routines for AK4531 codec + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Universal routines for AK4531 codec"); +MODULE_LICENSE("GPL"); + +#define chip_t ak4531_t + +static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531); +static void snd_ak4531_proc_done(ak4531_t * ak4531); + +/* + * + */ + +#if 0 + +static void snd_ak4531_dump(ak4531_t *ak4531) +{ + int idx; + + for (idx = 0; idx < 0x19; idx++) + printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]); +} + +#endif + +/* + * + */ + +#define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ak4531_info_single, \ + get: snd_ak4531_get_single, put: snd_ak4531_put_single, \ + private_value: reg | (shift << 16) | (mask << 24) | (invert << 22) } + +static int snd_ak4531_info_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ak4531_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int val; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + val = (ak4531->regs[reg] >> shift) & mask; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + if (invert) { + val = mask - val; + } + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_ak4531_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + int val; + + val = ucontrol->value.integer.value[0] & mask; + if (invert) { + val = mask - val; + } + val <<= shift; + spin_lock_irqsave(&ak4531->reg_lock, flags); + val = (ak4531->regs[reg] & ~(mask << shift)) | val; + change = val != ak4531->regs[reg]; + ak4531->write(ak4531, reg, ak4531->regs[reg] = val); + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ak4531_info_double, \ + get: snd_ak4531_get_double, put: snd_ak4531_put_double, \ + private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) } + +static int snd_ak4531_info_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ak4531_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int left, right; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + left = (ak4531->regs[left_reg] >> left_shift) & mask; + right = (ak4531->regs[right_reg] >> right_shift) & mask; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + if (invert) { + left = mask - left; + right = mask - right; + } + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + return 0; +} + +static int snd_ak4531_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x07; + int right_shift = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + int left, right; + + left = ucontrol->value.integer.value[0] & mask; + right = ucontrol->value.integer.value[1] & mask; + if (invert) { + left = mask - left; + right = mask - right; + } + left <<= left_shift; + right <<= right_shift; + spin_lock_irqsave(&ak4531->reg_lock, flags); + if (left_reg == right_reg) { + left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right; + change = left != ak4531->regs[left_reg]; + ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); + } else { + left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left; + right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right; + change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg]; + ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left); + ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right); + } + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ak4531_info_input_sw, \ + get: snd_ak4531_get_input_sw, put: snd_ak4531_put_input_sw, \ + private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) } + +static int snd_ak4531_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ak4531_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1; + ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1; + ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1; + ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1; + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return 0; +} + +static int snd_ak4531_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ak4531_t *ak4531 = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg1 = kcontrol->private_value & 0xff; + int reg2 = (kcontrol->private_value >> 8) & 0xff; + int left_shift = (kcontrol->private_value >> 16) & 0x0f; + int right_shift = (kcontrol->private_value >> 24) & 0x0f; + int change; + int val1, val2; + + spin_lock_irqsave(&ak4531->reg_lock, flags); + val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift)); + val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; + change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2]; + ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1); + ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2); + spin_unlock_irqrestore(&ak4531->reg_lock, flags); + return change; +} + +#define AK4531_CONTROLS (sizeof(snd_ak4531_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ak4531_controls[] = { + +AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1), +AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1), + +AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1), +AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1), + +AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1), +AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1), +AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0), +AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0), + +AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1), +AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1), +AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0), +AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5), + +AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1), +AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1), +AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0), +AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1), + +AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1), +AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1), +AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0), +AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3), + +AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), +AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), +AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), +AK4531_INPUT_SW("Aux Input Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), + +AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), +AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), +AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0), +AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0), + +AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1), +AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1), +AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0), +AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0), + +AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1), +AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1), +AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0), +AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0), + +AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0), +AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0), +AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0), + +AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0), +AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0) +}; + +static int snd_ak4531_free(ak4531_t *ak4531) +{ + if (ak4531) { + snd_ak4531_proc_done(ak4531); + if (ak4531->private_free) + ak4531->private_free(ak4531); + snd_magic_kfree(ak4531); + } + return 0; +} + +static int snd_ak4531_dev_free(snd_device_t *device) +{ + ak4531_t *ak4531 = snd_magic_cast(ak4531_t, device->device_data, return -ENXIO); + return snd_ak4531_free(ak4531); +} + +static u8 snd_ak4531_initial_map[0x19 + 1] = { + 0x9f, /* 00: Master Volume Lch */ + 0x9f, /* 01: Master Volume Rch */ + 0x9f, /* 02: Voice Volume Lch */ + 0x9f, /* 03: Voice Volume Rch */ + 0x9f, /* 04: FM Volume Lch */ + 0x9f, /* 05: FM Volume Rch */ + 0x9f, /* 06: CD Audio Volume Lch */ + 0x9f, /* 07: CD Audio Volume Rch */ + 0x9f, /* 08: Line Volume Lch */ + 0x9f, /* 09: Line Volume Rch */ + 0x9f, /* 0a: Aux Volume Lch */ + 0x9f, /* 0b: Aux Volume Rch */ + 0x9f, /* 0c: Mono1 Volume */ + 0x9f, /* 0d: Mono2 Volume */ + 0x9f, /* 0e: Mic Volume */ + 0x87, /* 0f: Mono-out Volume */ + 0x00, /* 10: Output Mixer SW1 */ + 0x00, /* 11: Output Mixer SW2 */ + 0x00, /* 12: Lch Input Mixer SW1 */ + 0x00, /* 13: Rch Input Mixer SW1 */ + 0x00, /* 14: Lch Input Mixer SW2 */ + 0x00, /* 15: Rch Input Mixer SW2 */ + 0x00, /* 16: Reset & Power Down */ + 0x00, /* 17: Clock Select */ + 0x00, /* 18: AD Input Select */ + 0x01 /* 19: Mic Amp Setup */ +}; + +int snd_ak4531_mixer(snd_card_t * card, ak4531_t * _ak4531, ak4531_t ** rak4531) +{ + int idx, err; + ak4531_t * ak4531; + static snd_device_ops_t ops = { + dev_free: snd_ak4531_dev_free, + }; + + snd_assert(rak4531 != NULL, return -EINVAL); + *rak4531 = NULL; + snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL); + ak4531 = snd_magic_kcalloc(ak4531_t, 0, GFP_KERNEL); + if (ak4531 == NULL) + return -ENOMEM; + *ak4531 = *_ak4531; + spin_lock_init(&ak4531->reg_lock); + if ((err = snd_component_add(card, "AK4531")) < 0) { + snd_ak4531_free(ak4531); + return err; + } + strcpy(card->mixername, "Asahi Kasei AK4531"); + ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */ + udelay(100); + ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */ + for (idx = 0; idx < 0x19; idx++) { + if (idx == AK4531_RESET || idx == AK4531_CLOCK) + continue; + ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */ + } + for (idx = 0; idx < AK4531_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) { + snd_ak4531_free(ak4531); + return err; + } + } + snd_ak4531_proc_init(card, ak4531); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ak4531, &ops)) < 0) { + snd_ak4531_free(ak4531); + return err; + } + +#if 0 + snd_ak4531_dump(ak4531); +#endif + *rak4531 = ak4531; + return 0; +} + +/* + + */ + +static void snd_ak4531_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ak4531_t *ak4531 = snd_magic_cast(ak4531_t, entry->private_data, return); + + snd_iprintf(buffer, "Asahi Kasei AK4531\n\n"); + snd_iprintf(buffer, "Recording source : %s\n" + "MIC gain : %s\n", + ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer", + ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB"); +} + +static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(card, "ak4531", card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ak4531; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_ak4531_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ak4531->proc_entry = entry; +} + +static void snd_ak4531_proc_done(ak4531_t * ak4531) +{ + if (ak4531->proc_entry) { + snd_info_unregister(ak4531->proc_entry); + ak4531->proc_entry = NULL; + } +} + +EXPORT_SYMBOL(snd_ak4531_mixer); + +/* + * INIT part + */ + +static int __init alsa_ak4531_init(void) +{ + return 0; +} + +static void __exit alsa_ak4531_exit(void) +{ +} + +module_init(alsa_ak4531_init) +module_exit(alsa_ak4531_exit) diff -Nru linux/sound/pci/ali5451/Makefile linux-2.4.19-pre5-mjc/sound/pci/ali5451/Makefile --- linux/sound/pci/ali5451/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ali5451/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ali5451.o + +list-multi := snd-ali5451.o + +snd-ali5451-objs := ali5451.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o + +include $(TOPDIR)/Rules.make + +snd-ali5451.o: $(snd-ali5451-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ali5451-objs) diff -Nru linux/sound/pci/ali5451/ali5451.c linux-2.4.19-pre5-mjc/sound/pci/ali5451/ali5451.c --- linux/sound/pci/ali5451/ali5451.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ali5451/ali5451.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2291 @@ +/* + * Matt Wu + * Apr 26, 2001 + * Routines for control of ALi pci audio M5451 + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Lcodecnse as published by + * the Free Software Foundation; either version 2 of the Lcodecnse, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public Lcodecnse for more details. + * + * You should have received a copy of the GNU General Public Lcodecnse + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __SNDRV_OSS_COMPAT__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Matt Wu "); +MODULE_DESCRIPTION("ALI M5451"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ALI,M5451,pci},{ALI,M5451}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ALI M5451 PCI Audio."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ALI M5451 PCI Audio."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ALI 5451 PCI Audio."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_pcm_channels, "PCM Channels"); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); + +/* + * Debug part definations + */ + +//#define ALI_DEBUG + +#ifdef ALI_DEBUG +#define snd_ali_printk(format, args...) printk(format, ##args); +#else +#define snd_ali_printk(format, args...) +#endif + +/* + * Constants defination + */ + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +#ifndef PCI_DEVICE_ID_ALI_5451 +#define PCI_DEVICE_ID_ALI_5451 0x5451 +#endif + +#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_ALI<<16)|PCI_DEVICE_ID_ALI_5451) + + +#define ALI_CHANNELS 32 + +#define ALI_PCM_IN_CHANNEL 31 +#define ALI_SPDIF_IN_CHANNEL 19 +#define ALI_SPDIF_OUT_CHANNEL 15 +#define ALI_CENTER_CHANNEL 24 +#define ALI_LEF_CHANNEL 23 +#define ALI_SURR_LEFT_CHANNEL 26 +#define ALI_SURR_RIGHT_CHANNEL 25 + +#define SNDRV_ALI_VOICE_TYPE_PCM 01 +#define SNDRV_ALI_VOICE_TYPE_OTH 02 + +#define ALI_5451_V02 0x02 + +/* + * Direct Registers + */ + +#define ALI_LEGACY_DMAR0 0x00 // ADR0 +#define ALI_LEGACY_DMAR4 0x04 // CNT0 +#define ALI_LEGACY_DMAR11 0x0b // MOD +#define ALI_LEGACY_DMAR15 0x0f // MMR +#define ALI_MPUR0 0x20 +#define ALI_MPUR1 0x21 +#define ALI_MPUR2 0x22 +#define ALI_MPUR3 0x23 + +#define ALI_AC97_WRITE 0x40 +#define ALI_AC97_READ 0x44 + +#define ALI_SCTRL 0x48 +#define ALI_AC97_GPIO 0x4c +#define ALI_SPDIF_CS 0x70 +#define ALI_SPDIF_CTRL 0x74 +#define ALI_START 0x80 +#define ALI_STOP 0x84 +#define ALI_CSPF 0x90 +#define ALI_AINT 0x98 +#define ALI_GC_CIR 0xa0 + #define ENDLP_IE 0x00001000 + #define MIDLP_IE 0x00002000 +#define ALI_AINTEN 0xa4 +#define ALI_VOLUME 0xa8 +#define ALI_SBDELTA_DELTA_R 0xac +#define ALI_MISCINT 0xb0 + #define ADDRESS_IRQ 0x00000020 + #define TARGET_REACHED 0x00008000 + #define MIXER_OVERFLOW 0x00000800 + #define MIXER_UNDERFLOW 0x00000400 +#define ALI_SBBL_SBCL 0xc0 +#define ALI_SBCTRL_SBE2R_SBDD 0xc4 +#define ALI_STIMER 0xc8 +#define ALI_GLOBAL_CONTROL 0xd4 + +#define ALI_CSO_ALPHA_FMS 0xe0 +#define ALI_LBA 0xe4 +#define ALI_ESO_DELTA 0xe8 +#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0 +#define ALI_EBUF1 0xf4 +#define ALI_EBUF2 0xf8 + +#define ALI_REG(codec, x) ((codec)->port + x) + +typedef struct snd_stru_ali ali_t; +typedef struct snd_ali_stru_voice snd_ali_voice_t; +#define chip_t ali_t + +typedef struct snd_ali_channel_control { + // register data + struct REGDATA { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + } data; + + // register addresses + struct REGS { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + unsigned int ac97read; + unsigned int ac97write; + } regs; + +} snd_ali_channel_control_t; + +struct snd_ali_stru_voice { + unsigned int number; + int use: 1, + pcm: 1, + midi: 1, + mode: 1, + synth: 1; + + /* PCM data */ + ali_t *codec; + snd_pcm_substream_t *substream; + snd_ali_voice_t *extra; + + int running: 1; + + int eso; /* final ESO value for channel */ + int count; /* runtime->period_size */ + + /* --- */ + + void *private_data; + void (*private_free)(void *private_data); +}; + + +typedef struct snd_stru_alidev { + + snd_ali_voice_t voices[ALI_CHANNELS]; + + unsigned int chcnt; /* num of opened channels */ + unsigned int chmap; /* bitmap for opened channels */ + unsigned int synthcount; + +} alidev_t; + + +#ifdef CONFIG_PM +#define ALI_GLOBAL_REGS 56 +#define ALI_CHANNEL_REGS 8 +typedef struct snd_ali_image { + unsigned long regs[ALI_GLOBAL_REGS]; + unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; +} ali_image_t; +#endif + + +struct snd_stru_ali { + unsigned long irq; + unsigned long port; + unsigned char revision; + + struct resource *res_port; + + struct pci_dev *pci; + struct pci_dev *pci_m1533; + struct pci_dev *pci_m7101; + + snd_card_t *card; + snd_pcm_t *pcm; + alidev_t synth; + snd_ali_channel_control_t chregs; + + /* S/PDIF Mask */ + unsigned int spdif_mask; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + ac97_t *ac97; + unsigned short ac97_ext_id; + unsigned short ac97_ext_status; + + spinlock_t reg_lock; + spinlock_t voice_alloc; + +#ifdef CONFIG_PM + ali_image_t *image; +#endif +}; + +static struct pci_device_id snd_ali_ids[] __devinitdata = { + {0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + {0, } +}; +MODULE_DEVICE_TABLE(pci, snd_ali_ids); + +static void snd_ali_clear_voices(ali_t *, unsigned int, unsigned int); +static unsigned short snd_ali_codec_peek(ali_t *, int, unsigned short); +static void snd_ali_codec_poke(ali_t *, int, unsigned short, unsigned short); + +/* + * Debug Part + */ + +#ifdef ALI_DEBUG + +static void ali_read_regs(ali_t *codec, int channel) +{ + int i,j; + unsigned int dwVal; + + printk("channel %d registers map:\n", channel); + outb((unsigned char)(channel & 0x001f), ALI_REG(codec,ALI_GC_CIR)); + + printk(" "); + for(j=0;j<8;j++) + printk("%2.2x ", j*4); + printk("\n"); + + for (i=0; i<=0xf8/4;i++) { + if(i%8 == 0) + printk("%2.2x ", (i*4/0x10)*0x10); + dwVal = inl(ALI_REG(codec,i*4)); + printk("%8.8x ", dwVal); + if ((i+1)%8 == 0) + printk("\n"); + } + printk("\n"); +} +static void ali_read_cfg(unsigned int vendor, unsigned deviceid) +{ + unsigned int dwVal; + struct pci_dev *pci_dev = NULL; + int i,j; + + + pci_dev = pci_find_device(vendor, deviceid, pci_dev); + if (pci_dev == NULL) + return ; + + printk("\nM%x PCI CFG\n", deviceid); + printk(" "); + for(j=0;j<8;j++) + printk("%d ",j); + printk("\n"); + + for(i=0;i<8;i++) { + printk("%d ",i); + for(j=0;j<8;j++) + { + pci_read_config_dword(pci_dev, i*0x20+j*4, &dwVal); + printk("%8.8x ", dwVal); + } + printk("\n"); + } + } +static void ali_read_ac97regs(ali_t *codec, int secondary) +{ + unsigned short i,j; + unsigned short wVal; + + printk("\ncodec %d registers map:\n", secondary); + + printk(" "); + for(j=0;j<8;j++) + printk("%2.2x ",j*2); + printk("\n"); + + for (i=0; i<64;i++) { + if(i%8 == 0) + printk("%2.2x ", (i/8)*0x10); + wVal = snd_ali_codec_peek(codec, secondary, i*2); + printk("%4.4x ", wVal); + if ((i+1)%8 == 0) + printk("\n"); + } + printk("\n"); +} + +#endif + +/* + * AC97 ACCESS + */ + +static inline unsigned int snd_ali_5451_peek(ali_t *codec, + unsigned int port ) +{ + return (unsigned int)inl(ALI_REG(codec, port)); +} + +static inline void snd_ali_5451_poke( ali_t *codec, + unsigned int port, + unsigned int val ) +{ + outl((unsigned int)val, ALI_REG(codec, port)); +} + +static int snd_ali_codec_ready( ali_t *codec, + unsigned int port, + int sched ) +{ + signed long end_time; + + end_time = jiffies + 10 * (HZ >> 2); + do { + if (!(snd_ali_5451_peek(codec,port) & 0x8000)) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("ali_codec_ready: codec is not ready.\n "); + return -EIO; +} + +static int snd_ali_stimer_ready(ali_t *codec, int sched) +{ + signed long end_time; + unsigned long dwChk1,dwChk2; + + dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + + end_time = jiffies + 10 * (HZ >> 2); + do { + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + if (dwChk2 != dwChk1) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("ali_stimer_read: stimer is not ready.\n"); + return -EIO; +} + +static void snd_ali_codec_poke(ali_t *codec,int secondary, + unsigned short reg, + unsigned short val) +{ + unsigned int dwVal = 0; + unsigned int port = 0; + + if (reg >= 0x80) { + snd_printk("ali_codec_poke: reg(%xh) invalid.\n", reg); + return; + } + + port = codec->chregs.regs.ac97write; + + if (snd_ali_codec_ready(codec, port, 0) < 0) + return; + if (snd_ali_stimer_ready(codec, 0) < 0) + return; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000 | (val << 16); + if (secondary) dwVal |= 0x0080; + if (codec->revision == ALI_5451_V02) dwVal |= 0x0100; + + snd_ali_5451_poke(codec,port,dwVal); + + return ; +} + +static unsigned short snd_ali_codec_peek( ali_t *codec, + int secondary, + unsigned short reg) +{ + unsigned int dwVal = 0; + unsigned int port = 0; + + if (reg >= 0x80) { + snd_printk("ali_codec_peek: reg(%xh) invalid.\n", reg); + return ~0; + } + + port = codec->chregs.regs.ac97read; + + if (snd_ali_codec_ready(codec, port, 0) < 0) + return ~0; + if (snd_ali_stimer_ready(codec, 0) < 0) + return ~0; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000; /* bit 15*/ + if (secondary) dwVal |= 0x0080; + + snd_ali_5451_poke(codec, port, dwVal); + + if (snd_ali_stimer_ready(codec, 0) < 0) + return ~0; + if (snd_ali_codec_ready(codec, port, 0) < 0) + return ~0; + + return (snd_ali_5451_peek(codec, port) & 0xffff0000)>>16; +} + +static void snd_ali_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val ) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + + snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val); + snd_ali_codec_poke(codec, 0, reg, val); + return ; +} + + +static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return -ENXIO); + + snd_ali_printk("codec_read reg=%xh.\n", reg); + return (snd_ali_codec_peek(codec, 0, reg)); +} + +/* + * AC97 Reset + */ + +static int snd_ali_reset_5451(ali_t *codec) +{ + struct pci_dev *pci_dev = NULL; + unsigned short wCount, wReg; + unsigned int dwVal; + + if ((pci_dev = codec->pci_m1533) != NULL) { + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + } + + pci_dev = codec->pci; + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); + udelay(500); + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); + udelay(5000); + + wCount = 200; + while(wCount--) { + wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + + return -1; +} + +#ifdef CODEC_RESET + +static int snd_ali_reset_codec(ali_t *codec) +{ + struct pci_dev *pci_dev = NULL; + unsigned char bVal = 0; + unsigned int dwVal; + unsigned short wCount, wReg; + + pci_dev = codec->pci_m1533; + + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal |= 0x02; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(5000); + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal &= 0xfd; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(15000); + + wCount = 200; + while(wCount--) { + wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN); + if((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + return -1; +} + +#endif + +/* + * ALI 5451 Controller + */ + +static void snd_ali_enable_special_channel(ali_t *codec, unsigned int channel) +{ + unsigned long dwVal = 0; + + dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL)); + dwVal |= 1 << (channel & 0x0000001f); + outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_disable_special_channel(ali_t *codec, unsigned int channel) +{ + unsigned long dwVal = 0; + + dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL)); + dwVal &= ~(1 << (channel & 0x0000001f)); + outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_enable_address_interrupt(ali_t * codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc |= ENDLP_IE; + gc |= MIDLP_IE; + outl( gc, ALI_REG(codec, ALI_GC_CIR)); +} + +static void snd_ali_disable_address_interrupt(ali_t * codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc &= ~ENDLP_IE; + gc &= ~MIDLP_IE; + outl(gc, ALI_REG(codec, ALI_GC_CIR)); +} + +#if 0 // not used +static void snd_ali_enable_voice_irq(ali_t *codec, unsigned int channel) +{ + unsigned int mask; + snd_ali_channel_control_t *pchregs = &(codec->chregs); + + snd_ali_printk("enable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten)); + pchregs->data.ainten |= mask; + outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten)); +} +#endif + +static void snd_ali_disable_voice_irq(ali_t *codec, unsigned int channel) +{ + unsigned int mask; + snd_ali_channel_control_t *pchregs = &(codec->chregs); + + snd_ali_printk("disable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec,pchregs->regs.ainten)); + pchregs->data.ainten &= ~mask; + outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten)); +} + +static int snd_ali_alloc_pcm_channel(ali_t *codec, int channel) +{ + unsigned int idx = channel & 0x1f; + + if (codec->synth.chcnt >= ALI_CHANNELS){ + snd_printk("ali_alloc_pcm_channel: no free channels.\n"); + return -1; + } + + if (!(codec->synth.chmap & (1 << idx))) { + codec->synth.chmap |= 1 << idx; + codec->synth.chcnt++; + snd_ali_printk("alloc_pcm_channel no. %d.\n",idx); + return idx; + } + return -1; +} + +static int snd_ali_find_free_channel(ali_t * codec, int rec) +{ + int idx; + int result = -1; + + snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm"); + + // recording + if (rec) { + if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<11) && + ( codec->revision == ALI_5451_V02 )) + idx = ALI_SPDIF_IN_CHANNEL; + else + idx = ALI_PCM_IN_CHANNEL; + + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) { + return result; + } else { + snd_printk("ali_find_free_channel: record channel is busy now.\n"); + return -1; + } + } + + //playback... + if (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) { + idx = ALI_SPDIF_OUT_CHANNEL; + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) { + return result; + } else { + snd_printk("ali_find_free_channel: S/PDIF out channel is in busy now.\n"); + } + } + + for (idx = 0; idx < ALI_CHANNELS; idx++) { + if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) + return result; + } + snd_printk("ali_find_free_channel: no free channels.\n"); + return -1; +} + +static void snd_ali_free_channel_pcm(ali_t *codec, int channel) +{ + unsigned int idx = channel & 0x0000001f; + + snd_ali_printk("free_channel_pcm channel=%d\n",channel); + + if (channel < 0 || channel >= ALI_CHANNELS) + return; + + if (!(codec->synth.chmap & (1 << idx))) { + snd_printk("ali_free_channel_pcm: channel %d is not in use.\n",channel); + return; + } else { + codec->synth.chmap &= ~(1 << idx); + codec->synth.chcnt--; + } +} + +#if 0 // not used +static void snd_ali_start_voice(ali_t * codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("start_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec,codec->chregs.regs.start)); +} +#endif + +static void snd_ali_stop_voice(ali_t * codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("stop_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec, codec->chregs.regs.stop)); +} + +/* + * S/PDIF Part + */ + +static void snd_ali_delay(ali_t *codec,int interval) +{ + unsigned long begintimer,currenttimer; + + begintimer = inl(ALI_REG(codec, ALI_STIMER)); + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + + while (currenttimer < begintimer + interval) { + if(snd_ali_stimer_ready(codec, 1) < 0) + break; + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + } +} + +static void snd_ali_detect_spdif_rate(ali_t *codec) +{ + u16 wval = 0; + u16 count = 0; + u8 bval = 0, R1 = 0, R2 = 0; + + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + bval |= 0x1F; + outb(bval,ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + + while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) { + count ++; + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + R1 = bval & 0x1F; + } + + if (count > 50000) { + snd_printk("ali_detect_spdif_rate: timeout!\n"); + return; + } + + count = 0; + while (count++ <= 50000) { + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + R2 = bval & 0x1F; + if (R2 != R1) R1 = R2; else break; + } + + if (count > 50000) { + snd_printk("ali_detect_spdif_rate: timeout!\n"); + return; + } + + if (R2 >= 0x0b && R2 <= 0x0e) { + wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x09 << 8 | (u16)0x05; + outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x02,ALI_REG(codec,ALI_SPDIF_CS + 3)); + } else if (R2 == 0x12) { + wval = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + wval &= 0xE0F0; + wval |= (u16)0x0E << 8 | (u16)0x08; + outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0; + outb(bval|0x03,ALI_REG(codec,ALI_SPDIF_CS + 3)); + } +} + +static unsigned int snd_ali_get_spdif_in_rate(ali_t *codec) +{ + u32 dwRate = 0; + u8 bval = 0; + + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); + bval &= 0x7F; + bval |= 0x40; + outb(bval, ALI_REG(codec,ALI_SPDIF_CTRL)); + + snd_ali_detect_spdif_rate(codec); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)); + bval &= 0x0F; + + if (bval == 0) dwRate = 44100; + if (bval == 1) dwRate = 48000; + if (bval == 2) dwRate = 32000; + + return dwRate; +} + +static void snd_ali_enable_spdif_in(ali_t *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal |= 1<<11; + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + dwVal |= 0x02; + outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + +static void snd_ali_disable_spdif_in(ali_t *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal &= ~(1<<11); + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + + +static void snd_ali_set_spdif_out_rate(ali_t *codec, unsigned int rate) +{ + unsigned char bVal; + unsigned int dwRate = 0; + + if (rate == 32000) dwRate = 0x300; + if (rate == 44100) dwRate = 0; + if (rate == 48000) dwRate = 0x200; + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + bVal &= (unsigned char)(~(1<<6)); + + bVal |= 0x80; //select right + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2)); + + bVal &= (~0x80); //select left + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2)); +} + +static void snd_ali_enable_spdif_out(ali_t *codec) +{ + unsigned short wVal; + unsigned char bVal; + + struct pci_dev *pci_dev = NULL; + + pci_dev = codec->pci_m1533; + if (pci_dev == NULL) + return; + pci_read_config_byte(pci_dev, 0x61, &bVal); + bVal |= 0x40; + pci_write_config_byte(pci_dev, 0x61, bVal); + pci_read_config_byte(pci_dev, 0x7d, &bVal); + bVal |= 0x01; + pci_write_config_byte(pci_dev, 0x7d, bVal); + + pci_read_config_byte(pci_dev, 0x7e, &bVal); + bVal &= (~0x20); + bVal |= 0x10; + pci_write_config_byte(pci_dev, 0x7e, bVal); + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal | 0x20, ALI_REG(codec, ALI_SCTRL)); + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(bVal & ~(1<<6), ALI_REG(codec, ALI_SPDIF_CTRL)); + + { + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= (1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + snd_ali_disable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL); + } +} + +static void snd_ali_enable_spdif_chnout(ali_t *codec) +{ + unsigned short wVal = 0; + + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal &= ~(1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); +/* + wVal = inw(ALI_REG(codec, ALI_SPDIF_CS)); + if (flag & ALI_SPDIF_OUT_NON_PCM) + wVal |= 0x0002; + else + wVal &= (~0x0002); + outw(wVal, ALI_REG(codec, ALI_SPDIF_CS)); +*/ + snd_ali_enable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_chnout(ali_t *codec) +{ + unsigned short wVal = 0; + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= (1<<10); + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_out(ali_t *codec) +{ + unsigned char bVal; + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal & (~0x20), ALI_REG(codec, ALI_SCTRL)); + + snd_ali_disable_spdif_chnout(codec); +} + +static void snd_ali_update_ptr(ali_t *codec,int channel) +{ + snd_ali_voice_t *pvoice = NULL; + snd_pcm_runtime_t *runtime; + snd_ali_channel_control_t *pchregs = NULL; + unsigned int old, mask; +#ifdef ALI_DEBUG + unsigned int temp, cspf; +#endif + + pchregs = &(codec->chregs); + + // check if interrupt occured for channel + old = pchregs->data.aint; + mask = ((unsigned int) 1L) << (channel & 0x1f); + + if (!(old & mask)) + return; + + pvoice = &codec->synth.voices[channel]; + runtime = pvoice->substream->runtime; + + udelay(100); + spin_lock(&codec->reg_lock); + + if (pvoice->pcm && pvoice->substream) { + /* pcm interrupt */ +#ifdef ALI_DEBUG + outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR)); + temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask; +#endif + if (pvoice->running) { + snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",(u16)temp,cspf); + spin_unlock(&codec->reg_lock); + snd_pcm_period_elapsed(pvoice->substream); + spin_lock(&codec->reg_lock); + } else { + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + } else if (codec->synth.voices[channel].synth) { + /* synth interrupt */ + } else if (codec->synth.voices[channel].midi) { + /* midi interrupt */ + } else { + /* unknown interrupt */ + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + spin_unlock(&codec->reg_lock); + outl(mask,ALI_REG(codec,pchregs->regs.aint)); + pchregs->data.aint = old & (~mask); +} + +static void snd_ali_interrupt(ali_t * codec) +{ + int channel; + unsigned int audio_int; + snd_ali_channel_control_t *pchregs = NULL; + pchregs = &(codec->chregs); + + audio_int = inl(ALI_REG(codec, ALI_MISCINT)); + if (audio_int & ADDRESS_IRQ) { + // get interrupt status for all channels + pchregs->data.aint = inl(ALI_REG(codec,pchregs->regs.aint)); + for (channel = 0; channel < ALI_CHANNELS; channel++) { + snd_ali_update_ptr(codec, channel); + } + } + outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), + ALI_REG(codec,ALI_MISCINT)); +} + + +static void snd_ali_card_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + ali_t *codec = snd_magic_cast(ali_t, dev_id, return); + + if (codec == NULL) + return; + snd_ali_interrupt(codec); +} + + +static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec) +{ + snd_ali_voice_t *pvoice = NULL; + unsigned long flags; + int idx; + + snd_ali_printk("alloc_voice: type=%d rec=%d\n",type,rec); + + spin_lock_irqsave(&codec->voice_alloc, flags); + if (type == SNDRV_ALI_VOICE_TYPE_PCM) { + idx = snd_ali_find_free_channel(codec,rec); + if(idx < 0) { + snd_printk("ali_alloc_voice: err.\n"); + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return NULL; + } + pvoice = &(codec->synth.voices[idx]); + pvoice->use = 1; + pvoice->pcm = 1; + pvoice->mode = rec; + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return pvoice; + } + spin_unlock_irqrestore(&codec->voice_alloc, flags); + return NULL; +} + + +static void snd_ali_free_voice(ali_t * codec, snd_ali_voice_t *pvoice) +{ + unsigned long flags; + void (*private_free)(void *); + void *private_data; + + snd_ali_printk("free_voice: channel=%d\n",pvoice->number); + if (pvoice == NULL || !pvoice->use) + return; + snd_ali_clear_voices(codec, pvoice->number, pvoice->number); + spin_lock_irqsave(&codec->voice_alloc, flags); + private_free = pvoice->private_free; + private_data = pvoice->private_data; + pvoice->private_free = NULL; + pvoice->private_data = NULL; + if (pvoice->pcm) { + snd_ali_free_channel_pcm(codec, pvoice->number); + } + pvoice->use = pvoice->pcm = pvoice->synth = 0; + pvoice->substream = NULL; + spin_unlock_irqrestore(&codec->voice_alloc, flags); + if (private_free) + private_free(private_data); +} + + +static void snd_ali_clear_voices(ali_t * codec, + unsigned int v_min, + unsigned int v_max) +{ + unsigned int i; + + for (i = v_min; i <= v_max; i++) { + snd_ali_stop_voice(codec, i); + snd_ali_disable_voice_irq(codec, i); + } +} + +static void snd_ali_write_voice_regs(ali_t * codec, + unsigned int Channel, + unsigned int LBA, + unsigned int CSO, + unsigned int ESO, + unsigned int DELTA, + unsigned int ALPHA_FMS, + unsigned int GVSEL, + unsigned int PAN, + unsigned int VOL, + unsigned int CTRL, + unsigned int EC) +{ + unsigned int ctlcmds[4]; + + outb((unsigned char)(Channel & 0x001f),ALI_REG(codec,ALI_GC_CIR)); + + ctlcmds[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); + ctlcmds[1] = LBA; + ctlcmds[2] = (ESO << 16) | (DELTA & 0x0ffff); + ctlcmds[3] = (GVSEL << 31) | + ((PAN & 0x0000007f) << 24) | + ((VOL & 0x000000ff) << 16) | + ((CTRL & 0x0000000f) << 12) | + (EC & 0x00000fff); + + outb(Channel, ALI_REG(codec, ALI_GC_CIR)); + + outl(ctlcmds[0], ALI_REG(codec,ALI_CSO_ALPHA_FMS)); + outl(ctlcmds[1], ALI_REG(codec,ALI_LBA)); + outl(ctlcmds[2], ALI_REG(codec,ALI_ESO_DELTA)); + outl(ctlcmds[3], ALI_REG(codec,ALI_GVSEL_PAN_VOC_CTRL_EC)); + + outl(0x30000000, ALI_REG(codec, ALI_EBUF1)); /* Still Mode */ + outl(0x30000000, ALI_REG(codec, ALI_EBUF2)); /* Still Mode */ +} + +static unsigned int snd_ali_convert_rate(unsigned int rate, int rec) +{ + unsigned int delta; + + if (rate < 4000) rate = 4000; + if (rate > 48000) rate = 48000; + + if (rec) { + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + } else { + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; + } + + return delta; +} + +static unsigned int snd_ali_control_mode(snd_pcm_substream_t *substream) +{ + unsigned int CTRL; + snd_pcm_runtime_t *runtime = substream->runtime; + + /* set ctrl mode + CTRL default: 8-bit (unsigned) mono, loop mode enabled + */ + CTRL = 0x00000001; + if (snd_pcm_format_width(runtime->format) == 16) + CTRL |= 0x00000008; // 16-bit data + if (!snd_pcm_format_unsigned(runtime->format)) + CTRL |= 0x00000002; // signed data + if (runtime->channels > 1) + CTRL |= 0x00000004; // stereo data + return CTRL; +} + +/* + * PCM part + */ + +static int snd_ali_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_ali_trigger(snd_pcm_substream_t *substream, + int cmd) + +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *s; + unsigned int what, whati, capture_flag; + snd_ali_voice_t *pvoice = NULL, *evoice = NULL; + unsigned int val; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + what = whati = capture_flag = 0; + s = substream; + do { + if ((ali_t *) _snd_pcm_chip(s->pcm) == codec) { + pvoice = (snd_ali_voice_t *) s->runtime->private_data; + evoice = pvoice->extra; + what |= 1 << (pvoice->number & 0x1f); + if (evoice == NULL) { + whati |= 1 << (pvoice->number & 0x1f); + } else { + whati |= 1 << (evoice->number & 0x1f); + what |= 1 << (evoice->number & 0x1f); + } + if (cmd == SNDRV_PCM_TRIGGER_START) { + pvoice->running = 1; + if (evoice != NULL) + evoice->running = 1; + } + snd_pcm_trigger_done(s, substream); + if (pvoice->mode) + capture_flag = 1; + } + s = s->link_next; + } while (s != substream); + spin_lock(&codec->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + outl(what, ALI_REG(codec, ALI_STOP)); + pvoice->running = 0; + if (evoice != NULL) + evoice->running = 0; + } + val = inl(ALI_REG(codec, ALI_AINTEN)); + if (cmd == SNDRV_PCM_TRIGGER_START) { + val |= whati; + } else { + val &= ~whati; + } + outl(val, ALI_REG(codec, ALI_AINTEN)); + if (cmd == SNDRV_PCM_TRIGGER_START) { + outl(what, ALI_REG(codec, ALI_START)); + } + snd_ali_printk("trigger: what=%xh whati=%xh\n",what,whati); + spin_unlock(&codec->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice->extra; + int err; + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err < 0) return err; + + /* voice management */ + + if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + if (evoice == NULL) + return -ENOMEM; + pvoice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = evoice = NULL; + } + } + + return 0; +} + +static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice ? pvoice->extra : NULL; + + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = NULL; + } + return 0; +} + +static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ali_playback_prepare(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + snd_ali_voice_t *evoice = pvoice->extra; + unsigned long flags; + + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + + snd_ali_printk("playback_prepare ...\n"); + + spin_lock_irqsave(&codec->reg_lock, flags); + + /* set Delta (rate) value */ + Delta = snd_ali_convert_rate(runtime->rate, 0); + + if ((pvoice->number == ALI_SPDIF_IN_CHANNEL) || + (pvoice->number == ALI_PCM_IN_CHANNEL)) + snd_ali_disable_special_channel(codec, pvoice->number); + else if ((inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & (1<<15)) + && (pvoice->number == ALI_SPDIF_OUT_CHANNEL)) { + if (codec->revision == ALI_5451_V02) { + snd_ali_set_spdif_out_rate(codec, runtime->rate); + Delta = 0x1000; + } + } + + /* set Loop Back Address */ + LBA = runtime->dma_addr; + + /* set interrupt count size */ + pvoice->count = runtime->period_size; + + /* set target ESO for channel */ + pvoice->eso = runtime->buffer_size; + + snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",pvoice->eso,pvoice->count); + + /* set ESO to capture first MIDLP interrupt */ + ESO = pvoice->eso -1; + /* set ctrl mode */ + CTRL = snd_ali_control_mode(substream); + + GVSEL = 1; + PAN = 0; + VOL = 0; + EC = 0; + snd_ali_printk("playback_prepare:\n ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL); + snd_ali_write_voice_regs( codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + if (evoice != NULL) { + evoice->count = pvoice->count; + evoice->eso = pvoice->count << 1; + ESO = evoice->eso - 1; + snd_ali_write_voice_regs(codec, + evoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + (unsigned int)0x7f, + (unsigned int)0x3ff, + CTRL, + EC); + } + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; +} + + +static int snd_ali_capture_prepare(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned long flags; + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + u8 bValue; + + spin_lock_irqsave(&codec->reg_lock, flags); + + snd_ali_printk("capture_prepare...\n"); + + snd_ali_enable_special_channel(codec,pvoice->number); + + Delta = snd_ali_convert_rate(runtime->rate, 1); + + // Prepare capture intr channel + if (pvoice->number == ALI_SPDIF_IN_CHANNEL) { + + unsigned int rate; + + if (codec->revision != ALI_5451_V02) + return -1; + rate = snd_ali_get_spdif_in_rate(codec); + if (rate == 0) { + snd_printk("ali_capture_preapre: spdif rate detect err!\n"); + rate = 48000; + } + bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); + if (bValue & 0x10) { + outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL)); + printk("clear SPDIF parity error flag.\n"); + } + + if (rate != 48000) + Delta = ((rate << 12)/runtime->rate)&0x00ffff; + } + + // set target ESO for channel + pvoice->eso = runtime->buffer_size; + + // set interrupt count size + pvoice->count = runtime->period_size; + + // set Loop Back Address + LBA = runtime->dma_addr; + + // set ESO to capture first MIDLP interrupt + ESO = pvoice->eso - 1; + CTRL = snd_ali_control_mode(substream); + GVSEL = 0; + PAN = 0x00; + VOL = 0x00; + EC = 0; + + snd_ali_write_voice_regs( codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + + + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return 0; +} + + +static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned int cso; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + if (!pvoice->running) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock_irqrestore(&codec->reg_lock, flags); + snd_ali_printk("playback pointer returned cso=%xh.\n", cso); + + return cso; +} + + +static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + unsigned int cso; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + if (!pvoice->running) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return cso; +} + +static snd_pcm_hardware_t snd_ali_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (256*1024), + period_bytes_min: 64, + period_bytes_max: (256*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_ali_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + unsigned long flags; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + ali_t *codec; + + if (pvoice) { + codec = pvoice->codec; + spin_lock_irqsave(&codec->reg_lock, flags); + snd_ali_free_voice(pvoice->codec, pvoice); + spin_unlock_irqrestore(&codec->reg_lock, flags); + } +} + +static int snd_ali_playback_open(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice; + unsigned long flags = 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0); + if (pvoice == NULL) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return -EAGAIN; + } + pvoice->codec = codec; + spin_unlock_irqrestore(&codec->reg_lock, flags); + + pvoice->substream = substream; + runtime->private_data = pvoice; + runtime->private_free = snd_ali_pcm_free_substream; + + runtime->hw = snd_ali_playback; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +static int snd_ali_capture_open(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&codec->reg_lock, flags); + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1); + if (pvoice == NULL) { + spin_unlock_irqrestore(&codec->reg_lock, flags); + return -EAGAIN; + } + pvoice->codec = codec; + spin_unlock_irqrestore(&codec->reg_lock, flags); + + pvoice->substream = substream; + runtime->private_data = pvoice; + runtime->private_free = snd_ali_pcm_free_substream; + runtime->hw = snd_ali_capture; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +static int snd_ali_playback_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +static int snd_ali_capture_close(snd_pcm_substream_t * substream) +{ + ali_t *codec = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; + + snd_ali_disable_special_channel(codec,pvoice->number); + + return 0; +} + +static snd_pcm_ops_t snd_ali_playback_ops = { + open: snd_ali_playback_open, + close: snd_ali_playback_close, + ioctl: snd_ali_ioctl, + hw_params: snd_ali_playback_hw_params, + hw_free: snd_ali_playback_hw_free, + prepare: snd_ali_playback_prepare, + trigger: snd_ali_trigger, + pointer: snd_ali_playback_pointer, +}; + +static snd_pcm_ops_t snd_ali_capture_ops = { + open: snd_ali_capture_open, + close: snd_ali_capture_close, + ioctl: snd_ali_ioctl, + hw_params: snd_ali_capture_hw_params, + hw_free: snd_ali_capture_hw_free, + prepare: snd_ali_capture_prepare, + trigger: snd_ali_trigger, + pointer: snd_ali_capture_pointer, +}; + + +static void snd_ali_pcm_free(snd_pcm_t *pcm) +{ + ali_t *codec = snd_magic_cast(ali_t, pcm->private_data, return); + codec->pcm = NULL; +} + +static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) *rpcm = NULL; + err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm); + if (err < 0) { + snd_printk("snd_ali_pcm: err called snd_pcm_new.\n"); + return err; + } + pcm->private_data = codec; + pcm->private_free = snd_ali_pcm_free; + pcm->info_flags = 0; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops); + + snd_pcm_lib_preallocate_pci_pages_for_all(codec->pci, pcm, 64*1024, 128*1024); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "ALI 5451"); + codec->pcm = pcm; + if (rpcm) *rpcm = pcm; + return 0; +} + +#define ALI5451_SPDIF(xname, xindex, value) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ +info: snd_ali5451_spdif_info, get: snd_ali5451_spdif_get, \ +put: snd_ali5451_spdif_put, private_value: value} + +static int snd_ali5451_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ali5451_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + unsigned int enable; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + switch(kcontrol->private_value) { + case 0: + enable = (codec->spdif_mask & 0x02) ? 1 : 0; + break; + case 1: + enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0; + break; + case 2: + enable = (codec->spdif_mask & 0x01) ? 1 : 0; + break; + default: + break; + } + ucontrol->value.integer.value[0] = enable; + spin_unlock_irqrestore(&codec->reg_lock, flags); + return 0; +} + +static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + unsigned int change = 0, enable = 0; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irqsave(&codec->reg_lock, flags); + switch (kcontrol->private_value) { + case 0: + change = (codec->spdif_mask & 0x02) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x02; + snd_ali_enable_spdif_out(codec); + } else { + codec->spdif_mask &= ~(0x02); + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_out(codec); + } + } + break; + case 1: + change = (codec->spdif_mask & 0x04) ? 1 : 0; + change = change ^ enable; + if (change && (codec->spdif_mask & 0x02)) { + if (enable) { + codec->spdif_mask |= 0x04; + snd_ali_enable_spdif_chnout(codec); + } else { + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_chnout(codec); + } + } + break; + case 2: + change = (codec->spdif_mask & 0x01) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x01; + snd_ali_enable_spdif_in(codec); + } else { + codec->spdif_mask &= ~(0x01); + snd_ali_disable_spdif_in(codec); + } + } + break; + default: + break; + } + spin_unlock_irqrestore(&codec->reg_lock, flags); + + return change; +} + +static snd_kcontrol_new_t snd_ali5451_mixer_spdif[] __devinit = { + /* spdif aplayback switch */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, 0), + /* spdif out to spdif channel */ + ALI5451_SPDIF("IEC958 Channel Output Switch", 0, 1), + /* spdif in from spdif channel */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2) +}; + +static void snd_ali_mixer_free_ac97(ac97_t *ac97) +{ + ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + codec->ac97 = NULL; +} + +static int __devinit snd_ali_mixer(ali_t * codec) +{ + ac97_t ac97; + int err, idx; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ali_codec_write; + ac97.read = snd_ali_codec_read; + ac97.private_data = codec; + ac97.private_free = snd_ali_mixer_free_ac97; + if ((err = snd_ac97_mixer(codec->card, &ac97, &codec->ac97)) < 0) { + snd_printk("ali mixer creating error.\n"); + return err; + } + if (codec->revision == ALI_5451_V02) { + for(idx = 0; idx < 3; idx++) { + err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec)); + if (err < 0) return err; + } + } + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_ali_suspend(struct pci_dev *dev, u32 state) +#else +static void snd_ali_suspend(struct pci_dev *dev) +#endif +{ +#ifndef PCI_OLD_SUSPEND + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO); +#else + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return); +#endif + ali_image_t *im; + unsigned long flags; + int i, j; + + im = chip->image; + if (! im) +#ifndef PCI_OLD_SUSPEND + return -ENXIO; +#else + return; +#endif + + save_flags(flags); + cli(); + + im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT)); + // im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); + im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP)); + + // disable all IRQ bits + outl(0, ALI_REG(chip, ALI_MISCINT)); + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP)) + continue; + im->regs[i] = inl(ALI_REG(chip, i*4)); + } + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0)); + } + + // stop all HW channel + outl(0xffffffff, ALI_REG(chip, ALI_STOP)); + + restore_flags(flags); +#ifndef PCI_OLD_SUSPEND + return 0; +#endif +} + +#ifndef PCI_OLD_SUSPEND +static int snd_ali_resume(struct pci_dev *dev) +#else +static void snd_ali_resume(struct pci_dev *dev) +#endif +{ +#ifndef PCI_OLD_SUSPEND + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return -ENXIO); +#else + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return); +#endif + ali_image_t *im; + unsigned long flags; + int i, j; + + im = chip->image; + if (! im) +#ifndef PCI_OLD_SUSPEND + return -ENXIO; +#else + return; +#endif + + pci_enable_device(chip->pci); + + save_flags(flags); + cli(); + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0)); + } + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || (i*4 == ALI_START)) + continue; + outl(im->regs[i], ALI_REG(chip, i*4)); + } + + snd_ac97_resume(chip->ac97); + + // start HW channel + outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START)); + // restore IRQ enable bits + outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT)); + + restore_flags(flags); +#ifndef PCI_OLD_SUSPEND + return 0; +#endif +} +#endif + +static int snd_ali_free(ali_t * codec) +{ + snd_ali_disable_address_interrupt(codec); + synchronize_irq(); + if (codec->irq >=0) + free_irq(codec->irq, (void *)codec); + if (codec->res_port) { + release_resource(codec->res_port); + kfree_nocheck(codec->res_port); + } +#ifdef CONFIG_PM + if (codec->image) + kfree(codec->image); +#endif + snd_magic_kfree(codec); + return 0; +} + +static int snd_ali_chip_init(ali_t *codec) +{ + unsigned int legacy; + unsigned char temp; + struct pci_dev *pci_dev = NULL; + + snd_ali_printk("chip initializing ... \n"); + + if (snd_ali_reset_5451(codec)) { + snd_printk("ali_chip_init: reset 5451 error.\n"); + return -1; + } + + if (codec->revision == ALI_5451_V02) { + pci_dev = codec->pci_m1533; + if (pci_dev == NULL) + return -1; + pci_read_config_byte(pci_dev, 0x59, &temp); + + pci_dev = pci_find_device(0x10b9,0x7101, pci_dev); + if (pci_dev == NULL) + return -1; + pci_read_config_byte(pci_dev,0xb8,&temp); + temp |= 1 << 6; + pci_write_config_byte(pci_dev, 0xB8, temp); + } + + pci_read_config_dword(codec->pci, 0x44, &legacy); + legacy &= 0xff00ff00; + legacy |= 0x000800aa; + pci_write_config_dword(codec->pci, 0x44, legacy); + + outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + outl(0x00000000, ALI_REG(codec, ALI_AINTEN)); + outl(0xffffffff, ALI_REG(codec, ALI_AINT)); + outl(0x00000000, ALI_REG(codec, ALI_VOLUME)); + outb(0x10, ALI_REG(codec, ALI_MPUR2)); + + codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID); + codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_STATUS); + if (codec->revision == ALI_5451_V02) { + snd_ali_enable_spdif_out(codec); + codec->spdif_mask = 0x00000002; + } + + snd_ali_printk("chip initialize succeed.\n"); + return 0; + +} + +static int __devinit snd_ali_resources(ali_t *codec) +{ + snd_ali_printk("resouces allocation ...\n"); + if ((codec->res_port = request_region(codec->port, 0x100, "ALI 5451")) == NULL) { + snd_ali_free(codec); + snd_printk("Unalbe to request io ports.\n"); + return -EBUSY; + } + + if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) { + snd_ali_free(codec); + snd_printk("Unable to request irq.\n"); + return -EBUSY; + } + codec->irq = codec->pci->irq; + snd_ali_printk("resouces allocated.\n"); + return 0; +} +static int snd_ali_dev_free(snd_device_t *device) +{ + ali_t *codec=snd_magic_cast(ali_t, device->device_data, return -ENXIO); + snd_ali_free(codec); + return 0; +} + +static int __devinit snd_ali_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + ali_t ** r_ali) +{ + ali_t *codec; + int i, err; + unsigned short cmdw = 0; + struct pci_dev *pci_dev = NULL; + static snd_device_ops_t ops = { + (snd_dev_free_t *)snd_ali_dev_free, + NULL, + NULL + }; + + *r_ali = NULL; + + snd_ali_printk("creating ...\n"); + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 30 bits */ + if (!pci_dma_supported(pci, 0x3fffffff)) { + snd_printk("architecture does not support 30bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x3fffffff); + + if ((codec = snd_magic_kcalloc(ali_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + spin_lock_init(&codec->reg_lock); + spin_lock_init(&codec->voice_alloc); + + codec->card = card; + codec->pci = pci; + codec->irq = -1; + codec->port = pci_resource_start(pci, 0); + pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision); + + if (pcm_streams < 1) + pcm_streams = 1; + if (pcm_streams > 32) + pcm_streams = 32; + + pci_set_master(pci); + pci_read_config_word(pci, PCI_COMMAND, &cmdw); + if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) { + cmdw |= PCI_COMMAND_IO; + pci_write_config_word(pci, PCI_COMMAND, cmdw); + } + pci_set_master(pci); + + if (snd_ali_resources(codec)) { + return -EBUSY; + } + + synchronize_irq(); + + codec->synth.chmap = 0; + codec->synth.chcnt = 0; + codec->spdif_mask = 0; + codec->synth.synthcount = 0; + + if (codec->revision == ALI_5451_V02) + codec->chregs.regs.ac97read = ALI_AC97_WRITE; + else + codec->chregs.regs.ac97read = ALI_AC97_READ; + codec->chregs.regs.ac97write = ALI_AC97_WRITE; + + codec->chregs.regs.start = ALI_START; + codec->chregs.regs.stop = ALI_STOP; + codec->chregs.regs.aint = ALI_AINT; + codec->chregs.regs.ainten = ALI_AINTEN; + + codec->chregs.data.start = 0x00; + codec->chregs.data.stop = 0x00; + codec->chregs.data.aint = 0x00; + codec->chregs.data.ainten = 0x00; + + pci_dev = pci_find_device(0x10b9,0x1533, pci_dev); + codec->pci_m1533 = pci_dev; + pci_dev = pci_find_device(0x10b9,0x7101, pci_dev); + codec->pci_m7101 = pci_dev; + + snd_ali_printk("snd_device_new is called.\n"); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) { + snd_ali_free(codec); + return err; + } + + /* initialise synth voices*/ + for (i = 0; i < ALI_CHANNELS; i++ ) { + codec->synth.voices[i].number = i; + } + + if ((err = snd_ali_chip_init(codec)) < 0) { + snd_printk("ali create: chip init error.\n"); + snd_ali_free(codec); + return err; + } + +#ifdef CONFIG_PM + codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL); + if (! codec->image) + snd_printk("can't allocate apm buffer\n"); +#endif + + snd_ali_enable_address_interrupt(codec); + + *r_ali = codec; + snd_ali_printk("created.\n"); + return 0; +} + +static int __devinit snd_ali_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ali_t *codec; + int err; + + snd_ali_printk("probe ...\n"); + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ali_create(card, pci, snd_pcm_channels[dev], &codec)) < 0) { + snd_card_free(card); + return err; + } + + snd_ali_printk("mixer building ...\n"); + if ((err = snd_ali_mixer(codec)) < 0) { + snd_card_free(card); + return err; + } + + snd_ali_printk("pcm building ...\n"); + if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "ALI5451"); + strcpy(card->shortname, "ALI 5451"); + + sprintf(card->longname, "%s at 0x%lx, irq %li", + card->shortname, codec->port, codec->irq); + + snd_ali_printk("register card.\n"); + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, codec); + dev++; + return 0; +} + +static void __devexit snd_ali_remove(struct pci_dev *pci) +{ + ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ALI 5451", + id_table: snd_ali_ids, + probe: snd_ali_probe, + remove: __devexit_p(snd_ali_remove), +#ifdef CONFIG_PM + suspend: snd_ali_suspend, + resume: snd_ali_resume, +#endif +}; + +static int __init alsa_card_ali_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ALi pci audio not found or device busy.\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ali_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ali_init) +module_exit(alsa_card_ali_exit) + +#ifndef MODULE + +/* format is: snd-ali5451=snd_enable,snd_index,snd_id,snd_pcm_channels */ + +static int __init alsa_card_ali_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ali5451=", alsa_card_ali_setup); + +#endif /* ifndef */ diff -Nru linux/sound/pci/als4000.c linux-2.4.19-pre5-mjc/sound/pci/als4000.c --- linux/sound/pci/als4000.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/als4000.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,729 @@ +/* + * card-als4000.c - driver for Avance Logic ALS4000 based soundcards. + * Copyright (C) 2000 by Bart Hartgers , + * Jaroslav Kysela + * + * Framework borrowed from Massimo Piccioni's card-als100.c. + * + * NOTES + * + * Since Avance does not provide any meaningful documentation, and I + * bought an ALS4000 based soundcard, I was forced to base this driver + * on reverse engineering. + * + * The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an + * ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport + * interface. These subsystems can be mapped into ISA io-port space, + * using the PCI-interface. In addition, the PCI-bit provides DMA and IRQ + * services to the subsystems. + * + * While ALS4000 is very similar to a SoundBlaster, the differences in + * DMA and capturing require more changes to the SoundBlaster than + * desirable, so I made this separate driver. + * + * The ALS4000 can do real full duplex playback/capture. + * + * BUGS + * The box suggests there is some support for 3D sound, but I did not + * investigate this yet. + * + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Bart Hartgers "); +MODULE_DESCRIPTION("Avance Logic ALS4000"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Avance Logic,ALS4000}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_joystick_port[SNDRV_CARDS] = +#ifdef CONFIG_ISA + {0x200}; /* enable as default */ +#else + {0}; /* disabled */ +#endif + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ALS4000 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ALS4000 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ALS4000 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_INDEX_DESC); +MODULE_PARM(snd_joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)"); +MODULE_PARM_SYNTAX(snd_joystick_port, SNDRV_ENABLED); + +#define chip_t sb_t + +typedef struct { + unsigned long gcr; +} snd_card_als4000_t; + +static struct pci_device_id snd_als4000_ids[] __devinitdata = { + { 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_als4000_ids); + +static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val) +{ + outb(reg, port+0x0c); + outl(val, port+0x08); +} + +static inline void snd_als4000_gcr_write(sb_t *sb, u32 reg, u32 val) +{ + snd_als4000_gcr_write_addr(sb->alt_port, reg, val); +} + +static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg) +{ + outb(reg, port+0x0c); + return inl(port+0x08); +} + +static inline u32 snd_als4000_gcr_read(sb_t *sb, u32 reg) +{ + return snd_als4000_gcr_read_addr(sb->alt_port, reg); +} + +static void snd_als4000_set_rate(sb_t *chip, unsigned int rate) +{ + if (!(chip->mode & SB_RATE_LOCK)) { + snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); + snd_sbdsp_command(chip, rate>>8); + snd_sbdsp_command(chip, rate); + } +} + +static void snd_als4000_set_capture_dma(sb_t *chip, dma_addr_t addr, unsigned size) +{ + snd_als4000_gcr_write(chip, 0xa2, addr); + snd_als4000_gcr_write(chip, 0xa3, (size-1)); +} + +static void snd_als4000_set_playback_dma(sb_t *chip, dma_addr_t addr, unsigned size) +{ + snd_als4000_gcr_write(chip, 0x91, addr); + snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000); +} + +#define ALS4000_FORMAT_SIGNED (1<<0) +#define ALS4000_FORMAT_16BIT (1<<1) +#define ALS4000_FORMAT_STEREO (1<<2) + +static int snd_als4000_get_format(snd_pcm_runtime_t *runtime) +{ + int result; + + result = 0; + if (snd_pcm_format_signed(runtime->format)) + result |= ALS4000_FORMAT_SIGNED; + if (snd_pcm_format_physical_width(runtime->format) == 16) + result |= ALS4000_FORMAT_16BIT; + if (runtime->channels > 1) + result |= ALS4000_FORMAT_STEREO; + return result; +} + +/* structure for setting up playback */ +static struct { + unsigned char dsp_cmd, dma_on, dma_off, format; +} playback_cmd_vals[]={ +/* ALS4000_FORMAT_U8_MONO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_MONO }, +/* ALS4000_FORMAT_S8_MONO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_MONO }, +/* ALS4000_FORMAT_U16L_MONO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_MONO }, +/* ALS4000_FORMAT_S16L_MONO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_MONO }, +/* ALS4000_FORMAT_U8_STEREO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_STEREO }, +/* ALS4000_FORMAT_S8_STEREO */ +{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_STEREO }, +/* ALS4000_FORMAT_U16L_STEREO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_STEREO }, +/* ALS4000_FORMAT_S16L_STEREO */ +{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_STEREO }, +}; +#define playback_cmd(chip) (playback_cmd_vals[(chip)->playback_format]) + +/* structure for setting up capture */ +enum { CMD_WIDTH8=0x04, CMD_SIGNED=0x10, CMD_MONO=0x80, CMD_STEREO=0xA0 }; +static unsigned char capture_cmd_vals[]= +{ +CMD_WIDTH8|CMD_MONO, /* ALS4000_FORMAT_U8_MONO */ +CMD_WIDTH8|CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S8_MONO */ +CMD_MONO, /* ALS4000_FORMAT_U16L_MONO */ +CMD_SIGNED|CMD_MONO, /* ALS4000_FORMAT_S16L_MONO */ +CMD_WIDTH8|CMD_STEREO, /* ALS4000_FORMAT_U8_STEREO */ +CMD_WIDTH8|CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S8_STEREO */ +CMD_STEREO, /* ALS4000_FORMAT_U16L_STEREO */ +CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S16L_STEREO */ +}; +#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format]) + +static int snd_als4000_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_als4000_hw_free(snd_pcm_substream_t * substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_als4000_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long size; + unsigned count; + + chip->capture_format = snd_als4000_get_format(runtime); + + size = snd_pcm_lib_buffer_bytes(substream); + count = snd_pcm_lib_period_bytes(substream); + + if (chip->capture_format & ALS4000_FORMAT_16BIT) + count >>=1; + count--; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_als4000_set_rate(chip, runtime->rate); + snd_als4000_set_capture_dma(chip, runtime->dma_addr, size); + spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_lock_irqsave(&chip->mixer_lock, flags ); + snd_sbmixer_write(chip, 0xdc, count); + snd_sbmixer_write(chip, 0xdd, count>>8); + spin_unlock_irqrestore(&chip->mixer_lock, flags ); + return 0; +} + +static int snd_als4000_playback_prepare(snd_pcm_substream_t *substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long size; + unsigned count; + + chip->playback_format = snd_als4000_get_format(runtime); + + size = snd_pcm_lib_buffer_bytes(substream); + count = snd_pcm_lib_period_bytes(substream); + + if (chip->playback_format & ALS4000_FORMAT_16BIT) + count >>=1; + count--; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_als4000_set_rate(chip, runtime->rate); + snd_als4000_set_playback_dma(chip, runtime->dma_addr, size); + + snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); + snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd); + snd_sbdsp_command(chip, playback_cmd(chip).format); + snd_sbdsp_command(chip, count); + snd_sbdsp_command(chip, count>>8); + snd_sbdsp_command(chip, playback_cmd(chip).dma_off); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +static int snd_als4000_capture_trigger(snd_pcm_substream_t * substream, int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irqsave(&chip->mixer_lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->mode |= SB_RATE_LOCK_CAPTURE; + snd_sbmixer_write(chip, 0xde, capture_cmd(chip)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + chip->mode &= ~SB_RATE_LOCK_CAPTURE; + snd_sbmixer_write(chip, 0xde, 0); + } else { + result = -EINVAL; + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return result; +} + +static int snd_als4000_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->mode |= SB_RATE_LOCK_PLAYBACK; + snd_sbdsp_command(chip, playback_cmd(chip).dma_on); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + snd_sbdsp_command(chip, playback_cmd(chip).dma_off); + chip->mode &= ~SB_RATE_LOCK_PLAYBACK; + } else { + result = -EINVAL; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_als4000_capture_pointer(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned int result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames( substream->runtime, result ); +} + +static snd_pcm_uframes_t snd_als4000_playback_pointer(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sb_t *chip = snd_pcm_substream_chip(substream); + unsigned result; + + spin_lock_irqsave(&chip->reg_lock, flags); + result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames( substream->runtime, result ); +} + +static void snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sb_t *chip = snd_magic_cast(sb_t, dev_id, return); + unsigned long flags; + unsigned gcr_status; + unsigned sb_status; + + /* find out which bit of the ALS4000 produced the interrupt */ + gcr_status = inb(chip->alt_port + 0xe); + + if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */ + snd_pcm_period_elapsed(chip->playback_substream); + if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */ + snd_pcm_period_elapsed(chip->capture_substream); + if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interupt */ + snd_mpu401_uart_interrupt(irq, chip->rmidi, regs); + /* release the gcr */ + outb(gcr_status, chip->alt_port + 0xe); + + spin_lock_irqsave(&chip->mixer_lock, flags); + sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + if (sb_status & SB_IRQTYPE_8BIT) + inb(SBP(chip, DATA_AVAIL)); + if (sb_status & SB_IRQTYPE_16BIT) + inb(SBP(chip, DATA_AVAIL_16)); + if (sb_status & SB_IRQTYPE_MPUIN) + inb(chip->mpu_port); + if (sb_status & 0x20) + inb(SBP(chip, RESET)); +} + +/*****************************************************************/ + +static snd_pcm_hardware_t snd_als4000_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */ + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0 +}; + +static snd_pcm_hardware_t snd_als4000_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, /* formats */ + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0 +}; + +/*****************************************************************/ + +static int snd_als4000_playback_open(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->playback_substream = substream; + runtime->hw = snd_als4000_playback; + return 0; +} + +static int snd_als4000_playback_close(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_als4000_capture_open(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_substream = substream; + runtime->hw = snd_als4000_capture; + return 0; +} + +static int snd_als4000_capture_close(snd_pcm_substream_t * substream) +{ + sb_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + snd_pcm_lib_free_pages(substream); + return 0; +} + +/******************************************************************/ + +static snd_pcm_ops_t snd_als4000_playback_ops = { + open: snd_als4000_playback_open, + close: snd_als4000_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_als4000_hw_params, + hw_free: snd_als4000_hw_free, + prepare: snd_als4000_playback_prepare, + trigger: snd_als4000_playback_trigger, + pointer: snd_als4000_playback_pointer +}; + +static snd_pcm_ops_t snd_als4000_capture_ops = { + open: snd_als4000_capture_open, + close: snd_als4000_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_als4000_hw_params, + hw_free: snd_als4000_hw_free, + prepare: snd_als4000_capture_prepare, + trigger: snd_als4000_capture_trigger, + pointer: snd_als4000_capture_pointer +}; + +static void snd_als4000_pcm_free(snd_pcm_t *pcm) +{ + sb_t *chip = snd_magic_cast(sb_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_als4000_pcm(sb_t *chip, int device) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0) + return err; + pcm->private_free = snd_als4000_pcm_free; + pcm->private_data = chip; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops); + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + chip->pcm = pcm; + + return 0; +} + +/******************************************************************/ + +static void __devinit snd_als4000_set_addr(unsigned long gcr, + unsigned int sb, + unsigned int mpu, + unsigned int opl, + unsigned int game) +{ + u32 confA = 0; + u32 confB = 0; + + if (mpu > 0) + confB |= (mpu | 1) << 16; + if (sb > 0) + confB |= (sb | 1); + if (game > 0) + confA |= (game | 1) << 16; + if (opl > 0) + confA |= (opl | 1); + snd_als4000_gcr_write_addr(gcr, 0xa8, confA); + snd_als4000_gcr_write_addr(gcr, 0xa9, confB); +} + +static void __devinit snd_als4000_configure(sb_t *chip) +{ + unsigned long flags; + unsigned tmp; + int i; + + /* do some more configuration */ + spin_lock_irqsave(&chip->mixer_lock, flags); + tmp = snd_sbmixer_read(chip, 0xc0); + snd_sbmixer_write(chip, 0xc0, tmp|0x80); + /* always select DMA channel 0, since we do not actually use DMA */ + snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0); + snd_sbmixer_write(chip, 0xc0, tmp&0x7f); + spin_unlock_irqrestore(&chip->mixer_lock, flags); + + spin_lock_irqsave(&chip->reg_lock,flags); + /* magic number. Enables interrupts(?) */ + snd_als4000_gcr_write(chip, 0x8c, 0x28000); + for(i = 0x91; i <= 0x96; ++i) + snd_als4000_gcr_write(chip, i, 0); + + snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99)); + spin_unlock_irqrestore(&chip->reg_lock,flags); +} + +static void snd_card_als4k_free( snd_card_t *card ) +{ + snd_card_als4000_t * acard = (snd_card_als4000_t *)card->private_data; + /* make sure that interrupts are disabled */ + snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0); +} + +static int __devinit snd_card_als4k_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + snd_card_als4000_t *acard; + unsigned long gcr; + struct resource *res_gcr_port; + sb_t *chip; + opl3_t *opl3; + unsigned short word; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) { + return err; + } + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + gcr = pci_resource_start(pci, 0); + if ((res_gcr_port = request_region(gcr, 0x40, "ALS4000")) == NULL) { + snd_printk("unable to grab region 0x%lx-0x%lx\n", gcr, gcr + 0x40 - 1); + return -EBUSY; + } + + pci_read_config_word(pci, PCI_COMMAND, &word); + pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO); + pci_set_master(pci); + + /* disable all legacy ISA stuff except for joystick */ + snd_als4000_set_addr(gcr, 0, 0, 0, snd_joystick_port[dev]); + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof( snd_card_als4000_t ) ); + if (card == NULL) { + release_resource(res_gcr_port); + kfree_nocheck(res_gcr_port); + return -ENOMEM; + } + + acard = (snd_card_als4000_t *)card->private_data; + acard->gcr = gcr; + card->private_free = snd_card_als4k_free; + + if ((err = snd_sbdsp_create(card, + gcr + 0x10, + pci->irq, + snd_als4000_interrupt, + -1, + -1, + SB_HW_ALS4000, + &chip)) < 0) { + release_resource(res_gcr_port); + kfree_nocheck(res_gcr_port); + snd_card_free(card); + return err; + } + + chip->pci = pci; + chip->alt_port = gcr; + chip->res_alt_port = res_gcr_port; + + snd_als4000_configure(chip); + + if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000, + gcr+0x30, 1, pci->irq, 0, + &chip->rmidi)) < 0) { + snd_card_free(card); + printk(KERN_ERR "als4000: no MPU-401device at 0x%lx ?\n", gcr+0x30); + return err; + } + + if ((err = snd_als4000_pcm(chip, 0)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sbmixer_new(chip)) < 0) { + snd_card_free(card); + return err; + } + + if (snd_opl3_create(card, gcr+0x10, gcr+0x12, + OPL3_HW_AUTO, 1, &opl3) < 0) { + printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx ?\n", + gcr+0x10, gcr+0x12 ); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + strcpy(card->driver, "ALS4000"); + strcpy(card->shortname, "Avance Logic ALS4000"); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->alt_port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_als4k_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ALS4000", + id_table: snd_als4000_ids, + probe: snd_card_als4k_probe, + remove: __devexit_p(snd_card_als4k_remove), +}; + +static int __init alsa_card_als4k_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "no ALS4000 based soundcards found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_als4k_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_als4k_init) +module_exit(alsa_card_als4k_exit) + +#ifndef MODULE + +/* format is: snd-als4000=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_als4000_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-als4000=", alsa_card_als4000_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/cmipci.c linux-2.4.19-pre5-mjc/sound/pci/cmipci.c --- linux/sound/pci/cmipci.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/cmipci.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2775 @@ +/* + * Driver for C-Media CMI8338 and 8738 PCI soundcards. + * Copyright (c) 2000 by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("C-Media CMI8x38 PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{C-Media,CMI8738}," + "{C-Media,CMI8738B}," + "{C-Media,CMI8338A}," + "{C-Media,CMI8338B}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ +static long snd_mpu_port[SNDRV_CARDS] = {0x330, [1 ... (SNDRV_CARDS-1)]=-1}; +static long snd_fm_port[SNDRV_CARDS] = {0x388, [1 ... (SNDRV_CARDS-1)]=-1}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable C-Media PCI soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{-1},{0x330},{0x320},{0x310},{0x300}},dialog:list"); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM port."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{-1},{0x388},{0x3c8},{0x3e0},{0x3e8}},dialog:list"); + +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 +#endif + +/* + * CM8x38 registers definition + */ + +#define CM_REG_FUNCTRL0 0x00 +#define CM_RST_CH1 0x00080000 +#define CM_RST_CH0 0x00040000 +#define CM_CHEN1 0x00020000 /* ch1: enable */ +#define CM_CHEN0 0x00010000 /* ch0: enable */ +#define CM_PAUSE1 0x00000008 /* ch1: pause */ +#define CM_PAUSE0 0x00000004 /* ch0: pause */ +#define CM_CHADC1 0x00000002 /* ch1, 0:playback, 1:record */ +#define CM_CHADC0 0x00000001 /* ch0, 0:playback, 1:record */ + +#define CM_REG_FUNCTRL1 0x04 +#define CM_ASFC_MASK 0x0000E000 /* ADC sampling frequency */ +#define CM_ASFC_SHIFT 13 +#define CM_DSFC_MASK 0x00001C00 /* DAC sampling frequency */ +#define CM_DSFC_SHIFT 10 +#define CM_SPDF_1 0x00000200 /* SPDIF IN/OUT at channel B */ +#define CM_SPDF_0 0x00000100 /* SPDIF OUT only channel A */ +#define CM_SPDFLOOP 0x00000080 /* ext. SPDIIF/OUT -> IN loopback */ +#define CM_SPDO2DAC 0x00000040 /* SPDIF/OUT can be heard from internal DAC */ +#define CM_INTRM 0x00000020 /* master control block (MCB) interrupt enabled */ +#define CM_BREQ 0x00000010 /* bus master enabled */ +#define CM_VOICE_EN 0x00000008 /* legacy voice (SB16,FM) */ +#define CM_UART_EN 0x00000004 /* UART */ +#define CM_JYSTK_EN 0x00000002 /* joy stick */ + +#define CM_REG_CHFORMAT 0x08 + +#define CM_CHB3D5C 0x80000000 /* 5 channels */ +#define CM_CHB3D 0x20000000 /* 4,5,6 channels */ + +#define CM_CHIP_MASK1 0x1f000000 +#define CM_CHIP_037 0x01000000 + +#define CM_AC3EN1 0x00100000 /* enable AC3: model 037 */ +#define CM_SPD24SEL 0x00020000 /* 24bit spdif: model 037 */ +#define CM_SPDIF_INVERSE 0x00010000 + +#define CM_ADCBITLEN_MASK 0x0000C000 +#define CM_ADCBITLEN_16 0x00000000 +#define CM_ADCBITLEN_15 0x00004000 +#define CM_ADCBITLEN_14 0x00008000 +#define CM_ADCBITLEN_13 0x0000C000 + +#define CM_ADCDACLEN_MASK 0x00003000 +#define CM_ADCDACLEN_060 0x00000000 +#define CM_ADCDACLEN_066 0x00001000 +#define CM_ADCDACLEN_130 0x00002000 +#define CM_ADCDACLEN_280 0x00003000 + +#define CM_CH1_SRATE_176K 0x00000800 +#define CM_CH1_SRATE_88K 0x00000400 +#define CM_CH0_SRATE_176K 0x00000200 +#define CM_CH0_SRATE_88K 0x00000100 + +#define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */ + +#define CM_CH1FMT_MASK 0x0000000C +#define CM_CH1FMT_SHIFT 2 +#define CM_CH0FMT_MASK 0x00000003 +#define CM_CH0FMT_SHIFT 0 + +#define CM_REG_INT_HLDCLR 0x0C +#define CM_CHIP_MASK2 0xff000000 +#define CM_CHIP_039 0x04000000 +#define CM_CHIP_039_6CH 0x01000000 +#define CM_TDMA_INT_EN 0x00040000 +#define CM_CH1_INT_EN 0x00020000 +#define CM_CH0_INT_EN 0x00010000 +#define CM_INT_HOLD 0x00000002 +#define CM_INT_CLEAR 0x00000001 + +#define CM_REG_INT_STATUS 0x10 +#define CM_INTR 0x80000000 +#define CM_UARTINT 0x00010000 +#define CM_LTDMAINT 0x00008000 +#define CM_HTDMAINT 0x00004000 +#define CM_CH1BUSY 0x00000008 +#define CM_CH0BUSY 0x00000004 +#define CM_CHINT1 0x00000002 +#define CM_CHINT0 0x00000001 + +#define CM_REG_LEGACY_CTRL 0x14 +#define CM_NXCHG 0x80000000 /* h/w multi channels? */ +#define CM_VMPU_MASK 0x60000000 /* MPU401 i/o port address */ +#define CM_VMPU_330 0x00000000 +#define CM_VMPU_320 0x20000000 +#define CM_VMPU_310 0x40000000 +#define CM_VMPU_300 0x60000000 +#define CM_VSBSEL_MASK 0x0C000000 /* SB16 base address */ +#define CM_VSBSEL_220 0x00000000 +#define CM_VSBSEL_240 0x04000000 +#define CM_VSBSEL_260 0x08000000 +#define CM_VSBSEL_280 0x0C000000 +#define CM_FMSEL_MASK 0x03000000 /* FM OPL3 base address */ +#define CM_FMSEL_388 0x00000000 +#define CM_FMSEL_3C8 0x01000000 +#define CM_FMSEL_3E0 0x02000000 +#define CM_FMSEL_3E8 0x03000000 +#define CM_ENSPDOUT 0x00800000 /* enable XPDIF/OUT to I/O interface */ +#define CM_SPDCOPYRHT 0x00400000 /* set copyright spdif in/out */ +#define CM_DAC2SPDO 0x00200000 /* enable wave+fm_midi -> SPDIF/OUT */ +#define CM_SETRETRY 0x00010000 /* 0: legacy i/o wait (default), 1: legacy i/o bus retry */ +#define CM_CHB3D6C 0x00008000 /* 5.1 channels support */ +#define CM_LINE_AS_BASS 0x00006000 /* use line-in as bass */ + +#define CM_REG_MISC_CTRL 0x18 +#define CM_PWD 0x80000000 +#define CM_RESET 0x40000000 +#define CM_SFIL_MASK 0x30000000 +#define CM_TXVX 0x08000000 +#define CM_N4SPK3D 0x04000000 /* 4ch output */ +#define CM_SPDO5V 0x02000000 /* 5V spdif output */ +#define CM_SPDIF48K 0x01000000 /* write */ +#define CM_SPATUS48K 0x01000000 /* read */ +#define CM_ENDBDAC 0x00800000 /* enable dual dac */ +#define CM_XCHGDAC 0x00400000 /* 0: front=ch0, 1: front=ch1 */ +#define CM_SPD32SEL 0x00200000 /* 0: 16bit SPDIF, 1: 32bit */ +#define CM_SPDFLOOPI 0x00100000 /* int. SPDIF-IN -> int. OUT */ +#define CM_FM_EN 0x00080000 /* enalbe FM */ +#define CM_AC3EN2 0x00040000 /* enable AC3: model 039 */ +#define CM_VIDWPDSB 0x00010000 +#define CM_SPDF_AC97 0x00008000 /* 0: SPDIF/OUT 44.1K, 1: 48K */ +#define CM_MASK_EN 0x00004000 +#define CM_VIDWPPRT 0x00002000 +#define CM_SFILENB 0x00001000 +#define CM_MMODE_MASK 0x00000E00 +#define CM_SPDIF_SELECT 0x00000100 /* for model > 039 ? */ +#define CM_ENCENTER 0x00000080 /* shared with FLINKON? */ +#define CM_FLINKON 0x00000080 +#define CM_FLINKOFF 0x00000040 +#define CM_MIDSMP 0x00000010 +#define CM_UPDDMA_MASK 0x0000000C +#define CM_TWAIT_MASK 0x00000003 + + /* byte */ +#define CM_REG_MIXER0 0x20 + +#define CM_REG_SB16_DATA 0x22 +#define CM_REG_SB16_ADDR 0x23 + +#define CM_REG_MIXER1 0x24 +#define CM_FMMUTE 0x80 /* mute FM */ +#define CM_FMMUTE_SHIFT 7 +#define CM_WSMUTE 0x40 /* mute PCM */ +#define CM_WSMUTE_SHIFT 6 +#define CM_SPK4 0x20 /* lin-in -> rear line out */ +#define CM_SPK4_SHIFT 5 +#define CM_REAR2FRONT 0x10 /* exchange rear/front */ +#define CM_REAR2FRONT_SHIFT 4 +#define CM_WAVEINL 0x08 /* digital wave rec. left chan */ +#define CM_WAVEINL_SHIFT 3 +#define CM_WAVEINR 0x04 /* digical wave rec. right */ +#define CM_WAVEINR_SHIFT 2 +#define CM_X3DEN 0x02 /* 3D surround enable */ +#define CM_X3DEN_SHIFT 1 +#define CM_CDPLAY 0x01 /* enable SPDIF/IN PCM -> DAC */ +#define CM_CDPLAY_SHIFT 0 + +#define CM_REG_MIXER2 0x25 +#define CM_RAUXREN 0x80 /* AUX right capture */ +#define CM_RAUXREN_SHIFT 7 +#define CM_RAUXLEN 0x40 /* AUX left capture */ +#define CM_RAUXLEN_SHIFT 6 +#define CM_VAUXRM 0x20 /* AUX right mute */ +#define CM_VAUXRM_SHIFT 5 +#define CM_VAUXLM 0x10 /* AUX left mute */ +#define CM_VAUXLM_SHIFT 4 +#define CM_VADMIC_MASK 0x0e /* mic gain level (0-3) << 1 */ +#define CM_VADMIC_SHIFT 1 +#define CM_MICGAINZ 0x01 /* mic boost */ +#define CM_MICGAINZ_SHIFT 0 + +#define CM_REG_AUX_VOL 0x26 +#define CM_VAUXL_MASK 0xf0 +#define CM_VAUXR_MASK 0x0f + +#define CM_REG_MISC 0x27 +#define CM_XGPO1 0x20 +#define CM_XGPBIO 0x04 +#define CM_SPDVALID 0x02 /* spdif input valid check */ +#define CM_DMAUTO 0x01 + +#define CM_REG_AC97 0x28 /* hmmm.. do we have ac97 link? */ + +/* + * extended registers + */ +#define CM_REG_CH0_FRAME1 0x80 /* base address */ +#define CM_REG_CH0_FRAME2 0x84 +#define CM_REG_CH1_FRAME1 0x88 /* 0-15: count of samples at bus master; buffer size */ +#define CM_REG_CH1_FRAME2 0x8C /* 16-31: count of samples at codec; fragment size */ + +/* + * size of i/o region + */ +#define CM_EXTENT_CODEC 0x100 +#define CM_EXTENT_MIDI 0x2 +#define CM_EXTENT_SYNTH 0x4 + +/* + * pci ids + */ +#ifndef PCI_VENDOR_ID_CMEDIA +#define PCI_VENDOR_ID_CMEDIA 0x13F6 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A +#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B +#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 +#endif + +/* + * channels for playback / capture + */ +#define CM_CH_PLAY 0 +#define CM_CH_CAPT 1 + +/* + * flags to check device open/close + */ +#define CM_OPEN_NONE 0 +#define CM_OPEN_CH_MASK 0x01 +#define CM_OPEN_DAC 0x10 +#define CM_OPEN_ADC 0x20 +#define CM_OPEN_SPDIF 0x40 +#define CM_OPEN_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC) +#define CM_OPEN_PLAYBACK2 (CM_CH_CAPT | CM_OPEN_DAC) +#define CM_OPEN_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC) +#define CM_OPEN_SPDIF_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_SPDIF) +#define CM_OPEN_SPDIF_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC | CM_OPEN_SPDIF) + + +#if CM_CH_PLAY == 1 +#define CM_PLAYBACK_SRATE_176K CM_CH1_SRATE_176K +#define CM_PLAYBACK_SPDF CM_SPDF_1 +#define CM_CAPTURE_SPDF CM_SPDF_0 +#else +#define CM_PLAYBACK_SRATE_176K CM_CH0_SRATE_176K +#define CM_PLAYBACK_SPDF CM_SPDF_0 +#define CM_CAPTURE_SPDF CM_SPDF_1 +#endif + + +/* + * define this if you want soft ac3 encoding - it's still buggy.. + */ +/* #define DO_SOFT_AC3 */ + + +/* + * driver data + */ + +typedef struct snd_stru_cmipci cmipci_t; +typedef struct snd_stru_cmipci_pcm cmipci_pcm_t; + +#define chip_t cmipci_t + +struct snd_stru_cmipci_pcm { + snd_pcm_substream_t *substream; + int running; /* dac/adc running? */ + unsigned int dma_size; /* in frames */ + unsigned int period_size; /* in frames */ + unsigned int offset; /* physical address of the buffer */ + unsigned int fmt; /* format bits */ + int ch; /* channel (0/1) */ + unsigned int is_dac; /* is dac? */ + int bytes_per_frame; + int shift; + int ac3_shift; /* extra shift: 1 on soft ac3 mode */ +}; + +/* mixer elements toggled/resumed during ac3 playback */ +struct cmipci_mixer_auto_switches { + const char *name; /* switch to toggle */ + int toggle_on; /* value to change when ac3 mode */ +}; +static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = { + {"PCM Playback Switch", 0}, + {"IEC958 Output Switch", 1}, + {"IEC958 Mix Analog", 0}, + // {"IEC958 Out To DAC", 1}, // no longer used + {"IEC958 Loop", 0}, +}; +#define CM_SAVED_MIXERS (sizeof(cm_saved_mixer)/sizeof(cm_saved_mixer[0])) + +struct snd_stru_cmipci { + snd_card_t *card; + + struct pci_dev *pci; + unsigned int device; /* device ID */ + int irq; + + unsigned long iobase; + struct resource *res_iobase; + unsigned int ctrl; /* FUNCTRL0 current value */ + + snd_pcm_t *pcm; /* DAC/ADC PCM */ + snd_pcm_t *pcm2; /* 2nd DAC */ + snd_pcm_t *pcm_spdif; /* SPDIF */ + + int chip_version; + int max_channels; + unsigned int has_dual_dac: 1; + unsigned int can_ac3_sw: 1; + unsigned int can_ac3_hw: 1; + unsigned int can_multi_ch: 1; + + unsigned int spdif_playback_avail: 1; /* spdif ready? */ + unsigned int spdif_playback_enabled: 1; /* spdif switch enabled? */ + int spdif_counter; /* for software AC3 */ + + unsigned int dig_status; + unsigned int dig_pcm_status; + snd_kcontrol_t *spdif_pcm_ctl; + + snd_pcm_hardware_t *hw_info[3]; /* for playbacks */ + + int opened[2]; /* open mode */ + struct semaphore open_mutex; + + int mixer_insensitive: 1; + snd_kcontrol_t *mixer_res_ctl[CM_SAVED_MIXERS]; + int mixer_res_status[CM_SAVED_MIXERS]; + + opl3_t *opl3; + snd_hwdep_t *opl3hwdep; + + cmipci_pcm_t channel[2]; /* ch0 - DAC, ch1 - ADC or 2nd DAC */ + + /* external MIDI */ + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; +}; + + +/* read/write operations for dword register */ +inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data) +{ + outl(data, cm->iobase + cmd); +} +inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd) +{ + return inl(cm->iobase + cmd); +} + +/* read/write operations for word register */ +inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data) +{ + outw(data, cm->iobase + cmd); +} +inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd) +{ + return inw(cm->iobase + cmd); +} + +/* read/write operations for byte register */ +inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data) +{ + outb(data, cm->iobase + cmd); +} + +inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd) +{ + return inb(cm->iobase + cmd); +} + +/* bit operations for dword register */ +static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +{ + unsigned int val; + val = inl(cm->iobase + cmd); + val |= flag; + outl(val, cm->iobase + cmd); +} + +static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag) +{ + unsigned int val; + val = inl(cm->iobase + cmd); + val &= ~flag; + outl(val, cm->iobase + cmd); +} + +#if 0 // not used +/* bit operations for byte register */ +static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +{ + unsigned char val; + val = inb(cm->iobase + cmd); + val |= flag; + outb(val, cm->iobase + cmd); +} + +static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag) +{ + unsigned char val; + val = inb(cm->iobase + cmd); + val &= ~flag; + outb(val, cm->iobase + cmd); +} +#endif + + +/* + * PCM interface + */ + +/* + * calculate frequency + */ + +static int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 }; +#define RATES (sizeof(rates) / sizeof(rates[0])) + +static unsigned int snd_cmipci_rate_freq(unsigned int rate) +{ + int i; + for (i = 0; i < RATES; i++) { + if (rates[i] == rate) + return i; + } + snd_BUG(); + return 0; +} + +static int snd_cmipci_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_cmipci_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + + +/* + */ + +static unsigned int hw_channels[] = {1, 2, 4, 5, 6}; +static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = { + count: 3, + list: hw_channels, + mask: 0, +}; +static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = { + count: 5, + list: hw_channels, + mask: 0, +}; + +static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels) +{ + unsigned long flags; + + if (channels > 2) { + if (! cm->can_multi_ch) + return -EINVAL; + if (rec->fmt != 0x03) /* stereo 16bit only */ + return -EINVAL; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); + if (channels > 4) { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + } else { + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + } + if (channels == 6) { + snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + } else { + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + + } else { + if (cm->can_multi_ch) { + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); + snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + } + return 0; +} + + +/* + * prepare playback/capture channel + * channel to be used must have been set in rec->ch. + */ +static int snd_cmipci_pcm_prepare(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream) +{ + unsigned long flags; + unsigned int reg, freq, val; + snd_pcm_runtime_t *runtime = substream->runtime; + + rec->fmt = 0; + rec->shift = 0; + if (snd_pcm_format_width(runtime->format) >= 16) { + rec->fmt |= 0x02; + if (snd_pcm_format_width(runtime->format) > 16) + rec->shift++; /* 24/32bit */ + } + if (runtime->channels > 1) + rec->fmt |= 0x01; + if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) { + snd_printd("cannot set dac channels\n"); + return -EINVAL; + } + + rec->offset = runtime->dma_addr; + /* buffer and period sizes in frame */ + rec->dma_size = runtime->buffer_size << rec->shift; + rec->period_size = runtime->period_size << rec->shift; + rec->dma_size <<= rec->ac3_shift; + rec->period_size <<= rec->ac3_shift; + + spin_lock_irqsave(&cm->reg_lock, flags); + + /* set buffer address */ + reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; + snd_cmipci_write(cm, reg, rec->offset); + /* program sample counts */ + reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; + snd_cmipci_write_w(cm, reg, rec->dma_size - 1); + snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1); + + /* set adc/dac flag */ + val = rec->ch ? CM_CHADC1 : CM_CHADC0; + if (rec->is_dac) + cm->ctrl &= ~val; + else + cm->ctrl |= val; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); + + /* set sample rate */ + freq = snd_cmipci_rate_freq(runtime->rate); + val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); + if (rec->ch) { + val &= ~CM_ASFC_MASK; + val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK; + } else { + val &= ~CM_DSFC_MASK; + val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK; + } + snd_cmipci_write(cm, CM_REG_FUNCTRL1, val); + //snd_printd("cmipci: functrl1 = %08x\n", val); + + /* set format */ + val = snd_cmipci_read(cm, CM_REG_CHFORMAT); + if (rec->ch) { + val &= ~CM_CH1FMT_MASK; + val |= rec->fmt << CM_CH1FMT_SHIFT; + } else { + val &= ~CM_CH0FMT_MASK; + val |= rec->fmt << CM_CH0FMT_SHIFT; + } + snd_cmipci_write(cm, CM_REG_CHFORMAT, val); + //snd_printd("cmipci: chformat = %08x\n", val); + + rec->running = 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return 0; +} + +/* + * PCM trigger/stop + */ +static int snd_cmipci_pcm_trigger(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream, int cmd) +{ + unsigned int inthld, chen, reset, pause; + int result = 0; + + inthld = CM_CH0_INT_EN << rec->ch; + chen = CM_CHEN0 << rec->ch; + reset = CM_RST_CH0 << rec->ch; + pause = CM_PAUSE0 << rec->ch; + + spin_lock(&cm->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + rec->running = 1; + /* set interrupt */ + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld); + cm->ctrl |= chen; + /* enable channel */ + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_STOP: + rec->running = 0; + /* disable interrupt */ + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld); + /* reset */ + cm->ctrl &= ~chen; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + cm->ctrl |= pause; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cm->ctrl &= ~pause; + snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&cm->reg_lock); + return result; +} + +/* + * return the current pointer + */ +static snd_pcm_uframes_t snd_cmipci_pcm_pointer(cmipci_t *cm, cmipci_pcm_t *rec, + snd_pcm_substream_t *substream) +{ + size_t ptr; + unsigned int reg; + if (!rec->running) + return 0; +#if 1 // this seems better.. + reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; + ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1); + ptr >>= rec->shift; +#else + reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; + ptr = snd_cmipci_read(cm, reg) - rec->offset; + ptr = bytes_to_frames(substream->runtime, ptr); +#endif + ptr >>= rec->ac3_shift; + return ptr; +} + +/* + * playback + */ + +static int snd_cmipci_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], substream, cmd); +} + +static snd_pcm_uframes_t snd_cmipci_playback_pointer(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream); +} + + + +/* + * capture + */ + +static int snd_cmipci_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], substream, cmd); +} + +static snd_pcm_uframes_t snd_cmipci_capture_pointer(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream); +} + +#ifdef DO_SOFT_AC3 +/* + * special tricks for soft ac3 transfer: + * + * we compose an iec958 subframe from 16bit ac3 sample and + * write the raw subframe via 32bit data mode. + */ + +/* find parity for bit 4~30 */ +static unsigned parity(unsigned int data) +{ + unsigned int parity = 0; + int counter = 4; + + data >>= 4; /* start from bit 4 */ + while (counter <= 30) { + if (data & 1) + parity++; + data >>= 1; + counter++; + } + return parity & 1; +} + +/* + * compose 32bit iec958 subframe with non-audio data. + * bit 0-3 = preamble + * 4-7 = aux (=0) + * 8-27 = data (12-27 for 16bit) + * 28 = validity (=0) + * 29 = user data (=0) + * 30 = channel status + * 31 = parity + * + * channel status is assumed as consumer, non-audio + * thus all 0 except bit 1 + */ +inline static u32 convert_ac3_32bit(cmipci_t *cm, u32 val) +{ + u32 data = (u32)val << 12; + + if (cm->spdif_counter == 2 || cm->spdif_counter == 3) /* bit 1 */ + data |= 0x40000000; /* indicate AC-3 raw data */ + if (parity(data)) /* parity bit 4-30 */ + data |= 0x80000000; + if (cm->spdif_counter == 0) + data |= 3; /* preamble 'M' */ + else if (cm->spdif_counter & 1) + data |= 5; /* odd, 'W' */ + else + data |= 9; /* even, 'M' */ + + cm->spdif_counter++; + if (cm->spdif_counter == 384) + cm->spdif_counter = 0; + + return data; +} + + +static int snd_cmipci_ac3_copy(snd_pcm_substream_t *subs, int channel, + snd_pcm_uframes_t pos, void *src, + snd_pcm_uframes_t count) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + u32 *dst; + u16 *srcp = src, val; + snd_pcm_uframes_t offset; + snd_pcm_runtime_t *runtime = subs->runtime; + + if (! cm->channel[CM_CH_PLAY].ac3_shift) + return copy_from_user(runtime->dma_area + frames_to_bytes(runtime, pos), + src, frames_to_bytes(runtime, count)); + + if (! access_ok(VERIFY_READ, src, count)) + return -EFAULT; + + /* frame = 16bit stereo */ + offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2); + dst = (u32*)(runtime->dma_area + offset); + + count /= 2; + while (count-- > 0) { + get_user(val, srcp); + srcp++; + *dst++ = convert_ac3_32bit(cm, val); + } + + return 0; +} + +static int snd_cmipci_ac3_silence(snd_pcm_substream_t *subs, int channel, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + u32 *dst; + snd_pcm_uframes_t offset; + snd_pcm_runtime_t *runtime = subs->runtime; + + if (! cm->channel[CM_CH_PLAY].ac3_shift) + return snd_pcm_format_set_silence(runtime->format, + runtime->dma_area + frames_to_bytes(runtime, pos), count); + + /* frame = 16bit stereo */ + offset = (pos << 1) % (cm->channel[CM_CH_PLAY].dma_size << 2); + dst = (u32*)(subs->runtime->dma_area + offset); + + count /= 2; + while (count-- > 0) { + *dst++ = convert_ac3_32bit(cm, 0); + } + + return 0; +} +#endif /* DO_SOFT_AC3 */ + + +/* + * hw preparation for spdif + */ + +static int snd_cmipci_spdif_default_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_default_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cmipci_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i, change; + unsigned int val; + + val = 0; + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->dig_status; + chip->dig_status = val; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_cmipci_spdif_default_info, + get: snd_cmipci_spdif_default_get, + put: snd_cmipci_spdif_default_put +}; + +static int snd_cmipci_spdif_mask_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_cmipci_spdif_mask_info, + get: snd_cmipci_spdif_mask_get, +}; + +static int snd_cmipci_spdif_stream_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_cmipci_spdif_stream_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cmipci_spdif_stream_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i, change; + unsigned int val; + + val = 0; + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->dig_pcm_status; + chip->dig_pcm_status = val; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_cmipci_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_cmipci_spdif_stream_info, + get: snd_cmipci_spdif_stream_get, + put: snd_cmipci_spdif_stream_put +}; + +/* + */ + +/* save mixer setting and mute for AC3 playback */ +static void save_mixer_state(cmipci_t *cm) +{ + if (! cm->mixer_insensitive) { + int i; + for (i = 0; i < CM_SAVED_MIXERS; i++) { + snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; + if (ctl) { + snd_ctl_elem_value_t val; + int event; + memset(&val, 0, sizeof(val)); + ctl->get(ctl, &val); + cm->mixer_res_status[i] = val.value.integer.value[0]; + val.value.integer.value[0] = cm_saved_mixer[i].toggle_on; + event = SNDRV_CTL_EVENT_MASK_INFO; + if (cm->mixer_res_status[i] != val.value.integer.value[0]) { + ctl->put(ctl, &val); /* toggle */ + event |= SNDRV_CTL_EVENT_MASK_VALUE; + } + ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(cm->card, event, &ctl->id); + } + } + cm->mixer_insensitive = 1; + } +} + + +/* restore the previously saved mixer status */ +static void restore_mixer_state(cmipci_t *cm) +{ + if (cm->mixer_insensitive) { + int i; + cm->mixer_insensitive = 0; /* at first clear this; + otherwise the changes will be ignored */ + for (i = 0; i < CM_SAVED_MIXERS; i++) { + snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; + if (ctl) { + snd_ctl_elem_value_t val; + int event; + + memset(&val, 0, sizeof(val)); + ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->get(ctl, &val); + event = SNDRV_CTL_EVENT_MASK_INFO; + if (val.value.integer.value[0] != cm->mixer_res_status[i]) { + val.value.integer.value[0] = cm->mixer_res_status[i]; + ctl->put(ctl, &val); + event |= SNDRV_CTL_EVENT_MASK_VALUE; + } + snd_ctl_notify(cm->card, event, &ctl->id); + } + } + } +} + +/* spinlock held! */ +static void setup_ac3(cmipci_t *cm, int do_ac3, int rate) +{ + cm->channel[CM_CH_PLAY].ac3_shift = 0; + cm->spdif_counter = 0; + + if (do_ac3) { + /* AC3EN for 037 */ + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); + /* AC3EN for 039 */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); + + if (cm->can_ac3_hw) { + /* SPD24SEL for 037, 0x02 */ + /* SPD24SEL for 039, 0x20, but cannot be set */ + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + } else { /* can_ac3_sw */ +#ifdef DO_SOFT_AC3 + /* FIXME: ugly hack! */ + subs->runtime->buffer_size /= 2; + /* SPD32SEL for 037 & 039, 0x20 */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + /* set 176K sample rate to fix 033 HW bug */ + if (cm->chip_version == 33) { + if (rate >= 48000) { + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_176K); + } else { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); + } + } + cm->channel[CM_CH_PLAY].ac3_shift = 1; /* use 32bit */ +#endif /* DO_SOFT_AC3 */ + } + + } else { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); + + if (cm->can_ac3_hw) { + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + } else { + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); + } + } +} + +static void setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3) +{ + int rate; + unsigned long flags; + + rate = subs->runtime->rate; + + if (up && do_ac3) + save_mixer_state(cm); + + spin_lock_irqsave(&cm->reg_lock, flags); + cm->spdif_playback_avail = up; + if (up) { + /* they are controlled via "IEC958 Output Switch" */ + /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ + /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ + if (cm->spdif_playback_enabled) + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + setup_ac3(cm, do_ac3, rate); + + if (rate == 48000) + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); + else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); + + } else { + /* they are controlled via "IEC958 Output Switch" */ + /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ + /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ + snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + setup_ac3(cm, 0, 0); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); +} + + +/* + * preparation + */ + +/* playback - enable spdif only on the certain condition */ +static int snd_cmipci_playback_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + int rate = substream->runtime->rate; + int do_spdif, do_ac3; + do_spdif = ((rate == 44100 || rate == 48000) && + substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && + substream->runtime->channels == 2); + do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; +#ifdef DO_SOFT_AC3 + if (do_ac3 && cm->can_ac3_sw) + do_spdif = 0; +#endif + setup_spdif_playback(cm, substream, do_spdif, do_ac3); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); +} + +/* playback (via device #2) - enable spdif always */ +static int snd_cmipci_playback_spdif_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + setup_spdif_playback(cm, substream, 1, cm->dig_pcm_status & IEC958_AES0_NONAUDIO); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); +} + +static int snd_cmipci_playback_hw_free(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + setup_spdif_playback(cm, substream, 0, 0); + restore_mixer_state(cm); + return snd_cmipci_hw_free(substream); +} + +/* capture */ +static int snd_cmipci_capture_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); +} + +/* capture with spdif (via device #2) */ +static int snd_cmipci_capture_spdif_prepare(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); +} + +static int snd_cmipci_capture_spdif_hw_free(snd_pcm_substream_t *subs) +{ + cmipci_t *cm = snd_pcm_substream_chip(subs); + unsigned long flags; + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + spin_unlock_irqrestore(&cm->reg_lock, flags); + + return snd_cmipci_hw_free(subs); +} + + +/* + * interrupt handler + */ +static void snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, dev_id, return); + unsigned int status; + + /* fastpath out, to ease interrupt sharing */ + status = snd_cmipci_read(cm, CM_REG_INT_STATUS); + if (!(status & CM_INTR)) + return; + + /* acknowledge interrupt */ + spin_lock(&cm->reg_lock); + if (status & CM_CHINT0) { + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, CM_CH0_INT_EN); + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, CM_CH0_INT_EN); + } + if (status & CM_CHINT1) { + snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, CM_CH1_INT_EN); + snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, CM_CH1_INT_EN); + } + spin_unlock(&cm->reg_lock); + + if (cm->rmidi && (status & CM_UARTINT)) + snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data, regs); + + if (cm->pcm) { + if ((status & CM_CHINT0) && cm->channel[0].running) + snd_pcm_period_elapsed(cm->channel[0].substream); + if ((status & CM_CHINT1) && cm->channel[1].running) + snd_pcm_period_elapsed(cm->channel[1].substream); + } +} + +/* + * h/w infos + */ + +/* playback on channel A */ +static snd_pcm_hardware_t snd_cmipci_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* capture on channel B */ +static snd_pcm_hardware_t snd_cmipci_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* playback on channel B - stereo 16bit only? */ +static snd_pcm_hardware_t snd_cmipci_playback2 = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, + rate_min: 5512, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* spdif playback on channel A */ +static snd_pcm_hardware_t snd_cmipci_playback_spdif = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + formats: SNDRV_PCM_FMTBIT_S16_LE /*| SNDRV_PCM_FMTBIT_S32_LE*/, + rates: SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + rate_min: 44100, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* spdif capture on channel B */ +static snd_pcm_hardware_t snd_cmipci_capture_spdif = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + rate_min: 44100, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * check device open/close + */ +static int open_device_check(cmipci_t *cm, int mode, snd_pcm_substream_t *subs) +{ + unsigned long flags; + int ch = mode & CM_OPEN_CH_MASK; + + /* FIXME: a file should wait until the device becomes free + * when it's opened on blocking mode. however, since the current + * pcm framework doesn't pass file pointer before actually opened, + * we can't know whether blocking mode or not in open callback.. + */ + down(&cm->open_mutex); + if (cm->opened[ch]) { + up(&cm->open_mutex); + return -EBUSY; + } + cm->opened[ch] = mode; + cm->channel[ch].substream = subs; + if (! (mode & CM_OPEN_DAC)) { + /* disable dual DAC mode */ + cm->channel[ch].is_dac = 0; + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + up(&cm->open_mutex); + return 0; +} + +static void close_device_check(cmipci_t *cm, int mode) +{ + unsigned long flags; + int ch = mode & CM_OPEN_CH_MASK; + + down(&cm->open_mutex); + if (cm->opened[ch] == mode) { + cm->channel[ch].running = 0; + cm->channel[ch].substream = NULL; + cm->opened[ch] = 0; + if (! cm->channel[ch].is_dac) { + /* enable dual DAC mode again */ + cm->channel[ch].is_dac = 1; + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); + spin_unlock_irqrestore(&cm->reg_lock, flags); + } + } + up(&cm->open_mutex); +} + +/* + */ + +static int snd_cmipci_playback_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0) + return err; + runtime->hw = snd_cmipci_playback; + if (cm->can_multi_ch) { + runtime->hw.channels_max = cm->max_channels; + if (cm->max_channels == 4) + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4); + else + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6); + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_capture_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0) + return err; + runtime->hw = snd_cmipci_capture; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_playback2_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */ + return err; + runtime->hw = snd_cmipci_playback2; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); + return 0; +} + +static int snd_cmipci_playback_spdif_open(snd_pcm_substream_t *substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */ + return err; + runtime->hw = snd_cmipci_playback_spdif; + if (cm->can_ac3_hw) { + runtime->hw.info |= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID; + } + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); + cm->dig_pcm_status = cm->dig_status; + return 0; +} + +static int snd_cmipci_capture_spdif_open(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */ + return err; + runtime->hw = snd_cmipci_capture_spdif; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); + return 0; +} + + +/* + */ + +static int snd_cmipci_playback_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_PLAYBACK); + return 0; +} + +static int snd_cmipci_capture_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_CAPTURE); + return 0; +} + +static int snd_cmipci_playback2_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_PLAYBACK2); + return 0; +} + +static int snd_cmipci_playback_spdif_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_SPDIF_PLAYBACK); + return 0; +} + +static int snd_cmipci_capture_spdif_close(snd_pcm_substream_t * substream) +{ + cmipci_t *cm = snd_pcm_substream_chip(substream); + close_device_check(cm, CM_OPEN_SPDIF_CAPTURE); + return 0; +} + + +/* + */ + +static snd_pcm_ops_t snd_cmipci_playback_ops = { + open: snd_cmipci_playback_open, + close: snd_cmipci_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_playback_hw_free, + prepare: snd_cmipci_playback_prepare, + trigger: snd_cmipci_playback_trigger, + pointer: snd_cmipci_playback_pointer, +}; + +static snd_pcm_ops_t snd_cmipci_capture_ops = { + open: snd_cmipci_capture_open, + close: snd_cmipci_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_hw_free, + prepare: snd_cmipci_capture_prepare, + trigger: snd_cmipci_capture_trigger, + pointer: snd_cmipci_capture_pointer, +}; + +static snd_pcm_ops_t snd_cmipci_playback2_ops = { + open: snd_cmipci_playback2_open, + close: snd_cmipci_playback2_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_hw_free, + prepare: snd_cmipci_capture_prepare, /* channel B */ + trigger: snd_cmipci_capture_trigger, /* channel B */ + pointer: snd_cmipci_capture_pointer, /* channel B */ +}; + +static snd_pcm_ops_t snd_cmipci_playback_spdif_ops = { + open: snd_cmipci_playback_spdif_open, + close: snd_cmipci_playback_spdif_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_playback_hw_free, + prepare: snd_cmipci_playback_spdif_prepare, /* set up rate */ + trigger: snd_cmipci_playback_trigger, + pointer: snd_cmipci_playback_pointer, +#ifdef DO_SOFT_AC3 + copy: snd_cmipci_ac3_copy, + silence: snd_cmipci_ac3_silence, +#endif +}; + +static snd_pcm_ops_t snd_cmipci_capture_spdif_ops = { + open: snd_cmipci_capture_spdif_open, + close: snd_cmipci_capture_spdif_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cmipci_hw_params, + hw_free: snd_cmipci_capture_spdif_hw_free, + prepare: snd_cmipci_capture_spdif_prepare, + trigger: snd_cmipci_capture_trigger, + pointer: snd_cmipci_capture_pointer, +}; + + +/* + */ + +static void snd_cmipci_pcm_free(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_cmipci_pcm_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI DAC/ADC"); + cm->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +static int __devinit snd_cmipci_pcm2_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback2_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI 2nd DAC"); + cm->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops); + + pcm->private_data = cm; + pcm->private_free = snd_cmipci_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "C-Media PCI IEC958"); + cm->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + + return 0; +} + +/* + * mixer interface: + * - CM8338/8738 has a compatible mixer interface with SB16, but + * lack of some elements like tone control, i/o gain and AGC. + * - Access to native registers: + * - A 3D switch + * - Output mute switches + */ + +static void snd_cmipci_mixer_write(cmipci_t *s, unsigned char idx, unsigned char data) +{ + outb(idx, s->iobase + CM_REG_SB16_ADDR); + outb(data, s->iobase + CM_REG_SB16_DATA); +} + +static unsigned char snd_cmipci_mixer_read(cmipci_t *s, unsigned char idx) +{ + unsigned char v; + + outb(idx, s->iobase + CM_REG_SB16_ADDR); + v = inb(s->iobase + CM_REG_SB16_DATA); + return v; +} + +/* + * general mixer element + */ +typedef struct cmipci_sb_reg { + unsigned int left_reg, right_reg; + unsigned int left_shift, right_shift; + unsigned int mask; + unsigned int invert: 1; + unsigned int stereo: 1; +} cmipci_sb_reg_t; + +#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \ + ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23)) + +#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_volume, \ + get: snd_cmipci_get_volume, put: snd_cmipci_put_volume, \ + private_value: COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \ +} + +#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1) +#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0) +#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1) +#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0) + +static void cmipci_sb_reg_decode(cmipci_sb_reg_t *r, unsigned long val) +{ + r->left_reg = val & 0xff; + r->right_reg = (val >> 8) & 0xff; + r->left_shift = (val >> 16) & 0x07; + r->right_shift = (val >> 19) & 0x07; + r->invert = (val >> 22) & 1; + r->stereo = (val >> 23) & 1; + r->mask = (val >> 24) & 0xff; +} + +static int snd_cmipci_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + cmipci_sb_reg_t reg; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = reg.stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = reg.mask; + return 0; +} + +static int snd_cmipci_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[0] = val; + if (reg.stereo) { + val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[1] = val; + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int change; + int left, right, oleft, oright; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + left = ucontrol->value.integer.value[0] & reg.mask; + if (reg.invert) + left = reg.mask - left; + left <<= reg.left_shift; + if (reg.stereo) { + right = ucontrol->value.integer.value[1] & reg.mask; + if (reg.invert) + right = reg.mask - right; + right <<= reg.right_shift; + } else + right = 0; + spin_lock_irqsave(&cm->reg_lock, flags); + oleft = snd_cmipci_mixer_read(cm, reg.left_reg); + left |= oleft & ~(reg.mask << reg.left_shift); + change = left != oleft; + if (reg.stereo) { + if (reg.left_reg != reg.right_reg) { + snd_cmipci_mixer_write(cm, reg.left_reg, left); + oright = snd_cmipci_mixer_read(cm, reg.right_reg); + } else + oright = left; + right |= oright & ~(reg.mask << reg.right_shift); + change |= right != oright; + snd_cmipci_mixer_write(cm, reg.right_reg, right); + } else + snd_cmipci_mixer_write(cm, reg.left_reg, left); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +/* + * input route (left,right) -> (left,right) + */ +#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_input_sw, \ + get: snd_cmipci_get_input_sw, put: snd_cmipci_put_input_sw, \ + private_value: COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \ +} + +static int snd_cmipci_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_cmipci_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int val1, val2; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + val1 = snd_cmipci_mixer_read(cm, reg.left_reg); + val2 = snd_cmipci_mixer_read(cm, reg.right_reg); + spin_unlock_irqrestore(&cm->reg_lock, flags); + ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1; + ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1; + ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1; + ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1; + return 0; +} + +static int snd_cmipci_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + cmipci_sb_reg_t reg; + int change; + int val1, val2, oval1, oval2; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oval1 = snd_cmipci_mixer_read(cm, reg.left_reg); + oval2 = snd_cmipci_mixer_read(cm, reg.right_reg); + val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); + val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); + val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift; + val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift; + val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift; + val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift; + change = val1 != oval1 || val2 != oval2; + snd_cmipci_mixer_write(cm, reg.left_reg, val1); + snd_cmipci_mixer_write(cm, reg.right_reg, val2); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +/* + * native mixer switches/volumes + */ + +#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \ +} + +#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \ +} + +#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \ +} + +#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \ + info: snd_cmipci_info_native_mixer, \ + get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \ + private_value: COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \ +} + +static int snd_cmipci_info_native_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + cmipci_sb_reg_t reg; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = reg.stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = reg.mask; + return 0; + +} + +static int snd_cmipci_get_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + cmipci_sb_reg_t reg; + unsigned long flags; + unsigned char oreg, val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oreg = inb(cm->iobase + reg.left_reg); + val = (oreg >> reg.left_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[0] = val; + if (reg.stereo) { + val = (oreg >> reg.right_shift) & reg.mask; + if (reg.invert) + val = reg.mask - val; + ucontrol->value.integer.value[1] = val; + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_put_native_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + cmipci_sb_reg_t reg; + unsigned long flags; + unsigned char oreg, nreg, val; + + cmipci_sb_reg_decode(®, kcontrol->private_value); + spin_lock_irqsave(&cm->reg_lock, flags); + oreg = inb(cm->iobase + reg.left_reg); + val = ucontrol->value.integer.value[0] & reg.mask; + if (reg.invert) + val = reg.mask - val; + nreg = oreg & ~(reg.mask << reg.left_shift); + nreg |= (val << reg.left_shift); + if (reg.stereo) { + val = ucontrol->value.integer.value[1] & reg.mask; + if (reg.invert) + val = reg.mask - val; + nreg &= ~(reg.mask << reg.right_shift); + nreg |= (val << reg.right_shift); + } + outb(nreg, cm->iobase + reg.left_reg); + spin_unlock_irqrestore(&cm->reg_lock, flags); + return (nreg != oreg); +} + +/* + * special case - check mixer sensitivity + */ +static int snd_cmipci_get_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + //cmipci_t *cm = snd_kcontrol_chip(kcontrol); + return snd_cmipci_get_native_mixer(kcontrol, ucontrol); +} + +static int snd_cmipci_put_native_mixer_sensitive(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + if (cm->mixer_insensitive) { + /* ignored */ + return 0; + } + return snd_cmipci_put_native_mixer(kcontrol, ucontrol); +} + + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = { + CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31), + CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0), + CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31), + //CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1), + { /* switch with sensitivity */ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Playback Switch", + info: snd_cmipci_info_native_mixer, + get: snd_cmipci_get_native_mixer_sensitive, + put: snd_cmipci_put_native_mixer_sensitive, + private_value: COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0), + }, + CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0), + CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31), + CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1), + CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5), + CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31), + CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1), + CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1), + CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31), + CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3), + CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3), + CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), + CMIPCI_SB_SW_MONO("Mic Playback Switch", 0), + CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), + CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), + CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), + CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 1), + CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), + CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), + CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), +}; + +/* + * other switches + */ + +typedef struct snd_cmipci_switch_args { + int reg; /* register index */ + unsigned int mask; /* mask bits */ + unsigned int mask_on; /* mask bits to turn on */ + int is_byte: 1; /* byte access? */ + int ac3_sensitive: 1; /* access forbidden during non-audio operation? */ +} snd_cmipci_switch_args_t; + +static int snd_cmipci_uswitch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int _snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) +{ + unsigned long flags; + unsigned int val; + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + + spin_lock_irqsave(&cm->reg_lock, flags); + if (args->ac3_sensitive && cm->mixer_insensitive) { + ucontrol->value.integer.value[0] = 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; + } + if (args->is_byte) + val = inb(cm->iobase + args->reg); + else + val = snd_cmipci_read(cm, args->reg); + ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0; + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; +} + +static int snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value; + snd_assert(args != NULL, return -EINVAL); + return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args); +} + +static int _snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) +{ + unsigned long flags; + unsigned int val; + int change; + cmipci_t *cm = snd_kcontrol_chip(kcontrol); + + spin_lock_irqsave(&cm->reg_lock, flags); + if (args->ac3_sensitive && cm->mixer_insensitive) { + /* ignored */ + spin_unlock_irqrestore(&cm->reg_lock, flags); + return 0; + } + if (args->is_byte) + val = inb(cm->iobase + args->reg); + else + val = snd_cmipci_read(cm, args->reg); + change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0); + if (change) { + val &= ~args->mask; + if (ucontrol->value.integer.value[0]) + val |= args->mask_on; + else + val |= (args->mask & ~args->mask_on); + if (args->is_byte) + outb((unsigned char)val, cm->iobase + args->reg); + else + snd_cmipci_write(cm, args->reg, val); + } + spin_unlock_irqrestore(&cm->reg_lock, flags); + return change; +} + +static int snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + snd_cmipci_switch_args_t *args = (snd_cmipci_switch_args_t*)kcontrol->private_value; + snd_assert(args != NULL, return -EINVAL); + return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args); +} + +#define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \ +static snd_cmipci_switch_args_t cmipci_switch_arg_##sname = { \ + reg: xreg, \ + mask: xmask, \ + mask_on: xmask_on, \ + is_byte: xis_byte, \ + ac3_sensitive: xac3, \ +} + +#define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \ + DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask, xis_byte, xac3) + +#if 0 /* these will be controlled in pcm device */ +DEFINE_BIT_SWITCH_ARG(spdif_in, CM_REG_FUNCTRL1, CM_SPDF_1, 0); +DEFINE_BIT_SWITCH_ARG(spdif_0, CM_REG_FUNCTRL1, CM_SPDF_0, 0); +DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0); +#endif +DEFINE_BIT_SWITCH_ARG(spdif_in_1_2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1); +DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0); +DEFINE_BIT_SWITCH_ARG(spdif_copyright, CM_REG_LEGACY_CTRL, CM_SPDCOPYRHT, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdif_dac_out, CM_REG_LEGACY_CTRL, CM_DAC2SPDO, 0, 1); +DEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */ +DEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1); +DEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0); +DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_CHFORMAT, CM_SPDIF_INVERSE, 0, 0); +DEFINE_BIT_SWITCH_ARG(spdi_phase2, CM_REG_CHFORMAT, CM_SPDIF_INVERSE2, 0, 0); +#if CM_CH_PLAY == 1 +DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */ +#else +DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0); +#endif +DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0); +DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0); +DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0); +DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); +DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0); + +#define DEFINE_SWITCH(sname, stype, sarg) \ +{ name: sname, \ + iface: stype, \ + info: snd_cmipci_uswitch_info, \ + get: snd_cmipci_uswitch_get, \ + put: snd_cmipci_uswitch_put, \ + private_value: (unsigned long)&cmipci_switch_arg_##sarg,\ +} + +#define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg) +#define DEFINE_MIXER_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_MIXER, sarg) + + +/* + * callbacks for spdif output switch + * needs toggle two registers.. + */ +static int snd_cmipci_spdout_enable_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int changed; + changed = _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); + changed |= _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); + return changed; +} + +static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + cmipci_t *chip = snd_kcontrol_chip(kcontrol); + int changed; + changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); + changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); + if (changed) { + if (ucontrol->value.integer.value[0]) { + if (chip->spdif_playback_avail) + snd_cmipci_set_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + } else { + if (chip->spdif_playback_avail) + snd_cmipci_clear_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); + } + } + chip->spdif_playback_enabled = ucontrol->value.integer.value[0]; + return changed; +} + + +/* both for CM8338/8738 */ +static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac), + DEFINE_MIXER_SWITCH("Four Channel Mode", fourch), + DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear), +}; + +/* only for CM8738 */ +static snd_kcontrol_new_t snd_cmipci_8738_mixer_switches[] __devinitdata = { +#if 0 /* controlled in pcm device */ + DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in), + DEFINE_MIXER_SWITCH("IEC958 Out", spdif_0), + DEFINE_MIXER_SWITCH("IEC958 Out 48KHz", spdo_48k), + DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac), +#endif + // DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable), + { name: "IEC958 Output Switch", + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + info: snd_cmipci_uswitch_info, + get: snd_cmipci_spdout_enable_get, + put: snd_cmipci_spdout_enable_put, + }, + DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid), + DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright), + DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v), + DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop), + DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor), +}; + +/* only for model 033/037 */ +static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out), + DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase), +}; + +/* only for model 039 or later */ +static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = { + DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass), + DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_1_2), + DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2), +}; + +/* card control switches */ +static snd_kcontrol_new_t snd_cmipci_control_switches[] __devinitdata = { + DEFINE_CARD_SWITCH("Joystick", joystick), + DEFINE_CARD_SWITCH("Modem", modem), +}; + + +static int __devinit snd_cmipci_mixer_new(cmipci_t *cm, int pcm_spdif_device) +{ + unsigned long flags; + snd_card_t *card; + snd_kcontrol_new_t *sw; + snd_kcontrol_t *kctl; + int idx, err; + + snd_assert(cm != NULL && cm->card != NULL, return -EINVAL); + + card = cm->card; + + strcpy(card->mixername, "CMedia PCI"); + + spin_lock_irqsave(&cm->reg_lock, flags); + snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ + spin_unlock_irqrestore(&cm->reg_lock, flags); + + for (idx = 0; idx < num_controls(snd_cmipci_mixers); idx++) { + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0) + return err; + } + + /* mixer switches */ + sw = snd_cmipci_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 || + cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) { + sw = snd_cmipci_8738_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_8738_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + cm->spdif_pcm_ctl = kctl; + if (cm->chip_version <= 37) { + sw = snd_cmipci_old_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_old_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + } + } + if (cm->chip_version >= 39) { + sw = snd_cmipci_extra_mixer_switches; + for (idx = 0; idx < num_controls(snd_cmipci_extra_mixer_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + } + + /* card switches */ + sw = snd_cmipci_control_switches; + for (idx = 0; idx < num_controls(snd_cmipci_control_switches); idx++, sw++) { + err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + if (err < 0) + return err; + } + + for (idx = 0; idx < CM_SAVED_MIXERS; idx++) { + snd_ctl_elem_id_t id; + snd_kcontrol_t *ctl; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, cm_saved_mixer[idx].name); + if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL) + cm->mixer_res_ctl[idx] = ctl; + } + + return 0; +} + + +/* + * proc interface + */ + +static void snd_cmipci_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t *buffer) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, entry->private_data, return); + int i; + + snd_iprintf(buffer, "%s\n\n", cm->card->longname); + for (i = 0; i < 0x40; i++) { + int v = inb(cm->iobase + i); + if (i % 4 == 0) + snd_iprintf(buffer, "%02x: ", i); + snd_iprintf(buffer, "%02x", v); + if (i % 4 == 3) + snd_iprintf(buffer, "\n"); + else + snd_iprintf(buffer, " "); + } +} + +static void __devinit snd_cmipci_proc_init(cmipci_t *cm) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(cm->card, "cmipci", cm->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = cm; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_cmipci_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + cm->proc_entry = entry; +} + +static void snd_cmipci_proc_done(cmipci_t *cm) +{ + if (cm->proc_entry) { + snd_info_unregister(cm->proc_entry); + cm->proc_entry = NULL; + } +} + + +static struct pci_device_id snd_cmipci_ids[] __devinitdata = { + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_AL, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + + +/* + * check chip version and capabilities + * driver name is modified according to the chip model + */ +static void __devinit query_chip(cmipci_t *cm) +{ + unsigned int detect; + + /* check reg 0Ch, bit 24-31 */ + detect = snd_cmipci_read(cm, CM_REG_INT_HLDCLR) & CM_CHIP_MASK2; + if (! detect) { + /* check reg 08h, bit 24-28 */ + detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1; + if (! detect) { + cm->chip_version = 33; + cm->max_channels = 2; + cm->can_ac3_sw = 1; + cm->has_dual_dac = 1; + } else { + cm->chip_version = 37; + cm->max_channels = 2; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + } + } else { + /* check reg 0Ch, bit 26 */ + if (detect & CM_CHIP_039) { + cm->chip_version = 39; + if (detect & CM_CHIP_039_6CH) + cm->max_channels = 6; + else + cm->max_channels = 4; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + cm->can_multi_ch = 1; + } else { + cm->chip_version = 55; /* 4 or 6 channels */ + cm->max_channels = 6; + cm->can_ac3_hw = 1; + cm->has_dual_dac = 1; + cm->can_multi_ch = 1; + } + } + + /* added -MCx suffix for chip supporting multi-channels */ + if (cm->can_multi_ch) + sprintf(cm->card->driver + strlen(cm->card->driver), + "-MC%d", cm->max_channels); +} + + +static int snd_cmipci_free(cmipci_t *cm) +{ + snd_cmipci_proc_done(cm); + + if (cm->irq >= 0) { + snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ + snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ + + /* reset mixer */ + snd_cmipci_mixer_write(cm, 0, 0); + + synchronize_irq(); + + free_irq(cm->irq, (void *)cm); + } + if (cm->res_iobase) { + release_resource(cm->res_iobase); + kfree_nocheck(cm->res_iobase); + } + snd_magic_kfree(cm); + return 0; +} + +static int snd_cmipci_dev_free(snd_device_t *device) +{ + cmipci_t *cm = snd_magic_cast(cmipci_t, device->device_data, return -ENXIO); + return snd_cmipci_free(cm); +} + +static int __devinit snd_cmipci_create(snd_card_t *card, + struct pci_dev *pci, + unsigned long iomidi, + unsigned long iosynth, + cmipci_t **rcmipci) +{ + cmipci_t *cm; + int err; + static snd_device_ops_t ops = { + dev_free: snd_cmipci_dev_free, + }; + unsigned int val = 0; + int pcm_index, pcm_spdif_index; + + *rcmipci = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + cm = snd_magic_kcalloc(cmipci_t, 0, GFP_KERNEL); + if (cm == NULL) + return -ENOMEM; + + spin_lock_init(&cm->reg_lock); + init_MUTEX(&cm->open_mutex); + cm->device = pci->device; + cm->card = card; + cm->pci = pci; + cm->irq = -1; + cm->iobase = pci_resource_start(pci, 0); + cm->channel[0].ch = 0; + cm->channel[1].ch = 1; + cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */ + + if ((cm->res_iobase = request_region(cm->iobase, CM_EXTENT_CODEC, card->driver)) == NULL) { + snd_printk("unable to grab ports 0x%lx-0x%lx\n", cm->iobase, cm->iobase + CM_EXTENT_CODEC - 1); + err = -EBUSY; + goto __error; + } + if (request_irq(pci->irq, snd_cmipci_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)cm)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + err = -EBUSY; + goto __error; + } + cm->irq = pci->irq; + + pci_set_master(cm->pci); + + + /* + * check chip version, max channels and capabilities + */ + + cm->chip_version = 0; + cm->max_channels = 2; + + query_chip(cm); + + cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF; + +#if CM_CH_PLAY == 1 + cm->ctrl = CM_CHADC0; /* default FUNCNTRL0 */ +#else + cm->ctrl = CM_CHADC1; /* default FUNCNTRL0 */ +#endif + + /* initialize codec registers */ + snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ + snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ + snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0); + + snd_cmipci_write(cm, CM_REG_CHFORMAT, 0); + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC|CM_N4SPK3D); +#if CM_CH_PLAY == 1 + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); +#else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); +#endif + + /* set MPU address */ + switch (iomidi) { + case 0x320: val = CM_VMPU_320; break; + case 0x310: val = CM_VMPU_310; break; + case 0x300: val = CM_VMPU_300; break; + case 0x330: val = CM_VMPU_330; break; + default: + iomidi = 0; break; + } + if (iomidi > 0) { + snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); + /* enable UART */ + snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN); + } + + /* set FM address */ + val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK; + switch (iosynth) { + case 0x3E8: val |= CM_FMSEL_3E8; break; + case 0x3E0: val |= CM_FMSEL_3E0; break; + case 0x3C8: val |= CM_FMSEL_3C8; break; + case 0x388: val |= CM_FMSEL_388; break; + default: + iosynth = 0; break; + } + if (iosynth > 0) { + snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); + /* enable FM */ + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); + + if (snd_opl3_create(card, iosynth, iosynth + 2, + OPL3_HW_OPL3, 0, &cm->opl3) < 0) { + printk(KERN_ERR "cmipci: no OPL device at 0x%lx\n", iosynth); + } else { + if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0) + printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n"); + } + } + + /* reset mixer */ + snd_cmipci_mixer_write(cm, 0, 0); + + snd_cmipci_proc_init(cm); + + /* create pcm devices */ + pcm_index = pcm_spdif_index = 0; + if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0) + goto __error; + pcm_index++; + if (cm->has_dual_dac) { + if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0) + goto __error; + pcm_index++; + } + if (cm->can_ac3_hw || cm->can_ac3_sw) { + pcm_spdif_index = pcm_index; + if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0) + goto __error; + } + + /* create mixer interface & switches */ + if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0) + goto __error; + + if (iomidi > 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, + iomidi, 0, + cm->irq, 0, &cm->rmidi)) < 0) { + printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi); + } + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) { + snd_cmipci_free(cm); + return err; + } + + *rcmipci = cm; + return 0; + + __error: + snd_cmipci_free(cm); + return err; +} + +/* + */ + +MODULE_DEVICE_TABLE(pci, snd_cmipci_ids); + +static int __devinit snd_cmipci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + cmipci_t *cm; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (! snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_CMEDIA_CM8738: + case PCI_DEVICE_ID_CMEDIA_CM8738B: + strcpy(card->driver, "CMI8738"); + break; + case PCI_DEVICE_ID_CMEDIA_CM8338A: + case PCI_DEVICE_ID_CMEDIA_CM8338B: + strcpy(card->driver, "CMI8338"); + break; + default: + strcpy(card->driver, "CMIPCI"); + break; + } + + if ((err = snd_cmipci_create(card, pci, + snd_mpu_port[dev], + snd_fm_port[dev], + &cm)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "C-Media PCI %s", card->driver); + sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i", + card->shortname, + cm->chip_version, + cm->iobase, + cm->irq); + + //snd_printd("%s is detected\n", card->longname); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; + +} + +static void __devexit snd_cmipci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + + +static struct pci_driver driver = { + name: "C-Media PCI", + id_table: snd_cmipci_ids, + probe: snd_cmipci_probe, + remove: __devexit_p(snd_cmipci_remove), +}; + +static int __init alsa_card_cmipci_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "C-Media PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cmipci_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cmipci_init) +module_exit(alsa_card_cmipci_exit) + +#ifndef MODULE + +/* format is: snd-cmipci=snd_enable,snd_index,snd_id, + snd_mpu_port,snd_fm_port */ + +static int __init alsa_card_cmipci_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cmipci=", alsa_card_cmipci_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/cs4281.c linux-2.4.19-pre5-mjc/sound/pci/cs4281.c --- linux/sound/pci/cs4281.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/cs4281.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1947 @@ +/* + * Driver for Cirrus Logic CS4281 based PCI soundcard + * Copyright (c) by Jaroslav Kysela , + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#ifndef LINUX_2_2 +#if defined(CONFIG_INPUT_GAMEPORT) || defined(CONFIG_INPUT_GAMEPORT_MODULE) +#define HAVE_GAMEPORT_SUPPORT +#endif +#endif + +#ifdef HAVE_GAMEPORT_SUPPORT +#include +#endif + + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Cirrus Logic CS4281"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Cirrus Logic,CS4281}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for CS4281 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for CS4281 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CS4281 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +/* + * + */ + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_4281 +#define PCI_DEVICE_ID_CIRRUS_4281 0x6005 +#endif + +/* + * Direct registers + */ + +#define CS4281_BA0_SIZE 0x1000 +#define CS4281_BA1_SIZE 0x10000 + +/* + * BA0 registers + */ +#define BA0_HISR 0x0000 /* Host Interrupt Status Register */ +#define BA0_HISR_INTENA (1<<31) /* Internal Interrupt Enable Bit */ +#define BA0_HISR_MIDI (1<<22) /* MIDI port interrupt */ +#define BA0_HISR_FIFOI (1<<20) /* FIFO polled interrupt */ +#define BA0_HISR_DMAI (1<<18) /* DMA interrupt (half or end) */ +#define BA0_HISR_FIFO(c) (1<<(12+(c))) /* FIFO channel interupt */ +#define BA0_HISR_DMA(c) (1<<(8+(c))) /* DMA channel interrupt */ +#define BA0_HISR_GPPI (1<<5) /* General Purpose Input (Primary chip) */ +#define BA0_HISR_GPSI (1<<4) /* General Purpose Input (Secondary chip) */ +#define BA0_HISR_GP3I (1<<3) /* GPIO3 pin Interrupt */ +#define BA0_HISR_GP1I (1<<2) /* GPIO1 pin Interrupt */ +#define BA0_HISR_VUPI (1<<1) /* VOLUP pin Interrupt */ +#define BA0_HISR_VDNI (1<<0) /* VOLDN pin Interrupt */ + +#define BA0_HICR 0x0008 /* Host Interrupt Control Register */ +#define BA0_HICR_CHGM (1<<1) /* INTENA Change Mask */ +#define BA0_HICR_IEV (1<<0) /* INTENA Value */ +#define BA0_HICR_EOI (3<<0) /* End of Interrupt command */ + +#define BA0_HIMR 0x000c /* Host Interrupt Mask Register */ + /* Use same contants as for BA0_HISR */ + +#define BA0_IIER 0x0010 /* ISA Interrupt Enable Register */ + +#define BA0_HDSR0 0x00f0 /* Host DMA Engine 0 Status Register */ +#define BA0_HDSR1 0x00f4 /* Host DMA Engine 1 Status Register */ +#define BA0_HDSR2 0x00f8 /* Host DMA Engine 2 Status Register */ +#define BA0_HDSR3 0x00fc /* Host DMA Engine 3 Status Register */ + +#define BA0_HDSR_CH1P (1<<25) /* Channel 1 Pending */ +#define BA0_HDSR_CH2P (1<<24) /* Channel 2 Pending */ +#define BA0_HDSR_DHTC (1<<17) /* DMA Half Terminal Count */ +#define BA0_HDSR_DTC (1<<16) /* DMA Terminal Count */ +#define BA0_HDSR_DRUN (1<<15) /* DMA Running */ +#define BA0_HDSR_RQ (1<<7) /* Pending Request */ + +#define BA0_DCA0 0x0110 /* Host DMA Engine 0 Current Address */ +#define BA0_DCC0 0x0114 /* Host DMA Engine 0 Current Count */ +#define BA0_DBA0 0x0118 /* Host DMA Engine 0 Base Address */ +#define BA0_DBC0 0x011c /* Host DMA Engine 0 Base Count */ +#define BA0_DCA1 0x0120 /* Host DMA Engine 1 Current Address */ +#define BA0_DCC1 0x0124 /* Host DMA Engine 1 Current Count */ +#define BA0_DBA1 0x0128 /* Host DMA Engine 1 Base Address */ +#define BA0_DBC1 0x012c /* Host DMA Engine 1 Base Count */ +#define BA0_DCA2 0x0130 /* Host DMA Engine 2 Current Address */ +#define BA0_DCC2 0x0134 /* Host DMA Engine 2 Current Count */ +#define BA0_DBA2 0x0138 /* Host DMA Engine 2 Base Address */ +#define BA0_DBC2 0x013c /* Host DMA Engine 2 Base Count */ +#define BA0_DCA3 0x0140 /* Host DMA Engine 3 Current Address */ +#define BA0_DCC3 0x0144 /* Host DMA Engine 3 Current Count */ +#define BA0_DBA3 0x0148 /* Host DMA Engine 3 Base Address */ +#define BA0_DBC3 0x014c /* Host DMA Engine 3 Base Count */ +#define BA0_DMR0 0x0150 /* Host DMA Engine 0 Mode */ +#define BA0_DCR0 0x0154 /* Host DMA Engine 0 Command */ +#define BA0_DMR1 0x0158 /* Host DMA Engine 1 Mode */ +#define BA0_DCR1 0x015c /* Host DMA Engine 1 Command */ +#define BA0_DMR2 0x0160 /* Host DMA Engine 2 Mode */ +#define BA0_DCR2 0x0164 /* Host DMA Engine 2 Command */ +#define BA0_DMR3 0x0168 /* Host DMA Engine 3 Mode */ +#define BA0_DCR3 0x016c /* Host DMA Engine 3 Command */ + +#define BA0_DMR_DMA (1<<29) /* Enable DMA mode */ +#define BA0_DMR_POLL (1<<28) /* Enable poll mode */ +#define BA0_DMR_TBC (1<<25) /* Transfer By Channel */ +#define BA0_DMR_CBC (1<<24) /* Count By Channel (0 = frame resolution) */ +#define BA0_DMR_SWAPC (1<<22) /* Swap Left/Right Channels */ +#define BA0_DMR_SIZE20 (1<<20) /* Sample is 20-bit */ +#define BA0_DMR_USIGN (1<<19) /* Unsigned */ +#define BA0_DMR_BEND (1<<18) /* Big Endian */ +#define BA0_DMR_MONO (1<<17) /* Mono */ +#define BA0_DMR_SIZE8 (1<<16) /* Sample is 8-bit */ +#define BA0_DMR_TYPE_DEMAND (0<<6) +#define BA0_DMR_TYPE_SINGLE (1<<6) +#define BA0_DMR_TYPE_BLOCK (2<<6) +#define BA0_DMR_TYPE_CASCADE (3<<6) /* Not supported */ +#define BA0_DMR_DEC (1<<5) /* Access Increment (0) or Decrement (1) */ +#define BA0_DMR_AUTO (1<<4) /* Auto-Initialize */ +#define BA0_DMR_TR_VERIFY (0<<2) /* Verify Transfer */ +#define BA0_DMR_TR_WRITE (1<<2) /* Write Transfer */ +#define BA0_DMR_TR_READ (2<<2) /* Read Transfer */ + +#define BA0_DCR_HTCIE (1<<17) /* Half Terminal Count Interrupt */ +#define BA0_DCR_TCIE (1<<16) /* Terminal Count Interrupt */ +#define BA0_DCR_MSK (1<<0) /* DMA Mask bit */ + +#define BA0_FCR0 0x0180 /* FIFO Control 0 */ +#define BA0_FCR1 0x0184 /* FIFO Control 1 */ +#define BA0_FCR2 0x0188 /* FIFO Control 2 */ +#define BA0_FCR3 0x018c /* FIFO Control 3 */ + +#define BA0_FCR_FEN (1<<31) /* FIFO Enable bit */ +#define BA0_FCR_DACZ (1<<30) /* DAC Zero */ +#define BA0_FCR_PSH (1<<29) /* Previous Sample Hold */ +#define BA0_FCR_RS(x) (((x)&0x1f)<<24) /* Right Slot Mapping */ +#define BA0_FCR_LS(x) (((x)&0x1f)<<16) /* Left Slot Mapping */ +#define BA0_FCR_SZ(x) (((x)&0x7f)<<8) /* FIFO buffer size (in samples) */ +#define BA0_FCR_OF(x) (((x)&0x7f)<<0) /* FIFO starting offset (in samples) */ + +#define BA0_FPDR0 0x0190 /* FIFO Polled Data 0 */ +#define BA0_FPDR1 0x0194 /* FIFO Polled Data 1 */ +#define BA0_FPDR2 0x0198 /* FIFO Polled Data 2 */ +#define BA0_FPDR3 0x019c /* FIFO Polled Data 3 */ + +#define BA0_FCHS 0x020c /* FIFO Channel Status */ +#define BA0_FCHS_RCO(x) (1<<(7+(((x)&3)<<3))) /* Right Channel Out */ +#define BA0_FCHS_LCO(x) (1<<(6+(((x)&3)<<3))) /* Left Channel Out */ +#define BA0_FCHS_MRP(x) (1<<(5+(((x)&3)<<3))) /* Move Read Pointer */ +#define BA0_FCHS_FE(x) (1<<(4+(((x)&3)<<3))) /* FIFO Empty */ +#define BA0_FCHS_FF(x) (1<<(3+(((x)&3)<<3))) /* FIFO Full */ +#define BA0_FCHS_IOR(x) (1<<(2+(((x)&3)<<3))) /* Internal Overrun Flag */ +#define BA0_FCHS_RCI(x) (1<<(1+(((x)&3)<<3))) /* Right Channel In */ +#define BA0_FCHS_LCI(x) (1<<(0+(((x)&3)<<3))) /* Left Channel In */ + +#define BA0_FSIC0 0x0210 /* FIFO Status and Interrupt Control 0 */ +#define BA0_FSIC1 0x0214 /* FIFO Status and Interrupt Control 1 */ +#define BA0_FSIC2 0x0218 /* FIFO Status and Interrupt Control 2 */ +#define BA0_FSIC3 0x021c /* FIFO Status and Interrupt Control 3 */ + +#define BA0_FSIC_FIC(x) (((x)&0x7f)<<24) /* FIFO Interrupt Count */ +#define BA0_FSIC_FORIE (1<<23) /* FIFO OverRun Interrupt Enable */ +#define BA0_FSIC_FURIE (1<<22) /* FIFO UnderRun Interrupt Enable */ +#define BA0_FSIC_FSCIE (1<<16) /* FIFO Sample Count Interrupt Enable */ +#define BA0_FSIC_FSC(x) (((x)&0x7f)<<8) /* FIFO Sample Count */ +#define BA0_FSIC_FOR (1<<7) /* FIFO OverRun */ +#define BA0_FSIC_FUR (1<<6) /* FIFO UnderRun */ +#define BA0_FSIC_FSCR (1<<0) /* FIFO Sample Count Reached */ + +#define BA0_PMCS 0x0344 /* Power Management Control/Status */ +#define BA0_CWPR 0x03e0 /* Configuration Write Protect */ +#define BA0_EPPMC 0x03e4 /* Extended PCI Power Management Control */ +#define BA0_GPIOR 0x03e8 /* GPIO Pin Interface Register */ + +#define BA0_SPMC 0x03ec /* Serial Port Power Management Control (& ASDIN2 enable) */ +#define BA0_SPMC_GIPPEN (1<<15) /* GP INT Primary PME# Enable */ +#define BA0_SPMC_GISPEN (1<<14) /* GP INT Secondary PME# Enable */ +#define BA0_SPMC_EESPD (1<<9) /* EEPROM Serial Port Disable */ +#define BA0_SPMC_ASDI2E (1<<8) /* ASDIN2 Enable */ +#define BA0_SPMC_ASDO (1<<7) /* Asynchronous ASDOUT Assertion */ +#define BA0_SPMC_WUP2 (1<<3) /* Wakeup for Secondary Input */ +#define BA0_SPMC_WUP1 (1<<2) /* Wakeup for Primary Input */ +#define BA0_SPMC_ASYNC (1<<1) /* Asynchronous ASYNC Assertion */ +#define BA0_SPMC_RSTN (1<<0) /* Reset Not! */ + +#define BA0_CFLR 0x03f0 /* Configuration Load Register (EEPROM or BIOS) */ +#define BA0_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ +#define BA0_IISR 0x03f4 /* ISA Interrupt Select */ +#define BA0_TMS 0x03f8 /* Test Register */ +#define BA0_SSVID 0x03fc /* Subsystem ID register */ + +#define BA0_CLKCR1 0x0400 /* Clock Control Register 1 */ +#define BA0_CLKCR1_CLKON (1<<25) /* Read Only */ +#define BA0_CLKCR1_DLLRDY (1<<24) /* DLL Ready */ +#define BA0_CLKCR1_DLLOS (1<<6) /* DLL Output Select */ +#define BA0_CLKCR1_SWCE (1<<5) /* Clock Enable */ +#define BA0_CLKCR1_DLLP (1<<4) /* DLL PowerUp */ +#define BA0_CLKCR1_DLLSS (((x)&3)<<3) /* DLL Source Select */ + +#define BA0_FRR 0x0410 /* Feature Reporting Register */ +#define BA0_SLT12O 0x041c /* Slot 12 GPIO Output Register for AC-Link */ + +#define BA0_SERMC 0x0420 /* Serial Port Master Control */ +#define BA0_SERMC_FCRN (1<<27) /* Force Codec Ready Not */ +#define BA0_SERMC_ODSEN2 (1<<25) /* On-Demand Support Enable ASDIN2 */ +#define BA0_SERMC_ODSEN1 (1<<24) /* On-Demand Support Enable ASDIN1 */ +#define BA0_SERMC_SXLB (1<<21) /* ASDIN2 to ASDOUT Loopback */ +#define BA0_SERMC_SLB (1<<20) /* ASDOUT to ASDIN2 Loopback */ +#define BA0_SERMC_LOVF (1<<19) /* Loopback Output Valid Frame bit */ +#define BA0_SERMC_TCID(x) (((x)&3)<<16) /* Target Secondary Codec ID */ +#define BA0_SERMC_PXLB (5<<1) /* Primary Port External Loopback */ +#define BA0_SERMC_PLB (4<<1) /* Primary Port Internal Loopback */ +#define BA0_SERMC_PTC (7<<1) /* Port Timing Configuration */ +#define BA0_SERMC_PTC_AC97 (1<<1) /* AC97 mode */ +#define BA0_SERMC_MSPE (1<<0) /* Master Serial Port Enable */ + +#define BA0_SERC1 0x0428 /* Serial Port Configuration 1 */ +#define BA0_SERC1_SO1F(x) (((x)&7)>>1) /* Primary Output Port Format */ +#define BA0_SERC1_AC97 (1<<1) +#define BA0_SERC1_SO1EN (1<<0) /* Primary Output Port Enable */ + +#define BA0_SERC2 0x042c /* Serial Port Configuration 2 */ +#define BA0_SERC2_SI1F(x) (((x)&7)>>1) /* Primary Input Port Format */ +#define BA0_SERC2_AC97 (1<<1) +#define BA0_SERC2_SI1EN (1<<0) /* Primary Input Port Enable */ + +#define BA0_SLT12M 0x045c /* Slot 12 Monitor Register for Primary AC-Link */ + +#define BA0_ACCTL 0x0460 /* AC'97 Control */ +#define BA0_ACCTL_TC (1<<6) /* Target Codec */ +#define BA0_ACCTL_CRW (1<<4) /* 0=Write, 1=Read Command */ +#define BA0_ACCTL_DCV (1<<3) /* Dynamic Command Valid */ +#define BA0_ACCTL_VFRM (1<<2) /* Valid Frame */ +#define BA0_ACCTL_ESYN (1<<1) /* Enable Sync */ + +#define BA0_ACSTS 0x0464 /* AC'97 Status */ +#define BA0_ACSTS_VSTS (1<<1) /* Valid Status */ +#define BA0_ACSTS_CRDY (1<<0) /* Codec Ready */ + +#define BA0_ACOSV 0x0468 /* AC'97 Output Slot Valid */ +#define BA0_ACOSV_SLV(x) (1<<((x)-3)) + +#define BA0_ACCAD 0x046c /* AC'97 Command Address */ +#define BA0_ACCDA 0x0470 /* AC'97 Command Data */ + +#define BA0_ACISV 0x0474 /* AC'97 Input Slot Valid */ +#define BA0_ACISV_SLV(x) (1<<((x)-3)) + +#define BA0_ACSAD 0x0478 /* AC'97 Status Address */ +#define BA0_ACSDA 0x047c /* AC'97 Status Data */ +#define BA0_JSPT 0x0480 /* Joystick poll/trigger */ +#define BA0_JSCTL 0x0484 /* Joystick control */ +#define BA0_JSC1 0x0488 /* Joystick control */ +#define BA0_JSC2 0x048c /* Joystick control */ +#define BA0_JSIO 0x04a0 + +#define BA0_MIDCR 0x0490 /* MIDI Control */ +#define BA0_MIDCR_MRST (1<<5) /* Reset MIDI Interface */ +#define BA0_MIDCR_MLB (1<<4) /* MIDI Loop Back Enable */ +#define BA0_MIDCR_TIE (1<<3) /* MIDI Transmuit Interrupt Enable */ +#define BA0_MIDCR_RIE (1<<2) /* MIDI Receive Interrupt Enable */ +#define BA0_MIDCR_RXE (1<<1) /* MIDI Receive Enable */ +#define BA0_MIDCR_TXE (1<<0) /* MIDI Transmit Enable */ + +#define BA0_MIDCMD 0x0494 /* MIDI Command (wo) */ + +#define BA0_MIDSR 0x0494 /* MIDI Status (ro) */ +#define BA0_MIDSR_RDA (1<<15) /* Sticky bit (RBE 1->0) */ +#define BA0_MIDSR_TBE (1<<14) /* Sticky bit (TBF 0->1) */ +#define BA0_MIDSR_RBE (1<<7) /* Receive Buffer Empty */ +#define BA0_MIDSR_TBF (1<<6) /* Transmit Buffer Full */ + +#define BA0_MIDWP 0x0498 /* MIDI Write */ +#define BA0_MIDRP 0x049c /* MIDI Read (ro) */ + +#define BA0_AODSD1 0x04a8 /* AC'97 On-Demand Slot Disable for primary link (ro) */ +#define BA0_AODSD1_NDS(x) (1<<((x)-3)) + +#define BA0_AODSD2 0x04ac /* AC'97 On-Demand Slot Disable for secondary link (ro) */ +#define BA0_AODSD2_NDS(x) (1<<((x)-3)) + +#define BA0_CFGI 0x04b0 /* Configure Interface (EEPROM interface) */ +#define BA0_SLT12M2 0x04dc /* Slot 12 Monitor Register 2 for secondary AC-link */ +#define BA0_ACSTS2 0x04e4 /* AC'97 Status Register 2 */ +#define BA0_ACISV2 0x04f4 /* AC'97 Input Slot Valid Register 2 */ +#define BA0_ACSAD2 0x04f8 /* AC'97 Status Address Register 2 */ +#define BA0_ACSDA2 0x04fc /* AC'97 Status Data Register 2 */ +#define BA0_FMSR 0x0730 /* FM Synthesis Status (ro) */ +#define BA0_B0AP 0x0730 /* FM Bank 0 Address Port (wo) */ +#define BA0_FMDP 0x0734 /* FM Data Port */ +#define BA0_B1AP 0x0738 /* FM Bank 1 Address Port */ +#define BA0_B1DP 0x073c /* FM Bank 1 Data Port */ + +#define BA0_SSPM 0x0740 /* Sound System Power Management */ +#define BA0_SSPM_MIXEN (1<<6) /* Playback SRC + FM/Wavetable MIX */ +#define BA0_SSPM_CSRCEN (1<<5) /* Capture Sample Rate Converter Enable */ +#define BA0_SSPM_PSRCEN (1<<4) /* Playback Sample Rate Converter Enable */ +#define BA0_SSPM_JSEN (1<<3) /* Joystick Enable */ +#define BA0_SSPM_ACLEN (1<<2) /* Serial Port Engine and AC-Link Enable */ +#define BA0_SSPM_FMEN (1<<1) /* FM Synthesis Block Enable */ + +#define BA0_DACSR 0x0744 /* DAC Sample Rate - Playback SRC */ +#define BA0_ADCSR 0x0748 /* ADC Sample Rate - Capture SRC */ + +#define BA0_SSCR 0x074c /* Sound System Control Register */ +#define BA0_SSCR_HVS1 (1<<23) /* Hardwave Volume Step (0=1,1=2) */ +#define BA0_SSCR_MVCS (1<<19) /* Master Volume Codec Select */ +#define BA0_SSCR_MVLD (1<<18) /* Master Volume Line Out Disable */ +#define BA0_SSCR_MVAD (1<<17) /* Master Volume Alternate Out Disable */ +#define BA0_SSCR_MVMD (1<<16) /* Master Volume Mono Out Disable */ +#define BA0_SSCR_XLPSRC (1<<8) /* External SRC Loopback Mode */ +#define BA0_SSCR_LPSRC (1<<7) /* SRC Loopback Mode */ +#define BA0_SSCR_CDTX (1<<5) /* CD Transfer Data */ +#define BA0_SSCR_HVC (1<<3) /* Harware Volume Control Enable */ + +#define BA0_FMLVC 0x0754 /* FM Synthesis Left Volume Control */ +#define BA0_FMRVC 0x0758 /* FM Synthesis Right Volume Control */ +#define BA0_SRCSA 0x075c /* SRC Slot Assignments */ +#define BA0_PPLVC 0x0760 /* PCM Playback Left Volume Control */ +#define BA0_PPRVC 0x0764 /* PCM Playback Right Volume Control */ + +/* Source Slot Numbers - Playback */ +#define SRCSLOT_LEFT_PCM_PLAYBACK 0 +#define SRCSLOT_RIGHT_PCM_PLAYBACK 1 +#define SRCSLOT_PHONE_LINE_1_DAC 2 +#define SRCSLOT_CENTER_PCM_PLAYBACK 3 +#define SRCSLOT_LEFT_SURROUND_PCM_PLAYBACK 4 +#define SRCSLOT_RIGHT_SURROUND_PCM_PLAYBACK 5 +#define SRCSLOT_LFE_PCM_PLAYBACK 6 +#define SRCSLOT_PHONE_LINE_2_DAC 7 +#define SRCSLOT_HEADSET_DAC 8 +#define SRCSLOT_LEFT_WT 29 /* invalid for BA0_SRCSA */ +#define SRCSLOT_RIGHT_WT 30 /* invalid for BA0_SRCSA */ + +/* Source Slot Numbers - Capture */ +#define SRCSLOT_LEFT_PCM_RECORD 10 +#define SRCSLOT_RIGHT_PCM_RECORD 11 +#define SRCSLOT_PHONE_LINE_1_ADC 12 +#define SRCSLOT_MIC_ADC 13 +#define SRCSLOT_PHONE_LINE_2_ADC 17 +#define SRCSLOT_HEADSET_ADC 18 +#define SRCSLOT_SECONDARY_LEFT_PCM_RECORD 20 +#define SRCSLOT_SECONDARY_RIGHT_PCM_RECORD 21 +#define SRCSLOT_SECONDARY_PHONE_LINE_1_ADC 22 +#define SRCSLOT_SECONDARY_MIC_ADC 23 +#define SRCSLOT_SECONDARY_PHONE_LINE_2_ADC 27 +#define SRCSLOT_SECONDARY_HEADSET_ADC 28 + +/* Source Slot Numbers - Others */ +#define SRCSLOT_POWER_DOWN 31 + +/* MIDI modes */ +#define CS4281_MODE_OUTPUT (1<<0) +#define CS4281_MODE_INPUT (1<<1) + +/* joystick bits */ +/* Bits for JSPT */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* Bits for JSCTL */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* Data register pairs masks */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* JS GPIO */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * + */ + +#define chip_t cs4281_t + +typedef struct snd_cs4281 cs4281_t; +typedef struct snd_cs4281_dma cs4281_dma_t; + +struct snd_cs4281_dma { + snd_pcm_substream_t *substream; + unsigned int regDBA; /* offset to DBA register */ + unsigned int regDCA; /* offset to DCA register */ + unsigned int regDBC; /* offset to DBC register */ + unsigned int regDCC; /* offset to DCC register */ + unsigned int regDMR; /* offset to DMR register */ + unsigned int regDCR; /* offset to DCR register */ + unsigned int regHDSR; /* offset to HDSR register */ + unsigned int regFCR; /* offset to FCR register */ + unsigned int regFSIC; /* offset to FSIC register */ + unsigned int valDMR; /* DMA mode */ + unsigned int valDCR; /* DMA command */ + unsigned int valFCR; /* FIFO control */ + unsigned int fifo_offset; /* FIFO offset within BA1 */ + unsigned char left_slot; /* FIFO left slot */ + unsigned char right_slot; /* FIFO right slot */ + int frag; /* period number */ +}; + +#ifdef HAVE_GAMEPORT_SUPPORT +typedef struct snd_cs4281_gameport { + struct gameport info; + cs4281_t *chip; +} cs4281_gameport_t; +#endif + +struct snd_cs4281 { + int irq; + + unsigned long ba0; /* virtual (accessible) address */ + unsigned long ba1; /* virtual (accessible) address */ + unsigned long ba0_addr; + unsigned long ba1_addr; + struct resource *ba0_res; + struct resource *ba1_res; + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + cs4281_dma_t dma[4]; + + unsigned char src_left_play_slot; + unsigned char src_right_play_slot; + unsigned char src_left_rec_slot; + unsigned char src_right_rec_slot; + + unsigned int spurious_dhtc_irq; + unsigned int spurious_dtc_irq; + + void *proc_entry_BA0; + void *proc_entry_BA1; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + snd_info_entry_t *proc_entry; + +#ifdef HAVE_GAMEPORT_SUPPORT + cs4281_gameport_t *gameport; +#endif +}; + +static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_cs4281_ids[] __devinitdata = { + { 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4281 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_cs4281_ids); + +/* + * constants + */ + +#define CS4281_FIFO_SIZE 32 + +/* + * common I/O routines + */ + +static void snd_cs4281_delay(unsigned int delay) +{ + if (delay > 999) { + signed long end_time; + delay = (delay * HZ) / 1000000; + if (delay < 1) + delay = 1; + end_time = jiffies + delay; + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + } else { + udelay(delay); + } +} + +static inline void snd_cs4281_pokeBA0(cs4281_t *chip, unsigned long offset, unsigned int val) +{ + writel(val, chip->ba0 + offset); +} + +static inline unsigned int snd_cs4281_peekBA0(cs4281_t *chip, unsigned long offset) +{ + return readl(chip->ba0 + offset); +} + +static void snd_cs4281_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + int count; + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs4281_pokeBA0(chip, BA0_ACCDA, val); + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM | + BA0_ACCTL_ESYN); + for (count = 0; count < 2000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) { + return; + } + } + snd_printk("AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val); +} + +static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO); + int count; + unsigned short result; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + snd_cs4281_peekBA0(chip, BA0_ACSDA); + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0); + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW | + BA0_ACCTL_VFRM | BA0_ACCTL_ESYN); + + + /* + * Wait for the read to occur. + */ + for (count = 0; count < 500; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) + goto __ok1; + } + + snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); + result = 0xffff; + goto __end; + + __ok1: + /* + * Wait for the valid status bit to go active. + */ + for (count = 0; count < 100; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_VSTS) + goto __ok2; + udelay(10); + } + + snd_printk("AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg); + result = 0xffff; + goto __end; + + __ok2: + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ + result = snd_cs4281_peekBA0(chip, BA0_ACSDA); + + __end: + return result; +} + +/* + * PCM part + */ + +static int snd_cs4281_trigger(snd_pcm_substream_t *substream, int cmd) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dma->valDCR |= BA0_DCR_MSK; + dma->valFCR |= BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dma->valDCR &= ~BA0_DCR_MSK; + dma->valFCR &= ~BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_START: + snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA); + dma->valDMR |= BA0_DMR_DMA; + dma->valDCR &= ~BA0_DCR_MSK; + dma->valFCR |= BA0_FCR_FEN; + break; + case SNDRV_PCM_TRIGGER_STOP: + dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL); + dma->valDCR |= BA0_DCR_MSK; + dma->valFCR &= ~BA0_FCR_FEN; + break; + default: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -EINVAL; + } + snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR); + snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR); + snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate) +{ + unsigned int val = ~0; + + if (real_rate) + *real_rate = rate; + /* special "hardcoded" rates */ + switch (rate) { + case 8000: return 5; + case 11025: return 4; + case 16000: return 3; + case 22050: return 2; + case 44100: return 1; + case 48000: return 0; + default: + goto __variable; + } + __variable: + val = 1536000 / rate; + if (real_rate) + *real_rate = 1536000 / val; + return val; +} + +static void snd_cs4281_mode(cs4281_t *chip, cs4281_dma_t *dma, snd_pcm_runtime_t *runtime, int capture, int src) +{ + int rec_mono; + + dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO | + (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ); + if (runtime->channels == 1) + dma->valDMR |= BA0_DMR_MONO; + if (snd_pcm_format_unsigned(runtime->format) > 0) + dma->valDMR |= BA0_DMR_USIGN; + if (snd_pcm_format_big_endian(runtime->format) > 0) + dma->valDMR |= BA0_DMR_BEND; + switch (snd_pcm_format_width(runtime->format)) { + case 8: dma->valDMR |= BA0_DMR_SIZE8; + if (runtime->channels == 1) + dma->valDMR |= BA0_DMR_SWAPC; + break; + case 32: dma->valDMR |= BA0_DMR_SIZE20; break; + } + dma->frag = 0; /* for workaround */ + dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK; + if (runtime->buffer_size != runtime->period_size) + dma->valDCR |= BA0_DCR_HTCIE; + /* Initialize DMA */ + snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr); + snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1); + rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO; + snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | + (chip->src_right_play_slot << 8) | + (chip->src_left_rec_slot << 16) | + ((rec_mono ? 31 : chip->src_right_rec_slot) << 24)); + if (!src) + goto __skip_src; + if (!capture) { + if (dma->left_slot == chip->src_left_play_slot) { + unsigned int val = snd_cs4281_rate(runtime->rate, NULL); + snd_assert(dma->right_slot == chip->src_right_play_slot, ); + snd_cs4281_pokeBA0(chip, BA0_DACSR, val); + } + } else { + if (dma->left_slot == chip->src_left_rec_slot) { + unsigned int val = snd_cs4281_rate(runtime->rate, NULL); + snd_assert(dma->right_slot == chip->src_right_rec_slot, ); + snd_cs4281_pokeBA0(chip, BA0_ADCSR, val); + } + } + __skip_src: + /* Initialize FIFO */ + dma->valFCR = BA0_FCR_LS(dma->left_slot) | + BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) | + BA0_FCR_SZ(CS4281_FIFO_SIZE) | + BA0_FCR_OF(dma->fifo_offset); + snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0)); + /* Clear FIFO Status and Interrupt Control Register */ + snd_cs4281_pokeBA0(chip, dma->regFSIC, 0); +} + +static int snd_cs4281_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_cs4281_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_cs4281_playback_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4281_mode(chip, dma, runtime, 0, 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_capture_prepare(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs4281_mode(chip, dma, runtime, 1, 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_cs4281_pointer(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; + cs4281_t *chip = snd_pcm_substream_chip(substream); + + // printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies); + return runtime->buffer_size - + snd_cs4281_peekBA0(chip, dma->regDCC); +} + +static snd_pcm_hardware_t snd_cs4281_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 2, + fifo_size: CS4281_FIFO_SIZE, +}; + +static snd_pcm_hardware_t snd_cs4281_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE | SNDRV_PCM_FMTBIT_S32_BE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: CS4281_FIFO_SIZE, +}; + +static int snd_cs4281_playback_open(snd_pcm_substream_t * substream) +{ + cs4281_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma; + + dma = &chip->dma[0]; + dma->substream = substream; + dma->left_slot = 0; + dma->right_slot = 1; + runtime->private_data = dma; + runtime->hw = snd_cs4281_playback; + snd_pcm_set_sync(substream); + /* should be detected from the AC'97 layer, but it seems + that although CS4297A rev B reports 18-bit ADC resolution, + samples are 20-bit */ + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); + return 0; +} + +static int snd_cs4281_capture_open(snd_pcm_substream_t * substream) +{ + cs4281_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + cs4281_dma_t *dma; + + dma = &chip->dma[1]; + dma->substream = substream; + dma->left_slot = 10; + dma->right_slot = 11; + runtime->private_data = dma; + runtime->hw = snd_cs4281_capture; + snd_pcm_set_sync(substream); + /* should be detected from the AC'97 layer, but it seems + that although CS4297A rev B reports 18-bit ADC resolution, + samples are 20-bit */ + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); + return 0; +} + +static int snd_cs4281_playback_close(snd_pcm_substream_t * substream) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + + dma->substream = NULL; + return 0; +} + +static int snd_cs4281_capture_close(snd_pcm_substream_t * substream) +{ + cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; + + dma->substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_cs4281_playback_ops = { + open: snd_cs4281_playback_open, + close: snd_cs4281_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4281_hw_params, + hw_free: snd_cs4281_hw_free, + prepare: snd_cs4281_playback_prepare, + trigger: snd_cs4281_trigger, + pointer: snd_cs4281_pointer, +}; + +static snd_pcm_ops_t snd_cs4281_capture_ops = { + open: snd_cs4281_capture_open, + close: snd_cs4281_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs4281_hw_params, + hw_free: snd_cs4281_hw_free, + prepare: snd_cs4281_capture_prepare, + trigger: snd_cs4281_trigger, + pointer: snd_cs4281_pointer, +}; + +static void snd_cs4281_pcm_free(snd_pcm_t *pcm) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_cs4281_pcm(cs4281_t * chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4281_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4281_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_cs4281_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "CS4281"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer section + */ + +static void snd_cs4281_mixer_free_ac97(ac97_t *ac97) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static int __devinit snd_cs4281_mixer(cs4281_t * chip) +{ + snd_card_t *card = chip->card; + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_cs4281_ac97_write; + ac97.read = snd_cs4281_ac97_read; + ac97.private_data = chip; + ac97.private_free = snd_cs4281_mixer_free_ac97; + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97)) < 0) + return err; + return 0; +} + +/* + + */ + +static void snd_cs4281_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return); + + snd_iprintf(buffer, "Cirrus Logic CS4281\n\n"); + snd_iprintf(buffer, "Spurious half IRQs : %u\n", chip->spurious_dhtc_irq); + snd_iprintf(buffer, "Spurious end IRQs : %u\n", chip->spurious_dtc_irq); +} + +static long snd_cs4281_BA0_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + + size = count; + if (file->f_pos + size > CS4281_BA0_SIZE) + size = (long)CS4281_BA0_SIZE - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = chip->ba0 + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static long snd_cs4281_BA1_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + + size = count; + if (file->f_pos + size > CS4281_BA1_SIZE) + size = (long)CS4281_BA1_SIZE - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = chip->ba1 + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = { + read: snd_cs4281_BA0_read, +}; + +static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = { + read: snd_cs4281_BA1_read, +}; + +static void __devinit snd_cs4281_proc_init(cs4281_t * chip) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(chip->card, "cs4281", chip->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_cs4281_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + chip->proc_entry = entry; + if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA0", chip->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs4281_proc_ops_BA0; + entry->size = CS4281_BA0_SIZE; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + chip->proc_entry_BA0 = entry; + if ((entry = snd_info_create_card_entry(chip->card, "cs4281_BA1", chip->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs4281_proc_ops_BA1; + entry->size = CS4281_BA1_SIZE; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + chip->proc_entry_BA1 = entry; +} + +static void snd_cs4281_proc_done(cs4281_t * chip) +{ + if (chip->proc_entry_BA1) { + snd_info_unregister(chip->proc_entry_BA1); + chip->proc_entry_BA1 = NULL; + } + if (chip->proc_entry_BA0) { + snd_info_unregister(chip->proc_entry_BA0); + chip->proc_entry_BA0 = NULL; + } + if (chip->proc_entry) { + snd_info_unregister(chip->proc_entry); + chip->proc_entry = NULL; + } +} + +/* + + */ + +static int snd_cs4281_free(cs4281_t *chip) +{ +#ifdef HAVE_GAMEPORT_SUPPORT + if (chip->gameport) { + gameport_unregister_port(&chip->gameport->info); + kfree(chip->gameport); + } +#endif + snd_cs4281_proc_done(chip); + synchronize_irq(); + + /* Mask interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff); + /* Stop the DLL Clock logic. */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); + /* Sound System Power Management - Turn Everything OFF */ + snd_cs4281_pokeBA0(chip, BA0_SSPM, 0); + /* PCI interface - D3 state */ + pci_set_power_state(chip->pci, 3); + + if (chip->ba0) + iounmap((void *) chip->ba0); + if (chip->ba1) + iounmap((void *) chip->ba1); + if (chip->ba0_res) { + release_resource(chip->ba0_res); + kfree_nocheck(chip->ba0_res); + } + if (chip->ba1_res) { + release_resource(chip->ba1_res); + kfree_nocheck(chip->ba1_res); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs4281_dev_free(snd_device_t *device) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, device->device_data, return -ENXIO); + return snd_cs4281_free(chip); +} + +static int __devinit snd_cs4281_create(snd_card_t * card, + struct pci_dev *pci, + cs4281_t ** rchip) +{ + cs4281_t *chip; + unsigned int tmp; + signed long end_time; + int err; + static snd_device_ops_t ops = { + dev_free: snd_cs4281_dev_free, + }; + + *rchip = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + chip = snd_magic_kcalloc(cs4281_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->ba0_addr = pci_resource_start(pci, 0); + chip->ba1_addr = pci_resource_start(pci, 1); + pci_set_master(pci); + + if ((chip->ba0_res = request_mem_region(chip->ba0_addr, CS4281_BA0_SIZE, "CS4281 BA0")) == NULL) { + snd_cs4281_free(chip); + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->ba0_addr, chip->ba0_addr + CS4281_BA0_SIZE - 1); + return -ENOMEM; + } + if ((chip->ba1_res = request_mem_region(chip->ba1_addr, CS4281_BA1_SIZE, "CS4281 BA1")) == NULL) { + snd_cs4281_free(chip); + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->ba1_addr, chip->ba1_addr + CS4281_BA1_SIZE - 1); + return -ENOMEM; + } + if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", (void *)chip)) { + snd_cs4281_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -ENOMEM; + } + chip->irq = pci->irq; + + chip->ba0 = (unsigned long) ioremap_nocache(chip->ba0_addr, CS4281_BA0_SIZE); + chip->ba1 = (unsigned long) ioremap_nocache(chip->ba1_addr, CS4281_BA1_SIZE); + if (!chip->ba0 || !chip->ba1) { + snd_cs4281_free(chip); + return -ENOMEM; + } + + tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); + if (tmp != BA0_CFLR_DEFAULT) { + snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT); + tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); + if (tmp != BA0_CFLR_DEFAULT) { + snd_printk("CFLR setup failed (0x%x)\n", tmp); + snd_cs4281_free(chip); + return -EIO; + } + } + + /* Set the 'Configuration Write Protect' register + * to 4281h. Allows vendor-defined configuration + * space between 0e4h and 0ffh to be written. */ + snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281); + + if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) { + snd_printk("SERC1 AC'97 check failed (0x%x)\n", tmp); + snd_cs4281_free(chip); + return -EIO; + } + if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) { + snd_printk("SERC2 AC'97 check failed (0x%x)\n", tmp); + snd_cs4281_free(chip); + return -EIO; + } + + /* Sound System Power Management */ + snd_cs4281_pokeBA0(chip, BA0_SSPM, BA0_SSPM_MIXEN | BA0_SSPM_CSRCEN | + BA0_SSPM_PSRCEN | BA0_SSPM_JSEN | + BA0_SSPM_ACLEN | BA0_SSPM_FMEN); + + /* Serial Port Power Management */ + /* Blast the clock control register to zero so that the + * PLL starts out in a known state, and blast the master serial + * port control register to zero so that the serial ports also + * start out in a known state. */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); + snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); + + /* Make ESYN go to zero to turn off + * the Sync pulse on the AC97 link. */ + snd_cs4281_pokeBA0(chip, BA0_ACCTL, 0); + udelay(50); + + /* Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS4281 that uses the ARST# line + * for a reset. */ + snd_cs4281_pokeBA0(chip, BA0_SPMC, 0); + udelay(50); + snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN); + snd_cs4281_delay(50000); + + /* + * Set the serial port timing configuration. + */ + snd_cs4281_pokeBA0(chip, BA0_SERMC, BA0_SERMC_TCID(1) | BA0_SERMC_PTC_AC97 | BA0_SERMC_MSPE); + + /* + * Start the DLL Clock logic. + */ + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP); + snd_cs4281_delay(50000); + snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP); + + /* + * Wait for the DLL ready signal from the clock logic. + */ + end_time = (jiffies + HZ / 4) + 1; + do { + /* + * Read the AC97 status register to see if we've seen a CODEC + * signal from the AC97 codec. + */ + if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY) + goto __ok0; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + snd_printk("DLLRDY not seen\n"); + snd_cs4281_free(chip); + return -EIO; + + __ok0: + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_ESYN); + + /* + * Wait for the codec ready signal from the AC97 codec. + */ + end_time = (jiffies + (3 * HZ) / 4) + 1; + do { + /* + * Read the AC97 status register to see if we've seen a CODEC + * signal from the AC97 codec. + */ + if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY) + goto __ok1; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + snd_printk("never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS)); + snd_cs4281_free(chip); + return -EIO; + + __ok1: + + /* + * Assert the valid frame signal so that we can start sending commands + * to the AC97 codec. + */ + + snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_VFRM | BA0_ACCTL_ESYN); + + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the codec is pumping ADC data across the AC-link. + */ + + end_time = (jiffies + (5 * HZ) / 4) + 1; + do { + /* + * Read the input slot valid register and see if input slots 3 + * 4 are valid yet. + */ + if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) + goto __ok2; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + snd_printk("never read ISV3 and ISV4 from AC'97\n"); + snd_cs4281_free(chip); + return -EIO; + + __ok2: + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 codec. + */ + snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4)); + + /* + * Initialize DMA structures + */ + for (tmp = 0; tmp < 4; tmp++) { + cs4281_dma_t *dma = &chip->dma[tmp]; + dma->regDBA = BA0_DBA0 + (tmp * 0x10); + dma->regDCA = BA0_DCA0 + (tmp * 0x10); + dma->regDBC = BA0_DBC0 + (tmp * 0x10); + dma->regDCC = BA0_DCC0 + (tmp * 0x10); + dma->regDMR = BA0_DMR0 + (tmp * 8); + dma->regDCR = BA0_DCR0 + (tmp * 8); + dma->regHDSR = BA0_HDSR0 + (tmp * 4); + dma->regFCR = BA0_FCR0 + (tmp * 4); + dma->regFSIC = BA0_FSIC0 + (tmp * 4); + dma->fifo_offset = tmp * CS4281_FIFO_SIZE; + snd_cs4281_pokeBA0(chip, dma->regFCR, + BA0_FCR_LS(31) | + BA0_FCR_RS(31) | + BA0_FCR_SZ(CS4281_FIFO_SIZE) | + BA0_FCR_OF(dma->fifo_offset)); + } + + chip->src_left_play_slot = 0; /* AC'97 left PCM playback (3) */ + chip->src_right_play_slot = 1; /* AC'97 right PCM playback (4) */ + chip->src_left_rec_slot = 10; /* AC'97 left PCM record (3) */ + chip->src_right_rec_slot = 11; /* AC'97 right PCM record (4) */ + + /* Initialize digital volume */ + snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0); + snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0); + + snd_cs4281_proc_init(chip); + + /* Enable IRQs */ + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); + /* Unmask interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff & ~( + BA0_HISR_MIDI | + BA0_HISR_DMAI | + BA0_HISR_DMA(0) | + BA0_HISR_DMA(1) | + BA0_HISR_DMA(2) | + BA0_HISR_DMA(3))); + synchronize_irq(); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs4281_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +/* + * MIDI section + */ + +static void snd_cs4281_midi_reset(cs4281_t *chip) +{ + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr | BA0_MIDCR_MRST); + udelay(100); + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); +} + +static int snd_cs4281_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr |= BA0_MIDCR_RXE; + chip->midi_input = substream; + if (!(chip->uartm & CS4281_MODE_OUTPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE); + chip->midi_input = NULL; + if (!(chip->uartm & CS4281_MODE_OUTPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS4281_MODE_INPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS4281_MODE_OUTPUT; + chip->midcr |= BA0_MIDCR_TXE; + chip->midi_input = substream; + if (!(chip->uartm & CS4281_MODE_INPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs4281_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE); + chip->midi_output = NULL; + if (!(chip->uartm & CS4281_MODE_INPUT)) { + snd_cs4281_midi_reset(chip); + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS4281_MODE_OUTPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static void snd_cs4281_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & BA0_MIDCR_RIE) == 0) { + chip->midcr |= BA0_MIDCR_RIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & BA0_MIDCR_RIE) { + chip->midcr &= ~BA0_MIDCR_RIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs4281_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & BA0_MIDCR_TIE) == 0) { + chip->midcr |= BA0_MIDCR_TIE; + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while ((chip->midcr & BA0_MIDCR_TIE) && + (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->midcr &= ~BA0_MIDCR_TIE; + } else { + snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte); + } + } + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & BA0_MIDCR_TIE) { + chip->midcr &= ~BA0_MIDCR_TIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_cs4281_midi_output = +{ + open: snd_cs4281_midi_output_open, + close: snd_cs4281_midi_output_close, + trigger: snd_cs4281_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_cs4281_midi_input = +{ + open: snd_cs4281_midi_input_open, + close: snd_cs4281_midi_input_close, + trigger: snd_cs4281_midi_input_trigger, +}; + +static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "CS4281"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +/* + * Interrupt handler + */ + +static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs4281_t *chip = snd_magic_cast(cs4281_t, dev_id, return); + unsigned int status, dma, val; + cs4281_dma_t *cdma; + + if (chip == NULL) + return; + status = snd_cs4281_peekBA0(chip, BA0_HISR); + if ((status & 0x7fffffff) == 0) { + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); + return; + } + + if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) { + for (dma = 0; dma < 4; dma++) + if (status & BA0_HISR_DMA(dma)) { + cdma = &chip->dma[dma]; + spin_lock(&chip->reg_lock); + /* ack DMA IRQ */ + val = snd_cs4281_peekBA0(chip, cdma->regHDSR); + /* workaround, sometimes CS4281 acknowledges */ + /* end or middle transfer position twice */ + cdma->frag++; + if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) { + cdma->frag--; + chip->spurious_dhtc_irq++; + spin_unlock(&chip->reg_lock); + continue; + } + if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) { + cdma->frag--; + chip->spurious_dtc_irq++; + spin_unlock(&chip->reg_lock); + continue; + } + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(cdma->substream); + } + } + + if ((status & BA0_HISR_MIDI) && chip->rmidi) { + unsigned char c; + + spin_lock(&chip->reg_lock); + while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) { + c = snd_cs4281_peekBA0(chip, BA0_MIDRP); + if ((chip->midcr & BA0_MIDCR_RIE) == 0) + continue; + snd_rawmidi_receive(chip->midi_input, &c, 1); + } + while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { + if ((chip->midcr & BA0_MIDCR_TIE) == 0) + break; + if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { + chip->midcr &= ~BA0_MIDCR_TIE; + snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); + break; + } + snd_cs4281_pokeBA0(chip, BA0_MIDWP, c); + } + spin_unlock(&chip->reg_lock); + } + + /* EOI to the PCI part... reenables interrupts */ + snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); +} + +#ifdef HAVE_GAMEPORT_SUPPORT +/* + * joystick support + */ +static void snd_cs4281_gameport_trigger(struct gameport *gameport) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + snd_assert(gp, return); + chip = snd_magic_cast(cs4281_t, gp->chip, return); + snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff); +} + +static unsigned char snd_cs4281_gameport_read(struct gameport *gameport) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + snd_assert(gp, return 0); + chip = snd_magic_cast(cs4281_t, gp->chip, return 0); + return snd_cs4281_peekBA0(chip, BA0_JSPT); +} + +#ifdef COOKED_MODE +static int snd_cs4281_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; + cs4281_t *chip; + unsigned js1, js2, jst; + + snd_assert(gp, return); + chip = snd_magic_cast(cs4281_t, gp->chip, return); + + js1 = snd_cs4281_peekBA0(chip, BA0_JSC1); + js2 = snd_cs4281_peekBA0(chip, BA0_JSC2); + jst = snd_cs4281_peekBA0(chip, BA0_JSPT); + + *buttons = (~jst >> 4) & 0x0F; + + axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; + axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; + axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; + axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; + + for(jst=0;jst<4;++jst) + if(axes[jst]==0xFFFF) axes[jst] = -1; + return 0; +} +#endif + +static int snd_cs4281_gameport_open(struct gameport *gameport, int mode) +{ + switch (mode) { +#ifdef COOKED_MODE + case GAMEPORT_MODE_COOKED: + return 0; +#endif + case GAMEPORT_MODE_RAW: + return 0; + default: + return -1; + } + return 0; +} + +static void __devinit snd_cs4281_gameport(cs4281_t *chip) +{ + cs4281_gameport_t *gp; + gp = kmalloc(sizeof(*gp), GFP_KERNEL); + if (! gp) { + snd_printk("cannot allocate gameport area\n"); + return; + } + memset(gp, 0, sizeof(*gp)); + gp->info.open = snd_cs4281_gameport_open; + gp->info.read = snd_cs4281_gameport_read; + gp->info.trigger = snd_cs4281_gameport_trigger; +#ifdef COOKED_MODE + gp->info.cooked_read = snd_cs4281_gameport_cooked_read; +#endif + gp->chip = chip; + chip->gameport = gp; + + snd_cs4281_pokeBA0(chip, BA0_JSIO, 0xFF); // ? + snd_cs4281_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); + gameport_register_port(&gp->info); +} + +#endif /* HAVE_GAMEPORT_SUPPORT */ + + +static int __devinit snd_cs4281_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + cs4281_t *chip; + opl3_t *opl3; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_cs4281_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_cs4281_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_create(card, + (chip->ba0 + BA0_B0AP) >> 2, + (chip->ba0 + BA0_B1AP) >> 2, + OPL3_HW_OPL3_CS4281, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef HAVE_GAMEPORT_SUPPORT + snd_cs4281_gameport(chip); +#endif + strcpy(card->driver, "CS4281"); + strcpy(card->shortname, "Cirrus Logic CS4281"); + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, + chip->ba0_addr, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_cs4281_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "CS4281", + id_table: snd_cs4281_ids, + probe: snd_cs4281_probe, + remove: __devexit_p(snd_cs4281_remove), +}; + +static int __init alsa_card_cs4281_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "CS4281 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cs4281_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cs4281_init) +module_exit(alsa_card_cs4281_exit) + +#ifndef MODULE + +/* format is: snd-cs4281=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_cs4281_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs4281=", alsa_card_cs4281_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/cs46xx/Makefile linux-2.4.19-pre5-mjc/sound/pci/cs46xx/Makefile --- linux/sound/pci/cs46xx/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _cs46xx.o + +list-multi := snd-cs46xx.o + +snd-cs46xx-objs := cs46xx.o cs46xx_lib.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_CS46XX) += snd-cs46xx.o + +include $(TOPDIR)/Rules.make + +snd-cs46xx.o: $(snd-cs46xx-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-cs46xx-objs) diff -Nru linux/sound/pci/cs46xx/cs46xx.c linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx.c --- linux/sound/pci/cs46xx/cs46xx.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,220 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - sometimes the sound is metallic and sibilant, unloading and + reloading the module may solve this. +*/ + +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Cirrus Logic,Sound Fusion (CS4280)}," + "{Cirrus Logic,Sound Fusion (CS4610)}," + "{Cirrus Logic,Sound Fusion (CS4612)}," + "{Cirrus Logic,Sound Fusion (CS4615)}," + "{Cirrus Logic,Sound Fusion (CS4622)}," + "{Cirrus Logic,Sound Fusion (CS4624)}," + "{Cirrus Logic,Sound Fusion (CS4630)}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the CS46xx soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the CS46xx soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable CS46xx soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_external_amp, "Force to enable external amplifer."); +MODULE_PARM_SYNTAX(snd_external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(snd_thinkpad, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_thinkpad, "Force to enable Thinkpad's CLKRUN control."); +MODULE_PARM_SYNTAX(snd_thinkpad, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +static struct pci_device_id snd_cs46xx_ids[] __devinitdata = { + { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4280 */ + { 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4612 */ + { 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4615 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids); + +static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + cs46xx_t *chip; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if ((err = snd_cs46xx_create(card, pci, + snd_external_amp[dev], snd_thinkpad[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + strcpy(card->driver, "CS46xx"); + strcpy(card->shortname, "Sound Fusion CS46xx"); + sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", + card->shortname, + chip->ba0_addr, + chip->ba1_addr, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_cs46xx_suspend(struct pci_dev *pci, u32 state) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO); + snd_cs46xx_suspend(chip); + return 0; +} +static int snd_card_cs46xx_resume(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return -ENXIO); + snd_cs46xx_resume(chip); + return 0; +} +#else +static void snd_card_cs46xx_suspend(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + snd_cs46xx_suspend(chip); +} +static void snd_card_cs46xx_resume(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + snd_cs46xx_resume(chip); +} +#endif +#endif + +static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Sound Fusion CS46xx", + id_table: snd_cs46xx_ids, + probe: snd_card_cs46xx_probe, + remove: __devexit_p(snd_card_cs46xx_remove), +#ifdef CONFIG_PM + suspend: snd_card_cs46xx_suspend, + resume: snd_card_cs46xx_resume, +#endif +}; + +static int __init alsa_card_cs46xx_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Sound Fusion CS46xx soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_cs46xx_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cs46xx_init) +module_exit(alsa_card_cs46xx_exit) + +#ifndef MODULE + +/* format is: snd-cs46xx=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_cs46xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-cs46xx=", alsa_card_cs46xx_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/cs46xx/cs46xx_image.h linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_image.h --- linux/sound/pci/cs46xx/cs46xx_image.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_image.h Mon Apr 8 22:31:24 2002 @@ -0,0 +1,3459 @@ +struct BA1struct BA1Struct = { +{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }}, +{0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00200040,0x00008010,0x00000000, +0x00000000,0x80000001,0x00000001,0x00060000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00900080,0x00000173,0x00000000, +0x00000000,0x00000010,0x00800000,0x00900000, +0xf2c0000f,0x00000200,0x00000000,0x00010600, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000163,0x330300c2, +0x06000000,0x00000000,0x80008000,0x80008000, +0x3fc0000f,0x00000301,0x00010400,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00b00000,0x00d0806d,0x330480c3, +0x04800000,0x00000001,0x00800001,0x0000ffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x066a0600,0x06350070,0x0000929d,0x929d929d, +0x00000000,0x0000735a,0x00000600,0x00000000, +0x929d735a,0x8734abfe,0x00010000,0x735a735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000804f,0x000000c3, +0x05000000,0x00a00010,0x00000000,0x80008000, +0x00000000,0x00000000,0x00000700,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000080,0x00a00000,0x0000809a,0x000000c2, +0x07400000,0x00000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x000107a0, +0x00c80028,0x000000c2,0x06800000,0x00000000, +0x06e00080,0x00300000,0x000080bb,0x000000c9, +0x07a00000,0x04000000,0x80008000,0xffffffff, +0x00c80028,0x00005555,0x00000000,0x00000780, +0x00c80028,0x000000c5,0xff800000,0x00000000, +0x00640080,0x00c00000,0x00008197,0x000000c9, +0x07800000,0x04000000,0x80008000,0xffffffff, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000805e,0x000000c1, +0x00000000,0x00800000,0x80008000,0x80008000, +0x00020000,0x0000ffff,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x929d0600,0x929d929d,0x929d929d,0x929d0000, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x00100635,0x060b013f,0x00000004, +0x00000001,0x007a0002,0x00000000,0x066e0610, +0x0105929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0xa431ac75,0x0001735a,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051, +0x00000000,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0x00000000,0x06400136, +0x0000270f,0x00010000,0x007a0000,0x00000000, +0x068e0645,0x0105929d,0x929d929d,0x929d929d, +0x929d929d,0x929d929d,0xa431ac75,0x0001735a, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75, +0x735a0100,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00010004, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00001705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00009705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00011705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00019705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00021705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00029705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00031705,0x00001400,0x000a411e,0x00001003, +0x00040730,0x00001002,0x000f619e,0x00001003, +0x00039705,0x00001400,0x000a411e,0x00001003, +0x000fe19e,0x00001003,0x0009c730,0x00001003, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00009705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00011705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00019705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00021705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00029705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00031705,0x00001400,0x000a211e,0x00001003, +0x00098730,0x00001002,0x000ee19e,0x00001003, +0x00039705,0x00001400,0x000a211e,0x00001003, +0x0000a730,0x00001008,0x000e2730,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x0000a731,0x00001002,0x0000a731,0x00001002, +0x00000000,0x00000000,0x000f619c,0x00001003, +0x0007f801,0x000c0000,0x00000037,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0000373c,0x00001000,0x00000000,0x00000000, +0x000ee19c,0x00001003,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000273c,0x00001000, +0x00000033,0x00001000,0x000e679e,0x00001003, +0x00007705,0x00001400,0x000ac71e,0x00001003, +0x00087fc1,0x000c3be0,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0000a730,0x00001003, +0x00000033,0x00001000,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x000c0000, +0x00000032,0x00001000,0x0000273d,0x00001000, +0x0004a730,0x00001003,0x00000f41,0x00097140, +0x0000a841,0x0009b240,0x0000a0c1,0x0009f040, +0x0001c641,0x00093540,0x0001cec1,0x0009b5c0, +0x00000000,0x00000000,0x0001bf05,0x0003fc40, +0x00002725,0x000aa400,0x00013705,0x00093a00, +0x0000002e,0x0009d6c0,0x00038630,0x00001004, +0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000, +0x00000000,0x000c70e0,0x0007d182,0x0002c640, +0x00000630,0x00001004,0x000799b8,0x0002c6c0, +0x00031705,0x00092240,0x00039f05,0x000932c0, +0x0003520a,0x00000000,0x00040731,0x0000100b, +0x00010705,0x000b20c0,0x00000000,0x000eba44, +0x00032108,0x000c60c4,0x00065208,0x000c2917, +0x000406b0,0x00001007,0x00012f05,0x00036880, +0x0002818e,0x000c0000,0x0004410a,0x00000000, +0x00040630,0x00001007,0x00029705,0x000c0000, +0x00000000,0x00000000,0x00003fc1,0x0003fc40, +0x000037c1,0x00091b40,0x00003fc1,0x000911c0, +0x000037c1,0x000957c0,0x00003fc1,0x000951c0, +0x000037c1,0x00000000,0x00003fc1,0x000991c0, +0x000037c1,0x00000000,0x00003fc1,0x0009d1c0, +0x000037c1,0x00000000,0x0001ccc1,0x000915c0, +0x0001c441,0x0009d800,0x0009cdc1,0x00091240, +0x0001c541,0x00091d00,0x0009cfc1,0x00095240, +0x0001c741,0x00095c80,0x000e8ca9,0x00099240, +0x000e85ad,0x00095640,0x00069ca9,0x00099d80, +0x000e952d,0x00099640,0x000eaca9,0x0009d6c0, +0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80, +0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0, +0x000ec5ad,0x0009da40,0x000edca9,0x0009d300, +0x000a6e0a,0x00001000,0x000ed52d,0x00091e40, +0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40, +0x0006fca9,0x00002500,0x000fb208,0x000c59a0, +0x000ef52d,0x0009de40,0x00068ca9,0x000912c1, +0x000683ad,0x00095241,0x00020f05,0x000991c1, +0x00000000,0x00000000,0x00086f88,0x00001000, +0x0009cf81,0x000b5340,0x0009c701,0x000b92c0, +0x0009de81,0x000bd300,0x0009d601,0x000b1700, +0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0, +0x000a0f81,0x000bd740,0x00020701,0x000b5c80, +0x000a1681,0x000b97c0,0x00021601,0x00002500, +0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0, +0x00021681,0x00002d00,0x00020f81,0x000bd800, +0x000a0701,0x000b5bc0,0x00021601,0x00003500, +0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0, +0x00021681,0x00003d00,0x00020f81,0x000b1d00, +0x000a0701,0x000b1fc0,0x00021601,0x00020500, +0x00020f81,0x000b1341,0x000a0701,0x000b9fc0, +0x00021681,0x00020d00,0x00020f81,0x000bde80, +0x000a0701,0x000bdfc0,0x00021601,0x00021500, +0x00020f81,0x000b9341,0x00020701,0x000b53c1, +0x00021681,0x00021d00,0x000a0f81,0x000d0380, +0x0000b601,0x000b15c0,0x00007b01,0x00000000, +0x00007b81,0x000bd1c0,0x00007b01,0x00000000, +0x00007b81,0x000b91c0,0x00007b01,0x000b57c0, +0x00007b81,0x000b51c0,0x00007b01,0x000b1b40, +0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0, +0x0007e488,0x000d7e45,0x00000000,0x000d7a44, +0x0007e48a,0x00000000,0x00011f05,0x00084080, +0x00000000,0x00000000,0x00001705,0x000b3540, +0x00008a01,0x000bf040,0x00007081,0x000bb5c0, +0x00055488,0x00000000,0x0000d482,0x0003fc40, +0x0003fc88,0x00000000,0x0001e401,0x000b3a00, +0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784, +0x000c86b0,0x00001007,0x00008281,0x000bb240, +0x0000b801,0x000b7140,0x00007888,0x00000000, +0x0000073c,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x00055288,0x000c555c, +0x0005528a,0x000c0000,0x0009fa88,0x000c5d00, +0x0000fa88,0x00000000,0x00000032,0x00001000, +0x0000073d,0x00001000,0x0007f188,0x000c0000, +0x00000000,0x00000000,0x0008c01c,0x00001003, +0x00002705,0x00001008,0x0008b201,0x000c1392, +0x0000ba01,0x00000000,0x00008731,0x00001400, +0x0004c108,0x000fe0c4,0x00057488,0x00000000, +0x000a6388,0x00001001,0x0008b334,0x000bc141, +0x0003020e,0x00000000,0x000886b0,0x00001008, +0x00003625,0x000c5dfa,0x000a638a,0x00001001, +0x0008020e,0x00001002,0x0008a6b0,0x00001008, +0x0007f301,0x00000000,0x00000000,0x00000000, +0x00002725,0x000a8c40,0x000000ae,0x00000000, +0x000d8630,0x00001008,0x00000000,0x000c74e0, +0x0007d182,0x0002d640,0x000a8630,0x00001008, +0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5, +0x0007420a,0x000c0000,0x00062208,0x000c4117, +0x00070630,0x00001009,0x00000000,0x000c0000, +0x0001022e,0x00000000,0x0003a630,0x00001009, +0x00000000,0x000c0000,0x00000036,0x00001000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x0002a730,0x00001008,0x0007f801,0x000c0000, +0x00000037,0x00001000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x0002a730,0x00001008, +0x00000033,0x00001000,0x0002a705,0x00001008, +0x00007a01,0x000c0000,0x000e6288,0x000d550a, +0x0006428a,0x00000000,0x00060730,0x0000100a, +0x00000000,0x000c0000,0x00000000,0x00000000, +0x0007aab0,0x00034880,0x00078fb0,0x0000100b, +0x00057488,0x00000000,0x00033b94,0x00081140, +0x000183ae,0x00000000,0x000786b0,0x0000100b, +0x00022f05,0x000c3545,0x0000eb8a,0x00000000, +0x00042731,0x00001003,0x0007aab0,0x00034880, +0x00048fb0,0x0000100a,0x00057488,0x00000000, +0x00033b94,0x00081140,0x000183ae,0x00000000, +0x000806b0,0x0000100b,0x00022f05,0x00000000, +0x00007401,0x00091140,0x00048f05,0x000951c0, +0x00042731,0x00001003,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x000fe19e,0x00001003,0x00000000,0x00000000, +0x0008e19c,0x00001003,0x000083c1,0x00093040, +0x00000f41,0x00097140,0x0000a841,0x0009b240, +0x0000a0c1,0x0009f040,0x0001c641,0x00093540, +0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44, +0x00055208,0x00000000,0x00010705,0x000a2880, +0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5, +0x0004ef0a,0x000c0000,0x00012f05,0x00036880, +0x00065308,0x000c2997,0x000d86b0,0x0000100a, +0x0004410a,0x000d40c7,0x00000000,0x00000000, +0x00080730,0x00001004,0x00056f0a,0x000ea105, +0x00000000,0x00000000,0x0000473d,0x00001000, +0x000f19b0,0x000bbc47,0x00080000,0x000bffc7, +0x0000273d,0x00001000,0x00000000,0x000eba44, +0x00048f05,0x0000f440,0x00007401,0x0000f7c0, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x00000000,0x000e5084, +0x00000000,0x000eba44,0x00087401,0x000e4782, +0x00000734,0x00001000,0x00010705,0x000a6880, +0x00006a88,0x000c75c4,0x0007c108,0x000c0000, +0x0007e721,0x000bed40,0x00005f25,0x000badc0, +0x0003ba97,0x000beb80,0x00065590,0x000b2e00, +0x00033217,0x00003ec0,0x00065590,0x000b8e40, +0x0003ed80,0x000491c0,0x00073fb0,0x00074c80, +0x000283a0,0x0000100c,0x000ee388,0x00042970, +0x00008301,0x00021ef2,0x000b8f14,0x0000000f, +0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6, +0x000032ac,0x000c3916,0x0004edc2,0x00074c80, +0x00078898,0x00001000,0x00038894,0x00000032, +0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6, +0x0004edc2,0x000c1956,0x0000722c,0x00034a00, +0x00041705,0x0009ed40,0x00058730,0x00001400, +0x000d7488,0x000c3a00,0x00048f05,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000} + }; diff -Nru linux/sound/pci/cs46xx/cs46xx_lib.c linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_lib.c --- linux/sound/pci/cs46xx/cs46xx_lib.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/cs46xx/cs46xx_lib.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2305 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Abramo Bagnara + * Cirrus Logic, Inc. + * Routines for control of Cirrus Logic CS461x chips + * + * BUGS: + * -- + * + * TODO: + * We need a DSP code to support multichannel outputs and S/PDIF. + * Unfortunately, it seems that Cirrus Logic, Inc. is not willing + * to provide us sufficient information about the DSP processor, + * so we can't update the driver. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t cs46xx_t + +/* + * constants + */ + +#if 0 +#define SND_CONFIG_CS46XX_ACCEPT_VALID /* REQUIRED ONLY FOR OSS EMULATION */ +#endif + +#define CS46XX_BA0_SIZE 0x1000 +#define CS46XX_BA1_DATA0_SIZE 0x3000 +#define CS46XX_BA1_DATA1_SIZE 0x3800 +#define CS46XX_BA1_PRG_SIZE 0x7000 +#define CS46XX_BA1_REG_SIZE 0x0100 + + +#define CS46XX_PERIOD_SIZE 2048 +#define CS46XX_FRAGS 2 +#define CS46XX_BUFFER_SIZE CS46XX_PERIOD_SIZE * CS46XX_FRAGS + +extern snd_pcm_ops_t snd_cs46xx_playback_ops; +extern snd_pcm_ops_t snd_cs46xx_playback_indirect_ops; +extern snd_pcm_ops_t snd_cs46xx_capture_ops; +extern snd_pcm_ops_t snd_cs46xx_capture_indirect_ops; + + +/* + * common I/O routines + */ + +static inline void snd_cs46xx_poke(cs46xx_t *chip, unsigned long reg, unsigned int val) +{ + unsigned int bank = reg >> 16; + unsigned int offset = reg & 0xffff; + writel(val, chip->region.idx[bank+1].remap_addr + offset); +} + +static inline unsigned int snd_cs46xx_peek(cs46xx_t *chip, unsigned long reg) +{ + unsigned int bank = reg >> 16; + unsigned int offset = reg & 0xffff; + return readl(chip->region.idx[bank+1].remap_addr + offset); +} + +static inline void snd_cs46xx_pokeBA0(cs46xx_t *chip, unsigned long offset, unsigned int val) +{ + writel(val, chip->region.name.ba0.remap_addr + offset); +} + +static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offset) +{ + return readl(chip->region.name.ba0.remap_addr + offset); +} + + +static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip, + unsigned short reg) +{ + int count; + unsigned short result; + + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h + * 5. if DCV not cleared, break and return error + * 6. Read ACSTS = Status Register = 464h, check VSTS bit + */ + + snd_cs46xx_peekBA0(chip, BA0_ACSDA); + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * set CRW - Read command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + + snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | + ACCTL_VFRM | ACCTL_ESYN | + ACCTL_RSTN); + + + /* + * Wait for the read to occur. + */ + for (count = 0; count < 1000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the read has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 17h + */ + if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) + goto ok1; + } + + snd_printk("AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); + result = 0xffff; + goto end; + + ok1: + /* + * Wait for the valid status bit to go active. + */ + for (count = 0; count < 100; count++) { + /* + * Read the AC97 status register. + * ACSTS = Status Register = 464h + * VSTS - Valid Status + */ + if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_VSTS) + goto ok2; + udelay(10); + } + + snd_printk("AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg); + result = 0xffff; + goto end; + + ok2: + /* + * Read the data returned from the AC97 register. + * ACSDA = Status Data Register = 474h + */ +#if 0 + printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg, + snd_cs46xx_peekBA0(chip, BA0_ACSDA), + snd_cs46xx_peekBA0(chip, BA0_ACCAD)); +#endif + result = snd_cs46xx_peekBA0(chip, BA0_ACSDA); + end: + return result; +} + +static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97, + unsigned short reg) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return -ENXIO); + unsigned short val; + chip->active_ctrl(chip, 1); + val = snd_cs46xx_codec_read(chip, reg); + chip->active_ctrl(chip, -1); + return val; +} + + +static void snd_cs46xx_codec_write(cs46xx_t *chip, + unsigned short reg, + unsigned short val) +{ + /* + * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address + * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 + * 3. Write ACCTL = Control Register = 460h for initiating the write + * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h + * 5. if DCV not cleared, break and return error + */ + int count; + + /* + * Setup the AC97 control registers on the CS461x to send the + * appropriate command to the AC97 to perform the read. + * ACCAD = Command Address Register = 46Ch + * ACCDA = Command Data Register = 470h + * ACCTL = Control Register = 460h + * set DCV - will clear when process completed + * reset CRW - Write command + * set VFRM - valid frame enabled + * set ESYN - ASYNC generation enabled + * set RSTN - ARST# inactive, AC97 codec not reset + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg); + snd_cs46xx_pokeBA0(chip, BA0_ACCDA, val); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | + ACCTL_ESYN | ACCTL_RSTN); + for (count = 0; count < 4000; count++) { + /* + * First, we want to wait for a short time. + */ + udelay(10); + /* + * Now, check to see if the write has completed. + * ACCTL = 460h, DCV should be reset by now and 460h = 07h + */ + if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) { + return; + } + } + snd_printk("AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val); +} + +static void snd_cs46xx_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); + int val2 = 0; + + chip->active_ctrl(chip, 1); + if (reg == AC97_CD) + val2 = snd_cs46xx_codec_read(chip, AC97_CD); + + snd_cs46xx_codec_write(chip, reg, val); + + + /* + * Adjust power if the mixer is selected/deselected according + * to the CD. + * + * IF the CD is a valid input source (mixer or direct) AND + * the CD is not muted THEN power is needed + * + * We do two things. When record select changes the input to + * add/remove the CD we adjust the power count if the CD is + * unmuted. + * + * When the CD mute changes we adjust the power level if the + * CD was a valid input. + * + * We also check for CD volume != 0, as the CD mute isn't + * normally tweaked from userspace. + */ + + /* CD mute change ? */ + + if (reg == AC97_CD) { + /* Mute bit change ? */ + if ((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) { + /* Mute on */ + if(val&0x8000 || val == 0x1f1f) + chip->amplifier_ctrl(chip, -1); + else /* Mute off power on */ + chip->amplifier_ctrl(chip, 1); + } + } + + chip->active_ctrl(chip, -1); +} + + +/* + * Chip initialization + */ + +int snd_cs46xx_download(cs46xx_t *chip, + u32 *src, + unsigned long offset, + unsigned long len) +{ + unsigned long dst; + unsigned int bank = offset >> 16; + offset = offset & 0xffff; + + snd_assert(!(offset & 3) && !(len & 3), return -EINVAL); + dst = chip->region.idx[bank+1].remap_addr + offset; + len /= sizeof(u32); + + /* writel already converts 32-bit value to right endianess */ + while (len-- > 0) { + writel(*src++, dst); + dst += sizeof(u32); + } + return 0; +} + +/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */ +#define BA1_DWORD_SIZE (13 * 1024 + 512) +#define BA1_MEMORY_COUNT 3 + +struct BA1struct { + struct { + unsigned long offset; + unsigned long size; + } memory[BA1_MEMORY_COUNT]; + u32 map[BA1_DWORD_SIZE]; +}; + +static +#include "cs46xx_image.h" + +int snd_cs46xx_download_image(cs46xx_t *chip) +{ + int idx, err; + unsigned long offset = 0; + + for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) { + if ((err = snd_cs46xx_download(chip, + &BA1Struct.map[offset], + BA1Struct.memory[idx].offset, + BA1Struct.memory[idx].size)) < 0) + return err; + offset += BA1Struct.memory[idx].size >> 2; + } + return 0; +} + +/* + * Chip reset + */ + +static void snd_cs46xx_reset(cs46xx_t *chip) +{ + int idx; + + /* + * Write the reset bit of the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP); + + /* + * Write the control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN); + + /* + * Clear the trap registers. + */ + for (idx = 0; idx < 8; idx++) { + snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx); + snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF); + } + snd_cs46xx_poke(chip, BA1_DREG, 0); + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + snd_cs46xx_poke(chip, BA1_FRMT, 0xadf); +} + +static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip) +{ + int idx, loop, powerdown = 0; + unsigned int tmp; + + /* + * See if the devices are powered down. If so, we must power them up first + * or they will not respond. + */ + tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1); + if (!(tmp & CLKCR1_SWCE)) { + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE); + powerdown = 1; + } + + /* + * We want to clear out the serial port FIFOs so we don't end up playing + * whatever random garbage happens to be in them. We fill the sample FIFOS + * with zero (silence). + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0); + + /* + * Fill all 256 sample FIFO locations. + */ + for (idx = 0; idx < 256; idx++) { + /* + * Make sure the previous FIFO write operation has completed. + */ + for (loop = 0; loop < 5; loop++) { + udelay(50); + if (!(snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY)) + break; + } + if (snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY) { + if (powerdown) + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); + } + /* + * Write the serial port FIFO index. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx); + /* + * Tell the serial port to load the new value into the FIFO location. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC); + } + /* + * Now, if we powered up the devices, then power them back down again. + * This is kinda ugly, but should never happen. + */ + if (powerdown) + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); +} + +static void snd_cs46xx_proc_start(cs46xx_t *chip) +{ + int cnt; + + /* + * Set the frame timer to reflect the number of cycles per frame. + */ + snd_cs46xx_poke(chip, BA1_FRMT, 0xadf); + /* + * Turn on the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); + /* + * Wait until the run at frame bit resets itself in the SP control + * register. + */ + for (cnt = 0; cnt < 25; cnt++) { + udelay(50); + if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)) + break; + } + + if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR) + snd_printk("SPCR_RUNFR never reset\n"); +} + +static void snd_cs46xx_proc_stop(cs46xx_t *chip) +{ + /* + * Turn off the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + snd_cs46xx_poke(chip, BA1_SPCR, 0); +} + +/* + * Sample rate routines + */ + +#define GOF_PER_SEC 200 + +static void snd_cs46xx_set_play_sample_rate(cs46xx_t *chip, unsigned int rate) +{ + unsigned long flags; + unsigned int tmp1, tmp2; + unsigned int phiIncr; + unsigned int correctionPerGOF, correctionPerSec; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M + * GOF_PER_SEC * correctionPerGOF + * + * i.e. + * + * phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + */ + tmp1 = rate << 16; + phiIncr = tmp1 / 48000; + tmp1 -= phiIncr * 48000; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / 48000; + phiIncr += tmp2; + tmp1 -= tmp2 * 48000; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + + /* + * Fill in the SampleRateConverter control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_PSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + snd_cs46xx_poke(chip, BA1_PPI, phiIncr); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs46xx_set_capture_sample_rate(cs46xx_t *chip, unsigned int rate) +{ + unsigned long flags; + unsigned int phiIncr, coeffIncr, tmp1, tmp2; + unsigned int correctionPerGOF, correctionPerSec, initialDelay; + unsigned int frameGroupLength, cnt; + + /* + * We can only decimate by up to a factor of 1/9th the hardware rate. + * Correct the value if an attempt is made to stray outside that limit. + */ + if ((rate * 9) < 48000) + rate = 48000 / 9; + + /* + * We can not capture at at rate greater than the Input Rate (48000). + * Return an error if an attempt is made to stray outside that limit. + */ + if (rate > 48000) + rate = 48000; + + /* + * Compute the values used to drive the actual sample rate conversion. + * The following formulas are being computed, using inline assembly + * since we need to use 64 bit arithmetic to compute the values: + * + * coeffIncr = -floor((Fs,out * 2^23) / Fs,in) + * phiIncr = floor((Fs,in * 2^26) / Fs,out) + * correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) / + * GOF_PER_SEC) + * correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - + * GOF_PER_SEC * correctionPerGOF + * initialDelay = ceil((24 * Fs,in) / Fs,out) + * + * i.e. + * + * coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) + * phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) + * correctionPerGOF:correctionPerSec = + * dividend:remainder(ulOther / GOF_PER_SEC) + * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) + */ + + tmp1 = rate << 16; + coeffIncr = tmp1 / 48000; + tmp1 -= coeffIncr * 48000; + tmp1 <<= 7; + coeffIncr <<= 7; + coeffIncr += tmp1 / 48000; + coeffIncr ^= 0xFFFFFFFF; + coeffIncr++; + tmp1 = 48000 << 16; + phiIncr = tmp1 / rate; + tmp1 -= phiIncr * rate; + tmp1 <<= 10; + phiIncr <<= 10; + tmp2 = tmp1 / rate; + phiIncr += tmp2; + tmp1 -= tmp2 * rate; + correctionPerGOF = tmp1 / GOF_PER_SEC; + tmp1 -= correctionPerGOF * GOF_PER_SEC; + correctionPerSec = tmp1; + initialDelay = ((48000 * 24) + rate - 1) / rate; + + /* + * Fill in the VariDecimate control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_CSRC, + ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); + snd_cs46xx_poke(chip, BA1_CCI, coeffIncr); + snd_cs46xx_poke(chip, BA1_CD, + (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); + snd_cs46xx_poke(chip, BA1_CPI, phiIncr); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* + * Figure out the frame group length for the write back task. Basically, + * this is just the factors of 24000 (2^6*3*5^3) that are not present in + * the output sample rate. + */ + frameGroupLength = 1; + for (cnt = 2; cnt <= 64; cnt *= 2) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 2; + } + if (((rate / 3) * 3) != rate) { + frameGroupLength *= 3; + } + for (cnt = 5; cnt <= 125; cnt *= 5) { + if (((rate / cnt) * cnt) != rate) + frameGroupLength *= 5; + } + + /* + * Fill in the WriteBack control block. + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength); + snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength)); + snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF); + snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000)); + snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * PCM part + */ + +static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream, + snd_pcm_uframes_t frames) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->play.appl_ptr; + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + chip->play.sw_ready += diff << chip->play.shift; + } + chip->play.sw_ready += frames << chip->play.shift; + chip->play.appl_ptr = runtime->control->appl_ptr + frames; + while (chip->play.hw_ready < CS46XX_BUFFER_SIZE && + chip->play.sw_ready > 0) { + size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->play.hw_data; + size_t sw_to_end = chip->play.sw_bufsize - chip->play.sw_data; + size_t bytes = CS46XX_BUFFER_SIZE - chip->play.hw_ready; + if (chip->play.sw_ready < bytes) + bytes = chip->play.sw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + memcpy(chip->play.hw_area + chip->play.hw_data, + runtime->dma_area + chip->play.sw_data, + bytes); + chip->play.hw_data += bytes; + if (chip->play.hw_data == CS46XX_BUFFER_SIZE) + chip->play.hw_data = 0; + chip->play.sw_data += bytes; + if (chip->play.sw_data == chip->play.sw_bufsize) + chip->play.sw_data = 0; + chip->play.hw_ready += bytes; + chip->play.sw_ready -= bytes; + } + return 0; +} + +static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream, + snd_pcm_uframes_t frames) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->capt.appl_ptr; + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + chip->capt.sw_ready -= diff << chip->capt.shift; + } + chip->capt.sw_ready -= frames << chip->capt.shift; + chip->capt.appl_ptr = runtime->control->appl_ptr + frames; + while (chip->capt.hw_ready > 0 && + chip->capt.sw_ready < chip->capt.sw_bufsize) { + size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->capt.hw_data; + size_t sw_to_end = chip->capt.sw_bufsize - chip->capt.sw_data; + size_t bytes = chip->capt.sw_bufsize - chip->capt.sw_ready; + if (chip->capt.hw_ready < bytes) + bytes = chip->capt.hw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + memcpy(runtime->dma_area + chip->capt.sw_data, + chip->capt.hw_area + chip->capt.hw_data, + bytes); + chip->capt.hw_data += bytes; + if (chip->capt.hw_data == CS46XX_BUFFER_SIZE) + chip->capt.hw_data = 0; + chip->capt.sw_data += bytes; + if (chip->capt.sw_data == chip->capt.sw_bufsize) + chip->capt.sw_data = 0; + chip->capt.hw_ready -= bytes; + chip->capt.sw_ready += bytes; + } + return 0; +} + +static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr; + return ptr >> chip->play.shift; +} + +static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr; + ssize_t bytes = ptr - chip->play.hw_io; + if (bytes < 0) + bytes += CS46XX_BUFFER_SIZE; + chip->play.hw_io = ptr; + chip->play.hw_ready -= bytes; + chip->play.sw_io += bytes; + if (chip->play.sw_io > chip->play.sw_bufsize) + chip->play.sw_io -= chip->play.sw_bufsize; + snd_cs46xx_playback_transfer(substream, 0); + return chip->play.sw_io >> chip->play.shift; +} + +static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + return ptr >> chip->capt.shift; +} + +static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + ssize_t bytes = ptr - chip->capt.hw_io; + if (bytes < 0) + bytes += CS46XX_BUFFER_SIZE; + chip->capt.hw_io = ptr; + chip->capt.hw_ready += bytes; + chip->capt.sw_io += bytes; + if (chip->capt.sw_io > chip->capt.sw_bufsize) + chip->capt.sw_io -= chip->capt.sw_bufsize; + snd_cs46xx_capture_transfer(substream, 0); + return chip->capt.sw_io >> chip->capt.shift; +} + +static int snd_cs46xx_playback_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t hwoff, + void *src, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t hwoffb = hwoff << chip->play.shift; + size_t bytes = frames << chip->play.shift; + char *hwbuf = runtime->dma_area + hwoffb; + if (copy_from_user(hwbuf, src, bytes)) + return -EFAULT; + spin_lock_irq(&runtime->lock); + snd_cs46xx_playback_transfer(substream, frames); + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_cs46xx_capture_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t hwoff, + void *dst, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + size_t hwoffb = hwoff << chip->capt.shift; + size_t bytes = frames << chip->capt.shift; + char *hwbuf = runtime->dma_area + hwoffb; + if (copy_to_user(dst, hwbuf, bytes)) + return -EFAULT; + spin_lock_irq(&runtime->lock); + snd_cs46xx_capture_transfer(substream, frames); + spin_unlock_irq(&runtime->lock); + return 0; +} + +static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + unsigned int tmp; + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->runtime->periods != CS46XX_FRAGS) + snd_cs46xx_playback_transfer(substream, 0); + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + tmp &= 0x0000ffff; + snd_cs46xx_poke(chip, BA1_PCTL, chip->play.ctl | tmp); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + tmp &= 0x0000ffff; + snd_cs46xx_poke(chip, BA1_PCTL, tmp); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + unsigned int tmp; + int result = 0; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + tmp &= 0xffff0000; + snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + tmp &= 0xffff0000; + snd_cs46xx_poke(chip, BA1_CCTL, tmp); + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if (params_periods(hw_params) == CS46XX_FRAGS) { + if (runtime->dma_area != chip->play.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = chip->play.hw_area; + runtime->dma_addr = chip->play.hw_addr; + runtime->dma_bytes = chip->play.hw_size; + substream->ops = &snd_cs46xx_playback_ops; + } else { + if (runtime->dma_area == chip->play.hw_area) { + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + substream->ops = &snd_cs46xx_playback_indirect_ops; + } + return 0; +} + +static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (runtime->dma_area != chip->play.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + return 0; +} + +static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned int tmp; + unsigned int pfie; + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + pfie = snd_cs46xx_peek(chip, BA1_PFIE); + pfie &= ~0x0000f03f; + + chip->play.shift = 2; + if (runtime->channels == 1) { + chip->play.shift--; + pfie |= 0x00002000; + } + if (snd_pcm_format_width(runtime->format) == 8) { + chip->play.shift--; + pfie |= 0x00001000; + } + if (snd_pcm_format_unsigned(runtime->format)) + pfie |= 0x00008000; + if (snd_pcm_format_big_endian(runtime->format)) + pfie |= 0x00004000; + + + chip->play.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); + chip->play.sw_data = chip->play.sw_io = chip->play.sw_ready = 0; + chip->play.hw_data = chip->play.hw_io = chip->play.hw_ready = 0; + chip->play.appl_ptr = 0; + snd_cs46xx_poke(chip, BA1_PBA, chip->play.hw_addr); + + tmp = snd_cs46xx_peek(chip, BA1_PDTC); + tmp &= ~0x000003ff; + tmp |= (4 << chip->play.shift) - 1; + snd_cs46xx_poke(chip, BA1_PDTC, tmp); + + snd_cs46xx_poke(chip, BA1_PFIE, pfie); + + snd_cs46xx_set_play_sample_rate(chip, runtime->rate); + return 0; +} + +static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if (runtime->periods == CS46XX_FRAGS) { + if (runtime->dma_area != chip->capt.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = chip->capt.hw_area; + runtime->dma_addr = chip->capt.hw_addr; + runtime->dma_bytes = chip->capt.hw_size; + substream->ops = &snd_cs46xx_capture_ops; + } else { + if (runtime->dma_area == chip->capt.hw_area) { + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + substream->ops = &snd_cs46xx_capture_indirect_ops; + } + return 0; +} + +static int snd_cs46xx_capture_hw_free(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (runtime->dma_area != chip->capt.hw_area) + snd_pcm_lib_free_pages(substream); + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + return 0; +} + +static int snd_cs46xx_capture_prepare(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_addr); + chip->capt.shift = 2; + chip->capt.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); + chip->capt.sw_data = chip->capt.sw_io = chip->capt.sw_ready = 0; + chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0; + chip->capt.appl_ptr = 0; + snd_cs46xx_set_capture_sample_rate(chip, runtime->rate); + return 0; +} + +static void snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, dev_id, return); + unsigned int status; + + /* + * Read the Interrupt Status Register to clear the interrupt + */ + status = snd_cs46xx_peekBA0(chip, BA0_HISR); + if ((status & 0x7fffffff) == 0) { + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV); + return; + } + + if ((status & HISR_VC0) && chip->pcm) { + if (chip->play.substream) + snd_pcm_period_elapsed(chip->play.substream); + } + if ((status & HISR_VC1) && chip->pcm) { + if (chip->capt.substream) + snd_pcm_period_elapsed(chip->capt.substream); + } + if ((status & HISR_MIDI) && chip->rmidi) { + unsigned char c; + + spin_lock(&chip->reg_lock); + while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) { + c = snd_cs46xx_peekBA0(chip, BA0_MIDRP); + if ((chip->midcr & MIDCR_RIE) == 0) + continue; + snd_rawmidi_receive(chip->midi_input, &c, 1); + } + while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { + if ((chip->midcr & MIDCR_TIE) == 0) + break; + if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { + chip->midcr &= ~MIDCR_TIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + break; + } + snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c); + } + spin_unlock(&chip->reg_lock); + } + /* + * EOI to the PCI part....reenables interrupts + */ + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV); +} + +static snd_pcm_hardware_t snd_cs46xx_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | +#ifdef SND_CONFIG_CS46XX_ACCEPT_VALID + /* NOT TRUE!!! OSS REQUIRES IT */ + SNDRV_PCM_INFO_MMAP_VALID | +#endif + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + formats: (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (256 * 1024), + period_bytes_min: CS46XX_PERIOD_SIZE, + period_bytes_max: CS46XX_PERIOD_SIZE, + periods_min: CS46XX_FRAGS, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_cs46xx_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | +#ifdef SND_CONFIG_CS46XX_ACCEPT_VALID + /* NOT TRUE!!! OSS REQUIRES IT */ + SNDRV_PCM_INFO_MMAP_VALID | +#endif + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (256 * 1024), + period_bytes_min: CS46XX_PERIOD_SIZE, + period_bytes_max: CS46XX_PERIOD_SIZE, + periods_min: CS46XX_FRAGS, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_cs46xx_playback_open(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + if ((chip->play.hw_area = snd_malloc_pci_pages(chip->pci, chip->play.hw_size, &chip->play.hw_addr)) == NULL) + return -ENOMEM; + chip->play.substream = substream; + substream->runtime->hw = snd_cs46xx_playback; + chip->active_ctrl(chip, 1); + chip->amplifier_ctrl(chip, 1); + return 0; +} + +static int snd_cs46xx_capture_open(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + if ((chip->capt.hw_area = snd_malloc_pci_pages(chip->pci, chip->capt.hw_size, &chip->capt.hw_addr)) == NULL) + return -ENOMEM; + chip->capt.substream = substream; + substream->runtime->hw = snd_cs46xx_capture; + chip->active_ctrl(chip, 1); + chip->amplifier_ctrl(chip, 1); + return 0; +} + +static int snd_cs46xx_playback_close(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + chip->play.substream = NULL; + snd_free_pci_pages(chip->pci, chip->play.hw_size, chip->play.hw_area, chip->play.hw_addr); + chip->active_ctrl(chip, -1); + chip->amplifier_ctrl(chip, -1); + return 0; +} + +static int snd_cs46xx_capture_close(snd_pcm_substream_t * substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + + chip->capt.substream = NULL; + snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr); + chip->active_ctrl(chip, -1); + chip->amplifier_ctrl(chip, -1); + return 0; +} + +snd_pcm_ops_t snd_cs46xx_playback_ops = { + open: snd_cs46xx_playback_open, + close: snd_cs46xx_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_playback_hw_params, + hw_free: snd_cs46xx_playback_hw_free, + prepare: snd_cs46xx_playback_prepare, + trigger: snd_cs46xx_playback_trigger, + pointer: snd_cs46xx_playback_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = { + open: snd_cs46xx_playback_open, + close: snd_cs46xx_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_playback_hw_params, + hw_free: snd_cs46xx_playback_hw_free, + prepare: snd_cs46xx_playback_prepare, + trigger: snd_cs46xx_playback_trigger, + copy: snd_cs46xx_playback_copy, + pointer: snd_cs46xx_playback_indirect_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_capture_ops = { + open: snd_cs46xx_capture_open, + close: snd_cs46xx_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_capture_hw_params, + hw_free: snd_cs46xx_capture_hw_free, + prepare: snd_cs46xx_capture_prepare, + trigger: snd_cs46xx_capture_trigger, + pointer: snd_cs46xx_capture_direct_pointer, +}; + +snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = { + open: snd_cs46xx_capture_open, + close: snd_cs46xx_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_cs46xx_capture_hw_params, + hw_free: snd_cs46xx_capture_hw_free, + prepare: snd_cs46xx_capture_prepare, + trigger: snd_cs46xx_capture_trigger, + copy: snd_cs46xx_capture_copy, + pointer: snd_cs46xx_capture_indirect_pointer, +}; + +static void snd_cs46xx_pcm_free(snd_pcm_t *pcm) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "CS46xx", device, 1, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_cs46xx_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs46xx_capture_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "CS46xx"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer routines + */ + +static void snd_cs46xx_mixer_free_ac97(ac97_t *ac97) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); + chip->ac97 = NULL; + chip->eapd_switch = NULL; +} + +static int snd_cs46xx_vol_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 32767; + return 0; +} + +static int snd_cs46xx_vol_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int val = snd_cs46xx_peek(chip, reg); + ucontrol->value.integer.value[0] = 0xffff - (val >> 16); + ucontrol->value.integer.value[1] = 0xffff - (val & 0xffff); + return 0; +} + +static int snd_cs46xx_vol_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + cs46xx_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int val = ((0xffff - ucontrol->value.integer.value[0]) << 16 | + (0xffff - ucontrol->value.integer.value[1])); + unsigned int old = snd_cs46xx_peek(chip, reg); + int change = (old != val); + if (change) + snd_cs46xx_poke(chip, reg, val); + return change; +} + +static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = { +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "DAC Volume", + info: snd_cs46xx_vol_info, + get: snd_cs46xx_vol_get, + put: snd_cs46xx_vol_put, + private_value: BA1_PVOL, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "ADC Volume", + info: snd_cs46xx_vol_info, + get: snd_cs46xx_vol_get, + put: snd_cs46xx_vol_put, + private_value: BA1_CVOL, +}}; + +int __devinit snd_cs46xx_mixer(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + ac97_t ac97; + snd_ctl_elem_id_t id; + int err; + int idx; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_cs46xx_ac97_write; + ac97.read = snd_cs46xx_ac97_read; + ac97.private_data = chip; + ac97.private_free = snd_cs46xx_mixer_free_ac97; + + snd_cs46xx_ac97_write(&ac97, AC97_MASTER, 0x8000); + for (idx = 0; idx < 100; ++idx) { + if (snd_cs46xx_ac97_read(&ac97, AC97_MASTER) == 0x8000) + goto _ok; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/100); + } + return -ENXIO; + + _ok: + if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97)) < 0) + return err; + for (idx = 0; idx < sizeof(snd_cs46xx_controls) / + sizeof(snd_cs46xx_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip); + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + + /* get EAPD mixer switch (for voyetra hack) */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "External Amplifier Power Down"); + chip->eapd_switch = snd_ctl_find_id(chip->card, &id); + + return 0; +} + +/* + * RawMIDI interface + */ + +static void snd_cs46xx_midi_reset(cs46xx_t *chip) +{ + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, MIDCR_MRST); + udelay(100); + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); +} + +static int snd_cs46xx_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + chip->active_ctrl(chip, 1); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS46XX_MODE_INPUT; + chip->midcr |= MIDCR_RXE; + chip->midi_input = substream; + if (!(chip->uartm & CS46XX_MODE_OUTPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs46xx_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE); + chip->midi_input = NULL; + if (!(chip->uartm & CS46XX_MODE_OUTPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS46XX_MODE_INPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->active_ctrl(chip, -1); + return 0; +} + +static int snd_cs46xx_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + chip->active_ctrl(chip, 1); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->uartm |= CS46XX_MODE_OUTPUT; + chip->midcr |= MIDCR_TXE; + chip->midi_output = substream; + if (!(chip->uartm & CS46XX_MODE_INPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_cs46xx_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE); + chip->midi_output = NULL; + if (!(chip->uartm & CS46XX_MODE_INPUT)) { + snd_cs46xx_midi_reset(chip); + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + chip->uartm &= ~CS46XX_MODE_OUTPUT; + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->active_ctrl(chip, -1); + return 0; +} + +static void snd_cs46xx_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & MIDCR_RIE) == 0) { + chip->midcr |= MIDCR_RIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & MIDCR_RIE) { + chip->midcr &= ~MIDCR_RIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_cs46xx_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (up) { + if ((chip->midcr & MIDCR_TIE) == 0) { + chip->midcr |= MIDCR_TIE; + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while ((chip->midcr & MIDCR_TIE) && + (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + chip->midcr &= ~MIDCR_TIE; + } else { + snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte); + } + } + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } else { + if (chip->midcr & MIDCR_TIE) { + chip->midcr &= ~MIDCR_TIE; + snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); + } + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_cs46xx_midi_output = +{ + open: snd_cs46xx_midi_output_open, + close: snd_cs46xx_midi_output_close, + trigger: snd_cs46xx_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_cs46xx_midi_input = +{ + open: snd_cs46xx_midi_input_open, + close: snd_cs46xx_midi_input_close, + trigger: snd_cs46xx_midi_input_trigger, +}; + +int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0) + return err; + strcpy(rmidi->name, "CS46XX"); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = chip; + chip->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = NULL; + return 0; +} + +/* + * proc interface + */ + +static long snd_cs46xx_io_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + snd_cs46xx_region_t *region = (snd_cs46xx_region_t *)entry->private_data; + + size = count; + if (file->f_pos + size > region->size) + size = region->size - file->f_pos; + if (size > 0) { + char *tmp; + long res; + unsigned long virt; + if ((tmp = kmalloc(size, GFP_KERNEL)) == NULL) + return -ENOMEM; + virt = region->remap_addr + file->f_pos; + memcpy_fromio(tmp, virt, size); + if (copy_to_user(buf, tmp, size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = { + read: snd_cs46xx_io_read, +}; + +static int __devinit snd_cs46xx_proc_init(snd_card_t * card, cs46xx_t *chip) +{ + snd_info_entry_t *entry; + int idx; + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + entry = snd_info_create_card_entry(card, region->name, card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &snd_cs46xx_proc_io_ops; + entry->size = region->size; + entry->mode = S_IFREG | S_IRUSR; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + region->proc_entry = entry; + } + return 0; +} + +static int snd_cs46xx_proc_done(cs46xx_t *chip) +{ + int idx; + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + if (region->proc_entry) { + snd_info_unregister((snd_info_entry_t *) region->proc_entry); + region->proc_entry = NULL; + } + } + return 0; +} + +/* + * stop the h/w + */ +static void snd_cs46xx_hw_stop(cs46xx_t *chip) +{ + unsigned int tmp; + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + tmp |= 0x00000010; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt disable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000011; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt disable */ + + /* + * Stop playback DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); + + /* + * Reset the processor. + */ + snd_cs46xx_reset(chip); + + snd_cs46xx_proc_stop(chip); + + /* + * Power down the PLL. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); +} + + +static int snd_cs46xx_free(cs46xx_t *chip) +{ + int idx; + + snd_assert(chip != NULL, return -EINVAL); + + if (chip->active_ctrl) + chip->active_ctrl(chip, 1); + +#ifdef CONFIG_PM + if (chip->pm_dev) + pm_unregister(chip->pm_dev); +#endif + if (chip->amplifier_ctrl) + chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */ + + snd_cs46xx_proc_done(chip); + + if (chip->region.idx[0].resource) + snd_cs46xx_hw_stop(chip); + + for (idx = 0; idx < 5; idx++) { + snd_cs46xx_region_t *region = &chip->region.idx[idx]; + if (region->remap_addr) + iounmap((void *) region->remap_addr); + if (region->resource) { + release_resource(region->resource); + kfree_nocheck(region->resource); + } + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + if (chip->active_ctrl) + chip->active_ctrl(chip, -chip->amplifier); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_cs46xx_dev_free(snd_device_t *device) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, device->device_data, return -ENXIO); + return snd_cs46xx_free(chip); +} + +/* + * initialize chip + */ + +static int snd_cs46xx_chip_init(cs46xx_t *chip, int busywait) +{ + unsigned int tmp; + int timeout; + + /* + * First, blast the clock control register to zero so that the PLL starts + * out in a known state, and blast the master serial port control register + * to zero so that the serial ports also start out in a known state. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0); + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0); + + /* + * If we are in AC97 mode, then we must set the part to a host controlled + * AC-link. Otherwise, we won't be able to bring up the link. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03); /* 1.03 codec */ + /* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */ + + /* + * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 + * spec) and then drive it high. This is done for non AC97 modes since + * there might be logic external to the CS461x that uses the ARST# line + * for a reset. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0); + udelay(50); + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN); + + /* + * The first thing we do here is to enable sync generation. As soon + * as we start receiving bit clock, we'll start producing the SYNC + * signal. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); + + /* + * Now wait for a short while to allow the AC97 part to start + * generating bit clock (so we don't try to start the PLL without an + * input clock). + */ + mdelay(1); + + /* + * Set the serial port timing configuration, so that + * the clock control circuit gets its clock from the correct place. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97); + + /* + * Write the selected clock control setup to the hardware. Do not turn on + * SWCE yet (if requested), so that the devices clocked by the output of + * PLL are not clocked until the PLL is stable. + */ + snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); + snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a); + snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8); + + /* + * Power up the PLL. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP); + + /* + * Wait until the PLL has stabilized. + */ + mdelay(1); + + /* + * Turn on clocking of the core so that we can setup the serial ports. + */ + snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE); + + /* + * Fill the serial port FIFOs with silence. + */ + snd_cs46xx_clear_serial_FIFOs(chip); + + /* + * Set the serial port FIFO pointer to the first sample in the FIFO. + */ + /* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */ + + /* + * Write the serial port configuration to the part. The master + * enable bit is not set until all other values have been written. + */ + snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); + snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); + snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); + + /* + * Wait for the codec ready signal from the AC97 codec. + */ + timeout = 150; + while (timeout-- > 0) { + /* + * Read the AC97 status register to see if we've seen a CODEC READY + * signal from the AC97 codec. + */ + if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY) + goto ok1; + if (busywait) + mdelay(10); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ+99)/100); + } + } + + + snd_printk("create - never read codec ready from AC'97\n"); + snd_printk("it is not probably bug, try to use CS4236 driver\n"); + snd_cs46xx_free(chip); + return -EIO; + ok1: + /* + * Assert the vaid frame signal so that we can start sending commands + * to the AC97 codec. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); + + /* + * Wait until we've sampled input slots 3 and 4 as valid, meaning that + * the codec is pumping ADC data across the AC-link. + */ + timeout = 150; + while (timeout-- > 0) { + /* + * Read the input slot valid register and see if input slots 3 and + * 4 are valid yet. + */ + if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) + goto ok2; + if (busywait) + mdelay(10); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ+99)/100); + } + } + + snd_printk("create - never read ISV3 & ISV4 from AC'97\n"); + snd_cs46xx_free(chip); + return -EIO; + ok2: + + /* + * Now, assert valid frame and the slot 3 and 4 valid bits. This will + * commense the transfer of digital audio data to the AC97 codec. + */ + snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); + + /* + * Power down the DAC and ADC. We will power them up (if) when we need + * them. + */ + /* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */ + + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + /* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */ + /* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */ + + /* + * Reset the processor. + */ + snd_cs46xx_reset(chip); + + /* + * Download the image to the processor. + */ + if (snd_cs46xx_download_image(chip) < 0) { + snd_printk("image download error\n"); + snd_cs46xx_free(chip); + return -EIO; + } + + /* + * Stop playback DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_PCTL); + chip->play.ctl = tmp & 0xffff0000; + snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff); + + /* + * Stop capture DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + chip->capt.ctl = tmp & 0x0000ffff; + snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); + + snd_cs46xx_set_play_sample_rate(chip, 8000); + snd_cs46xx_set_capture_sample_rate(chip, 8000); + + snd_cs46xx_proc_start(chip); + + /* + * Enable interrupts on the part. + */ + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000001; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ + + /* set the attenuation to 0dB */ + snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000); + snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000); + + return 0; +} + + +/* + * AMP control - null AMP + */ + +static void amp_none(cs46xx_t *chip, int change) +{ +} + +/* + * Crystal EAPD mode + */ + +static void amp_voyetra(cs46xx_t *chip, int change) +{ + /* Manage the EAPD bit on the Crystal 4297 + and the Analog AD1885 */ + + int old = chip->amplifier; + int oval, val; + + chip->amplifier += change; + oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN); + val = oval; + if (chip->amplifier && !old) { + /* Turn the EAPD amp on */ + val |= 0x8000; + } else if (old && !chip->amplifier) { + /* Turn the EAPD amp off */ + val &= ~0x8000; + } + if (val != oval) { + snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val); + if (chip->eapd_switch) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->eapd_switch->id); + } +} + + +/* + * Game Theatre XP card - EGPIO[2] is used to enable the external amp. + */ + +static void amp_hercules(cs46xx_t *chip, int change) +{ + int old = chip->amplifier; + + chip->amplifier += change; + if (chip->amplifier && !old) { + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, + EGPIODR_GPOE2); /* enable EGPIO2 output */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, + EGPIOPTR_GPPT2); /* open-drain on output */ + } else if (old && !chip->amplifier) { + snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 0); /* disable */ + snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 0); /* disable */ + } +} + + +#if 0 +/* + * Untested + */ + +static void amp_voyetra_4294(cs46xx_t *chip, int change) +{ + chip->amplifier += change; + + if (chip->amplifier) { + /* Switch the GPIO pins 7 and 8 to open drain */ + snd_cs46xx_codec_write(chip, 0x4C, + snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F); + snd_cs46xx_codec_write(chip, 0x4E, + snd_cs46xx_codec_read(chip, 0x4E) | 0x0180); + /* Now wake the AMP (this might be backwards) */ + snd_cs46xx_codec_write(chip, 0x54, + snd_cs46xx_codec_read(chip, 0x54) & ~0x0180); + } else { + snd_cs46xx_codec_write(chip, 0x54, + snd_cs46xx_codec_read(chip, 0x54) | 0x0180); + } +} +#endif + + +/* + * piix4 pci ids + */ +#ifndef PCI_VENDOR_ID_INTEL +#define PCI_VENDOR_ID_INTEL 0x8086 +#endif /* PCI_VENDOR_ID_INTEL */ + +#ifndef PCI_DEVICE_ID_INTEL_82371AB_3 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#endif /* PCI_DEVICE_ID_INTEL_82371AB_3 */ + +/* + * Handle the CLKRUN on a thinkpad. We must disable CLKRUN support + * whenever we need to beat on the chip. + * + * The original idea and code for this hack comes from David Kaiser at + * Linuxcare. Perhaps one day Crystal will document their chips well + * enough to make them useful. + */ + +static void clkrun_hack(cs46xx_t *chip, int change) +{ + u16 control; + int old; + + if (chip->acpi_dev == NULL) + return; + + old = chip->amplifier; + chip->amplifier += change; + + /* Read ACPI port */ + control = inw(chip->acpi_port + 0x10); + + /* Flip CLKRUN off while running */ + if (! chip->amplifier && old) + outw(control | 0x2000, chip->acpi_port + 0x10); + else if (chip->amplifier && ! old) + outw(control & ~0x2000, chip->acpi_port + 0x10); +} + + +/* + * detect intel piix4 + */ +static void clkrun_init(cs46xx_t *chip) +{ + u8 pp; + + chip->acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + if (chip->acpi_dev == NULL) + return; /* Not a thinkpad thats for sure */ + + /* Find the control port */ + pci_read_config_byte(chip->acpi_dev, 0x41, &pp); + chip->acpi_port = pp << 8; +} + + +/* + * Card subid table + */ + +struct cs_card_type +{ + u16 vendor; + u16 id; + char *name; + void (*init)(cs46xx_t *); + void (*amp)(cs46xx_t *, int); + void (*active)(cs46xx_t *, int); +}; + +static struct cs_card_type __initdata cards[] = { + {0x1489, 0x7001, "Genius Soundmaker 128 value", NULL, amp_none, NULL}, + {0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL}, + {0x1071, 0x6003, "Mitac MI6020/21", NULL, amp_voyetra, NULL}, + {0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0051, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0052, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0053, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + {0x1681, 0x0054, "Hercules Game Theatre XP", NULL, amp_hercules, NULL}, + /* Not sure if the 570 needs the clkrun hack */ + {PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", clkrun_init, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", clkrun_init, NULL, clkrun_hack}, + {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL}, + {0, 0, "Card without SSID set", NULL, NULL, NULL }, + {0, 0, NULL, NULL, NULL, NULL} +}; + + +/* + * APM support + */ +#ifdef CONFIG_PM +void snd_cs46xx_suspend(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + snd_pcm_suspend_all(chip->pcm); + // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); + // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); + snd_cs46xx_hw_stop(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +void snd_cs46xx_resume(cs46xx_t *chip) +{ + snd_card_t *card = chip->card; + int amp_saved; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + pci_enable_device(chip->pci); + amp_saved = chip->amplifier; + chip->amplifier = 0; + chip->active_ctrl(chip, 1); /* force to on */ + + snd_cs46xx_chip_init(chip, 1); + +#if 0 + snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, + chip->ac97_general_purpose); + snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL, + chip->ac97_powerdown); + mdelay(10); + snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN, + chip->ac97_powerdown); + mdelay(5); +#endif + + snd_ac97_resume(chip->ac97); + + if (amp_saved) + chip->amplifier_ctrl(chip, 1); /* try to turn on */ + if (! amp_saved) { + chip->amplifier = 1; + chip->active_ctrl(chip, -1); + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +static int snd_cs46xx_set_power_state(snd_card_t *card, unsigned int power_state) +{ + cs46xx_t *chip = snd_magic_cast(cs46xx_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_cs46xx_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_cs46xx_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} +#endif /* CONFIG_PM */ + + +/* + */ + +int __devinit snd_cs46xx_create(snd_card_t * card, + struct pci_dev * pci, + int external_amp, int thinkpad, + cs46xx_t ** rchip) +{ + cs46xx_t *chip; + int err, idx; + snd_cs46xx_region_t *region; + struct cs_card_type *cp; + u16 ss_card, ss_vendor; + static snd_device_ops_t ops = { + dev_free: snd_cs46xx_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(cs46xx_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->play.hw_size = PAGE_SIZE; + chip->capt.hw_size = PAGE_SIZE; + chip->irq = -1; + chip->ba0_addr = pci_resource_start(pci, 0); + chip->ba1_addr = pci_resource_start(pci, 1); + if (chip->ba0_addr == 0 || chip->ba0_addr == ~0 || + chip->ba1_addr == 0 || chip->ba1_addr == ~0) { + snd_cs46xx_free(chip); + snd_printk("wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n", chip->ba0_addr, chip->ba1_addr); + return -ENOMEM; + } + + region = &chip->region.name.ba0; + strcpy(region->name, "CS46xx_BA0"); + region->base = chip->ba0_addr; + region->size = CS46XX_BA0_SIZE; + + region = &chip->region.name.data0; + strcpy(region->name, "CS46xx_BA1_data0"); + region->base = chip->ba1_addr + BA1_SP_DMEM0; + region->size = CS46XX_BA1_DATA0_SIZE; + + region = &chip->region.name.data1; + strcpy(region->name, "CS46xx_BA1_data1"); + region->base = chip->ba1_addr + BA1_SP_DMEM1; + region->size = CS46XX_BA1_DATA1_SIZE; + + region = &chip->region.name.pmem; + strcpy(region->name, "CS46xx_BA1_pmem"); + region->base = chip->ba1_addr + BA1_SP_PMEM; + region->size = CS46XX_BA1_PRG_SIZE; + + region = &chip->region.name.reg; + strcpy(region->name, "CS46xx_BA1_reg"); + region->base = chip->ba1_addr + BA1_SP_REG; + region->size = CS46XX_BA1_REG_SIZE; + + /* set up amp and clkrun hack */ + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card); + + for (cp = &cards[0]; cp->name; cp++) { + if (cp->vendor == ss_vendor && cp->id == ss_card) { + snd_printd("hack for %s enabled\n", cp->name); + if (cp->init) + cp->init(chip); + chip->amplifier_ctrl = cp->amp; + chip->active_ctrl = cp->active; + break; + } + } + + if (external_amp) { + snd_printk("Crystal EAPD support forced on.\n"); + chip->amplifier_ctrl = amp_voyetra; + } + + if (thinkpad) { + snd_printk("Activating CLKRUN hack for Thinkpad.\n"); + chip->active_ctrl = clkrun_hack; + clkrun_init(chip); + } + + if (chip->amplifier_ctrl == NULL) + chip->amplifier_ctrl = amp_none; + if (chip->active_ctrl == NULL) + chip->active_ctrl = amp_none; + + chip->active_ctrl(chip, 1); + + pci_set_master(pci); + + for (idx = 0; idx < 5; idx++) { + region = &chip->region.idx[idx]; + if ((region->resource = request_mem_region(region->base, region->size, region->name)) == NULL) { + snd_cs46xx_free(chip); + snd_printk("unable to request memory region 0x%lx-0x%lx\n", region->base, region->base + region->size - 1); + return -EBUSY; + } + region->remap_addr = (unsigned long) ioremap_nocache(region->base, region->size); + if (region->remap_addr == 0) { + snd_cs46xx_free(chip); + snd_printk("%s ioremap problem\n", region->name); + return -ENOMEM; + } + } + if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS46XX", (void *) chip)) { + snd_cs46xx_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + err = snd_cs46xx_chip_init(chip, 0); + if (err < 0) { + snd_cs46xx_free(chip); + return err; + } + + snd_cs46xx_proc_init(card, chip); + +#ifdef CONFIG_PM + card->set_power_state = snd_cs46xx_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_cs46xx_free(chip); + return err; + } + + chip->active_ctrl(chip, -1); + + *rchip = chip; + return 0; +} diff -Nru linux/sound/pci/emu10k1/Makefile linux-2.4.19-pre5-mjc/sound/pci/emu10k1/Makefile --- linux/sound/pci/emu10k1/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,29 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _emu10k1.o + +list-multi := snd-emu10k1.o snd-emu10k1-synth.o + +export-objs := emu10k1_main.o + +snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ + irq.o memory.o voice.o emumpu401.o emupcm.o io.o \ + emuproc.o emumixer.o emufx.o +snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o snd-emu10k1-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-emu10k1.o: $(snd-emu10k1-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-objs) + +snd-emu10k1-synth.o: $(snd-emu10k1-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emu10k1-synth-objs) diff -Nru linux/sound/pci/emu10k1/emu10k1.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1.c --- linux/sound/pci/emu10k1/emu10k1.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,250 @@ +/* + * The driver for the EMU10K1 (SB Live!) based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("EMU10K1"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Creative Labs,SB Live!/PCI512/E-mu APS}," + "{Creative Labs,SB Audigy}}"); + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#define ENABLE_SYNTH +#include +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; +static int snd_max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64}; +static int snd_max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128}; +static int snd_enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable the EMU10K1 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_extin, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_extin, "Available external inputs for FX8010. Zero=default."); +MODULE_PARM_SYNTAX(snd_extin, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); +MODULE_PARM(snd_extout, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_extout, "Available external outputs for FX8010. Zero=default."); +MODULE_PARM_SYNTAX(snd_extout, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); +MODULE_PARM(snd_seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_seq_ports, "Allocated sequencer ports for internal synthesizer."); +MODULE_PARM_SYNTAX(snd_seq_ports, SNDRV_ENABLED "allows:{{0,32}}"); +MODULE_PARM(snd_max_synth_voices, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_max_synth_voices, "Maximum number of voices for WaveTable."); +MODULE_PARM_SYNTAX(snd_max_synth_voices, SNDRV_ENABLED); +MODULE_PARM(snd_max_buffer_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_max_buffer_size, "Maximum sample buffer size in MB."); +MODULE_PARM_SYNTAX(snd_max_buffer_size, SNDRV_ENABLED); +MODULE_PARM(snd_enable_ir, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable_ir, "Enable IR."); +MODULE_PARM_SYNTAX(snd_enable_ir, SNDRV_ENABLE_DESC); + +static struct pci_device_id snd_emu10k1_ids[] __devinitdata = { + { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */ + { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids); + +static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + emu10k1_t *emu; +#ifdef ENABLE_SYNTH + snd_seq_device_t *wave = NULL; +#endif + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if (snd_max_buffer_size[dev] < 32) + snd_max_buffer_size[dev] = 32; + else if (snd_max_buffer_size[dev] > 1024) + snd_max_buffer_size[dev] = 1024; + if ((err = snd_emu10k1_create(card, pci, snd_extin[dev], snd_extout[dev], + (long)snd_max_buffer_size[dev] * 1024 * 1024, + snd_enable_ir[dev], + &emu)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_emu10k1_fx8010_pcm(emu, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (!emu->APS) { /* APS board has not an AC97 mixer */ + if ((err = snd_emu10k1_mixer(emu)) < 0) { + snd_card_free(card); + return err; + } + } + if (emu->audigy) { + if ((err = snd_emu10k1_audigy_midi(emu)) < 0) { + snd_card_free(card); + return err; + } + } else { + if ((err = snd_emu10k1_midi(emu)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef ENABLE_SYNTH + if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, + sizeof(snd_emu10k1_synth_arg_t), &wave) < 0 || + wave == NULL) { + snd_printk("can't initialize Emu10k1 wavetable synth\n"); + } else { + snd_emu10k1_synth_arg_t *arg; + arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); + strcpy(wave->name, "Emu-10k1 Synth"); + arg->hwptr = emu; + arg->index = 1; + arg->seq_ports = snd_seq_ports[dev]; + arg->max_voices = snd_max_synth_voices[dev]; + } +#endif + + if (emu->audigy) { + strcpy(card->driver, "Audigy"); + strcpy(card->shortname, "Sound Blaster Audigy"); + } else if (emu->APS) { + strcpy(card->driver, "E-mu APS"); + strcpy(card->shortname, "E-mu APS"); + } else { + strcpy(card->driver, "EMU10K1"); + strcpy(card->shortname, "Sound Blaster Live!"); + } + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, emu->port, emu->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "EMU10K1/Audigy", + id_table: snd_emu10k1_ids, + probe: snd_card_emu10k1_probe, + remove: __devexit_p(snd_card_emu10k1_remove), +}; + +static int __init alsa_card_emu10k1_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "EMU10K1/Audigy soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_emu10k1_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_emu10k1_init) +module_exit(alsa_card_emu10k1_exit) + +#ifndef MODULE + +/* format is: snd-emu10k1=snd_enable,snd_index,snd_id, + snd_seq_ports,snd_max_synth_voices */ + +static int __init alsa_card_emu10k1_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_seq_ports[nr_dev]) == 2 && + get_option(&str,&snd_max_synth_voices[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-emu10k1=", alsa_card_emu10k1_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/emu10k1/emu10k1_callback.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_callback.c --- linux/sound/pci/emu10k1/emu10k1_callback.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_callback.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,541 @@ +/* + * synth callback routines for Emu10k1 + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emu10k1_synth_local.h" +#include + +/* voice status */ +enum { + V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END +}; + +/* Keeps track of what we are finding */ +typedef struct best_voice { + unsigned int time; + int voice; +} best_voice_t; + +/* + * prototypes + */ +static void lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only); +static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port); +static int start_voice(snd_emux_voice_t *vp); +static void trigger_voice(snd_emux_voice_t *vp); +static void release_voice(snd_emux_voice_t *vp); +static void update_voice(snd_emux_voice_t *vp, int update); +static void terminate_voice(snd_emux_voice_t *vp); +static void free_voice(snd_emux_voice_t *vp); + +static void set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp); +static void set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp); +static void set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp); + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + + +/* + * set up operators + */ +static snd_emux_operators_t emu10k1_ops = { + owner: THIS_MODULE, + get_voice: get_voice, + prepare: start_voice, + trigger: trigger_voice, + release: release_voice, + update: update_voice, + terminate: terminate_voice, + free_voice: free_voice, + sample_new: snd_emu10k1_sample_new, + sample_free: snd_emu10k1_sample_free, +}; + +void +snd_emu10k1_ops_setup(snd_emux_t *emu) +{ + emu->ops = emu10k1_ops; +} + + +/* + * get more voice for pcm + * + * terminate most inactive voice and give it as a pcm voice. + */ +int +snd_emu10k1_synth_get_voice(emu10k1_t *hw) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + best_voice_t best[V_END]; + unsigned long flags; + int i; + + emu = snd_magic_cast(snd_emux_t, hw->synth, return -EINVAL); + + spin_lock_irqsave(&emu->voice_lock, flags); + lookup_voices(emu, hw, best, 1); /* no OFF voices */ + for (i = 0; i < V_END; i++) { + if (best[i].voice >= 0) { + int ch; + vp = &emu->voices[best[i].voice]; + if ((ch = vp->ch) < 0) { + //printk("synth_get_voice: ch < 0 (%d) ??", i); + continue; + } + vp->emu->num_voices--; + vp->ch = -1; + vp->state = SNDRV_EMUX_ST_OFF; + spin_unlock_irqrestore(&emu->voice_lock, flags); + return ch; + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + /* not found */ + return -ENOMEM; +} + + +/* + * turn off the voice (not terminated) + */ +static void +release_voice(snd_emux_voice_t *vp) +{ + int dcysusv; + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; + snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv); + dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK; + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv); +} + + +/* + * terminate the voice + */ +static void +terminate_voice(snd_emux_voice_t *vp) +{ + emu10k1_t *hw; + + snd_assert(vp, return); + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); + if (vp->block) { + emu10k1_memblk_t *emem; + emem = (emu10k1_memblk_t *)vp->block; + if (emem->map_locked > 0) + emem->map_locked--; + } +} + +/* + * release the voice to system + */ +static void +free_voice(snd_emux_voice_t *vp) +{ + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + if (vp->ch >= 0) { + snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); + // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0); + snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff); + snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff); + snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]); + vp->emu->num_voices--; + vp->ch = -1; + } +} + + +/* + * update registers + */ +static void +update_voice(snd_emux_voice_t *vp, int update) +{ + emu10k1_t *hw; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + if (update & SNDRV_EMUX_UPDATE_VOLUME) + snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol); + if (update & SNDRV_EMUX_UPDATE_PITCH) + snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); + if (update & SNDRV_EMUX_UPDATE_PAN) { + snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan); + snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux); + } + if (update & SNDRV_EMUX_UPDATE_FMMOD) + set_fmmod(hw, vp); + if (update & SNDRV_EMUX_UPDATE_TREMFREQ) + snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); + if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) + set_fm2frq2(hw, vp); + if (update & SNDRV_EMUX_UPDATE_Q) + set_filterQ(hw, vp); +} + + +/* + * look up voice table - get the best voice in order of preference + */ +/* spinlock held! */ +static void +lookup_voices(snd_emux_t *emu, emu10k1_t *hw, best_voice_t *best, int active_only) +{ + snd_emux_voice_t *vp; + best_voice_t *bp; + int i; + + for (i = 0; i < V_END; i++) { + best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */; + best[i].voice = -1; + } + + /* + * Go through them all and get a best one to use. + * NOTE: could also look at volume and pick the quietest one. + */ + for (i = 0; i < emu->max_voices; i++) { + int state, val; + + vp = &emu->voices[i]; + state = vp->state; + if (state == SNDRV_EMUX_ST_OFF) { + if (vp->ch < 0) { + if (active_only) + continue; + bp = best + V_FREE; + } else + bp = best + V_OFF; + } + else if (state == SNDRV_EMUX_ST_RELEASED || + state == SNDRV_EMUX_ST_PENDING) { + bp = best + V_RELEASED; +#if 0 + val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch); + if (! val) + bp = best + V_OFF; +#endif + } + else if (state == SNDRV_EMUX_ST_STANDBY) + continue; + else if (state & SNDRV_EMUX_ST_ON) + bp = best + V_PLAYING; + else + continue; + + /* check if sample is finished playing (non-looping only) */ + if (bp != best + V_OFF && bp != best + V_FREE && + (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { + val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch); + if (val >= vp->reg.loopstart) + bp = best + V_OFF; + } + + if (vp->time < bp->time) { + bp->time = vp->time; + bp->voice = i; + } + } +} + +/* + * get an empty voice + * + * emu->voice_lock is already held. + */ +static snd_emux_voice_t * +get_voice(snd_emux_t *emu, snd_emux_port_t *port) +{ + emu10k1_t *hw; + snd_emux_voice_t *vp; + best_voice_t best[V_END]; + int i; + + hw = snd_magic_cast(emu10k1_t, emu->hw, return NULL); + + lookup_voices(emu, hw, best, 0); + for (i = 0; i < V_END; i++) { + if (best[i].voice >= 0) { + vp = &emu->voices[best[i].voice]; + if (vp->ch < 0) { + /* allocate a voice */ + emu10k1_voice_t *hwvoice; + if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 0, &hwvoice) < 0 || hwvoice == NULL) + continue; + vp->ch = hwvoice->number; + emu->num_voices++; + } + return vp; + } + } + + /* not found */ + return NULL; +} + +/* + * prepare envelopes and LFOs + */ +static int +start_voice(snd_emux_voice_t *vp) +{ + unsigned int temp; + int ch; + unsigned int addr, mapped_offset; + snd_midi_channel_t *chan; + emu10k1_t *hw; + emu10k1_memblk_t *emem; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return -EINVAL); + ch = vp->ch; + snd_assert(ch >= 0, return -EINVAL); + chan = vp->chan; + + emem = (emu10k1_memblk_t *)vp->block; + if (emem == NULL) + return -EINVAL; + emem->map_locked++; + if (snd_emu10k1_memblk_map(hw, emem) < 0) { + // printk("emu: cannot map!\n"); + return -ENOMEM; + } + mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; + vp->reg.start += mapped_offset; + vp->reg.end += mapped_offset; + vp->reg.loopstart += mapped_offset; + vp->reg.loopend += mapped_offset; + + /* set channel routing */ + /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */ + if (hw->audigy) { + temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | + (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24); + snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp); + } else { + temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | + (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28); + snd_emu10k1_ptr_write(hw, FXRT, ch, temp); + } + + /* channel to be silent and idle */ + snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0080); + snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF); + snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF); + snd_emu10k1_ptr_write(hw, PTRX, ch, 0); + snd_emu10k1_ptr_write(hw, CPF, ch, 0); + + /* set pitch offset */ + snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); + + /* set envelope parameters */ + snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay); + snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld); + snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus); + snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay); + snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld); + /* decay/sustain parameter for volume envelope is used + for triggerg the voice */ + + /* cutoff and volume */ + temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol; + snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp); + + /* modulation envelope heights */ + snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe); + + /* lfo1/2 delay */ + snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay); + snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay); + + /* lfo1 pitch & cutoff shift */ + set_fmmod(hw, vp); + /* lfo1 volume & freq */ + snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); + /* lfo2 pitch & freq */ + set_fm2frq2(hw, vp); + + /* reverb and loop start (reverb 8bit, MSB) */ + temp = vp->reg.parm.reverb; + temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + addr = vp->reg.loopstart; + snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->reg.loopend; + temp = vp->reg.parm.chorus; + temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; + LIMITMAX(temp, 255); + temp = (temp <<24) | addr; + snd_emu10k1_ptr_write(hw, DSL, ch, temp); + + /* clear filter delay memory */ + snd_emu10k1_ptr_write(hw, Z1, ch, 0); + snd_emu10k1_ptr_write(hw, Z2, ch, 0); + + /* invalidate maps */ + temp = (hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(hw, MAPA, ch, temp); + snd_emu10k1_ptr_write(hw, MAPB, ch, temp); +#if 0 + /* cache */ + { + unsigned int val, sample; + val = 32; + if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + sample = 0x80808080; + else { + sample = 0; + val *= 2; + } + + /* cache */ + snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16); + snd_emu10k1_ptr_write(hw, CDE, ch, sample); + snd_emu10k1_ptr_write(hw, CDF, ch, sample); + + /* invalidate maps */ + temp = ((unsigned int)hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(hw, MAPA, ch, temp); + snd_emu10k1_ptr_write(hw, MAPB, ch, temp); + + /* fill cache */ + val -= 4; + val <<= 25; + val |= 0x1c << 16; + snd_emu10k1_ptr_write(hw, CCR, ch, val); + } +#endif + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->reg.start; + temp = vp->reg.parm.filterQ; + temp = (temp<<28) | addr; + if (vp->apitch < 0xe400) + temp |= CCCA_INTERPROM_0; + else { + unsigned int shift = (vp->apitch - 0xe000) >> 10; + temp |= shift << 25; + } + if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + temp |= CCCA_8BITSELECT; + snd_emu10k1_ptr_write(hw, CCCA, ch, temp); + + /* reset volume */ + temp = (unsigned int)vp->vtarget << 16; + snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget); + snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00); + return 0; +} + +/* + * Start envelope + */ +static void +trigger_voice(snd_emux_voice_t *vp) +{ + unsigned int temp, ptarget; + emu10k1_t *hw; + emu10k1_memblk_t *emem; + + hw = snd_magic_cast(emu10k1_t, vp->hw, return); + + emem = (emu10k1_memblk_t *)vp->block; + if (! emem || emem->mapped_page < 0) + return; /* not mapped */ + +#if 0 + ptarget = (unsigned int)vp->ptarget << 16; +#else + ptarget = IP_TO_CP(vp->apitch); +#endif + /* set pitch target and pan (volume) */ + temp = ptarget | (vp->apan << 8) | vp->aaux; + snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp); + + /* pitch target */ + snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget); + + /* trigger voice */ + snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK); +} + +#define MOD_SENSE 18 + +/* set lfo1 modulation height and cutoff */ +static void +set_fmmod(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fmmod; + short pitch; + unsigned char cutoff; + int modulation; + + pitch = (char)(vp->reg.parm.fmmod>>8); + cutoff = (vp->reg.parm.fmmod & 0xff); + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fmmod = ((unsigned char)pitch<<8) | cutoff; + snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod); +} + +/* set lfo2 pitch & frequency */ +static void +set_fm2frq2(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned short fm2frq2; + short pitch; + unsigned char freq; + int modulation; + + pitch = (char)(vp->reg.parm.fm2frq2>>8); + freq = vp->reg.parm.fm2frq2 & 0xff; + modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; + pitch += (MOD_SENSE * modulation) / 1200; + LIMITVALUE(pitch, -128, 127); + fm2frq2 = ((unsigned char)pitch<<8) | freq; + snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2); +} + +/* set filterQ */ +static void +set_filterQ(emu10k1_t *hw, snd_emux_voice_t *vp) +{ + unsigned int val; + val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE; + val |= (vp->reg.parm.filterQ << 28); + snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val); +} diff -Nru linux/sound/pci/emu10k1/emu10k1_main.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_main.c --- linux/sound/pci/emu10k1/emu10k1_main.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_main.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,677 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include + +#if 0 +MODULE_AUTHOR("Jaroslav Kysela , Creative Labs, Inc."); +MODULE_DESCRIPTION("Routines for control of EMU10K1 chips"); +MODULE_LICENSE("GPL"); +#endif + +/************************************************************************* + * EMU10K1 init / done + *************************************************************************/ + +void snd_emu10k1_voice_init(emu10k1_t * emu, int ch) +{ + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + snd_emu10k1_ptr_write(emu, IP, ch, 0); + snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + snd_emu10k1_ptr_write(emu, CCR, ch, 0); + + snd_emu10k1_ptr_write(emu, PSST, ch, 0); + snd_emu10k1_ptr_write(emu, DSL, ch, 0x10); + snd_emu10k1_ptr_write(emu, CCCA, ch, 0); + snd_emu10k1_ptr_write(emu, Z1, ch, 0); + snd_emu10k1_ptr_write(emu, Z2, ch, 0); + snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000); + + snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0); + snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0); + snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PEFE, ch, 0); + snd_emu10k1_ptr_write(emu, FMMOD, ch, 0); + snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0); + + /*** these are last so OFF prevents writing ***/ + snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0); + snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0); + snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0); + + /* Audigy extra stuffs */ + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100); + snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0); + } +} + +static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) +{ + int ch, idx, err; + unsigned int silent_page; + + emu->fx8010.itram_size = (16 * 1024)/2; + emu->fx8010.etram_size = 0; + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + + /* disable channel interrupt */ + outl(0, emu->port + INTE); + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + if (emu->audigy){ + snd_emu10k1_ptr_write(emu, 0x5e, 0, 0xf00); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x5f, 0, 0x3); /* ?? */ + } + + /* init envelope engine */ + for (ch = 0; ch < NUM_G; ch++) { + emu->voices[ch].emu = emu; + emu->voices[ch].number = ch; + snd_emu10k1_voice_init(emu, ch); + } + + /* + * Init to 0x02109204 : + * Clock accuracy = 0 (1000ppm) + * Sample Rate = 2 (48kHz) + * Audio Channel = 1 (Left of 2) + * Source Number = 0 (Unspecified) + * Generation Status = 1 (Original for Cat Code 12) + * Cat Code = 12 (Digital Signal Mixer) + * Mode = 0 (Mode 0) + * Emphasis = 0 (None) + * CP = 1 (Copyright unasserted) + * AN = 0 (Audio data) + * P = 0 (Consumer) + */ + snd_emu10k1_ptr_write(emu, SPCS0, 0, + emu->spdif_bits[0] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_emu10k1_ptr_write(emu, SPCS1, 0, + emu->spdif_bits[1] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_emu10k1_ptr_write(emu, SPCS2, 0, + emu->spdif_bits[2] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + /* + * Clear page with silence & setup all pointers to this page + */ + memset(emu->silent_page, 0, PAGE_SIZE); + silent_page = emu->silent_page_dmaaddr << 1; + for (idx = 0; idx < MAXPAGES; idx++) + emu->ptb_pages[idx] = silent_page | idx; + snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages_dmaaddr); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ + snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */ + + silent_page = (emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page); + snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); + } + + /* + * Hokay, setup HCFG + * Mute Disable Audio = 0 + * Lock Tank Memory = 1 + * Lock Sound Memory = 0 + * Auto Mute = 1 + */ + if (emu->audigy) + outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + else if (emu->model == 0x20 || + emu->model == 0xc400 || + (emu->model == 0x21 && emu->revision < 6)) + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG); + else + // With on-chip joystick + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + + if (enable_ir) { /* enable IR for SB Live */ + unsigned int reg = inl(emu->port + HCFG); + outl(reg | HCFG_GPOUT2, emu->port + HCFG); + udelay(500); + outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG); + udelay(100); + outl(reg, emu->port + HCFG); + } + + if (!emu->APS) { /* enable analog output */ + unsigned int reg = inl(emu->port + HCFG); + outl(reg | HCFG_GPOUT0, emu->port + HCFG); + } + + /* + * Initialize the effect engine + */ + if ((err = snd_emu10k1_init_efx(emu)) < 0) + return err; + + /* + * Enable the audio bit + */ + outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); + + /* Enable analog/digital outs on audigy */ + if (emu->audigy) + outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); + +#if 0 + { + unsigned int tmp; + /* FIXME: the following routine disables LiveDrive-II !! */ + // TOSLink detection + emu->tos_link = 0; + tmp = inl(emu->port + HCFG); + if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { + outl(tmp|0x800, emu->port + HCFG); + udelay(50); + if (tmp != (inl(emu->port + HCFG) & ~0x800)) { + emu->tos_link = 1; + outl(tmp, emu->port + HCFG); + } + } + } +#endif + + snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE); + + emu->reserved_page = (emu10k1_memblk_t *)snd_emu10k1_synth_alloc(emu, 4096); + if (emu->reserved_page) + emu->reserved_page->map_locked = 1; + + return 0; +} + +static int snd_emu10k1_done(emu10k1_t * emu) +{ + int ch; + + outl(0, emu->port + INTE); + + /* + * Shutdown the chip + */ + for (ch = 0; ch < NUM_G; ch++) + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, VTFT, ch, 0); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + } + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, 0); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, 0); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, 0x8000); + + /* disable channel interrupt */ + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + /* remove reserved page */ + if (emu->reserved_page != NULL) { + snd_emu10k1_synth_free(emu, (snd_util_memblk_t *)emu->reserved_page); + emu->reserved_page = NULL; + } + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); + snd_emu10k1_ptr_write(emu, PTB, 0, 0); + + snd_emu10k1_free_efx(emu); + + return 0; +} + +/************************************************************************* + * ECARD functional implementation + *************************************************************************/ + +/* In A1 Silicon, these bits are in the HC register */ +#define HOOKN_BIT (1L << 12) +#define HANDN_BIT (1L << 11) +#define PULSEN_BIT (1L << 10) + +#define EC_GDI1 (1 << 13) +#define EC_GDI0 (1 << 14) + +#define EC_NUM_CONTROL_BITS 20 + +#define EC_AC3_DATA_SELN 0x0001L +#define EC_EE_DATA_SEL 0x0002L +#define EC_EE_CNTRL_SELN 0x0004L +#define EC_EECLK 0x0008L +#define EC_EECS 0x0010L +#define EC_EESDO 0x0020L +#define EC_TRIM_CSN 0x0040L +#define EC_TRIM_SCLK 0x0080L +#define EC_TRIM_SDATA 0x0100L +#define EC_TRIM_MUTEN 0x0200L +#define EC_ADCCAL 0x0400L +#define EC_ADCRSTN 0x0800L +#define EC_DACCAL 0x1000L +#define EC_DACMUTEN 0x2000L +#define EC_LEDN 0x4000L + +#define EC_SPDIF0_SEL_SHIFT 15 +#define EC_SPDIF1_SEL_SHIFT 17 +#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT) +#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT) +#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK) +#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK) +#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should + * be incremented any time the EEPROM's + * format is changed. */ + +#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */ + +/* Addresses for special values stored in to EEPROM */ +#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */ +#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */ +#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */ + +#define EC_LAST_PROMFILE_ADDR 0x2f + +#define EC_SERIALNUM_ADDR 0x30 /* First word of serial number. The + * can be up to 30 characters in length + * and is stored as a NULL-terminated + * ASCII string. Any unused bytes must be + * filled with zeros */ +#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */ + + +/* Most of this stuff is pretty self-evident. According to the hardware + * dudes, we need to leave the ADCCAL bit low in order to avoid a DC + * offset problem. Weird. + */ +#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \ + EC_TRIM_CSN) + + +#define EC_DEFAULT_ADC_GAIN 0xC4C4 +#define EC_DEFAULT_SPDIF0_SEL 0x0 +#define EC_DEFAULT_SPDIF1_SEL 0x4 + +/************************************************************************** + * @func Clock bits into the Ecard's control latch. The Ecard uses a + * control latch will is loaded bit-serially by toggling the Modem control + * lines from function 2 on the E8010. This function hides these details + * and presents the illusion that we are actually writing to a distinct + * register. + */ + +static void snd_emu10k1_ecard_write(emu10k1_t * emu, unsigned int value) +{ + unsigned short count; + unsigned int data; + unsigned long hc_port; + unsigned int hc_value; + + hc_port = emu->port + HCFG; + hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT); + outl(hc_value, hc_port); + + for (count = 0; count < EC_NUM_CONTROL_BITS; count++) { + + /* Set up the value */ + data = ((value & 0x1) ? PULSEN_BIT : 0); + value >>= 1; + + outl(hc_value | data, hc_port); + + /* Clock the shift register */ + outl(hc_value | data | HANDN_BIT, hc_port); + outl(hc_value | data, hc_port); + } + + /* Latch the bits */ + outl(hc_value | HOOKN_BIT, hc_port); + outl(hc_value, hc_port); +} + +/************************************************************************** + * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The + * trim value consists of a 16bit value which is composed of two + * 8 bit gain/trim values, one for the left channel and one for the + * right channel. The following table maps from the Gain/Attenuation + * value in decibels into the corresponding bit pattern for a single + * channel. + */ + +static void snd_emu10k1_ecard_setadcgain(emu10k1_t * emu, + unsigned short gain) +{ + unsigned int bit; + + /* Enable writing to the TRIM registers */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + /* Do it again to insure that we meet hold time requirements */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + for (bit = (1 << 15); bit; bit >>= 1) { + unsigned int value; + + value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA); + + if (gain & bit) + value |= EC_TRIM_SDATA; + + /* Clock the bit */ + snd_emu10k1_ecard_write(emu, value); + snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK); + snd_emu10k1_ecard_write(emu, value); + } + + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); +} + +static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu) +{ + unsigned int hc_value; + + /* Set up the initial settings */ + emu->ecard_ctrl = EC_RAW_RUN_MODE | + EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) | + EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL); + + /* Step 0: Set the codec type in the hardware control register + * and enable audio output */ + hc_value = inl(emu->port + HCFG); + outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG); + inl(emu->port + HCFG); + + /* Step 1: Turn off the led and deassert TRIM_CS */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 2: Calibrate the ADC and DAC */ + snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 3: Wait for awhile; XXX We can't get away with this + * under a real operating system; we'll need to block and wait that + * way. */ + snd_emu10k1_wait(emu, 48000); + + /* Step 4: Switch off the DAC and ADC calibration. Note + * That ADC_CAL is actually an inverted signal, so we assert + * it here to stop calibration. */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 4: Switch into run mode */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); + + /* Step 5: Set the analog input gain */ + snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN); + + return 0; +} + +/* + * Create the EMU10K1 instance + */ + +static int snd_emu10k1_free(emu10k1_t *emu) +{ + snd_emu10k1_proc_done(emu); + if (emu->res_port != NULL) { /* avoid access to already used hardware */ + snd_emu10k1_fx8010_tram_setup(emu, 0); + snd_emu10k1_done(emu); + } + if (emu->memhdr) + snd_util_memhdr_free(emu->memhdr); + if (emu->silent_page) + snd_free_pci_pages(emu->pci, EMUPAGESIZE, emu->silent_page, emu->silent_page_dmaaddr); + if (emu->ptb_pages) + snd_free_pci_pages(emu->pci, 32 * 1024, (void *)emu->ptb_pages, emu->ptb_pages_dmaaddr); + if (emu->page_ptr_table) + vfree(emu->page_ptr_table); + if (emu->page_addr_table) + vfree(emu->page_addr_table); + if (emu->res_port) { + release_resource(emu->res_port); + kfree_nocheck(emu->res_port); + } + if (emu->irq >= 0) + free_irq(emu->irq, (void *)emu); + snd_magic_kfree(emu); + return 0; +} + +static int snd_emu10k1_dev_free(snd_device_t *device) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, device->device_data, return -ENXIO); + return snd_emu10k1_free(emu); +} + +int __devinit snd_emu10k1_create(snd_card_t * card, + struct pci_dev * pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + emu10k1_t ** remu) +{ + emu10k1_t *emu; + int err; + static snd_device_ops_t ops = { + dev_free: snd_emu10k1_dev_free, + }; + + *remu = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 31 bits */ + if (!pci_dma_supported(pci, 0x7fffffff)) { + snd_printk("architecture does not support 31bit PCI busmaster DMA\n"); + return -ENXIO; + } + if (pci->driver_data) + pci_set_dma_mask(pci, 0xffffffff); /* audigy */ + else + pci_set_dma_mask(pci, 0x7fffffff); + + emu = snd_magic_kcalloc(emu10k1_t, 0, GFP_KERNEL); + if (emu == NULL) + return -ENOMEM; + emu->card = card; + spin_lock_init(&emu->reg_lock); + spin_lock_init(&emu->emu_lock); + spin_lock_init(&emu->voice_lock); + spin_lock_init(&emu->synth_lock); + spin_lock_init(&emu->memblk_lock); + init_MUTEX(&emu->ptb_lock); + init_MUTEX(&emu->fx8010.lock); + INIT_LIST_HEAD(&emu->mapped_link_head); + INIT_LIST_HEAD(&emu->mapped_order_link_head); + emu->pci = pci; + emu->irq = -1; + emu->synth = NULL; + emu->get_synth_voice = NULL; + emu->port = pci_resource_start(pci, 0); + + // emu->audigy = (int)pci->driver_data; + if (pci->device == 0x0004) + emu->audigy = 1; + + if (emu->audigy) + emu->gpr_base = A_FXGPREGBASE; + else + emu->gpr_base = FXGPREGBASE; + + if ((emu->res_port = request_region(emu->port, 0x20, "EMU10K1")) == NULL) { + snd_emu10k1_free(emu); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) { + snd_emu10k1_free(emu); + return -EBUSY; + } + emu->irq = pci->irq; + + emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT; + emu->ptb_pages = snd_malloc_pci_pages(pci, 32 * 1024, &emu->ptb_pages_dmaaddr); + if (emu->ptb_pages == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + + emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*)); + emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long)); + if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + + emu->silent_page = snd_malloc_pci_pages(pci, EMUPAGESIZE, &emu->silent_page_dmaaddr); + if (emu->silent_page == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE); + if (emu->memhdr == NULL) { + snd_emu10k1_free(emu); + return -ENOMEM; + } + emu->memhdr->block_extra_size = sizeof(emu10k1_memblk_t) - sizeof(snd_util_memblk_t); + + pci_set_master(pci); + /* read revision & serial */ + pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&emu->revision); + pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model); + emu->card_type = EMU10K1_CARD_CREATIVE; + if (emu->serial == 0x40011102) { + emu->card_type = EMU10K1_CARD_EMUAPS; + emu->APS = 1; + } + + emu->fx8010.fxbus_mask = 0x303f; + if (extin_mask == 0) + extin_mask = 0x1fcf; + if (extout_mask == 0) + extout_mask = 0x3fff; + emu->fx8010.extin_mask = extin_mask; + emu->fx8010.extout_mask = extout_mask; + + if (emu->APS) { + if ((err = snd_emu10k1_ecard_init(emu)) < 0) { + snd_emu10k1_free(emu); + return err; + } + } else { + /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version + does not support this, it shouldn't do any harm */ + snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); + } + + if ((err = snd_emu10k1_init(emu, enable_ir)) < 0) { + snd_emu10k1_free(emu); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0) { + snd_emu10k1_free(emu); + return err; + } + + snd_emu10k1_proc_init(emu); + + *remu = emu; + return 0; +} + +/* memory.c */ +EXPORT_SYMBOL(snd_emu10k1_synth_alloc); +EXPORT_SYMBOL(snd_emu10k1_synth_free); +EXPORT_SYMBOL(snd_emu10k1_synth_bzero); +EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user); +EXPORT_SYMBOL(snd_emu10k1_memblk_map); +/* voice.c */ +EXPORT_SYMBOL(snd_emu10k1_voice_alloc); +EXPORT_SYMBOL(snd_emu10k1_voice_free); +/* io.c */ +EXPORT_SYMBOL(snd_emu10k1_ptr_read); +EXPORT_SYMBOL(snd_emu10k1_ptr_write); diff -Nru linux/sound/pci/emu10k1/emu10k1_patch.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_patch.c --- linux/sound/pci/emu10k1/emu10k1_patch.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_patch.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,224 @@ +/* + * Patch transfer callback for Emu10k1 + * + * Copyright (C) 2000 Takashi iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * All the code for loading in a patch. There is very little that is + * chip specific here. Just the actual writing to the board. + */ + +#define __NO_VERSION__ +#include "emu10k1_synth_local.h" + +/* + */ +#define BLANK_LOOP_START 4 +#define BLANK_LOOP_END 8 +#define BLANK_LOOP_SIZE 12 +#define BLANK_HEAD_SIZE 32 + +/* + * allocate a sample block and copy data from userspace + */ +int +snd_emu10k1_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr, const void *data, long count) +{ + int offset; + int truesize, size, loopsize, blocksize; + int loopend, sampleend; + unsigned int start_addr; + emu10k1_t *emu; + + emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + snd_assert(sp != NULL, return -EINVAL); + snd_assert(hdr != NULL, return -EINVAL); + + if (sp->v.size == 0) { + snd_printd("emu: rom font for sample %d\n", sp->v.sample); + return 0; + } + + /* recalculate address offset */ + sp->v.end -= sp->v.start; + sp->v.loopstart -= sp->v.start; + sp->v.loopend -= sp->v.start; + sp->v.start = 0; + + /* some samples have invalid data. the addresses are corrected in voice info */ + sampleend = sp->v.end; + if (sampleend > sp->v.size) + sampleend = sp->v.size; + loopend = sp->v.loopend; + if (loopend > sampleend) + loopend = sampleend; + + /* be sure loop points start < end */ + if (sp->v.loopstart >= sp->v.loopend) { + int tmp = sp->v.loopstart; + sp->v.loopstart = sp->v.loopend; + sp->v.loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->v.size + BLANK_HEAD_SIZE; + loopsize = 0; +#if 0 /* not supported */ + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) + loopsize = sp->v.loopend - sp->v.loopstart; + truesize += loopsize; +#endif + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + + /* try to allocate a memory block */ + blocksize = truesize; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + blocksize *= 2; + sp->block = snd_emu10k1_synth_alloc(emu, blocksize); + if (sp->block == NULL) { + snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize); + /* not ENOMEM (for compatibility with OSS) */ + return -ENOSPC; + } + /* set the total size */ + sp->v.truesize = blocksize; + + /* write blank samples at head */ + offset = 0; + size = BLANK_HEAD_SIZE; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + snd_assert(offset + size <= blocksize, return -EINVAL); + snd_emu10k1_synth_bzero(emu, sp->block, offset, size); + offset += size; + + /* copy start->loopend */ + size = loopend; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + snd_assert(offset + size <= blocksize, return -EINVAL); + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; + } + offset += size; + data += size; + +#if 0 /* not suppported yet */ + /* handle reverse (or bidirectional) loop */ + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { + /* copy loop in reverse */ + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { + int woffset; + unsigned short *wblock = (unsigned short*)block; + woffset = offset / 2; + snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL); + for (i = 0; i < loopsize; i++) + wblock[woffset + i] = wblock[woffset - i -1]; + offset += loopsize * 2; + } else { + snd_assert(offset + loopsize <= blocksize, return -EINVAL); + for (i = 0; i < loopsize; i++) + block[offset + i] = block[offset - i -1]; + offset += loopsize; + } + + /* modify loop pointers */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { + sp->v.loopend += loopsize; + } else { + sp->v.loopstart += loopsize; + sp->v.loopend += loopsize; + } + /* add sample pointer */ + sp->v.end += loopsize; + } +#endif + + /* loopend -> sample end */ + size = sp->v.size - loopend; + snd_assert(size >= 0, return -EINVAL); + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + size *= 2; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; + } + offset += size; + + /* clear rest of samples (if any) */ + if (offset < blocksize) + snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); + + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + +#if 0 /* not supported yet */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { + /* unsigned -> signed */ + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { + unsigned short *wblock = (unsigned short*)block; + for (i = 0; i < truesize; i++) + wblock[i] ^= 0x8000; + } else { + for (i = 0; i < truesize; i++) + block[i] ^= 0x80; + } + } +#endif + + /* recalculate offset */ + start_addr = BLANK_HEAD_SIZE * 2; + if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) + start_addr >>= 1; + sp->v.start += start_addr; + sp->v.end += start_addr; + sp->v.loopstart += start_addr; + sp->v.loopend += start_addr; + + return 0; +} + +/* + * free a sample block + */ +int +snd_emu10k1_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, + snd_util_memhdr_t *hdr) +{ + emu10k1_t *emu; + + emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + snd_assert(sp != NULL, return -EINVAL); + snd_assert(hdr != NULL, return -EINVAL); + + if (sp->block) { + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + } + return 0; +} + diff -Nru linux/sound/pci/emu10k1/emu10k1_synth.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth.c --- linux/sound/pci/emu10k1/emu10k1_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Routines for control of EMU10K1 WaveTable synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "emu10k1_synth_local.h" +#include + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth"); +MODULE_LICENSE("GPL"); + +/* + * create a new hardware dependent device for Emu10k1 + */ +int snd_emu10k1_synth_new_device(snd_seq_device_t *dev) +{ + snd_emux_t *emu; + emu10k1_t *hw; + snd_emu10k1_synth_arg_t *arg; + unsigned long flags; + + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (arg == NULL) + return -EINVAL; + + if (arg->seq_ports <= 0) + return 0; /* nothing */ + if (arg->max_voices < 1) + arg->max_voices = 1; + else if (arg->max_voices > 64) + arg->max_voices = 64; + + if (snd_emux_new(&emu) < 0) + return -ENOMEM; + + snd_emu10k1_ops_setup(emu); + emu->hw = hw = arg->hwptr; + emu->max_voices = arg->max_voices; + emu->num_ports = arg->seq_ports; + emu->pitch_shift = -501; + emu->memhdr = hw->memhdr; + emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */ + emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */ + + if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) { + snd_emux_free(emu); + emu->hw = NULL; + return -ENOMEM; + } + + spin_lock_irqsave(&hw->voice_lock, flags); + hw->synth = emu; + hw->get_synth_voice = snd_emu10k1_synth_get_voice; + spin_unlock_irqrestore(&hw->voice_lock, flags); + + dev->driver_data = emu; + + return 0; +} + +int snd_emu10k1_synth_delete_device(snd_seq_device_t *dev) +{ + snd_emux_t *emu; + emu10k1_t *hw; + unsigned long flags; + + if (dev->driver_data == NULL) + return 0; /* not registered actually */ + + emu = snd_magic_cast(snd_emux_t, dev->driver_data, return -EINVAL); + + hw = snd_magic_cast(emu10k1_t, emu->hw, return -EINVAL); + spin_lock_irqsave(&hw->voice_lock, flags); + hw->synth = NULL; + hw->get_synth_voice = NULL; + spin_unlock_irqrestore(&hw->voice_lock, flags); + + snd_emux_free(emu); + return 0; +} + + +EXPORT_NO_SYMBOLS; + + +/* + * INIT part + */ + +static int __init alsa_emu10k1_synth_init(void) +{ + + static snd_seq_dev_ops_t ops = { + snd_emu10k1_synth_new_device, + snd_emu10k1_synth_delete_device, + }; + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, sizeof(snd_emu10k1_synth_arg_t)); +} + +static void __exit alsa_emu10k1_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH); +} + +module_init(alsa_emu10k1_synth_init) +module_exit(alsa_emu10k1_synth_exit) diff -Nru linux/sound/pci/emu10k1/emu10k1_synth_local.h linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth_local.h --- linux/sound/pci/emu10k1/emu10k1_synth_local.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emu10k1_synth_local.h Mon Apr 8 22:31:24 2002 @@ -0,0 +1,38 @@ +#ifndef __EMU10K1_SYNTH_LOCAL_H +#define __EMU10K1_SYNTH_LOCAL_H +/* + * Local defininitons for Emu10k1 wavetable + * + * Copyright (C) 2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +/* emu10k1_patch.c */ +int snd_emu10k1_sample_new(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *_data, long count); +int snd_emu10k1_sample_free(snd_emux_t *private_data, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); +int snd_emu10k1_memhdr_init(snd_emux_t *emu); + +/* emu10k1_callback.c */ +void snd_emu10k1_ops_setup(snd_emux_t *emu); +int snd_emu10k1_synth_get_voice(emu10k1_t *hw); + + +#endif /* __EMU10K1_SYNTH_LOCAL_H */ diff -Nru linux/sound/pci/emu10k1/emufx.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emufx.c --- linux/sound/pci/emu10k1/emufx.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emufx.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2227 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for effect processor FX8010 + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#define chip_t emu10k1_t + +#if 0 /* for testing purposes - digital out -> capture */ +#define EMU10K1_CAPTURE_DIGITAL_OUT +#endif +#if 0 /* for testing purposes - set S/PDIF to AC3 output */ +#define EMU10K1_SET_AC3_IEC958 +#endif +#if 0 /* for testing purposes - feed the front signal to Center/LFE outputs */ +#define EMU10K1_CENTER_LFE_FROM_FRONT +#endif + +/* + * Tables + */ + +static char *fxbuses[16] = { + /* 0x00 */ "PCM Left", + /* 0x01 */ "PCM Right", + /* 0x02 */ "PCM Surround Left", + /* 0x03 */ "PCM Surround Right", + /* 0x04 */ "MIDI Left", + /* 0x05 */ "MIDI Right", + /* 0x06 */ "Center", + /* 0x07 */ "LFE", + /* 0x08 */ NULL, + /* 0x09 */ NULL, + /* 0x0a */ NULL, + /* 0x0b */ NULL, + /* 0x0c */ "MIDI Reverb", + /* 0x0d */ "MIDI Chorus", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *creative_ins[16] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "TTL IEC958 Left", + /* 0x03 */ "TTL IEC958 Right", + /* 0x04 */ "Zoom Video Left", + /* 0x05 */ "Zoom Video Right", + /* 0x06 */ "Optical IEC958 Left", + /* 0x07 */ "Optical IEC958 Right", + /* 0x08 */ "Line/Mic 1 Left", + /* 0x09 */ "Line/Mic 1 Right", + /* 0x0a */ "Coaxial IEC958 Left", + /* 0x0b */ "Coaxial IEC958 Right", + /* 0x0c */ "Line/Mic 2 Left", + /* 0x0d */ "Line/Mic 2 Right", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *audigy_ins[16] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "Audigy CD Left", + /* 0x03 */ "Audigy CD Right", + /* 0x04 */ NULL, + /* 0x05 */ NULL, + /* 0x06 */ NULL, + /* 0x07 */ NULL, + /* 0x08 */ "Line/Mic 2 Left", + /* 0x09 */ "Line/Mic 2 Right", + /* 0x0a */ NULL, + /* 0x0b */ NULL, + /* 0x0c */ "Aux2 Left", + /* 0x0d */ "Aux2 Right", + /* 0x0e */ NULL, + /* 0x0f */ NULL +}; + +static char *creative_outs[32] = { + /* 0x00 */ "AC97 Left", + /* 0x01 */ "AC97 Right", + /* 0x02 */ "Optical IEC958 Left", + /* 0x03 */ "Optical IEC958 Right", + /* 0x04 */ "Center", + /* 0x05 */ "LFE", + /* 0x06 */ "Headphone Left", + /* 0x07 */ "Headphone Right", + /* 0x08 */ "Surround Left", + /* 0x09 */ "Surround Right", + /* 0x0a */ "PCM Capture Left", + /* 0x0b */ "PCM Capture Right", + /* 0x0c */ "MIC Capture", + /* 0x0d */ NULL, + /* 0x0e */ NULL, + /* 0x0f */ NULL, + /* 0x10 */ NULL, + /* 0x11 */ "Analog Center", + /* 0x12 */ "Analog LFE", + /* 0x13 */ NULL, + /* 0x14 */ NULL, + /* 0x15 */ NULL, + /* 0x16 */ NULL, + /* 0x17 */ NULL, + /* 0x18 */ NULL, + /* 0x19 */ NULL, + /* 0x1a */ NULL, + /* 0x1b */ NULL, + /* 0x1c */ NULL, + /* 0x1d */ NULL, + /* 0x1e */ NULL, + /* 0x1f */ NULL, +}; + +static char *audigy_outs[32] = { + /* 0x00 */ "Digital Front Left", + /* 0x01 */ "Digital Front Right", + /* 0x02 */ "Digital Center", + /* 0x03 */ "Digital LEF", + /* 0x04 */ "Headphone Left", + /* 0x05 */ "Headphone Right", + /* 0x06 */ "Digital Rear Left", + /* 0x07 */ "Digital Rear Right", + /* 0x08 */ "Front Left", + /* 0x09 */ "Front Right", + /* 0x0a */ "Center", + /* 0x0b */ "LFE", + /* 0x0c */ NULL, + /* 0x0d */ NULL, + /* 0x0e */ "Rear Left", + /* 0x0f */ "Rear Right", + /* 0x10 */ "AC97 Front Left", + /* 0x11 */ "AC97 Front Right", + /* 0x12 */ "ADC Caputre Left", + /* 0x13 */ "ADC Capture Right", + /* 0x14 */ NULL, + /* 0x15 */ NULL, + /* 0x16 */ NULL, + /* 0x17 */ NULL, + /* 0x18 */ NULL, + /* 0x19 */ NULL, + /* 0x1a */ NULL, + /* 0x1b */ NULL, + /* 0x1c */ NULL, + /* 0x1d */ NULL, + /* 0x1e */ NULL, + /* 0x1f */ NULL, +}; + +static const u32 bass_table[41][5] = { + { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 }, + { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d }, + { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee }, + { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c }, + { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b }, + { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 }, + { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f }, + { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 }, + { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 }, + { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 }, + { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 }, + { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be }, + { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b }, + { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c }, + { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b }, + { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 }, + { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a }, + { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 }, + { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 }, + { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e }, + { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 }, + { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 }, + { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 }, + { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 }, + { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e }, + { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 }, + { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 }, + { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 }, + { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 }, + { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 }, + { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca }, + { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 }, + { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 }, + { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 }, + { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 }, + { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a }, + { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f }, + { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 }, + { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 }, + { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 } +}; + +static const u32 treble_table[41][5] = { + { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 }, + { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 }, + { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 }, + { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca }, + { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 }, + { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 }, + { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 }, + { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 }, + { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 }, + { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df }, + { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff }, + { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 }, + { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c }, + { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 }, + { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 }, + { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 }, + { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 }, + { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d }, + { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 }, + { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 }, + { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f }, + { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb }, + { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 }, + { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 }, + { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd }, + { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 }, + { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad }, + { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 }, + { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 }, + { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c }, + { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 }, + { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 }, + { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 }, + { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 }, + { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 }, + { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 }, + { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d }, + { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b }, + { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 }, + { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd }, + { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 } +}; + +static const u32 db_table[101] = { + 0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540, + 0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8, + 0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1, + 0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0, + 0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9, + 0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb, + 0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005, + 0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d, + 0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd, + 0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8, + 0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481, + 0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333, + 0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d, + 0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6, + 0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d, + 0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf, + 0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038, + 0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a, + 0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea, + 0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272, + 0x7fffffff, +}; + +static const u32 onoff_table[2] = { + 0x00000000, 0x00000001 +}; + +/* + */ + +static inline mm_segment_t snd_enter_user(void) +{ + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + return fs; +} + +static inline void snd_leave_user(mm_segment_t fs) +{ + set_fs(fs); +} + +/* + * controls + */ + +static int snd_emu10k1_gpr_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + + if (ctl->min == 0 && ctl->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ctl->vcount; + uinfo->value.integer.min = ctl->min; + uinfo->value.integer.max = ctl->max; + return 0; +} + +static int snd_emu10k1_gpr_ctl_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + unsigned long flags; + int i; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (i = 0; i < ctl->vcount; i++) + ucontrol->value.integer.value[i] = ctl->value[i]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_gpr_ctl_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + snd_emu10k1_fx8010_ctl_t *ctl = (snd_emu10k1_fx8010_ctl_t *)kcontrol->private_value; + unsigned long flags; + unsigned int nval, val; + int i, j, change = 0; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (i = 0; i < ctl->vcount; i++) { + nval = ucontrol->value.integer.value[i]; + if (nval < ctl->min) + nval = ctl->min; + if (nval > ctl->max) + nval = ctl->max; + if (nval != ctl->value[i]) + change = 1; + val = ctl->value[i] = nval; + switch (ctl->translation) { + case EMU10K1_GPR_TRANSLATION_NONE: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val); + break; + case EMU10K1_GPR_TRANSLATION_TABLE100: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]); + break; + case EMU10K1_GRP_TRANSLATION_BASS: + snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); + for (j = 0; j < 5; j++) + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]); + break; + case EMU10K1_GRP_TRANSLATION_TREBLE: + snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); + for (j = 0; j < 5; j++) + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]); + break; + case EMU10K1_GPR_TRANSLATION_ONOFF: + snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]); + break; + } + } + __error: + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +/* + * Interrupt handler + */ + +static void snd_emu10k1_fx8010_interrupt(emu10k1_t *emu) +{ + snd_emu10k1_fx8010_irq_t *irq, *nirq; + + irq = emu->fx8010.irq_handlers; + while (irq) { + nirq = irq->next; /* irq ptr can be removed from list */ + if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) { + if (irq->handler) + irq->handler(emu, irq->private_data); + snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1); + } + irq = nirq; + } +} + +static int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu, + snd_fx8010_irq_handler_t *handler, + unsigned char gpr_running, + void *private_data, + snd_emu10k1_fx8010_irq_t **r_irq) +{ + snd_emu10k1_fx8010_irq_t *irq; + unsigned long flags; + + snd_runtime_check(emu, return -EINVAL); + snd_runtime_check(handler, return -EINVAL); + irq = kmalloc(sizeof(*irq), GFP_KERNEL); + if (irq == NULL) + return -ENOMEM; + irq->handler = handler; + irq->gpr_running = gpr_running; + irq->private_data = private_data; + irq->next = NULL; + spin_lock_irqsave(&emu->fx8010.irq_lock, flags); + if (emu->fx8010.irq_handlers == NULL) { + emu->fx8010.irq_handlers = irq; + emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt; + snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE); + } else { + irq->next = emu->fx8010.irq_handlers; + emu->fx8010.irq_handlers = irq; + } + spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); + if (r_irq) + *r_irq = irq; + return 0; +} + +static int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu, + snd_emu10k1_fx8010_irq_t *irq) +{ + snd_emu10k1_fx8010_irq_t *tmp; + unsigned long flags; + + snd_runtime_check(irq, return -EINVAL); + spin_lock_irqsave(&emu->fx8010.irq_lock, flags); + if ((tmp = emu->fx8010.irq_handlers) == irq) { + emu->fx8010.irq_handlers = tmp->next; + if (emu->fx8010.irq_handlers == NULL) { + snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); + emu->dsp_interrupt = NULL; + } + } else { + while (tmp && tmp->next != irq) + tmp = tmp->next; + if (tmp) + tmp->next = tmp->next->next; + } + spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); + kfree(irq); + return 0; +} + +/* + * PCM streams + */ + +#define INITIAL_TRAM_SHIFT 14 +#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) + +static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data) +{ + snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, private_data, return); + snd_pcm_period_elapsed(substream); +} + +static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, + unsigned short *dst_right, + unsigned short *src, + unsigned int count, + unsigned int tram_shift) +{ + // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); + if ((tram_shift & 1) == 0) { + while (count--) { + *dst_left-- = *src++; + *dst_right-- = *src++; + } + } else { + while (count--) { + *dst_right-- = *src++; + *dst_left-- = *src++; + } + } +} + +static void snd_emu10k1_fx8010_playback_tram_poke(emu10k1_t *emu, + unsigned int *tram_pos, + unsigned int *tram_shift, + unsigned int tram_size, + unsigned short *src, + unsigned int frames) +{ + unsigned int count; + + while (frames > *tram_pos) { + count = *tram_pos + 1; + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + src, count, *tram_shift); + src += count * 2; + frames -= count; + *tram_pos = (tram_size / 2) - 1; + (*tram_shift)++; + } + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + src, frames, *tram_shift++); + *tram_pos -= frames; +} + +static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream, + snd_pcm_uframes_t frames) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - pcm->appl_ptr; + snd_pcm_uframes_t buffer_size = pcm->buffer_size / 2; + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + pcm->sw_ready += diff; + } + pcm->sw_ready += frames; + pcm->appl_ptr = appl_ptr + frames; + while (pcm->hw_ready < buffer_size && + pcm->sw_ready > 0) { + size_t hw_to_end = buffer_size - pcm->hw_data; + size_t sw_to_end = (runtime->buffer_size << 2) - pcm->sw_data; + size_t tframes = buffer_size - pcm->hw_ready; + if (pcm->sw_ready < tframes) + tframes = pcm->sw_ready; + if (hw_to_end < tframes) + tframes = hw_to_end; + if (sw_to_end < tframes) + tframes = sw_to_end; + snd_emu10k1_fx8010_playback_tram_poke(emu, &pcm->tram_pos, &pcm->tram_shift, + pcm->buffer_size, + (unsigned short *)(runtime->dma_area + (pcm->sw_data << 2)), + tframes); + pcm->hw_data += tframes; + if (pcm->hw_data == buffer_size) + pcm->hw_data = 0; + pcm->sw_data += tframes; + if (pcm->sw_data == runtime->buffer_size) + pcm->sw_data = 0; + pcm->hw_ready += tframes; + pcm->sw_ready -= tframes; + } + return 0; +} + +static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + int i; + + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + int i; + + // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); + pcm->sw_data = pcm->sw_io = pcm->sw_ready = 0; + pcm->hw_data = pcm->hw_io = pcm->hw_ready = 0; + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + pcm->appl_ptr = 0; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); + return 0; +} + +static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned long flags; + int result = 0; + + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* follow thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +#ifdef EMU10K1_SET_AC3_IEC958 + { + int i; + for (i = 0; i < 3; i++) { + unsigned int bits; + bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | + 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; + snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); + } + } +#endif + result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); + if (result < 0) + goto __err; + snd_emu10k1_fx8010_playback_transfer(substream, 0); /* roll the ball */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + break; + default: + result = -EINVAL; + break; + } + __err: + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + size_t ptr; + snd_pcm_sframes_t frames; + + if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) + return 0; + ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0); + frames = ptr - pcm->hw_io; + if (frames < 0) + frames += runtime->buffer_size; + pcm->hw_io = ptr; + pcm->hw_ready -= frames; + pcm->sw_io += frames; + if (pcm->sw_io > runtime->buffer_size) + pcm->sw_io -= runtime->buffer_size; + snd_emu10k1_fx8010_playback_transfer(substream, 0); + return pcm->sw_io; +} + +static int snd_emu10k1_fx8010_playback_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t hwoff, + void *src, + snd_pcm_uframes_t frames) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + size_t hwoffb = hwoff << 2; + size_t bytes = frames << 2; + char *hwbuf = runtime->dma_area + hwoffb; + if (copy_from_user(hwbuf, src, bytes)) + return -EFAULT; + spin_lock_irq(&runtime->lock); + snd_emu10k1_fx8010_playback_transfer(substream, frames); + spin_unlock_irq(&runtime->lock); + return 0; +} + +static snd_pcm_hardware_t snd_emu10k1_fx8010_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_48000, + rate_min: 48000, + rate_max: 48000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: (128*1024), + period_bytes_min: 1024, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + runtime->hw = snd_emu10k1_fx8010_playback; + runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; + runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; + spin_lock(&emu->reg_lock); + if (pcm->valid == 0) { + spin_unlock(&emu->reg_lock); + return -ENODEV; + } + pcm->opened = 1; + spin_unlock(&emu->reg_lock); + return 0; +} + +static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + spin_lock(&emu->reg_lock); + pcm->opened = 0; + spin_unlock(&emu->reg_lock); + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = { + open: snd_emu10k1_fx8010_playback_open, + close: snd_emu10k1_fx8010_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_fx8010_playback_hw_params, + hw_free: snd_emu10k1_fx8010_playback_hw_free, + prepare: snd_emu10k1_fx8010_playback_prepare, + trigger: snd_emu10k1_fx8010_playback_trigger, + copy: snd_emu10k1_fx8010_playback_copy, + pointer: snd_emu10k1_fx8010_playback_pointer, +}; + +static void snd_emu10k1_fx8010_pcm_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_fx8010 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1", device, 8, 0, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_fx8010_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 FX8010"); + emu->pcm_fx8010 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +/************************************************************************* + * EMU10K1 effect manager + *************************************************************************/ + +static void snd_emu10k1_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr, + u32 op, u32 r, u32 a, u32 x, u32 y) +{ + snd_assert(*ptr < 512, return); + set_bit(*ptr, &icode->code_valid); + icode->code[*ptr ][0] = ((x & 0x3ff) << 10) | (y & 0x3ff); + icode->code[(*ptr)++][1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff); +} + +#define OP(icode, ptr, op, r, a, x, y) \ + snd_emu10k1_write_op(icode, ptr, op, r, a, x, y) + +static void snd_emu10k1_audigy_write_op(emu10k1_fx8010_code_t *icode, unsigned int *ptr, + u32 op, u32 r, u32 a, u32 x, u32 y) +{ + snd_assert(*ptr < 512, return); + set_bit(*ptr, &icode->code_valid); + icode->code[*ptr ][0] = ((x & 0x7ff) << 12) | (y & 0x7ff); + icode->code[(*ptr)++][1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff); +} + +#define A_OP(icode, ptr, op, r, a, x, y) \ + snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y) + +void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data) +{ + pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + snd_emu10k1_ptr_write(emu, pc, 0, data); +} + +unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc) +{ + pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + return snd_emu10k1_ptr_read(emu, pc, 0); +} + +static void snd_emu10k1_gpr_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int gpr; + + for (gpr = 0; gpr < 0x100; gpr++) { + if (!test_bit(gpr, &icode->gpr_valid)) + continue; + snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, icode->gpr_map[gpr]); + } +} + +static void snd_emu10k1_gpr_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int gpr; + + for (gpr = 0; gpr < 0x100; gpr++) { + set_bit(gpr, &icode->gpr_valid); + icode->gpr_map[gpr] = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0); + } +} + +static void snd_emu10k1_tram_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int tram; + + for (tram = 0; tram < 0xa0; tram++) { + if (!test_bit(tram, &icode->tram_valid)) + continue; + snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, icode->tram_data_map[tram]); + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, icode->tram_addr_map[tram]); + } +} + +static void snd_emu10k1_tram_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int tram; + + for (tram = 0; tram < 0xa0; tram++) { + set_bit(tram, &icode->tram_valid); + icode->tram_data_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0); + icode->tram_addr_map[tram] = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0); + } +} + +static void snd_emu10k1_code_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + u32 pc; + + for (pc = 0; pc < 512; pc++) { + if (!test_bit(pc, &icode->code_valid)) + continue; + snd_emu10k1_efx_write(emu, pc * 2, icode->code[pc][0]); + snd_emu10k1_efx_write(emu, pc * 2 + 1, icode->code[pc][1]); + } +} + +static void snd_emu10k1_code_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + u32 pc; + + for (pc = 0; pc < 512; pc++) { + set_bit(pc, &icode->code_valid); + icode->code[pc][0] = snd_emu10k1_efx_read(emu, pc * 2); + icode->code[pc][1] = snd_emu10k1_efx_read(emu, pc * 2 + 1); + } +} + +static snd_emu10k1_fx8010_ctl_t *snd_emu10k1_look_for_ctl(emu10k1_t *emu, snd_ctl_elem_id_t *id) +{ + snd_emu10k1_fx8010_ctl_t *ctl; + snd_kcontrol_t *kcontrol; + struct list_head *list; + + list_for_each(list, &emu->fx8010.gpr_ctl) { + ctl = emu10k1_gpr_ctl(list); + kcontrol = ctl->kcontrol; + if (kcontrol->id.iface == id->iface && + !strcmp(kcontrol->id.name, id->name) && + kcontrol->id.index == id->index) + return ctl; + } + return NULL; +} + +static int snd_emu10k1_verify_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int i; + snd_ctl_elem_id_t *_id, id; + emu10k1_fx8010_control_gpr_t *_gctl, gctl; + + for (i = 0, _id = icode->gpr_del_controls; + i < icode->gpr_del_control_count; i++, _id++) { + if (copy_from_user(&id, _id, sizeof(id))) + return -EFAULT; + if (snd_emu10k1_look_for_ctl(emu, &id) == NULL) + return -ENOENT; + } + for (i = 0, _gctl = icode->gpr_add_controls; + i < icode->gpr_add_control_count; i++) { + if (copy_from_user(&gctl, _gctl, sizeof(gctl))) + return -EFAULT; + if (snd_emu10k1_look_for_ctl(emu, &gctl.id)) + continue; + if (snd_ctl_find_id(emu->card, &gctl.id) != NULL) + return -EEXIST; + if (gctl.id.iface != SNDRV_CTL_ELEM_IFACE_MIXER && + gctl.id.iface != SNDRV_CTL_ELEM_IFACE_PCM) + return -EINVAL; + } + return 0; +} + +static void snd_emu10k1_ctl_private_free(snd_kcontrol_t *kctl) +{ + snd_emu10k1_fx8010_ctl_t *ctl; + + ctl = (snd_emu10k1_fx8010_ctl_t *)kctl->private_value; + kctl->private_value = 0; + list_del(&ctl->list); + kfree(ctl); +} + +static void snd_emu10k1_add_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int i, j; + emu10k1_fx8010_control_gpr_t *_gctl, gctl; + snd_emu10k1_fx8010_ctl_t *ctl, nctl; + snd_kcontrol_new_t knew; + snd_kcontrol_t *kctl; + snd_ctl_elem_value_t val; + + for (i = 0, _gctl = icode->gpr_add_controls; + i < icode->gpr_add_control_count; i++, _gctl++) { + if (copy_from_user(&gctl, _gctl, sizeof(gctl))) + return; + snd_runtime_check(gctl.id.iface == SNDRV_CTL_ELEM_IFACE_MIXER || + gctl.id.iface == SNDRV_CTL_ELEM_IFACE_PCM, continue); + snd_runtime_check(gctl.id.name[0] != '\0', continue); + ctl = snd_emu10k1_look_for_ctl(emu, &gctl.id); + memset(&knew, 0, sizeof(knew)); + knew.iface = gctl.id.iface; + knew.name = gctl.id.name; + knew.index = gctl.id.index; + knew.info = snd_emu10k1_gpr_ctl_info; + knew.get = snd_emu10k1_gpr_ctl_get; + knew.put = snd_emu10k1_gpr_ctl_put; + memset(&nctl, 0, sizeof(nctl)); + nctl.vcount = gctl.vcount; + nctl.count = gctl.count; + for (j = 0; j < 32; j++) { + nctl.gpr[j] = gctl.gpr[j]; + nctl.value[j] = ~gctl.value[j]; + val.value.integer.value[j] = gctl.value[j]; + } + nctl.min = gctl.min; + nctl.max = gctl.max; + nctl.translation = gctl.translation; + if (ctl == NULL) { + ctl = (snd_emu10k1_fx8010_ctl_t *)kmalloc(sizeof(*ctl), GFP_KERNEL); + if (ctl == NULL) + continue; + knew.private_value = (unsigned long)ctl; + memcpy(ctl, &nctl, sizeof(nctl)); + if (snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu)) < 0) { + kfree(ctl); + continue; + } + kctl->private_free = snd_emu10k1_ctl_private_free; + ctl->kcontrol = kctl; + list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl); + } else { + /* overwrite */ + nctl.list = ctl->list; + nctl.kcontrol = ctl->kcontrol; + memcpy(ctl, &nctl, sizeof(nctl)); + snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id); + } + snd_emu10k1_gpr_ctl_put(ctl->kcontrol, &val); + } +} + +static void snd_emu10k1_del_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int i; + snd_ctl_elem_id_t *_id, id; + snd_emu10k1_fx8010_ctl_t *ctl; + + for (i = 0, _id = icode->gpr_del_controls; + i < icode->gpr_del_control_count; i++, _id++) { + snd_runtime_check(copy_from_user(&id, _id, sizeof(id)) == 0, continue); + ctl = snd_emu10k1_look_for_ctl(emu, &id); + snd_runtime_check(ctl == NULL, continue); + snd_ctl_remove(emu->card, ctl->kcontrol); + } +} + +static int snd_emu10k1_icode_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + int err = 0; + + down(&emu->fx8010.lock); + if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0) + goto __error; + strncpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name)-1); + emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0'; + /* stop FX processor - this may be dangerous, but it's better to miss + some samples than generate wrong ones - [jk] */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP); + /* ok, do the main job */ + snd_emu10k1_del_controls(emu, icode); + snd_emu10k1_gpr_poke(emu, icode); + snd_emu10k1_tram_poke(emu, icode); + snd_emu10k1_code_poke(emu, icode); + snd_emu10k1_add_controls(emu, icode); + /* start FX processor when the DSP code is updated */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg); + __error: + up(&emu->fx8010.lock); + return err; +} + +static int snd_emu10k1_icode_peek(emu10k1_t *emu, emu10k1_fx8010_code_t *icode) +{ + down(&emu->fx8010.lock); + strncpy(icode->name, emu->fx8010.name, sizeof(icode->name)-1); + emu->fx8010.name[sizeof(emu->fx8010.name)-1] = '\0'; + /* ok, do the main job */ + snd_emu10k1_gpr_peek(emu, icode); + snd_emu10k1_tram_peek(emu, icode); + snd_emu10k1_code_peek(emu, icode); + up(&emu->fx8010.lock); + return 0; +} + +static int snd_emu10k1_ipcm_poke(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm) +{ + int err = 0, i; + snd_emu10k1_fx8010_pcm_t *pcm; + + if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) + return -EINVAL; + if (ipcm->channels > 32) + return -EINVAL; + pcm = &emu->fx8010.pcm[ipcm->substream]; + down(&emu->fx8010.lock); + spin_lock_irq(&emu->reg_lock); + if (pcm->opened) { + err = -EBUSY; + goto __error; + } + if (ipcm->channels == 0) { /* remove */ + pcm->valid = 0; + } else { + /* FIXME: we need to add universal code to the PCM transfer routine */ + if (ipcm->channels != 2) { + err = -EINVAL; + goto __error; + } + pcm->valid = 1; + pcm->opened = 0; + pcm->channels = ipcm->channels; + pcm->tram_start = ipcm->tram_start; + pcm->buffer_size = ipcm->buffer_size; + pcm->gpr_size = ipcm->gpr_size; + pcm->gpr_count = ipcm->gpr_count; + pcm->gpr_tmpcount = ipcm->gpr_tmpcount; + pcm->gpr_ptr = ipcm->gpr_ptr; + pcm->gpr_trigger = ipcm->gpr_trigger; + pcm->gpr_running = ipcm->gpr_running; + for (i = 0; i < pcm->channels; i++) + pcm->etram[i] = ipcm->etram[i]; + } + __error: + spin_unlock_irq(&emu->reg_lock); + up(&emu->fx8010.lock); + return err; +} + +static int snd_emu10k1_ipcm_peek(emu10k1_t *emu, emu10k1_fx8010_pcm_t *ipcm) +{ + int err = 0, i; + snd_emu10k1_fx8010_pcm_t *pcm; + + if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT) + return -EINVAL; + pcm = &emu->fx8010.pcm[ipcm->substream]; + down(&emu->fx8010.lock); + spin_lock_irq(&emu->reg_lock); + ipcm->channels = pcm->channels; + ipcm->tram_start = pcm->tram_start; + ipcm->buffer_size = pcm->buffer_size; + ipcm->gpr_size = pcm->gpr_size; + ipcm->gpr_ptr = pcm->gpr_ptr; + ipcm->gpr_count = pcm->gpr_count; + ipcm->gpr_tmpcount = pcm->gpr_tmpcount; + ipcm->gpr_trigger = pcm->gpr_trigger; + ipcm->gpr_running = pcm->gpr_running; + for (i = 0; i < pcm->channels; i++) + ipcm->etram[i] = pcm->etram[i]; + ipcm->res1 = ipcm->res2 = 0; + ipcm->pad = 0; + spin_unlock_irq(&emu->reg_lock); + up(&emu->fx8010.lock); + return err; +} + +#define SND_EMU10K1_GPR_CONTROLS 41 +#define SND_EMU10K1_INPUTS 10 +#define SND_EMU10K1_PLAYBACK_CHANNELS 6 +#define SND_EMU10K1_CAPTURE_CHANNELS 4 + +static void __devinit snd_emu10k1_init_mono_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 1; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->min = 0; + ctl->max = 100; + ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; +} + +static void __devinit snd_emu10k1_init_stereo_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 2; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; + ctl->min = 0; + ctl->max = 100; + ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100; +} + +static void __devinit snd_emu10k1_init_mono_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 1; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->min = 0; + ctl->max = 1; + ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; +} + +static void __devinit snd_emu10k1_init_stereo_onoff_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) +{ + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, name); + ctl->vcount = ctl->count = 2; + ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; + ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; + ctl->min = 0; + ctl->max = 1; + ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; +} + + +/* + * initial DSP configuration for Audigy + */ + +static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu) +{ + int err, i, gpr, tmp, playback, capture, nctl; + u32 ptr; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_control_gpr_t *controls; + mm_segment_t seg; + + spin_lock_init(&emu->fx8010.irq_lock); + INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); + + if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + kfree(icode); + return -ENOMEM; + } + + /* clear free GPRs */ + for (i = 0; i < 256; i++) + set_bit(i, &icode->gpr_valid); + + strcpy(icode->name, "Audigy DSP code for ALSA"); + ptr = 0; + nctl = 0; + playback = 10; + capture = playback + 10; /* we reserve 10 voices */ + gpr = capture + 10; + tmp = 0x80; + + /* stop FX processor */ + snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP); + + /* Wave Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Surround Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Surround Playback Volume", gpr, 0); + gpr += 2; + + /* Wave Center Playback */ + /* Center = sub = Left/2 + Right/2 */ + A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_FXBUS(FXBUS_PCM_LEFT), 0xcd, A_FXBUS(FXBUS_PCM_RIGHT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_GPR(tmp)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Center Playback Volume", gpr, 0); + gpr++; + + /* Wave LFE Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_GPR(tmp)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Wave LFE Playback Volume", gpr, 0); + gpr++; + + /* Music Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+0), A_GPR(playback+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Capture */ + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Capture Volume", gpr, 0); + gpr += 2; + + /* Music Capture */ + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Music Capture Volume", gpr, 0); + gpr += 2; + + /* Surround Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 80); + gpr += 2; + + /* Center Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 80); + gpr++; + + /* LFE Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE)); + snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 80); + gpr++; + + /* + * inputs + */ +#define A_ADD_VOLUME_IN(var,vol,input) \ +A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) + + /* AC'97 Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_AC97_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_AC97_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "AC97 Playback Volume", gpr, 0); + gpr += 2; + /* AC'97 Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "AC97 Capture Volume", gpr, 100); + gpr += 2; + + /* Audigy CD Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_SPDIF_CD_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_SPDIF_CD_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Audigy CD Playback Volume", gpr, 0); + gpr += 2; + /* Audigy CD Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Audigy CD Capture Volume", gpr, 0); + gpr += 2; + + /* Line2 Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_LINE2_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_LINE2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Line2 Playback Volume", gpr, 0); + gpr += 2; + /* Line2 Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Line2 Capture Volume", gpr, 0); + gpr += 2; + + /* Aux2 Playback Volume */ + A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_AUX2_L); + A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_AUX2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Aux2 Playback Volume", gpr, 0); + gpr += 2; + /* Aux2 Capture Volume */ + A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L); + A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Aux2 Capture Volume", gpr, 0); + gpr += 2; + + /* + * outputs + */ +#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src)) +#define A_PUT_STEREO_OUTPUT(out1,out2,src) \ + {A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);} + + /* digital outputs */ + A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback); + A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2); + A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4); + A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5); + + /* analog speakers */ + //A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback); + A_PUT_STEREO_OUTPUT(A_EXTOUT_AC97_L, A_EXTOUT_AC97_R, playback); + A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2); + A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4); + A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5); + + /* headphone */ + A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback); + + /* ADC buffer */ + A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture); + A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1); + + /* + * ok, set up done.. + */ + + if (gpr > tmp) { + snd_BUG(); + err = -EIO; + goto __err; + } + /* clear remaining instruction memory */ + while (ptr < 0x200) + A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0); + + seg = snd_enter_user(); + icode->gpr_add_control_count = nctl; + icode->gpr_add_controls = controls; + err = snd_emu10k1_icode_poke(emu, icode); + snd_leave_user(seg); + + __err: + kfree(controls); + kfree(icode); + return err; +} + + +/* + * initial DSP configuration for Emu10k1 + */ + +/* when volume = max, then copy only to avoid volume modification */ +/* with iMAC0 (negative values) */ +static void __devinit _volume(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iMAC0, dst, C_00000000, src, vol); + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001); + OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000); +} +static void __devinit _volume_add(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001); + OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001); + OP(icode, ptr, iMAC0, dst, dst, src, vol); +} +static void __devinit _volume_out(emu10k1_fx8010_code_t *icode, u32 *ptr, u32 dst, u32 src, u32 vol) +{ + OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff); + OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000); + OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001); + OP(icode, ptr, iMAC0, dst, C_00000000, src, vol); +} + +#define VOLUME(icode, ptr, dst, src, vol) \ + _volume(icode, ptr, GPR(dst), GPR(src), GPR(vol)) +#define VOLUME_IN(icode, ptr, dst, src, vol) \ + _volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol)) +#define VOLUME_ADD(icode, ptr, dst, src, vol) \ + _volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol)) +#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \ + _volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol)) +#define VOLUME_OUT(icode, ptr, dst, src, vol) \ + _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol)) +#define _SWITCH(icode, ptr, dst, src, sw) \ + OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw); +#define SWITCH(icode, ptr, dst, src, sw) \ + _SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw)) +#define SWITCH_IN(icode, ptr, dst, src, sw) \ + _SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw)) +#define _SWITCH_NEG(icode, ptr, dst, src) \ + OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001); +#define SWITCH_NEG(icode, ptr, dst, src) \ + _SWITCH_NEG(icode, ptr, GPR(dst), GPR(src)) + + +static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) +{ + int err, i, z, gpr, tmp, playback, capture; + u32 ptr; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_pcm_t *ipcm; + emu10k1_fx8010_control_gpr_t *controls, *ctl; + mm_segment_t seg; + + spin_lock_init(&emu->fx8010.irq_lock); + INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); + + if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + kfree(icode); + return -ENOMEM; + } + if ((ipcm = snd_kcalloc(sizeof(emu10k1_fx8010_pcm_t), GFP_KERNEL)) == NULL) { + kfree(controls); + kfree(icode); + return -ENOMEM; + } + + /* clear free GPRs */ + for (i = 0; i < 256; i++) + set_bit(i, &icode->gpr_valid); + + /* clear TRAM data & address lines */ + for (i = 0; i < 160; i++) + set_bit(i, &icode->tram_valid); + + strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); + ptr = 0; i = 0; + /* we have 10 inputs */ + playback = SND_EMU10K1_INPUTS; + /* we have 6 playback channels and tone control doubles */ + capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); + gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS; + tmp = 0x88; /* we need 4 temporary GPR */ + /* from 0x8c to 0xff is the area for tone control */ + + /* stop FX processor */ + snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP); + + /* + * Process FX Buses + */ + OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004); + OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000); /* S/PDIF left */ + OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000); /* S/PDIF right */ + + /* Raw S/PDIF PCM */ + ipcm->substream = 0; + ipcm->channels = 2; + ipcm->tram_start = 0; + ipcm->buffer_size = (64 * 1024) / 2; + ipcm->gpr_size = gpr++; + ipcm->gpr_ptr = gpr++; + ipcm->gpr_count = gpr++; + ipcm->gpr_tmpcount = gpr++; + ipcm->gpr_trigger = gpr++; + ipcm->gpr_running = gpr++; + ipcm->etram[0] = 0; + ipcm->etram[1] = 1; + + icode->gpr_map[gpr + 0] = 0xfffff000; + icode->gpr_map[gpr + 1] = 0xffff0000; + icode->gpr_map[gpr + 2] = 0x70000000; + icode->gpr_map[gpr + 3] = 0x00000007; + icode->gpr_map[gpr + 4] = 0x001f << 11; + icode->gpr_map[gpr + 5] = 0x001c << 11; + icode->gpr_map[gpr + 6] = (0x22 - 0x01) - 1; /* skip at 01 to 22 */ + icode->gpr_map[gpr + 7] = (0x22 - 0x06) - 1; /* skip at 06 to 22 */ + icode->gpr_map[gpr + 8] = 0x2000000 + (2<<11); + icode->gpr_map[gpr + 9] = 0x4000000 + (2<<11); + icode->gpr_map[gpr + 10] = 1<<11; + icode->gpr_map[gpr + 11] = (0x24 - 0x0a) - 1; /* skip at 0a to 24 */ + icode->gpr_map[gpr + 12] = 0; + + /* if the trigger flag is not set, skip */ + /* 00: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_trigger), C_00000000, C_00000000); + /* 01: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr + 6)); + /* if the running flag is set, we're running */ + /* 02: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_running), C_00000000, C_00000000); + /* 03: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000004); + /* wait until ((GPR_DBAC>>11) & 0x1f) == 0x1c) */ + /* 04: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), GPR_DBAC, GPR(gpr + 4), C_00000000); + /* 05: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(gpr + 5)); + /* 06: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 7)); + /* 07: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000010, C_00000001, C_00000000); + + /* 08: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000000, C_00000001); + /* 09: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), GPR(gpr + 12), C_ffffffff, C_00000000); + /* 0a: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 11)); + /* 0b: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000001, C_00000000, C_00000000); + + /* 0c: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[0]), GPR(gpr + 0), C_00000000); + /* 0d: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000); + /* 0e: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2)); + /* 0f: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001); + /* 10: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(8), GPR(gpr + 1), GPR(gpr + 2)); + + /* 11: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[1]), GPR(gpr + 0), C_00000000); + /* 12: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000); + /* 13: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2)); + /* 14: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001); + /* 15: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(9), GPR(gpr + 1), GPR(gpr + 2)); + + /* 16: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(ipcm->gpr_ptr), C_00000001, C_00000000); + /* 17: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(ipcm->gpr_size)); + /* 18: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_MINUS, C_00000001); + /* 19: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), C_00000000, C_00000000, C_00000000); + /* 1a: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_ptr), GPR(tmp + 0), C_00000000, C_00000000); + + /* 1b: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_tmpcount), C_ffffffff, C_00000000); + /* 1c: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002); + /* 1d: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_count), C_00000000, C_00000000); + /* 1e: */ OP(icode, &ptr, iACC3, GPR_IRQ, C_80000000, C_00000000, C_00000000); + /* 1f: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000001, C_00010000); + + /* 20: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00010000, C_00000001); + /* 21: */ OP(icode, &ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000002); + + /* 22: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[0]), GPR(gpr + 8), GPR_DBAC, C_ffffffff); + /* 23: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[1]), GPR(gpr + 9), GPR_DBAC, C_ffffffff); + + /* 24: */ + gpr += 13; + + /* Wave Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME(icode, &ptr, playback + z, z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Wave Playback Volume", gpr, 100); + gpr += 2; + + /* Wave Surround Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME(icode, &ptr, playback + 2 + z, z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Wave Surround Playback Volume", gpr, 0); + gpr += 2; + + /* Wave Center/LFE Playback Volume */ + OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000); + OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002); + VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0); + VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Wave LFE Playback Volume", gpr++, 0); + + /* Wave Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, z, gpr + 2 + z); + VOLUME(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Wave Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Wave Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Music Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME_ADD(icode, &ptr, playback + z, 2 + z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Music Playback Volume", gpr, 100); + gpr += 2; + + /* Music Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, 2 + z, gpr + 2 + z); + VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Music Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Music Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Surround Digital Playback Volume */ + for (z = 0; z < 2; z++) + VOLUME_ADD(icode, &ptr, playback + 2 + z, 4 + z, gpr + z); + snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Playback Volume", gpr, 100); + gpr += 2; + + /* Surround Digital Capture Volume + Switch */ + for (z = 0; z < 2; z++) { + SWITCH(icode, &ptr, tmp + 0, 4 + z, gpr + 2 + z); + VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); + } + snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Surround Digital Capture Switch", gpr + 2, 0); + gpr += 4; + + /* Center Playback Volume */ + VOLUME_ADD(icode, &ptr, playback + 4, 6, gpr); + snd_emu10k1_init_mono_control(controls + i++, "Center Playback Volume", gpr++, 100); + + /* LFE Playback Volume + Switch */ + VOLUME_ADD(icode, &ptr, playback + 5, 7, gpr); + snd_emu10k1_init_mono_control(controls + i++, "LFE Playback Volume", gpr++, 100); + + /* + * Process inputs + */ + + if (emu->fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<fx8010.extin_mask & ((1<id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Bass"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_BASS; + ctl = &controls[i + 1]; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(ctl->id.name, "Tone Control - Treble"); + ctl->vcount = 2; + ctl->count = 10; + ctl->min = 0; + ctl->max = 40; + ctl->value[0] = ctl->value[1] = 20; + ctl->translation = EMU10K1_GRP_TRANSLATION_TREBLE; + +#define BASS_GPR 0x8c +#define TREBLE_GPR 0x96 + + for (z = 0; z < 5; z++) { + int j; + for (j = 0; j < 2; j++) { + controls[i + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j; + controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j; + } + } + for (z = 0; z < 3; z++) { /* front/rear/center-lfe */ + int j, k, l, d; + for (j = 0; j < 2; j++) { /* left/right */ + k = 0xa0 + (z * 8) + (j * 4); + l = 0xd0 + (z * 8) + (j * 4); + d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j; + + OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j)); + OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j)); + OP(icode, &ptr, iMACMV, GPR(k), GPR(d), GPR(k), GPR(BASS_GPR + 2 + j)); + OP(icode, &ptr, iMACMV, GPR(k+3), GPR(k+2), GPR(k+3), GPR(BASS_GPR + 8 + j)); + OP(icode, &ptr, iMAC0, GPR(k+2), GPR_ACCU, GPR(k+2), GPR(BASS_GPR + 6 + j)); + OP(icode, &ptr, iACC3, GPR(k+2), GPR(k+2), GPR(k+2), C_00000000); + + OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(k+2), GPR(TREBLE_GPR + 0 + j)); + OP(icode, &ptr, iMACMV, GPR(l+1), GPR(l), GPR(l+1), GPR(TREBLE_GPR + 4 + j)); + OP(icode, &ptr, iMACMV, GPR(l), GPR(k+2), GPR(l), GPR(TREBLE_GPR + 2 + j)); + OP(icode, &ptr, iMACMV, GPR(l+3), GPR(l+2), GPR(l+3), GPR(TREBLE_GPR + 8 + j)); + OP(icode, &ptr, iMAC0, GPR(l+2), GPR_ACCU, GPR(l+2), GPR(TREBLE_GPR + 6 + j)); + OP(icode, &ptr, iMACINT0, GPR(l+2), C_00000000, GPR(l+2), C_00000010); + + OP(icode, &ptr, iACC3, GPR(d), GPR(l+2), C_00000000, C_00000000); + + if (z == 2) /* center */ + break; + } + } + i += 2; + +#undef BASS_GPR +#undef TREBLE_GPR + + for (z = 0; z < 6; z++) { + SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0); + SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0); + SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1); + OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000); + } + snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0); + gpr += 2; + + /* + * Process outputs + */ + if (emu->fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & ((1<fx8010.extout_mask & (1<fx8010.extout_mask & (1<fx8010.extout_mask & (1< tmp) { + snd_BUG(); + err = -EIO; + goto __err; + } + if (i > SND_EMU10K1_GPR_CONTROLS) { + snd_BUG(); + err = -EIO; + goto __err; + } + + /* clear remaining instruction memory */ + while (ptr < 0x200) + OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000); + + if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0) + goto __err; + seg = snd_enter_user(); + icode->gpr_add_control_count = i; + icode->gpr_add_controls = controls; + err = snd_emu10k1_icode_poke(emu, icode); + snd_leave_user(seg); + if (err >= 0) + err = snd_emu10k1_ipcm_poke(emu, ipcm); + __err: + kfree(ipcm); + kfree(controls); + kfree(icode); + return err; +} + +int __devinit snd_emu10k1_init_efx(emu10k1_t *emu) +{ + if (emu->audigy) + return _snd_emu10k1_audigy_init_efx(emu); + else + return _snd_emu10k1_init_efx(emu); +} + +void snd_emu10k1_free_efx(emu10k1_t *emu) +{ + /* stop processor */ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP); +} + +#if 0 // FIXME: who use them? +int snd_emu10k1_fx8010_tone_control_activate(emu10k1_t *emu, int output) +{ + snd_runtime_check(output >= 0 && output < 6, return -EINVAL); + snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 1); + return 0; +} + +int snd_emu10k1_fx8010_tone_control_deactivate(emu10k1_t *emu, int output) +{ + snd_runtime_check(output >= 0 && output < 6, return -EINVAL); + snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 0); + return 0; +} +#endif + +int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size) +{ + u8 size_reg = 0; + + /* size is in samples */ + if (size != 0) { + size = (size - 1) >> 13; + + while (size) { + size >>= 1; + size_reg++; + } + size = 0x2000 << size_reg; + } + if (emu->fx8010.etram_size == size) + return 0; + spin_lock_irq(&emu->emu_lock); + outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG); + spin_unlock_irq(&emu->emu_lock); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); + snd_emu10k1_ptr_write(emu, TCBS, 0, 0); + if (emu->fx8010.etram_pages != NULL) { + snd_free_pci_pages(emu->pci, emu->fx8010.etram_size * 2, emu->fx8010.etram_pages, emu->fx8010.etram_pages_dmaaddr); + emu->fx8010.etram_pages = NULL; + emu->fx8010.etram_size = 0; + } + + if (size > 0) { + emu->fx8010.etram_pages = snd_malloc_pci_pages(emu->pci, size * 2, &emu->fx8010.etram_pages_dmaaddr); + if (emu->fx8010.etram_pages == NULL) + return -ENOMEM; + memset(emu->fx8010.etram_pages, 0, size * 2); + snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages_dmaaddr); + snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg); + spin_lock_irq(&emu->emu_lock); + outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG); + spin_unlock_irq(&emu->emu_lock); + } + + emu->fx8010.etram_size = size; + return 0; +} + +static int snd_emu10k1_fx8010_open(snd_hwdep_t * hw, struct file *file) +{ + return 0; +} + +static void copy_string(char *dst, char *src, char *null, int idx) +{ + if (src == NULL) + sprintf(dst, "%s %02X", null, idx); + else + strcpy(dst, src); +} + +static int snd_emu10k1_fx8010_info(emu10k1_t *emu, emu10k1_fx8010_info_t *info) +{ + char **fxbus, **extin, **extout; + unsigned short fxbus_mask, extin_mask, extout_mask; + int res; + + memset(info, 0, sizeof(info)); + info->card = emu->card_type; + info->internal_tram_size = emu->fx8010.itram_size; + info->external_tram_size = emu->fx8010.etram_size; + fxbus = fxbuses; + extin = emu->audigy ? audigy_ins : creative_ins; + extout = emu->audigy ? audigy_outs : creative_outs; + fxbus_mask = emu->fx8010.fxbus_mask; + extin_mask = emu->fx8010.extin_mask; + extout_mask = emu->fx8010.extout_mask; + for (res = 0; res < 16; res++, fxbus++, extin++, extout++) { + copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res); + copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res); + copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + } + for (res = 16; res < 32; res++, extout++) + copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + info->gpr_controls = emu->fx8010.gpr_count; + return 0; +} + +static int snd_emu10k1_fx8010_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, hw->private_data, return -ENXIO); + emu10k1_fx8010_info_t info; + emu10k1_fx8010_code_t *icode; + emu10k1_fx8010_pcm_t *ipcm; + unsigned int addr; + int res; + + switch (cmd) { + case SNDRV_EMU10K1_IOCTL_INFO: + if ((res = snd_emu10k1_fx8010_info(emu, &info)) < 0) + return res; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + case SNDRV_EMU10K1_IOCTL_CODE_POKE: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + icode = (emu10k1_fx8010_code_t *)kmalloc(sizeof(*icode), GFP_KERNEL); + if (icode == NULL) + return -ENOMEM; + if (copy_from_user(icode, (void *)arg, sizeof(*icode))) { + kfree(icode); + return -EFAULT; + } + res = snd_emu10k1_icode_poke(emu, icode); + kfree(icode); + return res; + case SNDRV_EMU10K1_IOCTL_CODE_PEEK: + icode = (emu10k1_fx8010_code_t *)snd_kcalloc(sizeof(*icode), GFP_KERNEL); + if (icode == NULL) + return -ENOMEM; + res = snd_emu10k1_icode_peek(emu, icode); + if (res == 0 && copy_to_user((void *)arg, icode, sizeof(*icode))) { + kfree(icode); + return -EFAULT; + } + kfree(icode); + return res; + case SNDRV_EMU10K1_IOCTL_PCM_POKE: + if (emu->audigy) + return -EINVAL; + ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL); + if (ipcm == NULL) + return -ENOMEM; + res = snd_emu10k1_ipcm_poke(emu, ipcm); + kfree(ipcm); + return res; + case SNDRV_EMU10K1_IOCTL_PCM_PEEK: + if (emu->audigy) + return -EINVAL; + ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL); + if (ipcm == NULL) + return -ENOMEM; + res = snd_emu10k1_ipcm_peek(emu, ipcm); + if (res == 0 && copy_to_user((void *)arg, ipcm, sizeof(*ipcm))) { + kfree(ipcm); + return -EFAULT; + } + kfree(ipcm); + return res; + case SNDRV_EMU10K1_IOCTL_TRAM_SETUP: + if (emu->audigy) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (get_user(addr, (unsigned int *)arg)) + return -EFAULT; + down(&emu->fx8010.lock); + res = snd_emu10k1_fx8010_tram_setup(emu, addr); + up(&emu->fx8010.lock); + return res; + case SNDRV_EMU10K1_IOCTL_STOP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP); + return 0; + case SNDRV_EMU10K1_IOCTL_CONTINUE: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = 0); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = 0); + return 0; + case SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_ZC); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_ZC); + udelay(10); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg); + return 0; + case SNDRV_EMU10K1_IOCTL_SINGLE_STEP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (get_user(addr, (unsigned int *)arg)) + return -EFAULT; + if (addr > 0x1ff) + return -EINVAL; + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr); + udelay(10); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr); + else + snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr); + return 0; + case SNDRV_EMU10K1_IOCTL_DBG_READ: + if (emu->audigy) + addr = snd_emu10k1_ptr_read(emu, A_DBG, 0); + else + addr = snd_emu10k1_ptr_read(emu, DBG, 0); + if (put_user(addr, (unsigned int *)arg)) + return -EFAULT; + return 0; + } + return -ENOTTY; +} + +static int snd_emu10k1_fx8010_release(snd_hwdep_t * hw, struct file *file) +{ + return 0; +} + +int __devinit snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep) +{ + snd_hwdep_t *hw; + int err; + + if (rhwdep) + *rhwdep = NULL; + if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0) + return err; + strcpy(hw->name, "EMU10K1 (FX8010)"); + hw->iface = SNDRV_HWDEP_IFACE_EMU10K1; + hw->ops.open = snd_emu10k1_fx8010_open; + hw->ops.ioctl = snd_emu10k1_fx8010_ioctl; + hw->ops.release = snd_emu10k1_fx8010_release; + hw->private_data = emu; + if (rhwdep) + *rhwdep = hw; + return 0; +} diff -Nru linux/sound/pci/emu10k1/emumixer.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumixer.c --- linux/sound/pci/emu10k1/emumixer.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumixer.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,549 @@ +/* + * Copyright (c) by Jaroslav Kysela , + * Takashi Iwai + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / mixer routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#define chip_t emu10k1_t + +static int snd_emu10k1_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_emu10k1_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + unsigned long flags; + + spin_lock_irqsave(&emu->reg_lock, flags); + ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value, change; + unsigned int val; + unsigned long flags; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&emu->reg_lock, flags); + change = val != emu->spdif_bits[idx]; + if (change) { + snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val); + emu->spdif_bits[idx] = val; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + info: snd_emu10k1_spdif_info, + get: snd_emu10k1_spdif_get_mask +}; + +static snd_kcontrol_new_t snd_emu10k1_spdif_control = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_emu10k1_spdif_info, + get: snd_emu10k1_spdif_get, + put: snd_emu10k1_spdif_put +}; + + +static void update_emu10k1_fxrt(emu10k1_t *emu, int voice, unsigned char *route) +{ + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, voice, + snd_emu10k1_compose_audigy_fxrt1(route)); + snd_emu10k1_ptr_write(emu, A_FXRT2, voice, + snd_emu10k1_compose_audigy_fxrt2(route)); + } else { + snd_emu10k1_ptr_write(emu, FXRT, voice, + snd_emu10k1_compose_send_routing(route)); + } +} + +static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char *volume) +{ + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]); + snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]); + snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]); + snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]); + if (emu->audigy) { + unsigned int val = ((unsigned int)volume[4] << 24) | + ((unsigned int)volume[5] << 16) | + ((unsigned int)volume[6] << 8) | + (unsigned int)volume[7]; + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val); + } +} + +static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = emu->audigy ? 3*8 : 3*4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f; + return 0; +} + +static int snd_emu10k1_send_routing_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int voice, idx; + int num_efx = emu->audigy ? 8 : 4; + int mask = emu->audigy ? 0x3f : 0x0f; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (voice = 0; voice < 3; voice++) + for (idx = 0; idx < num_efx; idx++) + ucontrol->value.integer.value[(voice * num_efx) + idx] = + mix->send_routing[voice][idx] & mask; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, voice, idx, val; + int num_efx = emu->audigy ? 8 : 4; + int mask = emu->audigy ? 0x3f : 0x0f; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (voice = 0; voice < 3; voice++) + for (idx = 0; idx < num_efx; idx++) { + val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask; + if (mix->send_routing[voice][idx] != val) { + mix->send_routing[voice][idx] = val; + change = 1; + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number, + &mix->send_routing[1][0]); + update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number, + &mix->send_routing[2][0]); + } else if (mix->epcm->voices[0]) { + update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number, + &mix->send_routing[0][0]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_send_routing_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "EMU10K1 PCM Send Routing", + info: snd_emu10k1_send_routing_info, + get: snd_emu10k1_send_routing_get, + put: snd_emu10k1_send_routing_put +}; + +static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = emu->audigy ? 3*8 : 3*4; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_emu10k1_send_volume_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int idx; + int num_efx = emu->audigy ? 8 : 4; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3*num_efx; idx++) + ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, idx, val; + int num_efx = emu->audigy ? 8 : 4; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3*num_efx; idx++) { + val = ucontrol->value.integer.value[idx] & 255; + if (mix->send_volume[idx/num_efx][idx%num_efx] != val) { + mix->send_volume[idx/num_efx][idx%num_efx] = val; + change = 1; + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number, + &mix->send_volume[1][0]); + update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number, + &mix->send_volume[2][0]); + } else if (mix->epcm->voices[0]) { + update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number, + &mix->send_volume[0][0]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_send_volume_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "EMU10K1 PCM Send Volume", + info: snd_emu10k1_send_volume_info, + get: snd_emu10k1_send_volume_get, + put: snd_emu10k1_send_volume_put +}; + +static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 3; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; + return 0; +} + +static int snd_emu10k1_attn_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3; idx++) + ucontrol->value.integer.value[idx] = mix->attn[idx]; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_pcm_mixer_t *mix = (emu10k1_pcm_mixer_t *)kcontrol->private_value; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + int change = 0, idx, val; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 3; idx++) { + val = ucontrol->value.integer.value[idx] & 0xffff; + if (mix->attn[idx] != val) { + mix->attn[idx] = val; + change = 1; + } + } + if (change && mix->epcm) { + if (mix->epcm->voices[0] && mix->epcm->voices[1]) { + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]); + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]); + } else if (mix->epcm->voices[0]) { + snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]); + } + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_attn_control = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "EMU10K1 PCM Volume", + info: snd_emu10k1_attn_info, + get: snd_emu10k1_attn_get, + put: snd_emu10k1_attn_put +}; + +static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_emu10k1_shared_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 0 : 1; + return 0; +} + +static int snd_emu10k1_shared_spdif_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int reg, val; + int change; + + spin_lock_irqsave(&emu->reg_lock, flags); + reg = inl(emu->port + HCFG); + val = ucontrol->value.integer.value[0] & 1 ? 0 : HCFG_GPOUT0; + change = (reg & HCFG_GPOUT0) != val; + reg &= ~HCFG_GPOUT0; + reg |= val; + outl(reg | val, emu->port + HCFG); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_shared_spdif = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "SB Live Analog/Digital Output Jack", + info: snd_emu10k1_shared_spdif_info, + get: snd_emu10k1_shared_spdif_get, + put: snd_emu10k1_shared_spdif_put +}; + +#if 0 // XXX: not working yet.. +/* + * Audigy analog / digital switches + */ +static int audigy_output_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int audigy_output_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = (unsigned int)kcontrol->private_value; + + ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & mask ? 0 : 1; + return 0; +} + +static int audigy_output_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + unsigned long flags; + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned int mask = (unsigned int)kcontrol->private_value; + unsigned int reg, oreg; + int change; + + spin_lock_irqsave(&emu->reg_lock, flags); + reg = oreg = inl(emu->port + A_IOCFG); + reg &= ~mask; + reg |= ucontrol->value.integer.value[0] & 1 ? 0 : mask; + change = (reg != oreg); + if (change) + outl(reg, emu->port + A_IOCFG); + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t audigy_output_analog = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Audigy Analog Output Switch", + info: audigy_output_info, + get: audigy_output_get, + put: audigy_output_put, + private_value: 0x40, +}; + +static snd_kcontrol_new_t audigy_output_digital = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Audigy Digital Output Switch", + info: audigy_output_info, + get: audigy_output_get, + put: audigy_output_put, + private_value: 0x04, +}; +#endif // XXX + + +/* + */ +static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + emu->ac97 = NULL; +} + +int __devinit snd_emu10k1_mixer(emu10k1_t *emu) +{ + ac97_t ac97; + int err, pcm, idx; + snd_kcontrol_t *kctl; + snd_card_t *card = emu->card; + + if (!emu->APS) { + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_emu10k1_ac97_write; + ac97.read = snd_emu10k1_ac97_read; + ac97.private_data = emu; + ac97.private_free = snd_emu10k1_mixer_free_ac97; + if ((err = snd_ac97_mixer(emu->card, &ac97, &emu->ac97)) < 0) + return err; + } else { + strcpy(emu->card->mixername, "EMU APS"); + } + + for (pcm = 0; pcm < 32; pcm++) { + emu10k1_pcm_mixer_t *mix; + int v; + + mix = &emu->pcm_mixer[pcm]; + mix->epcm = NULL; + + if ((kctl = mix->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + for (v = 0; v < 4; v++) + mix->send_routing[0][v] = + mix->send_routing[1][v] = + mix->send_routing[2][v] = v; + + if ((kctl = mix->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + memset(&mix->send_volume, 0, sizeof(mix->send_volume)); + mix->send_volume[0][0] = mix->send_volume[0][1] = + mix->send_volume[1][0] = mix->send_volume[2][1] = 255; + + if ((kctl = mix->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = (long)mix; + kctl->id.index = pcm; + if ((err = snd_ctl_add(card, kctl))) + return err; + mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; + } + + for (idx = 0; idx < 3; idx++) { + if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = idx; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL) + return -ENOMEM; + kctl->private_value = idx; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + if (emu->audigy) { +#if 0 // XXX + if ((kctl = snd_ctl_new1(&audigy_output_analog, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + if ((kctl = snd_ctl_new1(&audigy_output_digital, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; +#endif // XXX + } else { + if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL) + return -ENOMEM; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + return 0; +} diff -Nru linux/sound/pci/emu10k1/emumpu401.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumpu401.c --- linux/sound/pci/emu10k1/emumpu401.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emumpu401.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,376 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of EMU10K1 MPU-401 in UART mode + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +#define EMU10K1_MIDI_MODE_INPUT (1<<0) +#define EMU10K1_MIDI_MODE_OUTPUT (1<<1) + +static inline unsigned char mpu401_read(emu10k1_t *emu, emu10k1_midi_t *mpu, int idx) +{ + if (emu->audigy) + return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0); + else + return inb(emu->port + mpu->port + idx); +} + +static inline void mpu401_write(emu10k1_t *emu, emu10k1_midi_t *mpu, int data, int idx) +{ + if (emu->audigy) + snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data); + else + outb(data, emu->port + mpu->port + idx); +} + +#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0) +#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1) +#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0) +#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1) + +#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80)) +#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40)) + +#define MPU401_RESET 0xff +#define MPU401_ENTER_UART 0x3f +#define MPU401_ACK 0xfe + +static void mpu401_clear_rx(emu10k1_t *emu, emu10k1_midi_t *mpu) +{ + int timeout = 100000; + for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--) + mpu401_read_data(emu, mpu); +#ifdef CONFIG_SND_DEBUG + if (timeout <= 0) + snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu)); +#endif +} + +/* + + */ + +static void do_emu10k1_midi_interrupt(emu10k1_t *emu, emu10k1_midi_t *midi, unsigned int status) +{ + unsigned char byte; + + if (midi->rmidi == NULL) { + snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable); + return; + } + + spin_lock(&midi->input_lock); + if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) { + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + mpu401_clear_rx(emu, midi); + } else { + byte = mpu401_read_data(emu, midi); + spin_unlock(&midi->input_lock); + if (midi->substream_input) + snd_rawmidi_receive(midi->substream_input, &byte, 1); + spin_lock(&midi->input_lock); + } + } + spin_unlock(&midi->input_lock); + + spin_lock(&midi->output_lock); + if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) { + if (midi->substream_output && + snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { + mpu401_write_data(emu, midi, byte); + } else { + snd_emu10k1_intr_disable(emu, midi->tx_enable); + } + } + spin_unlock(&midi->output_lock); +} + +static void snd_emu10k1_midi_interrupt(emu10k1_t *emu, unsigned int status) +{ + do_emu10k1_midi_interrupt(emu, &emu->midi, status); +} + +static void snd_emu10k1_midi_interrupt2(emu10k1_t *emu, unsigned int status) +{ + do_emu10k1_midi_interrupt(emu, &emu->midi2, status); +} + +static void snd_emu10k1_midi_cmd(emu10k1_t * emu, emu10k1_midi_t *midi, unsigned char cmd, int ack) +{ + unsigned long flags; + int timeout, ok; + + spin_lock_irqsave(&midi->input_lock, flags); + mpu401_write_data(emu, midi, 0x00); + /* mpu401_clear_rx(emu, midi); */ + + mpu401_write_cmd(emu, midi, cmd); + if (ack) { + ok = 0; + timeout = 10000; + while (!ok && timeout-- > 0) { + if (mpu401_input_avail(emu, midi)) { + if (mpu401_read_data(emu, midi) == MPU401_ACK) + ok = 1; + } + } + if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK) + ok = 1; + } else { + ok = 1; + } + spin_unlock_irqrestore(&midi->input_lock, flags); + if (!ok) + snd_printk("midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n", + cmd, emu->port, + mpu401_read_stat(emu, midi), + mpu401_read_data(emu, midi)); +} + +static int snd_emu10k1_midi_input_open(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT; + midi->substream_input = substream; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1); + snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_output_open(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT; + midi->substream_output = substream; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1); + snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_input_close(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + snd_emu10k1_intr_disable(emu, midi->rx_enable); + midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT; + midi->substream_input = NULL; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static int snd_emu10k1_midi_output_close(snd_rawmidi_substream_t * substream) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return -ENXIO); + spin_lock_irqsave(&midi->open_lock, flags); + snd_emu10k1_intr_disable(emu, midi->tx_enable); + midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT; + midi->substream_output = NULL; + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { + spin_unlock_irqrestore(&midi->open_lock, flags); + snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); + } else { + spin_unlock_irqrestore(&midi->open_lock, flags); + } + return 0; +} + +static void snd_emu10k1_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + emu = midi->emu; + snd_assert(emu, return); + + if (up) + snd_emu10k1_intr_enable(emu, midi->rx_enable); + else + snd_emu10k1_intr_disable(emu, midi->rx_enable); +} + +static void snd_emu10k1_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + emu10k1_t *emu; + emu10k1_midi_t *midi = (emu10k1_midi_t *)substream->rmidi->private_data; + unsigned long flags; + + emu = midi->emu; + snd_assert(emu, return); + + if (up) { + int max = 4; + unsigned char byte; + + /* try to send some amount of bytes here before interrupts */ + spin_lock_irqsave(&midi->output_lock, flags); + while (max > 0) { + if (mpu401_output_ready(emu, midi)) { + if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) || + snd_rawmidi_transmit(substream, &byte, 1) != 1) { + /* no more data */ + spin_unlock_irqrestore(&midi->output_lock, flags); + return; + } + mpu401_write_data(emu, midi, byte); + max--; + } else { + break; + } + } + spin_unlock_irqrestore(&midi->output_lock, flags); + snd_emu10k1_intr_enable(emu, midi->tx_enable); + } else { + snd_emu10k1_intr_disable(emu, midi->tx_enable); + } +} + +/* + + */ + +static snd_rawmidi_ops_t snd_emu10k1_midi_output = +{ + open: snd_emu10k1_midi_output_open, + close: snd_emu10k1_midi_output_close, + trigger: snd_emu10k1_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_emu10k1_midi_input = +{ + open: snd_emu10k1_midi_input_open, + close: snd_emu10k1_midi_input_close, + trigger: snd_emu10k1_midi_input_trigger, +}; + +static void snd_emu10k1_midi_free(snd_rawmidi_t *rmidi) +{ + emu10k1_midi_t *midi = (emu10k1_midi_t *)rmidi->private_data; + midi->interrupt = NULL; + midi->rmidi = NULL; +} + +static int __devinit emu10k1_midi_init(emu10k1_t *emu, emu10k1_midi_t *midi, int device, char *name) +{ + snd_rawmidi_t *rmidi; + int err; + + if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0) + return err; + midi->emu = emu; + spin_lock_init(&midi->open_lock); + spin_lock_init(&midi->input_lock); + spin_lock_init(&midi->output_lock); + strcpy(rmidi->name, name); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = midi; + rmidi->private_free = snd_emu10k1_midi_free; + midi->rmidi = rmidi; + return 0; +} + +int __devinit snd_emu10k1_midi(emu10k1_t *emu) +{ + emu10k1_midi_t *midi = &emu->midi; + int err; + + if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0) + return err; + + midi->tx_enable = INTE_MIDITXENABLE; + midi->rx_enable = INTE_MIDIRXENABLE; + midi->port = MUDATA; + midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; + midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; + midi->interrupt = snd_emu10k1_midi_interrupt; + return 0; +} + +int __devinit snd_emu10k1_audigy_midi(emu10k1_t *emu) +{ + emu10k1_midi_t *midi; + int err; + + midi = &emu->midi; + if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0) + return err; + + midi->tx_enable = INTE_MIDITXENABLE; + midi->rx_enable = INTE_MIDIRXENABLE; + midi->port = A_MUDATA1; + midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; + midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; + midi->interrupt = snd_emu10k1_midi_interrupt; + + midi = &emu->midi2; + if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0) + return err; + + midi->tx_enable = INTE_A_MIDITXENABLE2; + midi->rx_enable = INTE_A_MIDIRXENABLE2; + midi->port = A_MUDATA2; + midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2; + midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2; + midi->interrupt = snd_emu10k1_midi_interrupt2; + return 0; +} diff -Nru linux/sound/pci/emu10k1/emupcm.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emupcm.c --- linux/sound/pci/emu10k1/emupcm.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emupcm.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1135 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / PCM routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +#define chip_t emu10k1_t + +static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice) +{ + emu10k1_pcm_t *epcm; + + if ((epcm = voice->epcm) == NULL) + return; + if (epcm->substream == NULL) + return; +#if 0 + printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", + epcm->substream->runtime->hw->pointer(emu, epcm->substream), + snd_pcm_lib_period_bytes(epcm->substream), + snd_pcm_lib_buffer_bytes(epcm->substream)); +#endif + snd_pcm_period_elapsed(epcm->substream); +} + +static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_ADCBUFHALFFULL) { + if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_substream); +} + +static void snd_emu10k1_pcm_ac97mic_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_MICBUFHALFFULL) { + if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_mic_substream); +} + +static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status) +{ +#if 0 + if (status & IPR_EFXBUFHALFFULL) { + if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME) + return; + } +#endif + snd_pcm_period_elapsed(emu->pcm_capture_efx_substream); +} + +static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices) +{ + int err; + + if (epcm->voices[1] != NULL && voices < 2) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + } + if (voices == 1 && epcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && epcm->voices[0] != NULL && epcm->voices[1] != NULL) + return 0; + if (voices > 1) { + if (epcm->voices[0] != NULL && epcm->voices[1] == NULL) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + } + } + err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, voices > 1, &epcm->voices[0]); + if (err < 0) + return err; + epcm->voices[0]->epcm = epcm; + if (voices > 1) { + epcm->voices[1] = &epcm->emu->voices[epcm->voices[0]->number + 1]; + epcm->voices[1]->epcm = epcm; + } + if (epcm->extra == NULL) { + err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, 0, &epcm->extra); + if (err < 0) { + // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + if (epcm->voices[1]) + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + return err; + } + epcm->extra->epcm = epcm; + epcm->extra->interrupt = snd_emu10k1_pcm_interrupt; + } + return 0; +} + +static unsigned int capture_period_sizes[31] = { + 384, 448, 512, 640, + 384*2, 448*2, 512*2, 640*2, + 384*4, 448*4, 512*4, 640*4, + 384*8, 448*8, 512*8, 640*8, + 384*16, 448*16, 512*16, 640*16, + 384*32, 448*32, 512*32, 640*32, + 384*64, 448*64, 512*64, 640*64, + 384*128,448*128,512*128 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = { + count: 31, + list: capture_period_sizes, + mask: 0 +}; + +static unsigned int capture_rates[8] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = { + count: 8, + list: capture_rates, + mask: 0 +}; + +static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate) +{ + switch (rate) { + case 8000: return ADCCR_SAMPLERATE_8; + case 11025: return ADCCR_SAMPLERATE_11; + case 16000: return ADCCR_SAMPLERATE_16; + case 22050: return ADCCR_SAMPLERATE_22; + case 24000: return ADCCR_SAMPLERATE_24; + case 32000: return ADCCR_SAMPLERATE_32; + case 44100: return ADCCR_SAMPLERATE_44; + case 48000: return ADCCR_SAMPLERATE_48; + default: + snd_BUG(); + return ADCCR_SAMPLERATE_8; + } +} + +static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate) +{ + switch (rate) { + case 8000: return A_ADCCR_SAMPLERATE_8; + case 11025: return A_ADCCR_SAMPLERATE_11; + case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */ + case 16000: return ADCCR_SAMPLERATE_16; + case 22050: return ADCCR_SAMPLERATE_22; + case 24000: return ADCCR_SAMPLERATE_24; + case 32000: return ADCCR_SAMPLERATE_32; + case 44100: return ADCCR_SAMPLERATE_44; + case 48000: return ADCCR_SAMPLERATE_48; + default: + snd_BUG(); + return A_ADCCR_SAMPLERATE_8; + } +} + +static unsigned int emu10k1_calc_pitch_target(unsigned int rate) +{ + unsigned int pitch_target; + + pitch_target = (rate << 8) / 375; + pitch_target = (pitch_target >> 1) + (pitch_target & 1); + return pitch_target; +} + +#define PITCH_48000 0x00004000 +#define PITCH_96000 0x00008000 +#define PITCH_85000 0x00007155 +#define PITCH_80726 0x00006ba2 +#define PITCH_67882 0x00005a82 +#define PITCH_57081 0x00004c1c + +static unsigned int emu10k1_select_interprom(unsigned int pitch_target) +{ + if (pitch_target == PITCH_48000) + return CCCA_INTERPROM_0; + else if (pitch_target < PITCH_48000) + return CCCA_INTERPROM_1; + else if (pitch_target >= PITCH_96000) + return CCCA_INTERPROM_0; + else if (pitch_target >= PITCH_85000) + return CCCA_INTERPROM_6; + else if (pitch_target >= PITCH_80726) + return CCCA_INTERPROM_5; + else if (pitch_target >= PITCH_67882) + return CCCA_INTERPROM_4; + else if (pitch_target >= PITCH_57081) + return CCCA_INTERPROM_3; + else + return CCCA_INTERPROM_2; +} + + +static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, + int master, int extra, + emu10k1_voice_t *evoice, + unsigned int start_addr, + unsigned int end_addr) +{ + snd_pcm_substream_t *substream = evoice->epcm->substream; + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; + unsigned int silent_page, tmp; + int voice, stereo, w_16; + unsigned char attn, send_amount[8]; + unsigned char send_routing[8]; + unsigned long flags; + unsigned int pitch_target; + + voice = evoice->number; + stereo = runtime->channels == 2; + w_16 = snd_pcm_format_width(runtime->format) == 16; + + if (!extra && stereo) { + start_addr >>= 1; + end_addr >>= 1; + } + if (w_16) { + start_addr >>= 1; + end_addr >>= 1; + } + + spin_lock_irqsave(&emu->reg_lock, flags); + + /* volume parameters */ + if (extra) { + attn = 0; + memset(send_routing, 0, sizeof(send_routing)); + send_routing[0] = 0; + send_routing[1] = 1; + send_routing[2] = 2; + send_routing[3] = 3; + memset(send_amount, 0, sizeof(send_amount)); + } else { + tmp = stereo ? (master ? 1 : 2) : 0; + memcpy(send_routing, &mix->send_routing[tmp][0], 8); + memcpy(send_amount, &mix->send_volume[tmp][0], 8); + } + + if (master) { + unsigned int ccis = stereo ? 28 : 30; + if (w_16) + ccis *= 2; + evoice->epcm->ccca_start_addr = start_addr + ccis; + if (extra) { + start_addr += ccis; + end_addr += ccis; + } + if (stereo && !extra) { + snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); + snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK); + } else { + snd_emu10k1_ptr_write(emu, CPF, voice, 0); + } + } + + // setup routing + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXRT1, voice, + ((unsigned int)send_routing[3] << 24) | + ((unsigned int)send_routing[2] << 16) | + ((unsigned int)send_routing[1] << 8) | + (unsigned int)send_routing[0]); + snd_emu10k1_ptr_write(emu, A_FXRT2, voice, + ((unsigned int)send_routing[7] << 24) | + ((unsigned int)send_routing[6] << 16) | + ((unsigned int)send_routing[5] << 8) | + (unsigned int)send_routing[4]); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, + ((unsigned int)send_amount[4] << 24) | + ((unsigned int)send_amount[5] << 16) | + ((unsigned int)send_amount[6] << 8) | + (unsigned int)send_amount[7]); + } else + snd_emu10k1_ptr_write(emu, FXRT, voice, + snd_emu10k1_compose_send_routing(send_routing)); + // Stop CA + // Assumption that PT is already 0 so no harm overwriting + snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); + snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); + snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); + pitch_target = emu10k1_calc_pitch_target(runtime->rate); + snd_emu10k1_ptr_write(emu, CCCA, voice, evoice->epcm->ccca_start_addr | + emu10k1_select_interprom(pitch_target) | + (w_16 ? 0 : CCCA_8BITSELECT)); + // Clear filter delay memory + snd_emu10k1_ptr_write(emu, Z1, voice, 0); + snd_emu10k1_ptr_write(emu, Z2, voice, 0); + // invalidate maps + silent_page = ((unsigned int)emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); + snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); + // modulation envelope + snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); + snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); + snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0); + snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f); + snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000); + snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000); + snd_emu10k1_ptr_write(emu, FMMOD, voice, 0); + snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0); + snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0); + snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000); + // volume envelope + snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f); + snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000); + // filter envelope + snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f); + // pitch envelope + snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0); + + spin_unlock_irqrestore(&emu->reg_lock, flags); +} + +static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) + return err; + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0) { /* change */ + snd_util_memblk_t *memblk; + if (epcm->memblk != NULL) + snd_emu10k1_free_pages(emu, epcm->memblk); + memblk = snd_emu10k1_alloc_pages(emu, runtime->dma_addr, runtime->dma_bytes); + if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) { + epcm->start_addr = 0; + return -ENOMEM; + } + epcm->start_addr = ((emu10k1_memblk_t *)memblk)->mapped_page << PAGE_SHIFT; + } + return 0; +} + +static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm; + + if (runtime->private_data == NULL) + return 0; + epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + if (epcm->extra) { + snd_emu10k1_voice_free(epcm->emu, epcm->extra); + epcm->extra = NULL; + } + if (epcm->voices[1]) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); + epcm->voices[1] = NULL; + } + if (epcm->voices[0]) { + snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); + epcm->voices[0] = NULL; + } + if (epcm->memblk) { + snd_emu10k1_free_pages(emu, epcm->memblk); + epcm->memblk = NULL; + epcm->start_addr = 0; + } + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int start_addr, end_addr; + + start_addr = epcm->start_addr; + end_addr = snd_pcm_lib_period_bytes(substream); + if (runtime->channels == 2) + end_addr >>= 1; + end_addr += start_addr; + snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, + start_addr, end_addr); + end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); + snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], + start_addr, end_addr); + if (epcm->voices[1]) + snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], + start_addr, end_addr); + return 0; +} + +static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_emu10k1_capture_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + int idx; + + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + break; + default: + break; + } + snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr); + epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream); + epcm->capture_bs_val = 0; + for (idx = 0; idx < 31; idx++) { + if (capture_period_sizes[idx] == epcm->capture_bufsize) { + epcm->capture_bs_val = idx + 1; + break; + } + } + if (epcm->capture_bs_val == 0) { + snd_BUG(); + epcm->capture_bs_val++; + } + if (epcm->type == CAPTURE_AC97ADC) { + epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE; + if (runtime->channels > 1) + epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE; + epcm->capture_cr_val |= emu->audigy ? + snd_emu10k1_audigy_capture_rate_reg(runtime->rate) : + snd_emu10k1_capture_rate_reg(runtime->rate); + } + return 0; +} + +static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_t *evoice) +{ + snd_pcm_runtime_t *runtime; + unsigned int voice, i, ccis, cra = 64, cs, sample; + + if (evoice == NULL) + return; + runtime = evoice->epcm->substream->runtime; + voice = evoice->number; + sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; + if (runtime->channels > 1) { + ccis = 28; + cs = 4; + } else { + ccis = 30; + cs = 2; + } + if (sample == 0) /* 16-bit */ + ccis *= 2; + for (i = 0; i < cs; i++) + snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); + // reset cache + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); + snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); + if (runtime->channels > 1) { + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); + snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); + } + // fill cache + snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis); +} + +static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra) +{ + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; + emu10k1_pcm_mixer_t *mix; + unsigned int voice, pitch, pitch_target, tmp; + unsigned int attn; + + if (evoice == NULL) /* skip second voice for mono */ + return; + substream = evoice->epcm->substream; + runtime = substream->runtime; + mix = &emu->pcm_mixer[substream->number]; + voice = evoice->number; + pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; + pitch_target = emu10k1_calc_pitch_target(runtime->rate); + attn = extra ? 0 : 0x00ff; + tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; + snd_emu10k1_ptr_write(emu, IFATN, voice, attn); + snd_emu10k1_ptr_write(emu, VTFT, voice, (mix->attn[tmp] << 16) | 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, voice, (mix->attn[tmp] << 16) | 0xffff); + snd_emu10k1_voice_clear_loop_stop(emu, voice); + if (extra) + snd_emu10k1_voice_intr_enable(emu, voice); + snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f); + snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); + if (master) + snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); + snd_emu10k1_ptr_write(emu, IP, voice, pitch); +} + +static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice) +{ + unsigned int voice; + + if (evoice == NULL) + return; + voice = evoice->number; + snd_emu10k1_voice_intr_disable(emu, voice); + snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0); + snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0); + snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff); + snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); + snd_emu10k1_ptr_write(emu, IP, voice, 0); +} + +static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned long flags; + int result = 0; + + // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */ + snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[0]); + /* follow thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0); + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0); + snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); + epcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + epcm->running = 0; + snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); + snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]); + snd_emu10k1_playback_stop_voice(emu, epcm->extra); + break; + default: + result = -EINVAL; + break; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned long flags; + int result = 0; + + // printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream)); + spin_lock_irqsave(&emu->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + outl(epcm->capture_ipr, emu->port + IPR); + snd_emu10k1_intr_enable(emu, epcm->capture_inte); + // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); + printk(">> FXWC = 0x%x\n", snd_emu10k1_ptr_read(emu, FXWC, 0)); + break; + default: + break; + } + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val); + epcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + epcm->running = 0; + snd_emu10k1_intr_disable(emu, epcm->capture_inte); + outl(epcm->capture_ipr, emu->port + IPR); + snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); + switch (epcm->type) { + case CAPTURE_AC97ADC: + snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); + break; + case CAPTURE_EFX: + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + break; + default: + break; + } + break; + default: + result = -EINVAL; + } + spin_unlock_irqrestore(&emu->reg_lock, flags); + return result; +} + +static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int ptr; + + if (!epcm->running) + return 0; + ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff; +#if 0 /* Perex's code */ + ptr += runtime->buffer_size; + ptr -= epcm->ccca_start_addr; + ptr %= runtime->buffer_size; +#else /* EMU10K1 Open Source code from Creative */ + if (ptr < epcm->ccca_start_addr) + ptr += runtime->buffer_size - epcm->ccca_start_addr; + else + ptr -= epcm->ccca_start_addr; +#endif + // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); + return ptr; +} + +static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + unsigned int ptr; + + if (!epcm->running) + return 0; + ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; + return bytes_to_frames(runtime, ptr); +} + +/* + * Playback support device description + */ + +static snd_pcm_hardware_t snd_emu10k1_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_emu10k1_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (64*1024), + period_bytes_min: 384, + period_bytes_max: (64*1024), + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +/* + * + */ + +static void snd_emu10k1_pcm_mixer_notify1(snd_card_t *card, snd_kcontrol_t *kctl, int activate) +{ + snd_runtime_check(kctl != NULL, return); + if (activate) + kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + else + kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); +} + +static void snd_emu10k1_pcm_mixer_notify(snd_card_t *card, emu10k1_pcm_mixer_t *mix, int activate) +{ + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_routing, activate); + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_volume, activate); + snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_attn, activate); +} + +static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return); + + if (epcm) + snd_magic_kfree(epcm); +} + +static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + emu10k1_pcm_mixer_t *mix; + snd_pcm_runtime_t *runtime = substream->runtime; + int i, err; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = PLAYBACK_EMUVOICE; + epcm->substream = substream; + runtime->private_data = epcm; + runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_playback; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { + snd_magic_kfree(epcm); + return err; + } + if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) { + snd_magic_kfree(epcm); + return err; + } + mix = &emu->pcm_mixer[substream->number]; + for (i = 0; i < 4; i++) + mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; + memset(&mix->send_volume, 0, sizeof(mix->send_volume)); + mix->send_volume[0][0] = mix->send_volume[0][1] = + mix->send_volume[1][0] = mix->send_volume[2][1] = 255; + mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; + mix->epcm = epcm; + snd_emu10k1_pcm_mixer_notify(emu->card, mix, 1); + return 0; +} + +static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; + + mix->epcm = NULL; + snd_emu10k1_pcm_mixer_notify(emu->card, mix, 0); + return 0; +} + +static int snd_emu10k1_capture_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + emu10k1_pcm_t *epcm; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_AC97ADC; + epcm->substream = substream; + epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL; + epcm->capture_inte = INTE_ADCBUFENABLE; + epcm->capture_ba_reg = ADCBA; + epcm->capture_bs_reg = ADCBS; + epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX; + runtime->private_data = epcm; + runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; + emu->pcm_capture_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates); + return 0; +} + +static int snd_emu10k1_capture_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_substream = NULL; + return 0; +} + +static int snd_emu10k1_capture_mic_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + snd_pcm_runtime_t *runtime = substream->runtime; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_AC97MIC; + epcm->substream = substream; + epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL; + epcm->capture_inte = INTE_MICBUFENABLE; + epcm->capture_ba_reg = MICBA; + epcm->capture_bs_reg = MICBS; + epcm->capture_idx_reg = MICIDX; + substream->runtime->private_data = epcm; + substream->runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + runtime->hw.rates = SNDRV_PCM_RATE_8000; + runtime->hw.rate_min = runtime->hw.rate_max = 8000; + runtime->hw.channels_min = 1; + emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; + emu->pcm_capture_mic_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + return 0; +} + +static int snd_emu10k1_capture_mic_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_mic_substream = NULL; + return 0; +} + +static int snd_emu10k1_capture_efx_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + emu10k1_pcm_t *epcm; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + int idx; + + epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + if (epcm == NULL) + return -ENOMEM; + epcm->emu = emu; + epcm->type = CAPTURE_EFX; + epcm->substream = substream; + epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL; + epcm->capture_inte = INTE_EFXBUFENABLE; + epcm->capture_ba_reg = FXBA; + epcm->capture_bs_reg = FXBS; + epcm->capture_idx_reg = FXIDX; + substream->runtime->private_data = epcm; + substream->runtime->private_free = snd_emu10k1_pcm_free_substream; + runtime->hw = snd_emu10k1_capture; + runtime->hw.rates = SNDRV_PCM_RATE_48000; + runtime->hw.rate_min = runtime->hw.rate_max = 48000; + spin_lock_irqsave(&emu->reg_lock, flags); + runtime->hw.channels_min = runtime->hw.channels_max = 0; + for (idx = 0; idx < 32; idx++) { + if (emu->efx_voices_mask & (1 << idx)) { + runtime->hw.channels_min++; + runtime->hw.channels_max++; + } + } + epcm->capture_cr_val = emu->efx_voices_mask; + spin_unlock_irqrestore(&emu->reg_lock, flags); + emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; + emu->pcm_capture_efx_substream = substream; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + return 0; +} + +static int snd_emu10k1_capture_efx_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + + emu->capture_interrupt = NULL; + emu->pcm_capture_efx_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_playback_ops = { + open: snd_emu10k1_playback_open, + close: snd_emu10k1_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_playback_hw_params, + hw_free: snd_emu10k1_playback_hw_free, + prepare: snd_emu10k1_playback_prepare, + trigger: snd_emu10k1_playback_trigger, + pointer: snd_emu10k1_playback_pointer, +}; + +static snd_pcm_ops_t snd_emu10k1_capture_ops = { + open: snd_emu10k1_capture_open, + close: snd_emu10k1_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_capture_hw_params, + hw_free: snd_emu10k1_capture_hw_free, + prepare: snd_emu10k1_capture_prepare, + trigger: snd_emu10k1_capture_trigger, + pointer: snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "EMU10K1"); + emu->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = { + open: snd_emu10k1_capture_mic_open, + close: snd_emu10k1_capture_mic_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_capture_hw_params, + hw_free: snd_emu10k1_capture_hw_free, + prepare: snd_emu10k1_capture_prepare, + trigger: snd_emu10k1_capture_trigger, + pointer: snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_mic = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_mic_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 MIC"); + emu->pcm_mic = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 32; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int idx; + + spin_lock_irqsave(&emu->reg_lock, flags); + for (idx = 0; idx < 32; idx++) + ucontrol->value.integer.value[idx] = (emu->efx_voices_mask & (1 << idx)) ? 1 : 0; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return 0; +} + +static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + emu10k1_t *emu = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval, bits; + int change, idx; + + for (idx = 0, nval = bits = 0; idx < 32; idx++) + if (ucontrol->value.integer.value[idx]) { + nval |= 1 << idx; + bits++; + } + if (bits != 1 && bits != 2 && bits != 4 && bits != 8) + return -EINVAL; + spin_lock_irqsave(&emu->reg_lock, flags); + change = nval != emu->efx_voices_mask; + emu->efx_voices_mask = nval; + spin_unlock_irqrestore(&emu->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = { + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "EFX voices mask", + info: snd_emu10k1_pcm_efx_voices_mask_info, + get: snd_emu10k1_pcm_efx_voices_mask_get, + put: snd_emu10k1_pcm_efx_voices_mask_put +}; + +static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = { + open: snd_emu10k1_capture_efx_open, + close: snd_emu10k1_capture_efx_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_emu10k1_capture_hw_params, + hw_free: snd_emu10k1_capture_hw_free, + prepare: snd_emu10k1_capture_prepare, + trigger: snd_emu10k1_capture_trigger, + pointer: snd_emu10k1_capture_pointer, +}; + +static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu->pcm_efx = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + + if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 0, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + pcm->private_free = snd_emu10k1_pcm_efx_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, "EMU10K1 EFX"); + emu->pcm_efx = pcm; + if (rpcm) + *rpcm = pcm; + + emu->efx_voices_mask = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; + snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu)); + + snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + + return 0; +} diff -Nru linux/sound/pci/emu10k1/emuproc.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emuproc.c --- linux/sound/pci/emu10k1/emuproc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/emuproc.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,348 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips / proc interface routines + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu, + snd_info_buffer_t * buffer, + char *title, + int status_reg, + int rate_reg) +{ + static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" }; + static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" }; + static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" }; + unsigned int status, rate = 0; + + status = snd_emu10k1_ptr_read(emu, status_reg, 0); + if (rate_reg > 0) + rate = snd_emu10k1_ptr_read(emu, rate_reg, 0); + + snd_iprintf(buffer, "\n%s\n", title); + + snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no"); + snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no"); + snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no"); + snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]); + snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6); + snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8); + snd_iprintf(buffer, "Generation Status : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy"); + snd_iprintf(buffer, "Source Mask : %i\n", (status & SPCS_SOURCENUMMASK) >> 16); + snd_iprintf(buffer, "Channel Number : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]); + snd_iprintf(buffer, "Sample Rate : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]); + snd_iprintf(buffer, "Clock Accuracy : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]); + + if (rate_reg > 0) { + snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off"); + snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE); + } +} + +static void snd_emu10k1_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + static char *outputs[32] = { + /* 00 */ "PCM Left", + /* 01 */ "PCM Right", + /* 02 */ "PCM Surround Left", + /* 03 */ "PCM Surround Right", + /* 04 */ "MIDI Left", + /* 05 */ "MIDI Right", + /* 06 */ "PCM Center", + /* 07 */ "PCM LFE", + /* 08 */ "???", + /* 09 */ "???", + /* 10 */ "???", + /* 11 */ "???", + /* 12 */ "MIDI Reverb", + /* 13 */ "MIDI Chorus", + /* 14 */ "???", + /* 15 */ "???", + /* 16 */ "???", + /* 17 */ "???", + /* 18 */ "ADC Left / CDROM S/PDIF Left", + /* 19 */ "ADC Right / CDROM S/PDIF Right", + /* 20 */ "MIC / Zoom Video Left", + /* 21 */ "Zoom Video Right", + /* 22 */ "S/PDIF Left", + /* 23 */ "S/PDIF Right", + /* 24 */ "???", + /* 25 */ "???", + /* 26 */ "???", + /* 27 */ "???", + /* 28 */ "???", + /* 29 */ "???", + /* 30 */ "???", + /* 31 */ "???" + }; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + unsigned int val; + int idx; + + snd_iprintf(buffer, "EMU10K1\n\n"); + val = emu->audigy ? + snd_emu10k1_ptr_read(emu, A_FXRT1, 0) : + snd_emu10k1_ptr_read(emu, FXRT, 0); + snd_iprintf(buffer, "Card : %s\n", + emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative")); + snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size); + snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_size); + snd_iprintf(buffer, "\n"); + if (emu->audigy) { + snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", + val & 0x3f, + (val >> 8) & 0x3f, + (val >> 16) & 0x3f, + (val >> 24) & 0x3f); + } else { + snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", + (val >> 16) & 0x0f, + (val >> 20) & 0x0f, + (val >> 24) & 0x0f, + (val >> 28) & 0x0f); + } + snd_iprintf(buffer, "\nCaptured FX Outputs :\n"); + for (idx = 0; idx < 32; idx++) { + if (emu->efx_voices_mask & (1 << idx)) + snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]); + } + snd_iprintf(buffer, "\nAll FX Outputs :\n"); + for (idx = 0; idx < 32; idx++) + snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1); + snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS); + snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS); + val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0); + snd_iprintf(buffer, "\nZoomed Video\n"); + snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE); +} + +static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + u32 pc; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + + snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name); + snd_iprintf(buffer, " Code dump :\n"); + for (pc = 0; pc < 512; pc++) { + u32 low, high; + + low = snd_emu10k1_efx_read(emu, pc * 2); + high = snd_emu10k1_efx_read(emu, pc * 2 + 1); + if (emu->audigy) + snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n", + (high >> 24) & 0x0f, + (high >> 12) & 0x7ff, + (high >> 0) & 0x7ff, + (low >> 12) & 0x7ff, + (low >> 0) & 0x7ff, + pc, + high, low); + else + snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n", + (high >> 20) & 0x0f, + (high >> 10) & 0x3ff, + (high >> 0) & 0x3ff, + (low >> 10) & 0x3ff, + (low >> 0) & 0x3ff, + pc, + high, low); + } +} + +#define TOTAL_SIZE_GPR (0x100*4) +#define TOTAL_SIZE_TANKMEM_DATA (0xa0*4) +#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4) +#define TOTAL_SIZE_CODE (0x200*8) + +static long snd_emu10k1_fx8010_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + long size; + emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return -ENXIO); + unsigned int offset; + + if (!strcmp(entry->name, "fx8010_tram_addr")) { + if (emu->audigy) return -EINVAL; + offset = TANKMEMADDRREGBASE; + } else if (!strcmp(entry->name, "fx8010_tram_data")) { + if (emu->audigy) return -EINVAL; + offset = TANKMEMDATAREGBASE; + } else if (!strcmp(entry->name, "fx8010_code")) { + offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE; + } else { + offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE; + } + size = count; + if (file->f_pos + size > entry->size) + size = (long)entry->size - file->f_pos; + if (size > 0) { + unsigned int *tmp; + long res; + unsigned int idx; + if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL) + return -ENOMEM; + for (idx = 0; idx < ((file->f_pos & 3) + size + 3) >> 2; idx++) + tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (file->f_pos >> 2), 0); + if (copy_to_user(buf, ((char *)tmp) + (file->f_pos & 3), size)) + res = -EFAULT; + else { + res = size; + file->f_pos += size; + } + kfree(tmp); + return res; + } + return 0; +} + +static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = { + read: snd_emu10k1_fx8010_read, +}; + +int __devinit snd_emu10k1_proc_init(emu10k1_t * emu) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(emu->card, "emu10k1", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 4096; + entry->c.text.read = snd_emu10k1_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry = entry; + entry = NULL; + if ((entry = snd_info_create_card_entry(emu->card, "fx8010_gpr", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_GPR; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_gpr = entry; + entry = NULL; + if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_data", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_TANKMEM_DATA; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_tram_data = entry; + entry = NULL; + if (!emu->audigy && (entry = snd_info_create_card_entry(emu->card, "fx8010_tram_addr", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_TANKMEM_ADDR; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_tram_addr = entry; + entry = NULL; + if ((entry = snd_info_create_card_entry(emu->card, "fx8010_code", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->size = TOTAL_SIZE_CODE; + entry->c.ops = &snd_emu10k1_proc_ops_fx8010; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_code = entry; + entry = NULL; + if ((entry = snd_info_create_card_entry(emu->card, "fx8010_acode", emu->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 64*1024; + entry->c.text.read = snd_emu10k1_proc_acode_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + emu->proc_entry_fx8010_iblocks = entry; + return 0; +} + +int snd_emu10k1_proc_done(emu10k1_t * emu) +{ + if (emu->proc_entry) { + snd_info_unregister(emu->proc_entry); + emu->proc_entry = NULL; + } + if (emu->proc_entry_fx8010_gpr) { + snd_info_unregister(emu->proc_entry_fx8010_gpr); + emu->proc_entry_fx8010_gpr = NULL; + } + if (emu->proc_entry_fx8010_tram_data) { + snd_info_unregister(emu->proc_entry_fx8010_tram_data); + emu->proc_entry_fx8010_tram_data = NULL; + } + if (emu->proc_entry_fx8010_tram_addr) { + snd_info_unregister(emu->proc_entry_fx8010_tram_addr); + emu->proc_entry_fx8010_tram_addr = NULL; + } + if (emu->proc_entry_fx8010_code) { + snd_info_unregister(emu->proc_entry_fx8010_code); + emu->proc_entry_fx8010_code = NULL; + } + if (emu->proc_entry_fx8010_iblocks) { + snd_info_unregister(emu->proc_entry_fx8010_iblocks); + emu->proc_entry_fx8010_iblocks = NULL; + } + return 0; +} diff -Nru linux/sound/pci/emu10k1/io.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/io.c --- linux/sound/pci/emu10k1/io.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/io.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,341 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn) +{ + unsigned long flags; + unsigned int regptr, val; + unsigned int mask; + + mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; + regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + unsigned char size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + val = inl(emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + + return (val & mask) >> offset; + } else { + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + val = inl(emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; + } +} + +void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data) +{ + unsigned int regptr; + unsigned long flags; + unsigned int mask; + + mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; + regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) { + unsigned char size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = (data << offset) & mask; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + data |= inl(emu->port + DATA) & ~mask; + outl(data, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + } else { + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + outl(data, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + } +} + +void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) | intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) & ~intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIEH << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val |= 1 << (voicenum - 32); + } else { + outl(CLIEL << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val |= 1 << voicenum; + } + outl(val, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIEH << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val &= ~(1 << (voicenum - 32)); + } else { + outl(CLIEL << 16, emu->port + PTR); + val = inl(emu->port + DATA); + val &= ~(1 << voicenum); + } + outl(val, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(CLIPH << 16, emu->port + PTR); + voicenum = 1 << (voicenum - 32); + } else { + outl(CLIPL << 16, emu->port + PTR); + voicenum = 1 << voicenum; + } + outl(voicenum, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int sol; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(SOLEH << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol |= 1 << (voicenum - 32); + } else { + outl(SOLEL << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol |= 1 << voicenum; + } + outl(sol, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum) +{ + unsigned long flags; + unsigned int sol; + + spin_lock_irqsave(&emu->emu_lock, flags); + /* voice interrupt */ + if (voicenum >= 32) { + outl(SOLEH << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol &= ~(1 << (voicenum - 32)); + } else { + outl(SOLEL << 16, emu->port + PTR); + sol = inl(emu->port + DATA); + sol &= ~(1 << voicenum); + } + outl(sol, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait) +{ + volatile unsigned count; + unsigned int newtime = 0, curtime; + + curtime = inl(emu->port + WC) >> 6; + while (wait-- > 0) { + count = 0; + while (count++ < 16384) { + newtime = inl(emu->port + WC) >> 6; + if (newtime != curtime) + break; + } + if (count >= 16384) + break; + curtime = newtime; + } +} + +unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return -ENXIO); + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + val = inw(emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; +} + +void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + outw(data, emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +/* + * convert rate to pitch + */ + +unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate) +{ + static u32 logMagTable[128] = { + 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, + 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, + 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, + 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, + 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, + 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, + 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, + 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, + 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, + 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, + 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, + 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, + 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, + 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, + 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, + 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df + }; + static char logSlopeTable[128] = { + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, + 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, + 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, + 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, + 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, + 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, + 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, + 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f + }; + int i; + + if (rate == 0) + return 0; /* Bail out if no leading "1" */ + rate *= 11185; /* Scale 48000 to 0x20002380 */ + for (i = 31; i > 0; i--) { + if (rate & 0x80000000) { /* Detect leading "1" */ + return (((unsigned int) (i - 15) << 20) + + logMagTable[0x7f & (rate >> 24)] + + (0x7f & (rate >> 17)) * + logSlopeTable[0x7f & (rate >> 24)]); + } + rate <<= 1; + } + + return 0; /* Should never reach this point */ +} + +/* + * Returns an attenuation based upon a cumulative volume value + * Algorithm calculates 0x200 - 0x10 log2 (input) + */ + +unsigned char snd_emu10k1_sum_vol_attn(unsigned int value) +{ + unsigned short count = 16, ans; + + if (value == 0) + return 0xFF; + + /* Find first SET bit. This is the integer part of the value */ + while ((value & 0x10000) == 0) { + value <<= 1; + count--; + } + + /* The REST of the data is the fractional part. */ + ans = (unsigned short) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12))); + if (ans > 0xFF) + ans = 0xFF; + + return (unsigned char) ans; +} diff -Nru linux/sound/pci/emu10k1/irq.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/irq.c --- linux/sound/pci/emu10k1/irq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/irq.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,149 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for IRQ control of EMU10K1 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +void snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + emu10k1_t *emu = snd_magic_cast(emu10k1_t, dev_id, return); + unsigned int status; + + while ((status = inl(emu->port + IPR)) != 0) { + // printk("irq - status = 0x%x\n", status); + if (status & IPR_PCIERROR) { + snd_printk("interrupt: PCI error\n"); + snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); + } + if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { + if (emu->hwvol_interrupt) + emu->hwvol_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); + outl(status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE), emu->port + IPR); + } + if (status & IPR_CHANNELLOOP) { + int voice; + int voice_max = status & IPR_CHANNELNUMBERMASK; + int voice_max_l; + u32 val; + emu10k1_voice_t *pvoice = emu->voices; + + val = snd_emu10k1_ptr_read(emu, CLIPL, 0); + voice_max_l = voice_max; + if (voice_max_l >= 0x20) + voice_max_l = 0x1f; + for (voice = 0; voice <= voice_max_l; voice++) { + if (val & 1) { + if (pvoice->use && pvoice->interrupt != NULL) { + pvoice->interrupt(emu, pvoice); + snd_emu10k1_voice_intr_ack(emu, voice); + } else { + snd_emu10k1_voice_intr_disable(emu, voice); + } + } + val >>= 1; + pvoice++; + } + if (voice_max > 0x1f) { + val = snd_emu10k1_ptr_read(emu, CLIPH, 0); + for (; voice <= voice_max; voice++) { + if(val & 1) { + if (pvoice->use && pvoice->interrupt != NULL) { + pvoice->interrupt(emu, pvoice); + snd_emu10k1_voice_intr_ack(emu, voice); + } else { + snd_emu10k1_voice_intr_disable(emu, voice); + } + } + val >>= 1; + pvoice++; + } + } + outl(IPR_CHANNELLOOP | (status & IPR_CHANNELNUMBERMASK), emu->port + IPR); + } + if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { + if (emu->capture_interrupt) + emu->capture_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); + outl(status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { + if (emu->capture_mic_interrupt) + emu->capture_mic_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); + outl(status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { + if (emu->capture_efx_interrupt) + emu->capture_efx_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); + outl(status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL), emu->port + IPR); + } + if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { + if (emu->midi.interrupt) + emu->midi.interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); + outl(status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY), emu->port + IPR); + } + if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { + if (emu->midi2.interrupt) + emu->midi2.interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); + outl(status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2), emu->port + IPR); + } + if (status & IPR_INTERVALTIMER) { + if (emu->timer_interrupt) + emu->timer_interrupt(emu); + else + snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); + outl(IPR_INTERVALTIMER, emu->port + IPR); + } + if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { + if (emu->spdif_interrupt) + emu->spdif_interrupt(emu, status); + else + snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); + outl(status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE), emu->port + IPR); + } + if (status & IPR_FXDSP) { + if (emu->dsp_interrupt) + emu->dsp_interrupt(emu); + else + snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); + outl(IPR_FXDSP, emu->port + IPR); + } + } +} diff -Nru linux/sound/pci/emu10k1/memory.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/memory.c --- linux/sound/pci/emu10k1/memory.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/memory.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,541 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Takashi Iwai + * + * EMU10K1 memory page allocation (PTB area) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +/* page arguments of these two macros are Emu page (4096 bytes), not like + * aligned pages in others + */ +#define __set_ptb_entry(emu,page,addr) \ + ((emu)->ptb_pages[page] = ((addr) << 1) | (page)) + +#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE) +#define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES) +/* get aligned page from offset address */ +#define get_aligned_page(offset) ((offset) >> PAGE_SHIFT) +/* get offset address from aligned page */ +#define aligned_page_offset(page) ((page) << PAGE_SHIFT) + +#if PAGE_SIZE == 4096 +/* page size == EMUPAGESIZE */ +/* fill PTB entrie(s) corresponding to page with addr */ +#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr) +/* fill PTB entrie(s) corresponding to page with silence pointer */ +#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page_dmaaddr) +#else +/* fill PTB entries -- we need to fill UNIT_PAGES entries */ +static inline void set_ptb_entry(emu10k1_t *emu, int page, dma_addr_t addr) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) { + __set_ptb_entry(emu, page, addr); + addr += EMUPAGESIZE; + } +} +static inline void set_silent_ptb(emu10k1_t *emu, int page) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) + /* do not increment ptr */ + __set_ptb_entry(emu, page, emu->silent_page_dmaaddr); +} +#endif /* PAGE_SIZE */ + + +/* + */ +static int synth_alloc_pages(emu10k1_t *hw, emu10k1_memblk_t *blk); +static int synth_free_pages(emu10k1_t *hw, emu10k1_memblk_t *blk); + +#define get_emu10k1_memblk(l,member) list_entry(l, emu10k1_memblk_t, member) + + +/* initialize emu10k1 part */ +static void emu10k1_memblk_init(emu10k1_memblk_t *blk) +{ + blk->mapped_page = -1; + INIT_LIST_HEAD(&blk->mapped_link); + INIT_LIST_HEAD(&blk->mapped_order_link); + blk->map_locked = 0; + + blk->first_page = get_aligned_page(blk->mem.offset); + blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1); + blk->pages = blk->last_page - blk->first_page + 1; +} + +/* + * search empty region on PTB with the given size + * + * if an empty region is found, return the page and store the next mapped block + * in nextp + * if not found, return a negative error code. + */ +static int search_empty_map_area(emu10k1_t *emu, int npages, struct list_head **nextp) +{ + int page = 0, found_page = -ENOMEM; + int max_size = npages; + int size; + struct list_head *candidate = &emu->mapped_link_head; + struct list_head *pos; + + list_for_each (pos, &emu->mapped_link_head) { + emu10k1_memblk_t *blk = get_emu10k1_memblk(pos, mapped_link); + snd_assert(blk->mapped_page >= 0, continue); + size = blk->mapped_page - page; + if (size == npages) { + *nextp = pos; + return page; + } + else if (size > max_size) { + /* we look for the maximum empty hole */ + max_size = size; + candidate = pos; + found_page = page; + } + page = blk->mapped_page + blk->pages; + } + size = MAX_ALIGN_PAGES - page; + if (size >= max_size) { + *nextp = pos; + return page; + } + *nextp = candidate; + return found_page; +} + +/* + * map a memory block onto emu10k1's PTB + * + * call with memblk_lock held + */ +static int map_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, pg; + struct list_head *next; + + page = search_empty_map_area(emu, blk->pages, &next); + if (page < 0) /* not found */ + return page; + /* insert this block in the proper position of mapped list */ + list_add_tail(&blk->mapped_link, next); + /* append this as a newest block in order list */ + list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head); + blk->mapped_page = page; + /* fill PTB */ + for (pg = blk->first_page; pg <= blk->last_page; pg++) { + set_ptb_entry(emu, page, emu->page_addr_table[pg]); + page++; + } + return 0; +} + +/* + * unmap the block + * return the size of resultant empty pages + * + * call with memblk_lock held + */ +static int unmap_memblk(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int start_page, end_page, mpage, pg; + struct list_head *p; + emu10k1_memblk_t *q; + + /* calculate the expected size of empty region */ + if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) { + q = get_emu10k1_memblk(p, mapped_link); + start_page = q->mapped_page + q->pages; + } else + start_page = 0; + if ((p = blk->mapped_link.next) != &emu->mapped_link_head) { + q = get_emu10k1_memblk(p, mapped_link); + end_page = q->mapped_page; + } else + end_page = MAX_ALIGN_PAGES; + + /* remove links */ + list_del(&blk->mapped_link); + list_del(&blk->mapped_order_link); + /* clear PTB */ + mpage = blk->mapped_page; + for (pg = blk->first_page; pg <= blk->last_page; pg++) { + set_silent_ptb(emu, mpage); + mpage++; + } + blk->mapped_page = -1; + return end_page - start_page; /* return the new empty size */ +} + +/* + * search empty pages with the given size, and create a memory block + * + * unlike synth_alloc the memory block is aligned to the page start + */ +static emu10k1_memblk_t * +search_empty(emu10k1_t *emu, int size) +{ + struct list_head *p; + emu10k1_memblk_t *blk; + int page, psize; + + psize = get_aligned_page(size + PAGE_SIZE -1); + page = 0; + list_for_each(p, &emu->memhdr->block) { + blk = get_emu10k1_memblk(p, mem.list); + if (page + psize <= blk->first_page) + goto __found_pages; + page = blk->last_page + 1; + } + if (page + psize > emu->max_cache_pages) + return NULL; + +__found_pages: + /* create a new memory block */ + blk = (emu10k1_memblk_t *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev); + if (blk == NULL) + return NULL; + blk->mem.offset = aligned_page_offset(page); /* set aligned offset */ + emu10k1_memblk_init(blk); + return blk; +} + + +/* + * check if the given pointer is valid for pages + */ +static int is_valid_page(dma_addr_t addr) +{ + if (addr & ~0x7fffffffUL) { + snd_printk("max memory size is 2GB!!\n"); + return 0; + } + if (addr & (EMUPAGESIZE-1)) { + snd_printk("page is not aligned\n"); + return 0; + } + return 1; +} + +/* + * map the given memory block on PTB. + * if the block is already mapped, update the link order. + * if no empty pages are found, tries to release unsed memory blocks + * and retry the mapping. + */ +int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int err; + int size; + struct list_head *p, *nextp; + emu10k1_memblk_t *deleted; + unsigned long flags; + + spin_lock_irqsave(&emu->memblk_lock, flags); + if (blk->mapped_page >= 0) { + /* update order link */ + list_del(&blk->mapped_order_link); + list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head); + spin_unlock_irqrestore(&emu->memblk_lock, flags); + return 0; + } + if ((err = map_memblk(emu, blk)) < 0) { + /* no enough page - try to unmap some blocks */ + /* starting from the oldest block */ + p = emu->mapped_order_link_head.next; + for (; p != &emu->mapped_order_link_head; p = nextp) { + nextp = p->next; + deleted = get_emu10k1_memblk(p, mapped_order_link); + if (deleted->map_locked) + continue; + size = unmap_memblk(emu, deleted); + if (size >= blk->pages) { + /* ok the empty region is enough large */ + err = map_memblk(emu, blk); + break; + } + } + } + spin_unlock_irqrestore(&emu->memblk_lock, flags); + return err; +} + +/* + * page allocation for DMA + */ +snd_util_memblk_t * +snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size) +{ + snd_util_memhdr_t *hdr; + emu10k1_memblk_t *blk; + int page, err; + + snd_assert(emu, return NULL); + snd_assert(size > 0 && size < MAXPAGES * EMUPAGESIZE, return NULL); + hdr = emu->memhdr; + snd_assert(hdr, return NULL); + + if (!is_valid_page(addr)) + return NULL; + + down(&hdr->block_mutex); + blk = search_empty(emu, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + /* fill buffer addresses but pointers are not stored so that + * snd_free_pci_pages() is not called in in synth_free() + */ + for (page = blk->first_page; page <= blk->last_page; page++) { + emu->page_addr_table[page] = addr; + emu->page_ptr_table[page] = NULL; + addr += PAGE_SIZE; + } + + /* set PTB entries */ + blk->map_locked = 1; /* do not unmap this block! */ + err = snd_emu10k1_memblk_map(emu, blk); + if (err < 0) { + __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk); + up(&hdr->block_mutex); + return NULL; + } + up(&hdr->block_mutex); + return (snd_util_memblk_t *)blk; +} + + +/* + * release DMA buffer from page table + */ +int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk) +{ + snd_assert(emu && blk, return -EINVAL); + return snd_emu10k1_synth_free(emu, blk); +} + + +/* + * memory allocation using multiple pages (for synth) + * Unlike the DMA allocation above, non-contiguous pages are assined. + */ + +/* + * allocate a synth sample area + */ +snd_util_memblk_t * +snd_emu10k1_synth_alloc(emu10k1_t *hw, unsigned int size) +{ + emu10k1_memblk_t *blk; + snd_util_memhdr_t *hdr = hw->memhdr; + + down(&hdr->block_mutex); + blk = (emu10k1_memblk_t *)__snd_util_mem_alloc(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + if (synth_alloc_pages(hw, blk)) { + __snd_util_mem_free(hdr, (snd_util_memblk_t *)blk); + up(&hdr->block_mutex); + return NULL; + } + snd_emu10k1_memblk_map(hw, blk); + up(&hdr->block_mutex); + return (snd_util_memblk_t *)blk; +} + + +/* + * free a synth sample area + */ +int +snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *memblk) +{ + snd_util_memhdr_t *hdr = emu->memhdr; + emu10k1_memblk_t *blk = (emu10k1_memblk_t *)memblk; + unsigned long flags; + + down(&hdr->block_mutex); + spin_lock_irqsave(&emu->memblk_lock, flags); + if (blk->mapped_page >= 0) + unmap_memblk(emu, blk); + spin_unlock_irqrestore(&emu->memblk_lock, flags); + synth_free_pages(emu, blk); + __snd_util_mem_free(hdr, memblk); + up(&hdr->block_mutex); + return 0; +} + + +/* check new allocation range */ +static void get_single_page_range(snd_util_memhdr_t *hdr, emu10k1_memblk_t *blk, int *first_page_ret, int *last_page_ret) +{ + struct list_head *p; + emu10k1_memblk_t *q; + int first_page, last_page; + first_page = blk->first_page; + if ((p = blk->mem.list.prev) != &hdr->block) { + q = get_emu10k1_memblk(p, mem.list); + if (q->last_page == first_page) + first_page++; /* first page was already allocated */ + } + last_page = blk->last_page; + if ((p = blk->mem.list.next) != &hdr->block) { + q = get_emu10k1_memblk(p, mem.list); + if (q->first_page == last_page) + last_page--; /* last page was already allocated */ + } + *first_page_ret = first_page; + *last_page_ret = last_page; +} + +/* + * allocate kernel pages + */ +static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, first_page, last_page; + void *ptr; + dma_addr_t addr; + + emu10k1_memblk_init(blk); + get_single_page_range(emu->memhdr, blk, &first_page, &last_page); + /* allocate kernel pages */ + for (page = first_page; page <= last_page; page++) { + ptr = snd_malloc_pci_pages(emu->pci, PAGE_SIZE, &addr); + if (ptr == NULL) + goto __fail; + if (! is_valid_page(addr)) { + snd_free_pci_pages(emu->pci, PAGE_SIZE, ptr, addr); + goto __fail; + } + emu->page_addr_table[page] = addr; + emu->page_ptr_table[page] = ptr; + } + return 0; + +__fail: + /* release allocated pages */ + last_page = page - 1; + for (page = first_page; page <= last_page; page++) { + snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + emu->page_addr_table[page] = 0; + emu->page_ptr_table[page] = NULL; + } + + return -ENOMEM; +} + +/* + * free pages + */ +static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) +{ + int page, first_page, last_page; + + get_single_page_range(emu->memhdr, blk, &first_page, &last_page); + for (page = first_page; page <= last_page; page++) { + if (emu->page_ptr_table[page]) + snd_free_pci_pages(emu->pci, PAGE_SIZE, emu->page_ptr_table[page], emu->page_addr_table[page]); + emu->page_addr_table[page] = 0; + emu->page_ptr_table[page] = NULL; + } + + return 0; +} + +/* calculate buffer pointer from offset address */ +inline static void *offset_ptr(emu10k1_t *emu, int page, int offset) +{ + char *ptr; + snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL); + ptr = emu->page_ptr_table[page]; + if (! ptr) { + printk("emu10k1: access to NULL ptr: page = %d\n", page); + return NULL; + } + ptr += offset & (PAGE_SIZE - 1); + return (void*)ptr; +} + +/* + * bzero(blk + offset, size) + */ +int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size) +{ + int page, nextofs, end_offset, temp, temp1; + void *ptr; + emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk; + + offset += blk->offset & (PAGE_SIZE - 1); + end_offset = offset + size; + page = get_aligned_page(offset); + do { + nextofs = aligned_page_offset(page + 1); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + ptr = offset_ptr(emu, page + p->first_page, offset); + if (ptr) + memset(ptr, 0, temp); + offset = nextofs; + page++; + } while (offset < end_offset); + return 0; +} + +/* + * copy_from_user(blk + offset, data, size) + */ +int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size) +{ + int page, nextofs, end_offset, temp, temp1; + void *ptr; + emu10k1_memblk_t *p = (emu10k1_memblk_t *)blk; + + offset += blk->offset & (PAGE_SIZE - 1); + end_offset = offset + size; + page = get_aligned_page(offset); + do { + nextofs = aligned_page_offset(page + 1); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + ptr = offset_ptr(emu, page + p->first_page, offset); + if (ptr && copy_from_user(ptr, data, temp)) + return -EFAULT; + offset = nextofs; + data += temp; + page++; + } while (offset < end_offset); + return 0; +} diff -Nru linux/sound/pci/emu10k1/voice.c linux-2.4.19-pre5-mjc/sound/pci/emu10k1/voice.c --- linux/sound/pci/emu10k1/voice.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/emu10k1/voice.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,112 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips - voice manager + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include + +static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) +{ + emu10k1_voice_t *voice, *voice2; + int idx; + + *rvoice = NULL; + for (idx = 0; idx < 64; idx += pair ? 2 : 1) { + voice = &emu->voices[idx]; + voice2 = pair ? &emu->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case EMU10K1_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case EMU10K1_SYNTH: + voice->synth = 1; + break; + case EMU10K1_MIDI: + voice->midi = 1; + break; + } + // printk("voice alloc - %i, pair = %i\n", voice->number, pair); + *rvoice = voice; + return 0; + } + return -ENOMEM; +} + +int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) +{ + unsigned long flags; + int result; + + snd_assert(rvoice != NULL, return -EINVAL); + snd_assert(!pair || type == EMU10K1_PCM, return -EINVAL); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (;;) { + result = voice_alloc(emu, type, pair, rvoice); + if (result == 0 || type != EMU10K1_PCM) + break; + + /* free a voice from synth */ + if (emu->get_synth_voice) { + result = emu->get_synth_voice(emu); + if (result >= 0) { + emu10k1_voice_t *pvoice = &emu->voices[result]; + pvoice->interrupt = NULL; + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->epcm = NULL; + } + } + if (result < 0) + break; + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + return result; +} + +int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice) +{ + unsigned long flags; + + snd_assert(pvoice != NULL, return -EINVAL); + spin_lock_irqsave(&emu->voice_lock, flags); + pvoice->interrupt = NULL; + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->epcm = NULL; + snd_emu10k1_voice_init(emu, pvoice->number); + spin_unlock_irqrestore(&emu->voice_lock, flags); + return 0; +} diff -Nru linux/sound/pci/ens1370.c linux-2.4.19-pre5-mjc/sound/pci/ens1370.c --- linux/sound/pci/ens1370.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ens1370.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2090 @@ +/* + * Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard + * Copyright (c) by Jaroslav Kysela , + * Thomas Sailer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t ensoniq_t + +#ifndef CHIP1371 +#undef CHIP1370 +#define CHIP1370 +#endif + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela , Thomas Sailer "); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +#ifdef CHIP1370 +MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370"); +MODULE_DEVICES("{{Ensoniq,AudioPCI-97 ES1370}," + "{Creative Labs,SB PCI64/128 (ES1370)}}"); +#endif +#ifdef CHIP1371 +MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+"); +MODULE_DEVICES("{{Ensoniq,AudioPCI ES1371/73}," + "{Ensoniq,AudioPCI ES1373}," + "{Creative Labs,Ectiva EV1938}," + "{Creative Labs,SB PCI64/128 (ES1371/73)}," + "{Creative Labs,Vibra PCI128}," + "{Ectiva,EV1938}}"); +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Ensoniq AudioPCI soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#endif +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 +#endif + +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define CT5880REV_CT5880_C 0x02 +#define CT5880REV_CT5880_D 0x03 /* ??? -jk */ +#define CT5880REV_CT5880_E 0x04 /* mw */ +#define ES1371REV_ES1371_B 0x09 +#define EV1938REV_EV1938_A 0x00 +#define ES1371REV_ES1373_8 0x08 + +/* + * Direct registers + */ + +#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x) + +#define ES_REG_CONTROL 0x00 /* R/W: Interrupt/Chip select control register */ +#define ES_1370_ADC_STOP (1<<31) /* disable capture buffer transfers */ +#define ES_1370_XCTL1 (1<<30) /* general purpose output bit */ +#define ES_1371_SPDIF_EN (1<<26) /* SPDIF enable */ +#define ES_1371_JOY_ASEL(o) (((o)&0x03)<<24) /* joystick port mapping */ +#define ES_1371_JOY_ASELM (0x03<<24) /* mask for above */ +#define ES_1371_JOY_ASELI(i) (((i)>>24)&0x03) +#define ES_1371_GPIO_IN(i) (((i)>>20)&0x0f) /* GPIO in [3:0] pins - R/O */ +#define ES_1370_PCLKDIVO(o) (((o)&0x1fff)<<16) /* clock divide ratio for DAC2 */ +#define ES_1370_PCLKDIVM ((0x1fff)<<16) /* mask for above */ +#define ES_1370_PCLKDIVI(i) (((i)>>16)&0x1fff) /* clock divide ratio for DAC2 */ +#define ES_1371_GPIO_OUT(o) (((o)&0x0f)<<16) /* GPIO out [3:0] pins - W/R */ +#define ES_1371_GPIO_OUTM (0x0f<<16) /* mask for above */ +#define ES_MSFMTSEL (1<<15) /* MPEG serial data format; 0 = SONY, 1 = I2S */ +#define ES_1370_M_SBB (1<<14) /* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */ +#define ES_1371_SYNC_RES (1<<14) /* Warm AC97 reset */ +#define ES_1370_WTSRSEL(o) (((o)&0x03)<<12) /* fixed frequency clock for DAC1 */ +#define ES_1370_WTSRSELM (0x03<<12) /* mask for above */ +#define ES_1371_ADC_STOP (1<<13) /* disable CCB transfer capture information */ +#define ES_1371_PWR_INTRM (1<<12) /* power level change interrupts enable */ +#define ES_1370_DAC_SYNC (1<<11) /* DAC's are synchronous */ +#define ES_1371_M_CB (1<<11) /* capture clock source; 0 = ADC; 1 = I2S */ +#define ES_CCB_INTRM (1<<10) /* CCB voice interrupts enable */ +#define ES_1370_M_CB (1<<9) /* capture clock source; 0 = ADC; 1 = MPEG */ +#define ES_1370_XCTL0 (1<<8) /* generap purpose output bit */ +#define ES_1371_PDLEV(o) (((o)&0x03)<<8) /* current power down level */ +#define ES_1371_PDLEVM (0x03<<8) /* mask for above */ +#define ES_BREQ (1<<7) /* memory bus request enable */ +#define ES_DAC1_EN (1<<6) /* DAC1 playback channel enable */ +#define ES_DAC2_EN (1<<5) /* DAC2 playback channel enable */ +#define ES_ADC_EN (1<<4) /* ADC capture channel enable */ +#define ES_UART_EN (1<<3) /* UART enable */ +#define ES_JYSTK_EN (1<<2) /* Joystick module enable */ +#define ES_1370_CDC_EN (1<<1) /* Codec interface enable */ +#define ES_1371_XTALCKDIS (1<<1) /* Xtal clock disable */ +#define ES_1370_SERR_DISABLE (1<<0) /* PCI serr signal disable */ +#define ES_1371_PCICLKDIS (1<<0) /* PCI clock disable */ +#define ES_REG_STATUS 0x04 /* R/O: Interrupt/Chip select status register */ +#define ES_INTR (1<<31) /* Interrupt is pending */ +#define ES_1371_ST_AC97_RST (1<<29) /* CT5880 AC'97 Reset bit */ +#define ES_1371_ST_SPDIF_EN (1<<18) /* SPDIF enable */ +#define ES_1371_ST_SPDIF_TEST (1<<17) /* SPDIF test */ +#define ES_1371_TEST (1<<16) /* test ASIC */ +#define ES_1370_CSTAT (1<<10) /* CODEC is busy or register write in progress */ +#define ES_1370_CBUSY (1<<9) /* CODEC is busy */ +#define ES_1370_CWRIP (1<<8) /* CODEC register write in progress */ +#define ES_1371_SYNC_ERR (1<<8) /* CODEC synchronization error occured */ +#define ES_1371_VC(i) (((i)>>6)&0x03) /* voice code from CCB module */ +#define ES_1370_VC(i) (((i)>>5)&0x03) /* voice code from CCB module */ +#define ES_1371_MPWR (1<<5) /* power level interrupt pending */ +#define ES_MCCB (1<<4) /* CCB interrupt pending */ +#define ES_UART (1<<3) /* UART interrupt pending */ +#define ES_DAC1 (1<<2) /* DAC1 channel interrupt pending */ +#define ES_DAC2 (1<<1) /* DAC2 channel interrupt pending */ +#define ES_ADC (1<<0) /* ADC channel interrupt pending */ +#define ES_REG_UART_DATA 0x08 /* R/W: UART data register */ +#define ES_REG_UART_STATUS 0x09 /* R/O: UART status register */ +#define ES_RXINT (1<<7) /* RX interrupt occured */ +#define ES_TXINT (1<<2) /* TX interrupt occured */ +#define ES_TXRDY (1<<1) /* transmitter ready */ +#define ES_RXRDY (1<<0) /* receiver ready */ +#define ES_REG_UART_CONTROL 0x09 /* W/O: UART control register */ +#define ES_RXINTEN (1<<7) /* RX interrupt enable */ +#define ES_TXINTENO(o) (((o)&0x03)<<5) /* TX interrupt enable */ +#define ES_TXINTENM (0x03<<5) /* mask for above */ +#define ES_TXINTENI(i) (((i)>>5)&0x03) +#define ES_CNTRL(o) (((o)&0x03)<<0) /* control */ +#define ES_CNTRLM (0x03<<0) /* mask for above */ +#define ES_REG_UART_RES 0x0a /* R/W: UART reserver register */ +#define ES_TEST_MODE (1<<0) /* test mode enabled */ +#define ES_REG_MEM_PAGE 0x0c /* R/W: Memory page register */ +#define ES_MEM_PAGEO(o) (((o)&0x0f)<<0) /* memory page select - out */ +#define ES_MEM_PAGEM (0x0f<<0) /* mask for above */ +#define ES_MEM_PAGEI(i) (((i)>>0)&0x0f) /* memory page select - in */ +#define ES_REG_1370_CODEC 0x10 /* W/O: Codec write register address */ +#define ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0)) +#define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */ +#define ES_1371_CODEC_RDY (1<<31) /* codec ready */ +#define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */ +#define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */ +#define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0)) +#define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD) +#define ES_1371_CODEC_READ(i) (((i)>>0)&0xffff) + +#define ES_REG_1371_SMPRATE 0x10 /* W/R: Codec rate converter interface register */ +#define ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25) /* address of the sample rate converter */ +#define ES_1371_SRC_RAM_ADDRM (0x7f<<25) /* mask for above */ +#define ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f) /* address of the sample rate converter */ +#define ES_1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */ +#define ES_1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */ +#define ES_1371_SRC_DISABLE (1<<22) /* sample rate converter disable */ +#define ES_1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_P2 (1<<20) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_R1 (1<<19) /* capture channel accumulator update disable */ +#define ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */ +#define ES_1371_SRC_RAM_DATAM (0xffff<<0) /* mask for above */ +#define ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */ + +#define ES_REG_1371_LEGACY 0x18 /* W/R: Legacy control/status register */ +#define ES_1371_JFAST (1<<31) /* fast joystick timing */ +#define ES_1371_HIB (1<<30) /* host interrupt blocking enable */ +#define ES_1371_VSB (1<<29) /* SB; 0 = addr 0x220xH, 1 = 0x22FxH */ +#define ES_1371_VMPUO(o) (((o)&0x03)<<27) /* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */ +#define ES_1371_VMPUM (0x03<<27) /* mask for above */ +#define ES_1371_VMPUI(i) (((i)>>27)&0x03) /* base register address */ +#define ES_1371_VCDCO(o) (((o)&0x03)<<25) /* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */ +#define ES_1371_VCDCM (0x03<<25) /* mask for above */ +#define ES_1371_VCDCI(i) (((i)>>25)&0x03) /* CODEC address */ +#define ES_1371_FIRQ (1<<24) /* force an interrupt */ +#define ES_1371_SDMACAP (1<<23) /* enable event capture for slave DMA controller */ +#define ES_1371_SPICAP (1<<22) /* enable event capture for slave IRQ controller */ +#define ES_1371_MDMACAP (1<<21) /* enable event capture for master DMA controller */ +#define ES_1371_MPICAP (1<<20) /* enable event capture for master IRQ controller */ +#define ES_1371_ADCAP (1<<19) /* enable event capture for ADLIB register; 0x388xH */ +#define ES_1371_SVCAP (1<<18) /* enable event capture for SB registers */ +#define ES_1371_CDCCAP (1<<17) /* enable event capture for CODEC registers */ +#define ES_1371_BACAP (1<<16) /* enable event capture for SoundScape base address */ +#define ES_1371_EXI(i) (((i)>>8)&0x07) /* event number */ +#define ES_1371_AI(i) (((i)>>3)&0x1f) /* event significant I/O address */ +#define ES_1371_WR (1<<2) /* event capture; 0 = read; 1 = write */ +#define ES_1371_LEGINT (1<<0) /* interrupt for legacy events; 0 = interrupt did occur */ + +#define ES_REG_SERIAL 0x20 /* R/W: Serial interface control register */ +#define ES_1371_DAC_TEST (1<<22) /* DAC test mode enable */ +#define ES_P2_END_INCO(o) (((o)&0x07)<<19) /* binary offset value to increment / loop end */ +#define ES_P2_END_INCM (0x07<<19) /* mask for above */ +#define ES_P2_END_INCI(i) (((i)>>16)&0x07) /* binary offset value to increment / loop end */ +#define ES_P2_ST_INCO(o) (((o)&0x07)<<16) /* binary offset value to increment / start */ +#define ES_P2_ST_INCM (0x07<<16) /* mask for above */ +#define ES_P2_ST_INCI(i) (((i)<<16)&0x07) /* binary offset value to increment / start */ +#define ES_R1_LOOP_SEL (1<<15) /* ADC; 0 - loop mode; 1 = stop mode */ +#define ES_P2_LOOP_SEL (1<<14) /* DAC2; 0 - loop mode; 1 = stop mode */ +#define ES_P1_LOOP_SEL (1<<13) /* DAC1; 0 - loop mode; 1 = stop mode */ +#define ES_P2_PAUSE (1<<12) /* DAC2; 0 - play mode; 1 = pause mode */ +#define ES_P1_PAUSE (1<<11) /* DAC1; 0 - play mode; 1 = pause mode */ +#define ES_R1_INT_EN (1<<10) /* ADC interrupt enable */ +#define ES_P2_INT_EN (1<<9) /* DAC2 interrupt enable */ +#define ES_P1_INT_EN (1<<8) /* DAC1 interrupt enable */ +#define ES_P1_SCT_RLD (1<<7) /* force sample counter reload for DAC1 */ +#define ES_P2_DAC_SEN (1<<6) /* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */ +#define ES_R1_MODEO(o) (((o)&0x03)<<4) /* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */ +#define ES_R1_MODEM (0x03<<4) /* mask for above */ +#define ES_R1_MODEI(i) (((i)>>4)&0x03) +#define ES_P2_MODEO(o) (((o)&0x03)<<2) /* DAC2 mode; -- '' -- */ +#define ES_P2_MODEM (0x03<<2) /* mask for above */ +#define ES_P2_MODEI(i) (((i)>>2)&0x03) +#define ES_P1_MODEO(o) (((o)&0x03)<<0) /* DAC1 mode; -- '' -- */ +#define ES_P1_MODEM (0x03<<0) /* mask for above */ +#define ES_P1_MODEI(i) (((i)>>0)&0x03) + +#define ES_REG_DAC1_COUNT 0x24 /* R/W: DAC1 sample count register */ +#define ES_REG_DAC2_COUNT 0x28 /* R/W: DAC2 sample count register */ +#define ES_REG_ADC_COUNT 0x2c /* R/W: ADC sample count register */ +#define ES_REG_CURR_COUNT(i) (((i)>>16)&0xffff) +#define ES_REG_COUNTO(o) (((o)&0xffff)<<0) +#define ES_REG_COUNTM (0xffff<<0) +#define ES_REG_COUNTI(i) (((i)>>0)&0xffff) + +#define ES_REG_DAC1_FRAME 0x30 /* R/W: PAGE 0x0c; DAC1 frame address */ +#define ES_REG_DAC1_SIZE 0x34 /* R/W: PAGE 0x0c; DAC1 frame size */ +#define ES_REG_DAC2_FRAME 0x38 /* R/W: PAGE 0x0c; DAC2 frame address */ +#define ES_REG_DAC2_SIZE 0x3c /* R/W: PAGE 0x0c; DAC2 frame size */ +#define ES_REG_ADC_FRAME 0x30 /* R/W: PAGE 0x0d; ADC frame address */ +#define ES_REG_ADC_SIZE 0x34 /* R/W: PAGE 0x0d; ADC frame size */ +#define ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16) +#define ES_REG_FCURR_COUNTM (0xffff<<16) +#define ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc) +#define ES_REG_FSIZEO(o) (((o)&0xffff)<<0) +#define ES_REG_FSIZEM (0xffff<<0) +#define ES_REG_FSIZEI(i) (((i)>>0)&0xffff) +#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */ +#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */ + +#define ES_REG_UART_FIFO 0x30 /* R/W: PAGE 0x0e; UART FIFO register */ +#define ES_REG_UF_VALID (1<<8) +#define ES_REG_UF_BYTEO(o) (((o)&0xff)<<0) +#define ES_REG_UF_BYTEM (0xff<<0) +#define ES_REG_UF_BYTEI(i) (((i)>>0)&0xff) + + +/* + * Pages + */ + +#define ES_PAGE_DAC 0x0c +#define ES_PAGE_ADC 0x0d +#define ES_PAGE_UART 0x0e +#define ES_PAGE_UART1 0x0f + +/* + * Sample rate converter addresses + */ + +#define ES_SMPREG_DAC1 0x70 +#define ES_SMPREG_DAC2 0x74 +#define ES_SMPREG_ADC 0x78 +#define ES_SMPREG_VOL_ADC 0x6c +#define ES_SMPREG_VOL_DAC1 0x7c +#define ES_SMPREG_VOL_DAC2 0x7e +#define ES_SMPREG_TRUNC_N 0x00 +#define ES_SMPREG_INT_REGS 0x01 +#define ES_SMPREG_ACCUM_FRAC 0x02 +#define ES_SMPREG_VFREQ_FRAC 0x03 + +/* + * Some contants + */ + +#define ES_1370_SRCLOCK 1411200 +#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2) + +/* + * Open modes + */ + +#define ES_MODE_PLAY1 0x0001 +#define ES_MODE_PLAY2 0x0002 +#define ES_MODE_CAPTURE 0x0004 + +#define ES_MODE_OUTPUT 0x0001 /* for MIDI */ +#define ES_MODE_INPUT 0x0002 /* for MIDI */ + +/* + + */ + +typedef struct _snd_ensoniq ensoniq_t; + +struct _snd_ensoniq { + spinlock_t reg_lock; + + int irq; + + unsigned long playback1size; + unsigned long playback2size; + unsigned long capture3size; + + unsigned long port; + struct resource *res_port; + unsigned int mode; + unsigned int uartm; /* UART mode */ + + unsigned int ctrl; /* control register */ + unsigned int sctrl; /* serial control register */ + unsigned int cssr; /* control status register */ + unsigned int uartc; /* uart control register */ + unsigned int rev; /* chip revision */ + + union { + struct { + ac97_t *ac97; + } es1371; + struct { + int pclkdiv_lock; + ak4531_t *ak4531; + } es1370; + } u; + + struct pci_dev *pci; + unsigned short subsystem_vendor_id; + unsigned short subsystem_device_id; + snd_card_t *card; + snd_pcm_t *pcm1; /* DAC1/ADC PCM */ + snd_pcm_t *pcm2; /* DAC2 PCM */ + snd_pcm_substream_t *playback1_substream; + snd_pcm_substream_t *playback2_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p1_dma_size; + unsigned int p2_dma_size; + unsigned int c_dma_size; + unsigned int p1_period_size; + unsigned int p2_period_size; + unsigned int c_period_size; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *midi_input; + snd_rawmidi_substream_t *midi_output; + + snd_info_entry_t *proc_entry; + +#ifdef CHIP1370 + unsigned char *bugbuf; + dma_addr_t bugbuf_addr; +#endif +}; + +static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_audiopci_ids[] __devinitdata = { +#ifdef CHIP1370 + { 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1370 */ +#endif +#ifdef CHIP1371 + { 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1371 */ + { 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1373 - CT5880 */ + { 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ectiva EV1938 */ +#endif + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_audiopci_ids); + +/* + * constants + */ + +#define POLL_COUNT 0xa000 + +#ifdef CHIP1370 +static unsigned int snd_es1370_fixed_rates[] = + {5512, 11025, 22050, 44100}; +static snd_pcm_hw_constraint_list_t snd_es1370_hw_constraints_rates = { + count: 4, + list: snd_es1370_fixed_rates, + mask: 0, +}; +static ratnum_t es1370_clock = { + num: ES_1370_SRCLOCK, + den_min: 29, + den_max: 353, + den_step: 1, +}; +static snd_pcm_hw_constraint_ratnums_t snd_es1370_hw_constraints_clock = { + nrats: 1, + rats: &es1370_clock, +}; +#else +static ratden_t es1371_dac_clock = { + num_min: 3000 * (1 << 15), + num_max: 48000 * (1 << 15), + num_step: 3000, + den: 1 << 15, +}; +static snd_pcm_hw_constraint_ratdens_t snd_es1371_hw_constraints_dac_clock = { + nrats: 1, + rats: &es1371_dac_clock, +}; +static ratnum_t es1371_adc_clock = { + num: 48000 << 15, + den_min: 32768, + den_max: 393216, + den_step: 1, +}; +static snd_pcm_hw_constraint_ratnums_t snd_es1371_hw_constraints_adc_clock = { + nrats: 1, + rats: &es1371_adc_clock, +}; +#endif +static const unsigned int snd_ensoniq_sample_shift[] = + {0, 1, 1, 2}; + +/* + * common I/O routines + */ + +#ifdef CHIP1371 + +static unsigned int snd_es1371_wait_src_ready(ensoniq_t * ensoniq) +{ + unsigned int t, r = 0; + + for (t = 0; t < POLL_COUNT; t++) { + r = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((r & ES_1371_SRC_RAM_BUSY) == 0) + return r; + } + snd_printk("wait source ready timeout 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_SMPRATE), r); + return 0; +} + +static unsigned int snd_es1371_src_read(ensoniq_t * ensoniq, unsigned short reg) +{ + unsigned int temp, i, orig, r; + + /* wait for ready */ + temp = orig = snd_es1371_wait_src_ready(ensoniq); + + /* expose the SRC state bits */ + r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + /* now, wait for busy and the correct time to read */ + temp = snd_es1371_wait_src_ready(ensoniq); + + if ((temp & 0x00870000) != 0x00010000) { + /* wait for the right state */ + for (i = 0; i < POLL_COUNT; i++) { + temp = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((temp & 0x00870000) == 0x00010000) + break; + } + } + + /* hide the state bits */ + r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + return temp; +} + +static void snd_es1371_src_write(ensoniq_t * ensoniq, + unsigned short reg, unsigned short data) +{ + unsigned int r; + + r = snd_es1371_wait_src_ready(ensoniq) & + (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data); + outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE)); +} + +#endif /* CHIP1371 */ + +#ifdef CHIP1370 + +static void snd_es1370_codec_write(ak4531_t *ak4531, + unsigned short reg, unsigned short val) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + unsigned long flags; + signed long end_time = jiffies + HZ / 10; + +#if 0 + printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n", reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); +#endif + do { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) { + outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); +#endif + } while ((signed long)(end_time - jiffies) > 0); + snd_printk("codec write timeout, status = 0x%x\n", inl(ES_REG(ensoniq, STATUS))); +} + +#endif /* CHIP1370 */ + +#ifdef CHIP1371 + +static void snd_es1371_codec_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + unsigned long flags; + unsigned int t, x; + + for (t = 0; t < POLL_COUNT; t++) { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000) + break; + } + outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + } + snd_printk("codec write timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); +} + +static unsigned short snd_es1371_codec_read(ac97_t *ac97, + unsigned short reg) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return -ENXIO); + unsigned long flags; + unsigned int t, x, fail = 0; + + __again: + for (t = 0; t < POLL_COUNT; t++) { + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00010000) + break; + } + outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for WIP again */ + for (t = 0; t < POLL_COUNT; t++) { + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) + break; + } + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) { + if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) { + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return ES_1371_CODEC_READ(x); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + if (++fail > 10) { + snd_printk("codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), reg, inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; + } + goto __again; + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + } + snd_printk("es1371: codec read timeout at 0x%lx [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; +} + +static void snd_es1371_adc_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int n, truncm, freq, result; + + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + result = (48000UL << 15) / (freq / n); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8); +} + +static void snd_es1371_dac1_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)) | ES_1371_DIS_P1; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); +} + +static void snd_es1371_dac2_rate(ensoniq_t * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)) | ES_1371_DIS_P2; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); +} + +#endif /* CHIP1371 */ + +static int snd_ensoniq_trigger(snd_pcm_substream_t *substream, int cmd) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == ensoniq->playback1_substream) { + what |= ES_P1_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_P2_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) + return -EINVAL; + s = s->link_next; + } while (s != substream); + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + ensoniq->sctrl |= what; + else + ensoniq->sctrl &= ~what; + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + snd_pcm_substream_t *s = substream; + do { + if (s == ensoniq->playback1_substream) { + what |= ES_DAC1_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_DAC2_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) { + what |= ES_ADC_EN; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) + ensoniq->ctrl |= what; + else + ensoniq->ctrl &= ~what; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +/* + * PCM part + */ + +static int snd_ensoniq_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ensoniq_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ensoniq_playback1_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p1_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p1_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_DAC1_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME)); + outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE)); + ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM); + ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC1_COUNT)); +#ifdef CHIP1370 + ensoniq->ctrl &= ~ES_1370_WTSRSELM; + switch (runtime->rate) { + case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break; + case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break; + case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break; + case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break; + default: snd_BUG(); + } +#else + snd_es1371_dac1_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_playback2_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p2_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p2_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_DAC2_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME)); + outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE)); + ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN | + ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM); + ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) | + ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC2_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2; + } +#else + snd_es1371_dac2_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->c_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->ctrl &= ~ES_ADC_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME)); + outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE)); + ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM); + ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, ADC_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE; + } +#else + snd_es1371_adc_rate(ensoniq, runtime->rate); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_capture_pointer(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_hardware_t snd_ensoniq_playback1 = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: +#ifndef CHIP1370 + SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, +#else + (SNDRV_PCM_RATE_KNOT | /* 5512Hz rate */ + SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_44100), +#endif + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ensoniq_playback2 = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ensoniq_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ensoniq_playback1_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY1; + ensoniq->playback1_substream = substream; + runtime->hw = snd_ensoniq_playback1; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_rates); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback2_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY2; + ensoniq->playback2_substream = substream; + runtime->hw = snd_ensoniq_playback2; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_capture_open(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_CAPTURE; + ensoniq->capture_substream = substream; + runtime->hw = snd_ensoniq_capture; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_adc_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback1_close(snd_pcm_substream_t * substream) +{ + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback1_substream = NULL; + ensoniq->mode &= ~ES_MODE_PLAY1; + return 0; +} + +static int snd_ensoniq_playback2_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback2_substream = NULL; + spin_lock_irqsave(&ensoniq->reg_lock, flags); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2; +#endif + ensoniq->mode &= ~ES_MODE_PLAY2; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_capture_close(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->capture_substream = NULL; + spin_lock_irqsave(&ensoniq->reg_lock, flags); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE; +#endif + ensoniq->mode &= ~ES_MODE_CAPTURE; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static snd_pcm_ops_t snd_ensoniq_playback1_ops = { + open: snd_ensoniq_playback1_open, + close: snd_ensoniq_playback1_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ensoniq_hw_params, + hw_free: snd_ensoniq_hw_free, + prepare: snd_ensoniq_playback1_prepare, + trigger: snd_ensoniq_trigger, + pointer: snd_ensoniq_playback1_pointer, +}; + +static snd_pcm_ops_t snd_ensoniq_playback2_ops = { + open: snd_ensoniq_playback2_open, + close: snd_ensoniq_playback2_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ensoniq_hw_params, + hw_free: snd_ensoniq_hw_free, + prepare: snd_ensoniq_playback2_prepare, + trigger: snd_ensoniq_trigger, + pointer: snd_ensoniq_playback2_pointer, +}; + +static snd_pcm_ops_t snd_ensoniq_capture_ops = { + open: snd_ensoniq_capture_open, + close: snd_ensoniq_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ensoniq_hw_params, + hw_free: snd_ensoniq_hw_free, + prepare: snd_ensoniq_capture_prepare, + trigger: snd_ensoniq_trigger, + pointer: snd_ensoniq_capture_pointer, +}; + +static void snd_ensoniq_pcm_free(snd_pcm_t *pcm) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq->pcm1 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm); +#endif + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops); + + pcm->private_data = ensoniq; + pcm->private_free = snd_ensoniq_pcm_free; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC2/ADC"); +#else + strcpy(pcm->name, "ES1371 DAC2/ADC"); +#endif + ensoniq->pcm1 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static void snd_ensoniq_pcm_free2(snd_pcm_t *pcm) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq->pcm2 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm); +#endif + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); + + pcm->private_data = ensoniq; + pcm->private_free = snd_ensoniq_pcm_free2; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC1"); +#else + strcpy(pcm->name, "ES1371 DAC1"); +#endif + ensoniq->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer section + */ + +#ifdef CHIP1371 + +#define ES1371_SPDIF(xname) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_es1371_spdif_info, \ + get: snd_es1371_spdif_get, put: snd_es1371_spdif_put } + +static int snd_es1371_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1371_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1371_SPDIF_EN ? 1 : 0; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval1, nval2; + int change; + + nval1 = ucontrol->value.integer.value[0] ? ES_1371_SPDIF_EN : 0; + nval2 = ucontrol->value.integer.value[0] ? ES_1371_ST_SPDIF_EN : 0; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + change = (ensoniq->ctrl & ES_1371_SPDIF_EN) != nval1; + ensoniq->ctrl &= ~ES_1371_SPDIF_EN; + ensoniq->ctrl |= nval1; + ensoniq->cssr &= ~ES_1371_ST_SPDIF_EN; + ensoniq->cssr |= nval2; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_es1371_mixer_spdif __devinitdata = +ES1371_SPDIF("IEC958 Playback Switch"); + +static void snd_ensoniq_mixer_free_ac97(ac97_t *ac97) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + ensoniq->u.es1371.ac97 = NULL; +} + +static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) +{ + snd_card_t *card = ensoniq->card; + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_es1371_codec_write; + ac97.read = snd_es1371_codec_read; + ac97.private_data = ensoniq; + ac97.private_free = snd_ensoniq_mixer_free_ac97; + if ((err = snd_ac97_mixer(card, &ac97, &ensoniq->u.es1371.ac97)) < 0) + return err; + if (ensoniq->rev >= ES1371REV_ES1373_A) + snd_ctl_add(card, snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq)); + return 0; +} + +#endif /* CHIP1371 */ + +#define ENSONIQ_CONTROL(xname, mask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_ensoniq_control_info, \ + get: snd_ensoniq_control_get, put: snd_ensoniq_control_put, \ + private_value: mask } + +static int snd_ensoniq_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int mask = kcontrol->private_value; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int mask = kcontrol->private_value; + unsigned int nval; + int change; + + nval = ucontrol->value.integer.value[0] ? mask : 0; + spin_lock_irqsave(&ensoniq->reg_lock, flags); + change = (ensoniq->ctrl & mask) != nval; + ensoniq->ctrl &= ~mask; + ensoniq->ctrl |= nval; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return change; +} + +#ifdef CHIP1370 + +#define ES1370_CONTROLS 2 + +static snd_kcontrol_new_t snd_es1370_controls[2] __devinitdata = { +ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0), +ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1) +}; + +static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + ensoniq->u.es1370.ak4531 = NULL; +} + +static int __devinit snd_ensoniq_1370_mixer(ensoniq_t * ensoniq) +{ + snd_card_t *card = ensoniq->card; + ak4531_t ak4531; + int err, idx; + + /* try reset AK4531 */ + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + + memset(&ak4531, 0, sizeof(ak4531)); + ak4531.write = snd_es1370_codec_write; + ak4531.private_data = ensoniq; + ak4531.private_free = snd_ensoniq_mixer_free_ak4531; + if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0) + return err; + for (idx = 0; idx < ES1370_CONTROLS; idx++) + snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq)); + return 0; +} + +#endif /* CHIP1370 */ + +/* + * General Switches... + */ + +static snd_kcontrol_new_t snd_ensoniq_control_joystick __devinitdata = +ENSONIQ_CONTROL("Joystick Enable", ES_JYSTK_EN); + +#ifdef CHIP1371 + +#define ES1371_JOYSTICK_ADDR(xname) \ +{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_es1371_joystick_addr_info, \ + get: snd_es1371_joystick_addr_get, put: snd_es1371_joystick_addr_put } + +static int snd_es1371_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item >= 3) + uinfo->value.enumerated.item = 2; + sprintf(uinfo->value.enumerated.name, "port 0x%x", (uinfo->value.enumerated.item * 8) + 0x200); + return 0; +} + +static int snd_es1371_joystick_addr_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ucontrol->value.enumerated.item[0] = ES_1371_JOY_ASELI(ensoniq->ctrl); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_es1371_joystick_addr_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int nval; + int change; + + nval = ES_1371_JOY_ASEL(ucontrol->value.integer.value[0]); + spin_lock_irqsave(&ensoniq->reg_lock, flags); + change = (ensoniq->ctrl & ES_1371_JOY_ASELM) != nval; + ensoniq->ctrl &= ~ES_1371_JOY_ASELM; + ensoniq->ctrl |= nval; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_es1371_joystick_addr __devinitdata = +ES1371_JOYSTICK_ADDR("Joystick Address"); + +#endif /* CHIP1371 */ + +/* + + */ + +static void snd_ensoniq_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, entry->private_data, return); + +#ifdef CHIP1370 + snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n"); +#else + snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n"); +#endif + snd_iprintf(buffer, "Joystick enable : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off"); +#ifdef CHIP1370 + snd_iprintf(buffer, "MIC +5V bias : %s\n", ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off"); + snd_iprintf(buffer, "Line In to AOUT : %s\n", ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off"); +#else + snd_iprintf(buffer, "Joystick port : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200); +#endif +} + +static void __devinit snd_ensoniq_proc_init(ensoniq_t * ensoniq) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(ensoniq->card, "audiopci", ensoniq->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ensoniq; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_ensoniq_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ensoniq->proc_entry = entry; +} + +static void snd_ensoniq_proc_done(ensoniq_t * ensoniq) +{ + if (ensoniq->proc_entry) { + snd_info_unregister(ensoniq->proc_entry); + ensoniq->proc_entry = NULL; + } +} + +/* + + */ + +static int snd_ensoniq_free(ensoniq_t *ensoniq) +{ + snd_ensoniq_proc_done(ensoniq); + if (ensoniq->irq < 0) + goto __hw_end; +#ifdef CHIP1370 + outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#else + outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#endif + synchronize_irq(); + pci_set_power_state(ensoniq->pci, 3); + __hw_end: +#ifdef CHIP1370 + if (ensoniq->bugbuf) + snd_free_pci_pages(ensoniq->pci, 16, ensoniq->bugbuf, ensoniq->bugbuf_addr); +#endif + if (ensoniq->res_port) { + release_resource(ensoniq->res_port); + kfree_nocheck(ensoniq->res_port); + } + if (ensoniq->irq >= 0) + free_irq(ensoniq->irq, (void *)ensoniq); + snd_magic_kfree(ensoniq); + return 0; +} + +static int snd_ensoniq_dev_free(snd_device_t *device) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, device->device_data, return -ENXIO); + return snd_ensoniq_free(ensoniq); +} + +#ifdef CHIP1371 +static struct { + unsigned short svid; /* subsystem vendor ID */ + unsigned short sdid; /* subsystem device ID */ +} es1371_amplifier_hack[] = { + { svid: 0x107b, sdid: 0x2150 }, /* Gateway Solo 2150 */ + { svid: 0x13bd, sdid: 0x100c }, /* EV1938 on Mebius PC-MJ100V */ + { svid: 0x1102, sdid: 0x5938 }, /* Targa Xtender300 */ + { svid: 0x1102, sdid: 0x8938 }, /* IPC Topnote G notebook */ + { svid: PCI_ANY_ID, sdid: PCI_ANY_ID } +}; +static struct { + unsigned short vid; /* vendor ID */ + unsigned short did; /* device ID */ + unsigned char rev; /* revision */ +} es1371_ac97_reset_hack[] = { + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_C }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_D }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_E }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_CT5880_A }, + { vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_ES1373_8 }, + { vid: PCI_ANY_ID, did: PCI_ANY_ID, rev: 0 } +}; +#endif + +static int __devinit snd_ensoniq_create(snd_card_t * card, + struct pci_dev *pci, + ensoniq_t ** rensoniq) +{ + ensoniq_t *ensoniq; + unsigned short cmdw; + unsigned char cmdb; +#ifdef CHIP1371 + int idx; +#endif + int err; + static snd_device_ops_t ops = { + dev_free: snd_ensoniq_dev_free, + }; + + *rensoniq = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + ensoniq = snd_magic_kcalloc(ensoniq_t, 0, GFP_KERNEL); + if (ensoniq == NULL) + return -ENOMEM; + spin_lock_init(&ensoniq->reg_lock); + ensoniq->card = card; + ensoniq->pci = pci; + ensoniq->irq = -1; + ensoniq->port = pci_resource_start(pci, 0); + if ((ensoniq->res_port = request_region(ensoniq->port, 0x40, "Ensoniq AudioPCI")) == NULL) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ensoniq->port, ensoniq->port + 0x40 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ, "Ensoniq AudioPCI", (void *)ensoniq)) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + ensoniq->irq = pci->irq; +#ifdef CHIP1370 + if ((ensoniq->bugbuf = snd_malloc_pci_pages(pci, 16, &ensoniq->bugbuf_addr)) == NULL) { + snd_ensoniq_free(ensoniq); + snd_printk("unable to allocate space for phantom area - bugbuf\n"); + return -EBUSY; + } +#endif + pci_set_master(pci); + pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb); + ensoniq->rev = cmdb; + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw); + ensoniq->subsystem_vendor_id = cmdw; + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw); + ensoniq->subsystem_device_id = cmdw; + snd_ensoniq_proc_init(ensoniq); +#ifdef CHIP1370 +#if 0 + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#else /* get microphone working */ + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#endif + ensoniq->sctrl = 0; + /* initialize the chips */ + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(ensoniq->bugbuf_addr, ES_REG(ensoniq, PHANTOM_FRAME)); + outl(0, ES_REG(ensoniq, PHANTOM_COUNT)); +#else + ensoniq->ctrl = 0; + ensoniq->sctrl = 0; + ensoniq->cssr = 0; + for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++) + if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid && + ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) { + ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */ + break; + } + /* initialize the chips */ + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(0, ES_REG(ensoniq, 1371_LEGACY)); + for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++) + if (pci->vendor == es1371_ac97_reset_hack[idx].vid && + pci->device == es1371_ac97_reset_hack[idx].did && + ensoniq->rev == es1371_ac97_reset_hack[idx].rev) { + unsigned long tmo; + signed long tmo2; + + ensoniq->cssr |= ES_1371_ST_AC97_RST; + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + tmo = jiffies + (HZ / 50) + 1; + while (1) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(tmo2); + } + break; + } + /* AC'97 warm reset to start the bitclk */ + outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL)); + udelay(20); + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + /* Init the sample rate converter */ + snd_es1371_wait_src_ready(ensoniq); + outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE)); + for (idx = 0; idx < 0x80; idx++) + snd_es1371_src_write(ensoniq, idx, 0); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12); + snd_es1371_adc_rate(ensoniq, 22050); + snd_es1371_dac1_rate(ensoniq, 22050); + snd_es1371_dac2_rate(ensoniq, 22050); + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) - Thomas Sailer + */ + snd_es1371_wait_src_ready(ensoniq); + outl(0, ES_REG(ensoniq, 1371_SMPRATE)); + /* try reset codec directly */ + outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC)); +#endif + outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL)); + outb(0x00, ES_REG(ensoniq, UART_RES)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + snd_ctl_add(card, snd_ctl_new1(&snd_ensoniq_control_joystick, ensoniq)); +#ifdef CHIP1371 + snd_ctl_add(card, snd_ctl_new1(&snd_es1371_joystick_addr, ensoniq)); +#endif + synchronize_irq(); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) { + snd_ensoniq_free(ensoniq); + return err; + } + + *rensoniq = ensoniq; + return 0; +} + +/* + * MIDI section + */ + +static void snd_ensoniq_midi_interrupt(ensoniq_t * ensoniq) +{ + snd_rawmidi_t * rmidi = ensoniq->rmidi; + unsigned char status, mask, byte; + + if (rmidi == NULL) + return; + /* do Rx at first */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + byte = inb(ES_REG(ensoniq, UART_DATA)); + spin_unlock(&ensoniq->reg_lock); + snd_rawmidi_receive(ensoniq->midi_input, &byte, 1); + spin_lock(&ensoniq->reg_lock); + } + spin_unlock(&ensoniq->reg_lock); + + /* do Tx at second */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + if (snd_rawmidi_transmit(ensoniq->midi_output, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + mask &= ~ES_TXRDY; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + spin_unlock(&ensoniq->reg_lock); +} + +static int snd_ensoniq_midi_input_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->uartm |= ES_MODE_INPUT; + ensoniq->midi_input = substream; + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_input_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_input = NULL; + ensoniq->uartm &= ~ES_MODE_INPUT; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_output_open(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + ensoniq->uartm |= ES_MODE_OUTPUT; + ensoniq->midi_output = substream; + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static int snd_ensoniq_midi_output_close(snd_rawmidi_substream_t * substream) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_output = NULL; + ensoniq->uartm &= ~ES_MODE_OUTPUT; + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + return 0; +} + +static void snd_ensoniq_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + int idx; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if ((ensoniq->uartc & ES_RXINTEN) == 0) { + /* empty input FIFO */ + for (idx = 0; idx < 32; idx++) + inb(ES_REG(ensoniq, UART_DATA)); + ensoniq->uartc |= ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ensoniq->uartc & ES_RXINTEN) { + ensoniq->uartc &= ~ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static void snd_ensoniq_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) +{ + unsigned long flags; + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + unsigned char byte; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if (ES_TXINTENI(ensoniq->uartc) == 0) { + ensoniq->uartc |= ES_TXINTENO(1); + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while (ES_TXINTENI(ensoniq->uartc) == 1 && + (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ES_TXINTENI(ensoniq->uartc) == 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static snd_rawmidi_ops_t snd_ensoniq_midi_output = +{ + open: snd_ensoniq_midi_output_open, + close: snd_ensoniq_midi_output_close, + trigger: snd_ensoniq_midi_output_trigger, +}; + +static snd_rawmidi_ops_t snd_ensoniq_midi_input = +{ + open: snd_ensoniq_midi_input_open, + close: snd_ensoniq_midi_input_close, + trigger: snd_ensoniq_midi_input_trigger, +}; + +static int __devinit snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmidi_t **rrawmidi) +{ + snd_rawmidi_t *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0) + return err; +#ifdef CHIP1370 + strcpy(rmidi->name, "ES1370"); +#else + strcpy(rmidi->name, "ES1371"); +#endif + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = ensoniq; + ensoniq->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +/* + * Interrupt handler + */ + +static void snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, dev_id, return); + unsigned int status, sctrl; + + if (ensoniq == NULL) + return; + + status = inl(ES_REG(ensoniq, STATUS)); + if (!(status & ES_INTR)) + return; + + spin_lock(&ensoniq->reg_lock); + sctrl = ensoniq->sctrl; + if (status & ES_DAC1) + sctrl &= ~ES_P1_INT_EN; + if (status & ES_DAC2) + sctrl &= ~ES_P2_INT_EN; + if (status & ES_ADC) + sctrl &= ~ES_R1_INT_EN; + outl(sctrl, ES_REG(ensoniq, SERIAL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + + if (status & ES_UART) + snd_ensoniq_midi_interrupt(ensoniq); + if ((status & ES_DAC2) && ensoniq->playback2_substream) + snd_pcm_period_elapsed(ensoniq->playback2_substream); + if ((status & ES_ADC) && ensoniq->capture_substream) + snd_pcm_period_elapsed(ensoniq->capture_substream); + if ((status & ES_DAC1) && ensoniq->playback1_substream) + snd_pcm_period_elapsed(ensoniq->playback1_substream); +} + +static int __devinit snd_audiopci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ensoniq_t *ensoniq; + int err, pcm_devs[2]; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) { + snd_card_free(card); + return err; + } + + pcm_devs[0] = 0; pcm_devs[1] = 1; +#ifdef CHIP1370 + if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) { + snd_card_free(card); + return err; + } +#endif +#ifdef CHIP1371 + if ((err = snd_ensoniq_1371_mixer(ensoniq)) < 0) { + snd_card_free(card); + return err; + } +#endif + if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifdef CHIP1370 + strcpy(card->driver, "ES1370"); +#endif +#ifdef CHIP1371 + strcpy(card->driver, "ES1371"); +#endif + strcpy(card->shortname, "Ensoniq AudioPCI"); + sprintf(card->longname, "%s %s at 0x%lx, irq %i", + card->shortname, + card->driver, + ensoniq->port, + ensoniq->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_audiopci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Ensoniq AudioPCI", + id_table: snd_audiopci_ids, + probe: snd_audiopci_probe, + remove: __devexit_p(snd_audiopci_remove), +}; + +static int __init alsa_card_ens137x_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Ensoniq AudioPCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ens137x_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ens137x_init) +module_exit(alsa_card_ens137x_exit) + +#ifndef MODULE + +/* format is: snd-ens1370=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_ens137x_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +#if defined(CHIP1370) +__setup("snd-ens1370=", alsa_card_ens137x_setup); +#elif defined(CHIP1371) +__setup("snd-ens1371=", alsa_card_ens137x_setup); +#endif + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/ens1371.c linux-2.4.19-pre5-mjc/sound/pci/ens1371.c --- linux/sound/pci/ens1371.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ens1371.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,2 @@ +#define CHIP1371 +#include "ens1370.c" diff -Nru linux/sound/pci/es1938.c linux-2.4.19-pre5-mjc/sound/pci/es1938.c --- linux/sound/pci/es1938.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/es1938.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1720 @@ +/* + * Driver for ESS Solo-1 (ES1938, ES1946) soundcard + * Copyright (c) by Jaromir Koutek , + * Jaroslav Kysela , + * Thomas Sailer , + * Abramo Bagnara , + * Markus Gruber + * + * Rewritted from sonicvibes.c source. + * + * TODO: + * Rewrite better spinlocks + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - Capture data is written unaligned starting from dma_base + 1 so I need to + disable mmap and to add a copy callback. + - After several cycle of the following: + while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done + a "playback write error (DMA or IRQ trouble?)" may happen. + This is due to playback interrupts not generated. + I suspect a timing issue. + - Sometimes the interrupt handler is invoked wrongly during playback. + This generate some harmless "Unexpected hw_pointer: wrong interrupt + acknowledge". + I've seen that using small period sizes. + Reproducible with: + mpg123 test.mp3 & + hdparm -t -T /dev/hda +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#ifndef LINUX_2_2 +#include +#endif + +#define chip_t es1938_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaromir Koutek "); +MODULE_DESCRIPTION("ESS Solo-1"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,ES1938}," + "{TerraTec,128i PCI}}"); + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125d +#endif +#ifndef PCI_DEVICE_ID_ESS_ES1938 +#define PCI_DEVICE_ID_ESS_ES1938 0x1969 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ESS Solo-1 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x) + +#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x) + +#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x) + +#define SL_PCI_LEGACYCONTROL 0x40 +#define SL_PCI_CONFIG 0x50 +#define SL_PCI_DDMACONTROL 0x60 + +#define ESSIO_REG_AUDIO2DMAADDR 0 +#define ESSIO_REG_AUDIO2DMACOUNT 4 +#define ESSIO_REG_AUDIO2MODE 6 +#define ESSIO_REG_IRQCONTROL 7 + +#define ESSDM_REG_DMAADDR 0x00 +#define ESSDM_REG_DMACOUNT 0x04 +#define ESSDM_REG_DMACOMMAND 0x08 +#define ESSDM_REG_DMASTATUS 0x08 +#define ESSDM_REG_DMAMODE 0x0b +#define ESSDM_REG_DMACLEAR 0x0d +#define ESSDM_REG_DMAMASK 0x0f + +#define ESSSB_REG_FMLOWADDR 0x00 +#define ESSSB_REG_FMHIGHADDR 0x02 +#define ESSSB_REG_MIXERADDR 0x04 +#define ESSSB_REG_MIXERDATA 0x05 + +#define ESSSB_IREG_AUDIO1 0x14 +#define ESSSB_IREG_MICMIX 0x1a +#define ESSSB_IREG_RECSRC 0x1c +#define ESSSB_IREG_MASTER 0x32 +#define ESSSB_IREG_FM 0x36 +#define ESSSB_IREG_AUXACD 0x38 +#define ESSSB_IREG_AUXB 0x3a +#define ESSSB_IREG_PCSPEAKER 0x3c +#define ESSSB_IREG_LINE 0x3e +#define ESSSB_IREG_SPATCONTROL 0x50 +#define ESSSB_IREG_SPATLEVEL 0x52 +#define ESSSB_IREG_MASTER_LEFT 0x60 +#define ESSSB_IREG_MASTER_RIGHT 0x62 +#define ESSSB_IREG_MPU401CONTROL 0x64 +#define ESSSB_IREG_MICMIXRECORD 0x68 +#define ESSSB_IREG_AUDIO2RECORD 0x69 +#define ESSSB_IREG_AUXACDRECORD 0x6a +#define ESSSB_IREG_FMRECORD 0x6b +#define ESSSB_IREG_AUXBRECORD 0x6c +#define ESSSB_IREG_MONO 0x6d +#define ESSSB_IREG_LINERECORD 0x6e +#define ESSSB_IREG_MONORECORD 0x6f +#define ESSSB_IREG_AUDIO2SAMPLE 0x70 +#define ESSSB_IREG_AUDIO2MODE 0x71 +#define ESSSB_IREG_AUDIO2FILTER 0x72 +#define ESSSB_IREG_AUDIO2TCOUNTL 0x74 +#define ESSSB_IREG_AUDIO2TCOUNTH 0x76 +#define ESSSB_IREG_AUDIO2CONTROL1 0x78 +#define ESSSB_IREG_AUDIO2CONTROL2 0x7a +#define ESSSB_IREG_AUDIO2 0x7c + +#define ESSSB_REG_RESET 0x06 + +#define ESSSB_REG_READDATA 0x0a +#define ESSSB_REG_WRITEDATA 0x0c +#define ESSSB_REG_READSTATUS 0x0c + +#define ESSSB_REG_STATUS 0x0e + +#define ESS_CMD_EXTSAMPLERATE 0xa1 +#define ESS_CMD_FILTERDIV 0xa2 +#define ESS_CMD_DMACNTRELOADL 0xa4 +#define ESS_CMD_DMACNTRELOADH 0xa5 +#define ESS_CMD_ANALOGCONTROL 0xa8 +#define ESS_CMD_IRQCONTROL 0xb1 +#define ESS_CMD_DRQCONTROL 0xb2 +#define ESS_CMD_RECLEVEL 0xb4 +#define ESS_CMD_SETFORMAT 0xb6 +#define ESS_CMD_SETFORMAT2 0xb7 +#define ESS_CMD_DMACONTROL 0xb8 +#define ESS_CMD_DMATYPE 0xb9 +#define ESS_CMD_OFFSETLEFT 0xba +#define ESS_CMD_OFFSETRIGHT 0xbb +#define ESS_CMD_READREG 0xc0 +#define ESS_CMD_ENABLEEXT 0xc6 +#define ESS_CMD_PAUSEDMA 0xd0 +#define ESS_CMD_ENABLEAUDIO1 0xd1 +#define ESS_CMD_STOPAUDIO1 0xd3 +#define ESS_CMD_AUDIO1STATUS 0xd8 +#define ESS_CMD_CONTDMA 0xd4 +#define ESS_CMD_TESTIRQ 0xf2 + +#define ESS_RECSRC_MIC 0 +#define ESS_RECSRC_AUXACD 2 +#define ESS_RECSRC_AUXB 5 +#define ESS_RECSRC_LINE 6 +#define ESS_RECSRC_NONE 7 + +#define DAC1 0x01 +#define ADC1 0x02 +#define DAC2 0x04 + +/* + + */ + +typedef struct _snd_es1938 es1938_t; + +struct _snd_es1938 { + unsigned long dma1size; + unsigned long dma2size; + int irq; + + unsigned long io_port; + struct resource *res_io_port; + unsigned long sb_port; + struct resource *res_sb_port; + unsigned long vc_port; + struct resource *res_vc_port; + unsigned long mpu_port; + struct resource *res_mpu_port; + unsigned long game_port; + struct resource *res_game_port; + unsigned long ddma_port; + + unsigned char irqmask; + unsigned char revision; + + snd_kcontrol_t *hw_volume; + snd_kcontrol_t *hw_switch; + snd_kcontrol_t *master_volume; + snd_kcontrol_t *master_switch; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_substream_t *capture_substream; + snd_pcm_substream_t *playback1_substream; + snd_pcm_substream_t *playback2_substream; + snd_kmixer_t *mixer; + snd_rawmidi_t *rmidi; + + unsigned int dma1_size; + unsigned int dma2_size; + unsigned int dma1_start; + unsigned int dma2_start; + unsigned int dma1_shift; + unsigned int dma2_shift; + unsigned int active; + + spinlock_t reg_lock; + spinlock_t mixer_lock; + snd_info_entry_t *proc_entry; + +#ifndef LINUX_2_2 + struct gameport gameport; +#endif +}; + +static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static struct pci_device_id snd_es1938_ids[] __devinitdata = { + { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Solo-1 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_es1938_ids); + +#define RESET_LOOP_TIMEOUT 0x10000 +#define WRITE_LOOP_TIMEOUT 0x10000 +#define GET_LOOP_TIMEOUT 0x01000 + +#undef REG_DEBUG +/* ----------------------------------------------------------------- + * Write to a mixer register + * -----------------------------------------------------------------*/ +static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val) +{ + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + outb(val, SLSB_REG(chip, MIXERDATA)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x set to %02x\n", reg, val); +#endif +} + +/* ----------------------------------------------------------------- + * Read from a mixer register + * -----------------------------------------------------------------*/ +static int snd_es1938_mixer_read(es1938_t *chip, unsigned char reg) +{ + int data; + unsigned long flags; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + data = inb(SLSB_REG(chip, MIXERDATA)); + spin_unlock_irqrestore(&chip->mixer_lock, flags); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x now is %02x\n", reg, data); +#endif + return data; +} + +/* ----------------------------------------------------------------- + * Write to some bits of a mixer register (return old value) + * -----------------------------------------------------------------*/ +static int snd_es1938_mixer_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + unsigned long flags; + unsigned char old, new, oval; + spin_lock_irqsave(&chip->mixer_lock, flags); + outb(reg, SLSB_REG(chip, MIXERADDR)); + old = inb(SLSB_REG(chip, MIXERDATA)); + oval = old & mask; + if (val != oval) { + new = (old & ~mask) | (val & mask); + outb(new, SLSB_REG(chip, MIXERDATA)); +#ifdef REG_DEBUG + snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->mixer_lock, flags); + return oval; +} + +/* ----------------------------------------------------------------- + * Write command to Controller Registers + * -----------------------------------------------------------------*/ +static void snd_es1938_write_cmd(es1938_t *chip, unsigned char cmd) +{ + int i; + unsigned char v; + for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) { + if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) { + outb(cmd, SLSB_REG(chip, WRITEDATA)); + return; + } + } + printk("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v); +} + +/* ----------------------------------------------------------------- + * Read the Read Data Buffer + * -----------------------------------------------------------------*/ +static int snd_es1938_get_byte(es1938_t *chip) +{ + int i; + unsigned char v; + for (i = GET_LOOP_TIMEOUT; i; i--) + if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80) + return inb(SLSB_REG(chip, READDATA)); + snd_printk("get_byte timeout: status 0x02%x\n", v); + return -ENODEV; +} + +/* ----------------------------------------------------------------- + * Write value cmd register + * -----------------------------------------------------------------*/ +static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, reg); + snd_es1938_write_cmd(chip, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x set to %02x\n", reg, val); +#endif +} + +/* ----------------------------------------------------------------- + * Read data from cmd register and return it + * -----------------------------------------------------------------*/ +static unsigned char snd_es1938_read(es1938_t *chip, unsigned char reg) +{ + unsigned char val; + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, ESS_CMD_READREG); + snd_es1938_write_cmd(chip, reg); + val = snd_es1938_get_byte(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#ifdef REG_DEBUG + snd_printk("Reg %02x now is %02x\n", reg, val); +#endif + return val; +} + +/* ----------------------------------------------------------------- + * Write data to cmd register and return old value + * -----------------------------------------------------------------*/ +static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + unsigned long flags; + unsigned char old, new, oval; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_es1938_write_cmd(chip, ESS_CMD_READREG); + snd_es1938_write_cmd(chip, reg); + old = snd_es1938_get_byte(chip); + oval = old & mask; + if (val != oval) { + snd_es1938_write_cmd(chip, reg); + new = (old & ~mask) | (val & mask); + snd_es1938_write_cmd(chip, new); +#ifdef REG_DEBUG + snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, new); +#endif + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return oval; +} + +/* -------------------------------------------------------------------- + * Reset the chip + * --------------------------------------------------------------------*/ +static void snd_es1938_reset(es1938_t *chip) +{ + int i; + + outb(3, SLSB_REG(chip, RESET)); + inb(SLSB_REG(chip, RESET)); + outb(0, SLSB_REG(chip, RESET)); + for (i = 0; i < RESET_LOOP_TIMEOUT; i++) { + if (inb(SLSB_REG(chip, STATUS)) & 0x80) { + if (inb(SLSB_REG(chip, READDATA)) == 0xaa) + goto __next; + } + } + snd_printk("ESS Solo-1 reset failed\n"); + + __next: + snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT); + + /* Demand transfer DMA: 4 bytes per DMA request */ + snd_es1938_write(chip, ESS_CMD_DMATYPE, 2); + + /* Change behaviour of register A1 + 4x oversampling + 2nd channel DAC asynchronous */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32); + /* enable/select DMA channel and IRQ channel */ + snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50); + snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50); + snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1); + /* Set spatializer parameters to recommended values */ + snd_es1938_mixer_write(chip, 0x54, 0x8f); + snd_es1938_mixer_write(chip, 0x56, 0x95); + snd_es1938_mixer_write(chip, 0x58, 0x94); + snd_es1938_mixer_write(chip, 0x5a, 0x80); +} + +/* -------------------------------------------------------------------- + * Reset the FIFOs + * --------------------------------------------------------------------*/ +static void snd_es1938_reset_fifo(es1938_t *chip) +{ + outb(2, SLSB_REG(chip, RESET)); + outb(0, SLSB_REG(chip, RESET)); +} + +static ratnum_t clocks[2] = { + { + num: 793800, + den_min: 1, + den_max: 128, + den_step: 1, + }, + { + num: 768000, + den_min: 1, + den_max: 128, + den_step: 1, + } +}; + +static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { + nrats: 2, + rats: clocks, +}; + + +static void snd_es1938_rate_set(es1938_t *chip, + snd_pcm_substream_t *substream, + int mode) +{ + unsigned int bits, div0; + snd_pcm_runtime_t *runtime = substream->runtime; + if (runtime->rate_num == clocks[0].num) + bits = 128 - runtime->rate_den; + else + bits = 256 - runtime->rate_den; + + /* set filter register */ + div0 = 256 - 7160000*20/(8*82*runtime->rate); + + if (mode == DAC2) { + snd_es1938_mixer_write(chip, 0x70, bits); + snd_es1938_mixer_write(chip, 0x72, div0); + } else { + snd_es1938_write(chip, 0xA1, bits); + snd_es1938_write(chip, 0xA2, div0); + } +} + +/* -------------------------------------------------------------------- + * Configure Solo1 builtin DMA Controller + * --------------------------------------------------------------------*/ + +static void snd_es1938_playback1_setdma(es1938_t *chip) +{ + outb(0x00, SLIO_REG(chip, AUDIO2MODE)); + outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR)); + outw(0, SLIO_REG(chip, AUDIO2DMACOUNT)); + outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT)); +} + +static void snd_es1938_playback2_setdma(es1938_t *chip) +{ + /* Enable DMA controller */ + outb(0xc4, SLDM_REG(chip, DMACOMMAND)); + /* 1. Master reset */ + outb(0, SLDM_REG(chip, DMACLEAR)); + /* 2. Mask DMA */ + outb(1, SLDM_REG(chip, DMAMASK)); + outb(0x18, SLDM_REG(chip, DMAMODE)); + outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); + /* 3. Unmask DMA */ + outb(0, SLDM_REG(chip, DMAMASK)); +} + +static void snd_es1938_capture_setdma(es1938_t *chip) +{ + /* Enable DMA controller */ + outb(0xc4, SLDM_REG(chip, DMACOMMAND)); + /* 1. Master reset */ + outb(0, SLDM_REG(chip, DMACLEAR)); + /* 2. Mask DMA */ + outb(1, SLDM_REG(chip, DMAMASK)); + outb(0x14, SLDM_REG(chip, DMAMODE)); + outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); + /* 3. Unmask DMA */ + outb(0, SLDM_REG(chip, DMAMASK)); +} + +/* ---------------------------------------------------------------------- + * + * *** PCM part *** + */ + +static int snd_es1938_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + int val; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = 0x0f; + chip->active |= ADC1; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = 0x00; + chip->active &= ~ADC1; + break; + default: + return -EINVAL; + } + snd_es1938_write(chip, ESS_CMD_DMACONTROL, val); + return 0; +} + +static int snd_es1938_playback1_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* According to the documentation this should be: + 0x13 but that value may random swap stereo channels */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93); + outb(0x0a, SLIO_REG(chip, AUDIO2MODE)); + chip->active |= DAC2; + break; + case SNDRV_PCM_TRIGGER_STOP: + outb(0, SLIO_REG(chip, AUDIO2MODE)); + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0); + chip->active &= ~DAC2; + break; + default: + return -EINVAL; + } + return 0; +} + +static int snd_es1938_playback2_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + int val; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = 5; + chip->active |= DAC1; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = 0; + chip->active &= ~DAC1; + break; + default: + return -EINVAL; + } + snd_es1938_write(chip, ESS_CMD_DMACONTROL, val); + return 0; +} + +static int snd_es1938_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_trigger(substream, cmd); + case 1: + return snd_es1938_playback2_trigger(substream, cmd); + } + snd_BUG(); + return -EINVAL; +} + +/* -------------------------------------------------------------------- + * First channel for Extended Mode Audio 1 ADC Operation + * --------------------------------------------------------------------*/ +static int snd_es1938_capture_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + chip->dma1_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma1_shift = 2 - mono - is8; + + snd_es1938_reset_fifo(chip); + + /* program type */ + snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1)); + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, ADC1); + + count = 0x10000 - count; + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8); + + /* initialize and configure ADC */ + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, u ? 0x51 : 0x71); + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 0x90 | + (u ? 0x00 : 0x20) | + (is8 ? 0x00 : 0x04) | + (mono ? 0x40 : 0x08)); + + // snd_es1938_reset_fifo(chip); + + /* 11. configure system interrupt controller and DMA controller */ + snd_es1938_capture_setdma(chip); + + return 0; +} + + +/* ------------------------------------------------------------------------------ + * Second Audio channel DAC Operation + * ------------------------------------------------------------------------------*/ +static int snd_es1938_playback1_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma2_size = size; + chip->dma2_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma2_shift = 2 - mono - is8; + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, DAC2); + + count >>= 1; + count = 0x10000 - count; + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff); + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8); + + /* initialize and configure Audio 2 DAC */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) | (mono ? 0 : 2) | (is8 ? 0 : 1)); + + /* program DMA */ + snd_es1938_playback1_setdma(chip); + + return 0; +} + +static int snd_es1938_playback2_prepare(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int u, is8, mono; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + chip->dma1_size = size; + chip->dma1_start = runtime->dma_addr; + + mono = (runtime->channels > 1) ? 0 : 1; + is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1; + u = snd_pcm_format_unsigned(runtime->format); + + chip->dma1_shift = 2 - mono - is8; + + count = 0x10000 - count; + + /* reset */ + snd_es1938_reset_fifo(chip); + + snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1)); + + /* set clock and counters */ + snd_es1938_rate_set(chip, substream, DAC1); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff); + snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8); + + /* initialized and configure DAC */ + snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00); + snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71); + snd_es1938_write(chip, ESS_CMD_SETFORMAT2, + 0x90 | (mono ? 0x40 : 0x08) | + (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20)); + + /* program DMA */ + snd_es1938_playback2_setdma(chip); + + return 0; +} + +static int snd_es1938_playback_prepare(snd_pcm_substream_t *substream) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_prepare(substream); + case 1: + return snd_es1938_playback2_prepare(substream); + } + snd_BUG(); + return -EINVAL; +} + +static snd_pcm_uframes_t snd_es1938_capture_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + size_t old, new; +#if 1 + /* This stuff is *needed*, don't ask why - AB */ + old = inw(SLDM_REG(chip, DMACOUNT)); + while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) + old = new; + ptr = chip->dma1_size - 1 - new; +#else + ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; +#endif + return ptr >> chip->dma1_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback1_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; +#if 1 + ptr = chip->dma2_size - inw(SLIO_REG(chip, AUDIO2DMACOUNT)); +#else + ptr = inl(SLIO_REG(chip, AUDIO2DMAADDR)) - chip->dma2_start; +#endif + return ptr >> chip->dma2_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback2_pointer(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + size_t old, new; +#if 1 + /* This stuff is *needed*, don't ask why - AB */ + old = inw(SLDM_REG(chip, DMACOUNT)); + while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) + old = new; + ptr = chip->dma1_size - 1 - new; +#else + ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; +#endif + return ptr >> chip->dma1_shift; +} + +static snd_pcm_uframes_t snd_es1938_playback_pointer(snd_pcm_substream_t *substream) +{ + switch (substream->number) { + case 0: + return snd_es1938_playback1_pointer(substream); + case 1: + return snd_es1938_playback2_pointer(substream); + } + snd_BUG(); + return -EINVAL; +} + +static int snd_es1938_capture_copy(snd_pcm_substream_t *substream, + int channel, + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es1938_t *chip = snd_pcm_substream_chip(substream); + pos <<= chip->dma1_shift; + count <<= chip->dma1_shift; + snd_assert(pos + count <= chip->dma1_size, return -EINVAL); + if (pos + count < chip->dma1_size) + memcpy(dst, runtime->dma_area + pos + 1, count); + else { + memcpy(dst, runtime->dma_area + pos + 1, count - 1); + ((unsigned char *)dst)[count - 1] = runtime->dma_area[0]; + } + return 0; +} + +/* ---------------------------------------------------------------------- + * Audio1 Capture (ADC) + * ----------------------------------------------------------------------*/ +static snd_pcm_hardware_t snd_es1938_capture = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 6000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 256, +}; + +/* ----------------------------------------------------------------------- + * Audio2 Playback (DAC) + * -----------------------------------------------------------------------*/ +static snd_pcm_hardware_t snd_es1938_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 6000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 64, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 256, +}; + +static int snd_es1938_capture_open(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->playback2_substream) + return -EAGAIN; + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma2size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->capture_substream = substream; + runtime->hw = snd_es1938_capture; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00); + return 0; +} + +static int snd_es1938_playback_open(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + switch (substream->number) { + case 0: + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->playback1_substream = substream; + break; + case 1: + if (chip->capture_substream) + return -EAGAIN; + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->playback2_substream = substream; + break; + default: + snd_BUG(); + return -EINVAL; + } + runtime->hw = snd_es1938_playback; + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_clocks); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00); + return 0; +} + +static int snd_es1938_capture_close(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_substream = NULL; + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + return 0; +} + +static int snd_es1938_playback_close(snd_pcm_substream_t * substream) +{ + es1938_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + switch (substream->number) { + case 0: + chip->playback1_substream = NULL; + break; + case 1: + chip->playback2_substream = NULL; + break; + default: + snd_BUG(); + return -EINVAL; + } + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + return 0; +} + +static snd_pcm_ops_t snd_es1938_playback_ops = { + open: snd_es1938_playback_open, + close: snd_es1938_playback_close, + ioctl: snd_pcm_lib_ioctl, + prepare: snd_es1938_playback_prepare, + trigger: snd_es1938_playback_trigger, + pointer: snd_es1938_playback_pointer, +}; + +static snd_pcm_ops_t snd_es1938_capture_ops = { + open: snd_es1938_capture_open, + close: snd_es1938_capture_close, + ioctl: snd_pcm_lib_ioctl, + prepare: snd_es1938_capture_prepare, + trigger: snd_es1938_capture_trigger, + pointer: snd_es1938_capture_pointer, + copy: snd_es1938_capture_copy, +}; + +static void snd_es1938_free_pcm(snd_pcm_t *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init snd_es1938_new_pcm(es1938_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_es1938_free_pcm; + pcm->info_flags = 0; + strcpy(pcm->name, "ESS Solo-1"); + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* ------------------------------------------------------------------- + * + * *** Mixer part *** + */ + +static int snd_es1938_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[8] = { + "Mic", "Mic Master", "CD", "AOUT", + "Mic1", "Mix", "Line", "Master" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 8; + if (uinfo->value.enumerated.item > 7) + uinfo->value.enumerated.item = 7; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_es1938_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = snd_es1938_mixer_read(chip, 0x1c) & 0x07; + return 0; +} + +static int snd_es1938_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = ucontrol->value.enumerated.item[0]; + + if (val > 7) + return -EINVAL; + return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val; +} + +static int snd_es1938_info_spatializer_enable(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1938_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char val = snd_es1938_mixer_read(chip, 0x50); + ucontrol->value.integer.value[0] = !!(val & 8); + return 0; +} + +static int snd_es1938_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char oval, nval; + int change; + nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04; + oval = snd_es1938_mixer_read(chip, 0x50) & 0x0c; + change = nval != oval; + if (change) { + snd_es1938_mixer_write(chip, 0x50, nval & ~0x04); + snd_es1938_mixer_write(chip, 0x50, nval); + } + return change; +} + +static int snd_es1938_info_hw_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 63; + return 0; +} + +static int snd_es1938_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = snd_es1938_mixer_read(chip, 0x61) & 0x3f; + ucontrol->value.integer.value[1] = snd_es1938_mixer_read(chip, 0x63) & 0x3f; + return 0; +} + +static int snd_es1938_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1938_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = !(snd_es1938_mixer_read(chip, 0x61) & 0x40); + ucontrol->value.integer.value[1] = !(snd_es1938_mixer_read(chip, 0x63) & 0x40); + return 0; +} + +static void snd_es1938_hwv_free(snd_kcontrol_t *kcontrol) +{ + es1938_t *chip = snd_magic_cast(es1938_t, _snd_kcontrol_chip(kcontrol), return); + chip->master_volume = NULL; + chip->master_switch = NULL; + chip->hw_volume = NULL; + chip->hw_switch = NULL; +} + +static int snd_es1938_reg_bits(es1938_t *chip, unsigned char reg, + unsigned char mask, unsigned char val) +{ + if (reg < 0xa0) + return snd_es1938_mixer_bits(chip, reg, mask, val); + else + return snd_es1938_bits(chip, reg, mask, val); +} + +static int snd_es1938_reg_read(es1938_t *chip, unsigned char reg) +{ + if (reg < 0xa0) + return snd_es1938_mixer_read(chip, reg); + else + return snd_es1938_read(chip, reg); +} + +#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1938_info_single, \ + get: snd_es1938_get_single, put: snd_es1938_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_es1938_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1938_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int val; + + val = snd_es1938_reg_read(chip, reg); + ucontrol->value.integer.value[0] = (val >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_es1938_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned char val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + mask <<= shift; + val <<= shift; + return snd_es1938_reg_bits(chip, reg, mask, val) != val; +} + +#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_es1938_info_double, \ + get: snd_es1938_get_double, put: snd_es1938_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_es1938_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_es1938_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + unsigned char left, right; + + left = snd_es1938_reg_read(chip, left_reg); + if (left_reg != right_reg) + right = snd_es1938_reg_read(chip, right_reg); + else + right = left; + ucontrol->value.integer.value[0] = (left >> shift_left) & mask; + ucontrol->value.integer.value[1] = (right >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_es1938_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + es1938_t *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned char val1, val2, mask1, mask2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + mask1 = mask << shift_left; + mask2 = mask << shift_right; + if (left_reg != right_reg) { + change = 0; + if (snd_es1938_reg_bits(chip, left_reg, mask1, val1) != val1) + change = 1; + if (snd_es1938_reg_bits(chip, right_reg, mask2, val2) != val2) + change = 1; + } else { + change = (snd_es1938_reg_bits(chip, left_reg, mask1 | mask2, + val1 | val2) != (val1 | val2)); + } + return change; +} + +static snd_kcontrol_new_t snd_es1938_controls[] = { +ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0), +ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es1938_info_hw_volume, + get: snd_es1938_get_hw_volume, +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Hardware Master Playback Switch", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_es1938_info_hw_switch, + get: snd_es1938_get_hw_switch, +}, +ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0), +ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0), +ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0), +ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0), +ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0), +ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0), +ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0), +ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0), +ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), +ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Capture Source", + info: snd_es1938_info_mux, + get: snd_es1938_get_mux, + put: snd_es1938_put_mux, +}, +ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0), +ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0), +ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0), +ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0), +ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0), +ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0), +ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0), +ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0), +ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "3D Control - Switch", + info: snd_es1938_info_spatializer_enable, + get: snd_es1938_get_spatializer_enable, + put: snd_es1938_put_spatializer_enable, +}, +ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0) +}; + + +/* ---------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------- */ + +static int snd_es1938_free(es1938_t *chip) +{ +#ifndef LINUX_2_2 + if (chip->gameport.io) + gameport_unregister_port(&chip->gameport); +#endif + if (chip->res_io_port) { + release_resource(chip->res_io_port); + kfree_nocheck(chip->res_io_port); + } + if (chip->res_sb_port) { + release_resource(chip->res_sb_port); + kfree_nocheck(chip->res_sb_port); + } + if (chip->res_vc_port) { + release_resource(chip->res_vc_port); + kfree_nocheck(chip->res_vc_port); + } + if (chip->res_mpu_port) { + release_resource(chip->res_mpu_port); + kfree_nocheck(chip->res_mpu_port); + } + if (chip->res_game_port) { + release_resource(chip->res_game_port); + kfree_nocheck(chip->res_game_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1938_dev_free(snd_device_t *device) +{ + es1938_t *chip = snd_magic_cast(es1938_t, device->device_data, return -ENXIO); + return snd_es1938_free(chip); +} + +static int __init snd_es1938_create(snd_card_t * card, + struct pci_dev * pci, + unsigned long dma1size, + unsigned long dma2size, + es1938_t ** rchip) +{ + es1938_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_es1938_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + chip = snd_magic_kcalloc(es1938_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->mixer_lock); + chip->card = card; + chip->pci = pci; + chip->dma1size = dma1size; + chip->dma2size = dma2size; + chip->io_port = pci_resource_start(pci, 0); + if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 8 - 1); + return -EBUSY; + } + chip->sb_port = pci_resource_start(pci, 1); + if ((chip->res_sb_port = request_region(chip->sb_port, 0x10, "ESS Solo-1 SB")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab SB region 0x%lx-0x%lx\n", chip->sb_port, chip->sb_port + 0x10 - 1); + return -EBUSY; + } + chip->vc_port = pci_resource_start(pci, 2); + if ((chip->res_vc_port = request_region(chip->vc_port, 0x10, "ESS Solo-1 VC (DMA)")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab VC (DMA) region 0x%lx-0x%lx\n", chip->vc_port, chip->vc_port + 0x10 - 1); + return -EBUSY; + } + chip->mpu_port = pci_resource_start(pci, 3); + if ((chip->res_mpu_port = request_region(chip->mpu_port, 4, "ESS Solo-1 MIDI")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab MIDI region 0x%lx-0x%lx\n", chip->mpu_port, chip->mpu_port + 4 - 1); + return -EBUSY; + } + chip->game_port = pci_resource_start(pci, 4); + if ((chip->res_game_port = request_region(chip->game_port, 4, "ESS Solo-1 GAME")) == NULL) { + snd_es1938_free(chip); + snd_printk("unable to grab GAME region 0x%lx-0x%lx\n", chip->game_port, chip->game_port + 4 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) { + snd_es1938_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; +#ifdef ES1938_DDEBUG + snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n", + chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port); +#endif + /* reset chip */ + snd_es1938_reset(chip); + + /* configure native mode */ + + /* enable bus master */ + pci_set_master(pci); + + /* disable legacy audio */ + pci_write_config_word(pci, SL_PCI_LEGACYCONTROL, 0x805f); + + /* set DDMA base */ + chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */ + pci_write_config_word(pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1); + + /* set DMA/IRQ policy */ + pci_write_config_dword(pci, SL_PCI_CONFIG, 0); + + /* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/ + outb(0xf0, SLIO_REG(chip, IRQCONTROL)); + + /* reset DMA */ + outb(0, SLDM_REG(chip, DMACLEAR)); + + /* enable bus mastering */ + pci_set_master(pci); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1938_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +/* -------------------------------------------------------------------- + * Interrupt handler + * -------------------------------------------------------------------- */ +static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1938_t *chip = snd_magic_cast(es1938_t, dev_id, return); + unsigned char status, audiostatus; + + status = inb(SLIO_REG(chip, IRQCONTROL)); +#if 0 + printk("Es1938debug - interrupt status: =0x%x\n", status); +#endif + + /* AUDIO 1 */ + if (status & 0x10) { +#if 0 + printk("Es1938debug - AUDIO channel 1 interrupt\n"); + printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT))); + printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR))); + printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS))); +#endif + /* clear irq */ + audiostatus = inb(SLSB_REG(chip, STATUS)); + if (chip->active & ADC1) + snd_pcm_period_elapsed(chip->capture_substream); + else if (chip->active & DAC1) + snd_pcm_period_elapsed(chip->playback2_substream); + } + + /* AUDIO 2 */ + if (status & 0x20) { +#if 0 + printk("Es1938debug - AUDIO channel 2 interrupt\n"); + printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT))); + printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR))); + +#endif + /* clear irq */ + snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0); + if (chip->active & DAC2) + snd_pcm_period_elapsed(chip->playback1_substream); + } + + /* Hardware volume */ + if (status & 0x40) { + int split = snd_es1938_mixer_read(chip, 0x64) & 0x80; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id); + if (!split) { + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); + } + /* ack interrupt */ + snd_es1938_mixer_write(chip, 0x66, 0x00); + } + + /* MPU401 */ + if (status & 0x80) { + /* ack */ + snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); + printk("midi interrupt..\n"); + if (chip->rmidi) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } +} + +#define ES1938_DMA_SIZE 64 + +static int __init snd_es1938_mixer(snd_pcm_t *pcm) +{ + snd_card_t *card; + es1938_t *chip; + int err, idx; + + snd_assert(pcm != NULL && pcm->card != NULL, return -EINVAL); + + card = pcm->card; + chip = snd_pcm_chip(pcm); + + strcpy(card->mixername, pcm->name); + + for (idx = 0; idx < sizeof(snd_es1938_controls) / + sizeof(snd_es1938_controls[0]); idx++) { + snd_kcontrol_t *kctl; + kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip); + switch (idx) { + case 0: + chip->master_volume = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 1: + chip->master_switch = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 2: + chip->hw_volume = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + case 3: + chip->hw_switch = kctl; + kctl->private_free = snd_es1938_hwv_free; + break; + } + if ((err = snd_ctl_add(card, kctl)) < 0) + return err; + } + return 0; +} + + +static int __devinit snd_es1938_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + es1938_t *chip; + snd_pcm_t *pcm; + opl3_t *opl3; + int idx, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + for (idx = 0; idx < 5; idx++) { + if (pci_resource_start(pci, idx) == 0 || + !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) { + snd_card_free(card); + return -ENODEV; + } + } + if ((err = snd_es1938_create(card, pci, + 64 * 1024, + 64 * 1024, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1938_new_pcm(chip, 0, &pcm)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_es1938_mixer(pcm)) < 0) { + snd_card_free(card); + return err; + } + if (snd_opl3_create(card, + SLSB_REG(chip, FMLOWADDR), + SLSB_REG(chip, FMHIGHADDR), + OPL3_HW_OPL3, 1, &opl3) < 0) { + printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n", + SLSB_REG(chip, FMLOWADDR)); + } else { + if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) { + printk(KERN_ERR "es1938: unable to initialize MPU-401\n"); + } +#ifndef LINUX_2_2 + chip->gameport.io = chip->game_port; + gameport_register_port(&chip->gameport); +#endif + + strcpy(card->driver, "ES1938"); + strcpy(card->shortname, "ESS ES1938 (Solo-1)"); + sprintf(card->longname, "%s rev %i, irq %i", + card->shortname, + chip->revision, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_es1938_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ESS ES1938 (Solo-1)", + id_table: snd_es1938_ids, + probe: snd_es1938_probe, + remove: __devexit_p(snd_es1938_remove), +}; + +static int __init alsa_card_es1938_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ESS Solo-1 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_es1938_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_es1938_init) +module_exit(alsa_card_es1938_exit) + +#ifndef MODULE + +/* format is: snd-es1938=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_es1938_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1938=", alsa_card_es1938_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/es1968.c linux-2.4.19-pre5-mjc/sound/pci/es1968.c --- linux/sound/pci/es1968.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/es1968.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2840 @@ +/* + * Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99) + * Copyright (c) by Matze Braun . + * Takashi Iwai + * + * Most of the driver code comes from Zach Brown(zab@redhat.com) + * Alan Cox OSS Driver + * Rewritted from card-es1938.c source. + * + * TODO: + * Perhaps Synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Notes from Zach Brown about the driver code + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + */ + +#define __SND_OSS_COMPAT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t es1968_t + +#define CARD_NAME "ESS Maestro1/2" +#define DRIVER_NAME "ES1968" + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("ESS Maestro"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); +MODULE_DEVICES("{{ESS,Maestro 2e}," + "{ESS,Maestro 2}," + "{ESS,Maestro 1}," + "{TerraTec,DMX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 }; +static int snd_pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 }; +static int snd_pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 }; +static int snd_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_total_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_total_bufsize, "Total buffer size in kB."); +MODULE_PARM_SYNTAX(snd_total_bufsize, SNDRV_ENABLED ",allows:{{1,4096}},skill:advanced"); +MODULE_PARM(snd_pcm_substreams_p, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_pcm_substreams_p, SNDRV_ENABLED ",allows:{{1,8}}"); +MODULE_PARM(snd_pcm_substreams_c, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_pcm_substreams_c, SNDRV_ENABLED ",allows:{{0,8}}"); +MODULE_PARM(snd_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_clock, "Clock on " CARD_NAME " soundcard. (0 = auto-detect)"); +MODULE_PARM_SYNTAX(snd_clock, SNDRV_ENABLED); + + +/* PCI Dev ID's */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125D +#endif + +#define PCI_VENDOR_ID_ESS_OLD 0x1285 /* Platform Tech, the people the ESS + was bought form */ + +#ifndef PCI_DEVICE_ID_ESS_M2E +#define PCI_DEVICE_ID_ESS_M2E 0x1978 +#endif +#ifndef PCI_DEVICE_ID_ESS_M2 +#define PCI_DEVICE_ID_ESS_M2 0x1968 +#endif +#ifndef PCI_DEVICE_ID_ESS_M1 +#define PCI_DEVICE_ID_ESS_M1 0x0100 +#endif + +#define NR_APUS 64 +#define NR_APU_REGS 16 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + +/* Mode Flags */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +/* Values for the ESM_LEGACY_AUDIO_CONTROL */ + +#define ESS_ENABLE_AUDIO 0x8000 +#define ESS_ENABLE_SERIAL_IRQ 0x4000 +#define IO_ADRESS_ALIAS 0x0020 +#define MPU401_IRQ_ENABLE 0x0010 +#define MPU401_IO_ENABLE 0x0008 +#define GAME_IO_ENABLE 0x0004 +#define FM_IO_ENABLE 0x0002 +#define SB_IO_ENABLE 0x0001 + +/* Values for the ESM_CONFIG_A */ + +#define PIC_SNOOP1 0x4000 +#define PIC_SNOOP2 0x2000 +#define SAFEGUARD 0x0800 +#define DMA_CLEAR 0x0700 +#define DMA_DDMA 0x0000 +#define DMA_TDMA 0x0100 +#define DMA_PCPCI 0x0200 +#define POST_WRITE 0x0080 +#define ISA_TIMING 0x0040 +#define SWAP_LR 0x0020 +#define SUBTR_DECODE 0x0002 + +/* Values for the ESM_CONFIG_B */ + +#define SPDIF_CONFB 0x0100 +#define HWV_CONFB 0x0080 +#define DEBOUNCE 0x0040 +#define GPIO_CONFB 0x0020 +#define CHI_CONFB 0x0010 +#define IDMA_CONFB 0x0008 /*undoc */ +#define MIDI_FIX 0x0004 /*undoc */ +#define IRQ_TO_ISA 0x0001 /*undoc */ + +/* Values for Ring Bus Control B */ +#define RINGB_2CODEC_ID_MASK 0x0003 +#define RINGB_DIS_VALIDATION 0x0008 +#define RINGB_EN_SPDIF 0x0010 +#define RINGB_EN_2CODEC 0x0020 +#define RINGB_SING_BIT_DUAL 0x0040 + +/* ****Port Adresses**** */ + +/* Write & Read */ +#define ESM_INDEX 0x02 +#define ESM_DATA 0x00 + +/* AC97 + RingBus */ +#define ESM_AC97_INDEX 0x30 +#define ESM_AC97_DATA 0x32 +#define ESM_RING_BUS_DEST 0x34 +#define ESM_RING_BUS_CONTR_A 0x36 +#define ESM_RING_BUS_CONTR_B 0x38 +#define ESM_RING_BUS_SDO 0x3A + +/* WaveCache*/ +#define WC_INDEX 0x10 +#define WC_DATA 0x12 +#define WC_CONTROL 0x14 + +/* ASSP*/ +#define ASSP_INDEX 0x80 +#define ASSP_MEMORY 0x82 +#define ASSP_DATA 0x84 +#define ASSP_CONTROL_A 0xA2 +#define ASSP_CONTROL_B 0xA4 +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOSTW_INDEX 0xA8 +#define ASSP_HOSTW_DATA 0xAA +#define ASSP_HOSTW_IRQ 0xAC +/* Midi */ +#define ESM_MPU401_PORT 0x98 +/* Others */ +#define ESM_PORT_HOST_IRQ 0x18 + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* PCI Register */ + +#define ESM_LEGACY_AUDIO_CONTROL 0x40 +#define ESM_ACPI_COMMAND 0x54 +#define ESM_CONFIG_A 0x50 +#define ESM_CONFIG_B 0x52 +#define ESM_DDMA 0x60 + +/* Bob Bits */ +#define ESM_BOB_ENABLE 0x0001 +#define ESM_BOB_START 0x0001 + +/* Host IRQ Control Bits */ +#define ESM_RESET_MAESTRO 0x8000 +#define ESM_RESET_DIRECTSOUND 0x4000 +#define ESM_HIRQ_ClkRun 0x0100 +#define ESM_HIRQ_HW_VOLUME 0x0040 +#define ESM_HIRQ_HARPO 0x0030 /* What's that? */ +#define ESM_HIRQ_ASSP 0x0010 +#define ESM_HIRQ_DSIE 0x0004 +#define ESM_HIRQ_MPU401 0x0002 +#define ESM_HIRQ_SB 0x0001 + +/* Host IRQ Status Bits */ +#define ESM_MPU401_IRQ 0x02 +#define ESM_SB_IRQ 0x01 +#define ESM_SOUND_IRQ 0x04 +#define ESM_ASSP_IRQ 0x10 +#define ESM_HWVOL_IRQ 0x40 + +#define ESS_SYSCLK 50000000 +#define ESM_BOB_FREQ 200 +#define ESM_BOB_FREQ_MAX 800 + +#define ESM_FREQ_ESM1 (49152000L / 1024L) /* default rate 48000 */ +#define ESM_FREQ_ESM2 (50000000L / 1024L) + +/* APU Modes: reg 0x00, bit 4-7 */ +#define ESM_APU_MODE_SHIFT 4 +#define ESM_APU_MODE_MASK (0xf << 4) +#define ESM_APU_OFF 0x00 +#define ESM_APU_16BITLINEAR 0x01 /* 16-Bit Linear Sample Player */ +#define ESM_APU_16BITSTEREO 0x02 /* 16-Bit Stereo Sample Player */ +#define ESM_APU_8BITLINEAR 0x03 /* 8-Bit Linear Sample Player */ +#define ESM_APU_8BITSTEREO 0x04 /* 8-Bit Stereo Sample Player */ +#define ESM_APU_8BITDIFF 0x05 /* 8-Bit Differential Sample Playrer */ +#define ESM_APU_DIGITALDELAY 0x06 /* Digital Delay Line */ +#define ESM_APU_DUALTAP 0x07 /* Dual Tap Reader */ +#define ESM_APU_CORRELATOR 0x08 /* Correlator */ +#define ESM_APU_INPUTMIXER 0x09 /* Input Mixer */ +#define ESM_APU_WAVETABLE 0x0A /* Wave Table Mode */ +#define ESM_APU_SRCONVERTOR 0x0B /* Sample Rate Convertor */ +#define ESM_APU_16BITPINGPONG 0x0C /* 16-Bit Ping-Pong Sample Player */ +#define ESM_APU_RESERVED1 0x0D /* Reserved 1 */ +#define ESM_APU_RESERVED2 0x0E /* Reserved 2 */ +#define ESM_APU_RESERVED3 0x0F /* Reserved 3 */ + +/* reg 0x00 */ +#define ESM_APU_FILTER_Q_SHIFT 0 +#define ESM_APU_FILTER_Q_MASK (3 << 0) +/* APU Filtey Q Control */ +#define ESM_APU_FILTER_LESSQ 0x00 +#define ESM_APU_FILTER_MOREQ 0x03 + +#define ESM_APU_FILTER_TYPE_SHIFT 2 +#define ESM_APU_FILTER_TYPE_MASK (3 << 2) +#define ESM_APU_ENV_TYPE_SHIFT 8 +#define ESM_APU_ENV_TYPE_MASK (3 << 8) +#define ESM_APU_ENV_STATE_SHIFT 10 +#define ESM_APU_ENV_STATE_MASK (3 << 10) +#define ESM_APU_END_CURVE (1 << 12) +#define ESM_APU_INT_ON_LOOP (1 << 13) +#define ESM_APU_DMA_ENABLE (1 << 14) + +/* reg 0x02 */ +#define ESM_APU_SUBMIX_GROUP_SHIRT 0 +#define ESM_APU_SUBMIX_GROUP_MASK (7 << 0) +#define ESM_APU_SUBMIX_MODE (1 << 3) +#define ESM_APU_6dB (1 << 4) +#define ESM_APU_DUAL_EFFECT (1 << 5) +#define ESM_APU_EFFECT_CHANNELS_SHIFT 6 +#define ESM_APU_EFFECT_CHANNELS_MASK (3 << 6) + +/* reg 0x03 */ +#define ESM_APU_STEP_SIZE_MASK 0x0fff + +/* reg 0x04 */ +#define ESM_APU_PHASE_SHIFT 0 +#define ESM_APU_PHASE_MASK (0xff << 0) +#define ESM_APU_WAVE64K_PAGE_SHIFT 8 /* most 8bit of wave start offset */ +#define ESM_APU_WAVE64K_PAGE_MASK (0xff << 8) + +/* reg 0x05 - wave start offset */ +/* reg 0x06 - wave end offset */ +/* reg 0x07 - wave loop length */ + +/* reg 0x08 */ +#define ESM_APU_EFFECT_GAIN_SHIFT 0 +#define ESM_APU_EFFECT_GAIN_MASK (0xff << 0) +#define ESM_APU_TREMOLO_DEPTH_SHIFT 8 +#define ESM_APU_TREMOLO_DEPTH_MASK (0xf << 8) +#define ESM_APU_TREMOLO_RATE_SHIFT 12 +#define ESM_APU_TREMOLO_RATE_MASK (0xf << 12) + +/* reg 0x09 */ +/* bit 0-7 amplitude dest? */ +#define ESM_APU_AMPLITUDE_NOW_SHIFT 8 +#define ESM_APU_AMPLITUDE_NOW_MASK (0xff << 8) + +/* reg 0x0a */ +#define ESM_APU_POLAR_PAN_SHIFT 0 +#define ESM_APU_POLAR_PAN_MASK (0x3f << 0) +/* Polar Pan Control */ +#define ESM_APU_PAN_CENTER_CIRCLE 0x00 +#define ESM_APU_PAN_MIDDLE_RADIUS 0x01 +#define ESM_APU_PAN_OUTSIDE_RADIUS 0x02 + +#define ESM_APU_FILTER_TUNING_SHIFT 8 +#define ESM_APU_FILTER_TUNING_MASK (0xff << 8) + +/* reg 0x0b */ +#define ESM_APU_DATA_SRC_A_SHIFT 0 +#define ESM_APU_DATA_SRC_A_MASK (0x7f << 0) +#define ESM_APU_INV_POL_A (1 << 7) +#define ESM_APU_DATA_SRC_B_SHIFT 8 +#define ESM_APU_DATA_SRC_B_MASK (0x7f << 8) +#define ESM_APU_INV_POL_B (1 << 15) + +#define ESM_APU_VIBRATO_RATE_SHIFT 0 +#define ESM_APU_VIBRATO_RATE_MASK (0xf << 0) +#define ESM_APU_VIBRATO_DEPTH_SHIFT 4 +#define ESM_APU_VIBRATO_DEPTH_MASK (0xf << 4) +#define ESM_APU_VIBRATO_PHASE_SHIFT 8 +#define ESM_APU_VIBRATO_PHASE_MASK (0xff << 8) + +/* reg 0x0c */ +#define ESM_APU_RADIUS_SELECT (1 << 6) + +/* APU Filter Control */ +#define ESM_APU_FILTER_2POLE_LOPASS 0x00 +#define ESM_APU_FILTER_2POLE_BANDPASS 0x01 +#define ESM_APU_FILTER_2POLE_HIPASS 0x02 +#define ESM_APU_FILTER_1POLE_LOPASS 0x03 +#define ESM_APU_FILTER_1POLE_HIPASS 0x04 +#define ESM_APU_FILTER_OFF 0x05 + +/* APU ATFP Type */ +#define ESM_APU_ATFP_AMPLITUDE 0x00 +#define ESM_APU_ATFP_TREMELO 0x01 +#define ESM_APU_ATFP_FILTER 0x02 +#define ESM_APU_ATFP_PAN 0x03 + +/* APU ATFP Flags */ +#define ESM_APU_ATFP_FLG_OFF 0x00 +#define ESM_APU_ATFP_FLG_WAIT 0x01 +#define ESM_APU_ATFP_FLG_DONE 0x02 +#define ESM_APU_ATFP_FLG_INPROCESS 0x03 + + +/* capture mixing buffer size */ +#define ESM_MIXBUF_SIZE 512 + +#define ESM_MODE_PLAY 0 +#define ESM_MODE_CAPTURE 1 + +/* acpi states */ +enum { + ACPI_D0=0, + ACPI_D1, + ACPI_D2, + ACPI_D3 +}; + +/* bits in the acpi masks */ +#define ACPI_12MHZ ( 1 << 15) +#define ACPI_24MHZ ( 1 << 14) +#define ACPI_978 ( 1 << 13) +#define ACPI_SPDIF ( 1 << 12) +#define ACPI_GLUE ( 1 << 11) +#define ACPI__10 ( 1 << 10) /* reserved */ +#define ACPI_PCIINT ( 1 << 9) +#define ACPI_HV ( 1 << 8) /* hardware volume */ +#define ACPI_GPIO ( 1 << 7) +#define ACPI_ASSP ( 1 << 6) +#define ACPI_SB ( 1 << 5) /* sb emul */ +#define ACPI_FM ( 1 << 4) /* fm emul */ +#define ACPI_RB ( 1 << 3) /* ringbus / aclink */ +#define ACPI_MIDI ( 1 << 2) +#define ACPI_GP ( 1 << 1) /* game port */ +#define ACPI_WP ( 1 << 0) /* wave processor */ + +#define ACPI_ALL (0xffff) +#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \ + ACPI_MIDI|ACPI_GP|ACPI_WP)) +#define ACPI_NONE (ACPI__10) + +/* these masks indicate which units we care about at + which states */ +static u16 acpi_state_mask[] = { + [ACPI_D0] = ACPI_ALL, + [ACPI_D1] = ACPI_SLEEP, + [ACPI_D2] = ACPI_SLEEP, + [ACPI_D3] = ACPI_NONE +}; + + +typedef struct snd_es1968 es1968_t; +typedef struct snd_esschan esschan_t; +typedef struct snd_esm_memory esm_memory_t; + +/* APU use in the driver */ +enum snd_enum_apu_type { + ESM_APU_PCM_PLAY, + ESM_APU_PCM_CAPTURE, + ESM_APU_PCM_RATECONV, + ESM_APU_FREE +}; + +/* DMA Hack! */ +struct snd_esm_memory { + char *buf; + unsigned long addr; + int size; + int empty; /* status */ + struct list_head list; +}; + +/* Playback Channel */ +struct snd_esschan { + int running; + + u8 apu[4]; + u8 apu_mode[4]; + + /* playback/capture pcm buffer */ + esm_memory_t *memory; + /* capture mixer buffer */ + esm_memory_t *mixbuf; + + unsigned int hwptr; /* current hw pointer in bytes */ + unsigned int count; /* sample counter in bytes */ + unsigned int dma_size; /* total buffer size in bytes */ + unsigned int frag_size; /* period size in bytes */ + unsigned int wav_shift; + u16 base[4]; /* offset for ptr */ + + /* stereo/16bit flag */ + unsigned char fmt; + int mode; /* playback / capture */ + + int bob_freq; /* required timer frequency */ + + snd_pcm_substream_t *substream; + + /* linked list */ + struct list_head list; + +#ifdef CONFIG_PM + u16 wc_map[4]; +#endif +}; + +struct snd_es1968 { + /* Module Config */ + int total_bufsize; /* in bytes */ + + int playback_streams, capture_streams; + + unsigned int clock; /* clock */ + + /* buffer */ + void *dma_buf; + dma_addr_t dma_buf_addr; + unsigned long dma_buf_size; + + /* Resources... */ + int irq; + unsigned long io_port; + struct resource *res_io_port; + int type; + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + + /* DMA memory block */ + struct list_head buf_list; + + /* ALSA Stuff */ + ac97_t *ac97; + snd_kcontrol_t *master_switch; /* for h/w volume control */ + snd_kcontrol_t *master_volume; + + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; + struct tasklet_struct hwvol_tq; + + /* Maestro Stuff */ + u16 maestro_map[32]; + atomic_t bobclient; /* active timer instancs */ + int bob_freq; /* timer frequency */ + spinlock_t bob_lock; + struct semaphore memory_mutex; /* memory lock */ + + /* APU states */ + unsigned char apu[NR_APUS]; + + /* active substreams */ + struct list_head substream_list; + spinlock_t substream_lock; + +#ifdef CONFIG_PM + u16 apu_map[NR_APUS][NR_APU_REGS]; +#endif +}; + +static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +#define CARD_TYPE_ESS_ESOLDM1 0x12850100 +#define CARD_TYPE_ESS_ES1968 0x125d1968 +#define CARD_TYPE_ESS_ES1978 0x125d1978 + +static struct pci_device_id snd_es1968_ids[] __devinitdata = { + /* Maestro 1 */ + { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, + /* Maestro 2 */ + { 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, + /* Maestro 2E */ + { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_es1968_ids); + +/* ********************* + * Low Level Funcs! * + *********************/ + +/* no spinlock */ +static void __maestro_write(es1968_t *chip, u16 reg, u16 data) +{ + outw(reg, chip->io_port + ESM_INDEX); + outw(data, chip->io_port + ESM_DATA); + chip->maestro_map[reg] = data; +} + +inline static void maestro_write(es1968_t *chip, u16 reg, u16 data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __maestro_write(chip, reg, data); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* no spinlock */ +static u16 __maestro_read(es1968_t *chip, u16 reg) +{ + if (READABLE_MAP & (1 << reg)) { + outw(reg, chip->io_port + ESM_INDEX); + chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA); + } + return chip->maestro_map[reg]; +} + +inline static u16 maestro_read(es1968_t *chip, u16 reg) +{ + unsigned long flags; + u16 result; + spin_lock_irqsave(&chip->reg_lock, flags); + result = __maestro_read(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return result; +} + +/* Wait for the codec bus to be free */ +static int snd_es1968_ac97_wait(es1968_t *chip) +{ + int timeout = 100000; + + while (timeout-- > 0) { + if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1)) + return 0; + } + snd_printd("es1968: ac97 timeout\n"); + return 1; /* timeout */ +} + +static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_es1968_ac97_wait(chip); + + /* Write the bus */ + outw(val, chip->io_port + ESM_AC97_DATA); + mdelay(1); + outb(reg, chip->io_port + ESM_AC97_INDEX); + mdelay(1); + + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg) +{ + u16 data = 0; + es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return 0); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_es1968_ac97_wait(chip); + + outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); + mdelay(1); + + if (! snd_es1968_ac97_wait(chip)) { + data = inw(chip->io_port + ESM_AC97_DATA); + mdelay(1); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return data; +} + +/* no spinlock */ +static void apu_index_set(es1968_t *chip, u16 index) +{ + int i; + __maestro_write(chip, IDR1_CRAM_POINTER, index); + for (i = 0; i < 1000; i++) + if (__maestro_read(chip, IDR1_CRAM_POINTER) == index) + return; + snd_printd("es1968: APU register select failed. (Timeout)\n"); +} + +/* no spinlock */ +static void apu_data_set(es1968_t *chip, u16 data) +{ + int i; + for (i = 0; i < 1000; i++) { + if (__maestro_read(chip, IDR0_DATA_PORT) == data) + return; + __maestro_write(chip, IDR0_DATA_PORT, data); + } + snd_printd("es1968: APU register set probably failed (Timeout)!\n"); +} + +/* no spinlock */ +static void __apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) +{ + snd_assert(channel < NR_APUS, return); +#ifdef CONFIG_PM + chip->apu_map[channel][reg] = data; +#endif + reg |= (channel << 4); + apu_index_set(chip, reg); + apu_data_set(chip, data); +} + +inline static void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + spin_lock_irqsave(&chip->reg_lock, flags); + __apu_set_register(chip, channel, reg, data); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u16 __apu_get_register(es1968_t *chip, u16 channel, u8 reg) +{ + snd_assert(channel < NR_APUS, return 0); + reg |= (channel << 4); + apu_index_set(chip, reg); + return __maestro_read(chip, IDR0_DATA_PORT); +} + +inline static u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + spin_lock_irqsave(&chip->reg_lock, flags); + v = __apu_get_register(chip, channel, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return v; +} + +#if 0 /* ASSP is not supported */ + +static void assp_set_register(es1968_t *chip, u32 reg, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + outl(reg, chip->io_port + ASSP_INDEX); + outl(value, chip->io_port + ASSP_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u32 assp_get_register(es1968_t *chip, u32 reg) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&chip->reg_lock, flags); + outl(reg, chip->io_port + ASSP_INDEX); + value = inl(chip->io_port + ASSP_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return value; +} + +#endif + +static void wave_set_register(es1968_t *chip, u16 reg, u16 value) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + outw(reg, chip->io_port + WC_INDEX); + outw(value, chip->io_port + WC_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static u16 wave_get_register(es1968_t *chip, u16 reg) +{ + unsigned long flags; + u16 value; + + spin_lock_irqsave(&chip->reg_lock, flags); + outw(reg, chip->io_port + WC_INDEX); + value = inw(chip->io_port + WC_DATA); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return value; +} + +/* ******************* + * Bob the Timer! * + *******************/ + +static void snd_es1968_bob_stop(es1968_t *chip) +{ + u16 reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + reg = __maestro_read(chip, 0x11); + reg &= ~ESM_BOB_ENABLE; + __maestro_write(chip, 0x11, reg); + reg = __maestro_read(chip, 0x17); + reg &= ~ESM_BOB_START; + __maestro_write(chip, 0x17, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_bob_start(es1968_t *chip) +{ + int prescale; + int divide; + unsigned long flags; + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for (prescale = 5; prescale < 12; prescale++) + if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide = 1; + while ((prescale > 5) && (divide < 32)) { + prescale--; + divide <<= 1; + } + divide >>= 1; + + /* now fine-tune the divider for best match */ + for (; divide < 31; divide++) + if (chip->bob_freq > + ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if (divide == 0) { + divide++; + if (prescale > 5) + prescale--; + } else if (divide > 1) + divide--; + + spin_lock_irqsave(&chip->reg_lock, flags); + __maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + __maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1); + __maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_bob_inc(es1968_t *chip, int freq) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->bob_lock, flags); + atomic_inc(&chip->bobclient); + if (atomic_read(&chip->bobclient) == 1) { + chip->bob_freq = freq; + snd_es1968_bob_start(chip); + } else if (chip->bob_freq < freq) { + snd_es1968_bob_stop(chip); + chip->bob_freq = freq; + snd_es1968_bob_start(chip); + } + spin_unlock_irqrestore(&chip->bob_lock, flags); +} + +static void snd_es1968_bob_dec(es1968_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->bob_lock, flags); + atomic_dec(&chip->bobclient); + if (atomic_read(&chip->bobclient) <= 0) + snd_es1968_bob_stop(chip); + else if (chip->bob_freq > ESM_BOB_FREQ) { + /* check reduction of timer frequency */ + struct list_head *p; + int max_freq = ESM_BOB_FREQ; + spin_lock(&chip->substream_lock); + list_for_each(p, &chip->substream_list) { + esschan_t *es = list_entry(p, esschan_t, list); + if (max_freq < es->bob_freq) + max_freq = es->bob_freq; + } + spin_unlock(&chip->substream_lock); + if (max_freq != chip->bob_freq) { + snd_es1968_bob_stop(chip); + chip->bob_freq = max_freq; + snd_es1968_bob_start(chip); + } + } + spin_unlock_irqrestore(&chip->bob_lock, flags); +} + +static int +snd_es1968_calc_bob_rate(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + /* we acquire 4 interrupts per period for precise control.. */ + int freq = runtime->rate * 4; + if (es->fmt & ESS_FMT_STEREO) + freq <<= 1; + if (es->fmt & ESS_FMT_16BIT) + freq <<= 1; + freq /= es->frag_size; + if (freq < ESM_BOB_FREQ) + freq = ESM_BOB_FREQ; + else if (freq > ESM_BOB_FREQ_MAX) + freq = ESM_BOB_FREQ_MAX; + return freq; +} + + +/************* + * PCM Part * + *************/ + +static u32 snd_es1968_compute_rate(es1968_t *chip, u32 freq) +{ + u32 rate = (freq << 16) / chip->clock; +#if 0 /* XXX: do we need this? */ + if (rate > 0x10000) + rate = 0x10000; +#endif + return rate; +} + +/* get current pointer */ +inline static unsigned int +snd_es1968_get_dma_ptr(es1968_t *chip, esschan_t *es) +{ + unsigned int offset; + + offset = apu_get_register(chip, es->apu[0], 5); + + offset -= es->base[0]; + + return (offset & 0xFFFE); /* hardware is in words */ +} + +static void snd_es1968_apu_set_freq(es1968_t *chip, int apu, int freq) +{ + apu_set_register(chip, apu, 2, + (apu_get_register(chip, apu, 2) & 0x00FF) | + ((freq & 0xff) << 8) | 0x10); + apu_set_register(chip, apu, 3, freq >> 8); +} + +/* spin lock held */ +inline static void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode) +{ + /* dma on, no envelopes, filter to all 1s) */ + __apu_set_register(esm, apu, 0, 0x400f | mode); +} + +static void snd_es1968_pcm_start(es1968_t *chip, esschan_t *es) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (es->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + __apu_set_register(chip, es->apu[0], 5, es->base[0]); + snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]); + if (es->mode == ESM_MODE_CAPTURE) { + __apu_set_register(chip, es->apu[2], 5, es->base[2]); + snd_es1968_trigger_apu(chip, es->apu[2], es->apu_mode[2]); + } + if (es->fmt & ESS_FMT_STEREO) { + __apu_set_register(chip, es->apu[1], 5, es->base[1]); + snd_es1968_trigger_apu(chip, es->apu[1], es->apu_mode[1]); + if (es->mode == ESM_MODE_CAPTURE) { + __apu_set_register(chip, es->apu[3], 5, es->base[3]); + snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]); + } + } + es->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_es1968_pcm_stop(es1968_t *chip, esschan_t *es) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (! es->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + snd_es1968_trigger_apu(chip, es->apu[0], 0); + snd_es1968_trigger_apu(chip, es->apu[1], 0); + if (es->mode == ESM_MODE_CAPTURE) { + snd_es1968_trigger_apu(chip, es->apu[2], 0); + snd_es1968_trigger_apu(chip, es->apu[3], 0); + } + es->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* set the wavecache control reg */ +static void snd_es1968_program_wavecache(es1968_t *chip, esschan_t *es, + int channel, u32 addr, int capture) +{ + u32 tmpval = (addr - 0x10) & 0xFFF8; + + if (! capture) { + if (!(es->fmt & ESS_FMT_16BIT)) + tmpval |= 4; /* 8bit */ + if (es->fmt & ESS_FMT_STEREO) + tmpval |= 2; /* stereo */ + } + + /* set the wavecache control reg */ + wave_set_register(chip, es->apu[channel] << 3, tmpval); + +#ifdef CONFIG_PM + es->wc_map[channel] = tmpval; +#endif +} + + +static void snd_es1968_playback_setup(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + u32 pa; + int high_apu = 0; + int channel, apu; + int i, size; + unsigned long flags; + u32 freq; + + size = es->dma_size >> es->wav_shift; + + if (es->fmt & ESS_FMT_STEREO) + high_apu++; + + for (channel = 0; channel <= high_apu; channel++) { + apu = es->apu[channel]; + + snd_es1968_program_wavecache(chip, es, channel, es->memory->addr, 0); + + /* Offset to PCMBAR */ + pa = es->memory->addr; + pa -= chip->dma_buf_addr; + pa >>= 1; /* words */ + + pa |= 0x00400000; /* System RAM (Bit 22) */ + + if (es->fmt & ESS_FMT_STEREO) { + /* Enable stereo */ + if (channel) + pa |= 0x00800000; /* (Bit 23) */ + if (es->fmt & ESS_FMT_16BIT) + pa >>= 1; + } + + /* base offset of dma calcs when reading the pointer + on this left one */ + es->base[channel] = pa & 0xFFFF; + + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + /* Load the buffer into the wave engine */ + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); + apu_set_register(chip, apu, 5, pa & 0xFFFF); + apu_set_register(chip, apu, 6, (pa + size) & 0xFFFF); + /* setting loop == sample len */ + apu_set_register(chip, apu, 7, size); + + /* clear effects/env.. */ + apu_set_register(chip, apu, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(chip, apu, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(chip, apu, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + // apu_set_register(chip, apu, 0, 0x400F); + + if (es->fmt & ESS_FMT_16BIT) + es->apu_mode[channel] = 0x10; /* 16bit mono */ + else + es->apu_mode[channel] = 0x30; /* 8bit mono */ + + if (es->fmt & ESS_FMT_STEREO) { + /* set panning: left or right */ + /* Check: different panning. On my Canyon 3D Chipset the + Channels are swapped. I don't know, about the output + to the SPDif Link. Perhaps you have to change this + and not the APU Regs 4-5. */ + apu_set_register(chip, apu, 10, + 0x8F00 | (channel ? 0 : 0x10)); + es->apu_mode[channel] += 0x10; /* stereo */ + } else + apu_set_register(chip, apu, 10, 0x8F08); + } + + spin_lock_irqsave(&chip->reg_lock, flags); + /* clear WP interupts */ + outw(1, chip->io_port + 0x04); + /* enable WP ints */ + outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + freq = runtime->rate; + /* set frequency */ + if (freq > 48000) + freq = 48000; + if (freq < 4000) + freq = 4000; + + /* hmmm.. */ + if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO)) + freq >>= 1; + + freq = snd_es1968_compute_rate(chip, freq); + + /* Load the frequency, turn on 6dB */ + snd_es1968_apu_set_freq(chip, es->apu[0], freq); + snd_es1968_apu_set_freq(chip, es->apu[1], freq); +} + +static void snd_es1968_capture_setup(es1968_t *chip, esschan_t *es, + snd_pcm_runtime_t *runtime) +{ + int apu_step = 2; + int channel, apu; + int i, size; + u32 freq; + unsigned long flags; + + size = es->dma_size >> es->wav_shift; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if (es->fmt & ESS_FMT_STEREO) + apu_step = 1; + + /* APU assignments: + 0 = mono/left SRC + 1 = right SRC + 2 = mono/left Input Mixer + 3 = right Input Mixer */ + for (channel = 0; channel < 4; channel += apu_step) { + int bsize, route; + u32 pa; + + apu = es->apu[channel]; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if (channel & 2) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if (!(channel & 0x01)) { + pa = es->mixbuf->addr; + } else { + pa = es->mixbuf->addr + ESM_MIXBUF_SIZE / 2; + } + + /* we source from a 'magic' apu */ + bsize = ESM_MIXBUF_SIZE / 4; /* half of this channels alloc, in words */ + /* parallel in crap, see maestro reg 0xC [8-11] */ + route = 0x14 + channel - 2; + es->apu_mode[channel] = 0x90; /* Input Mixer */ + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if (!(channel & 0x01)) + pa = es->memory->addr; + else + pa = es->memory->addr + size * 2; /* size is in word */ + + es->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = es->apu[channel + 2]; + } + + /* set the wavecache control reg */ + snd_es1968_program_wavecache(chip, es, channel, pa, 1); + + /* Offset to PCMBAR */ + pa -= chip->dma_buf_addr; + pa >>= 1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + es->base[channel] = pa & 0xFFFF; + + pa |= 0x00400000; /* bit 22 -> System RAM */ + + /* Begin loading the APU */ + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(chip, apu, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8); + /* XXX reg is little endian.. */ + apu_set_register(chip, apu, 5, pa & 0xFFFF); + apu_set_register(chip, apu, 6, (pa + bsize) & 0xFFFF); + apu_set_register(chip, apu, 7, bsize); +#if 0 + if (es->fmt & ESS_FMT_STEREO) /* ??? really ??? */ + apu_set_register(chip, apu, 7, bsize - 1); +#endif + + /* clear effects/env.. */ + apu_set_register(chip, apu, 8, 0x00F0); + /* amplitude now? sure. why not. */ + apu_set_register(chip, apu, 9, 0x0000); + /* set filter tune, radius, polar pan */ + apu_set_register(chip, apu, 10, 0x8F08); + /* route input */ + apu_set_register(chip, apu, 11, route); + /* dma on, no envelopes, filter to all 1s) */ + // apu_set_register(chip, apu, 0, 0x400F); + } + + spin_lock_irqsave(&chip->reg_lock, flags); + /* clear WP interupts */ + outw(1, chip->io_port + 0x04); + /* enable WP ints */ + outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + freq = runtime->rate; + /* Sample Rate conversion APUs don't like 0x10000 for their rate */ + if (freq > 47999) + freq = 47999; + if (freq < 4000) + freq = 4000; + + freq = snd_es1968_compute_rate(chip, freq); + + /* Load the frequency, turn on 6dB */ + snd_es1968_apu_set_freq(chip, es->apu[0], freq); + snd_es1968_apu_set_freq(chip, es->apu[1], freq); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + snd_es1968_apu_set_freq(chip, es->apu[2], freq); + snd_es1968_apu_set_freq(chip, es->apu[3], freq); +} + +/******************* + * ALSA Interface * + *******************/ + +static int snd_es1968_pcm_prepare(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + + es->dma_size = snd_pcm_lib_buffer_bytes(substream); + es->frag_size = snd_pcm_lib_period_bytes(substream); + + es->wav_shift = 1; /* maestro handles always 16bit */ + es->fmt = 0; + if (snd_pcm_format_width(runtime->format) == 16) + es->fmt |= ESS_FMT_16BIT; + if (runtime->channels > 1) { + es->fmt |= ESS_FMT_STEREO; + if (es->fmt & ESS_FMT_16BIT) /* 8bit is already word shifted */ + es->wav_shift++; + } + es->bob_freq = snd_es1968_calc_bob_rate(chip, es, runtime); + + switch (es->mode) { + case ESM_MODE_PLAY: + snd_es1968_playback_setup(chip, es, runtime); + break; + case ESM_MODE_CAPTURE: + snd_es1968_capture_setup(chip, es, runtime); + break; + } + + return 0; +} + +static int snd_es1968_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (es->running) + return 0; + snd_es1968_bob_inc(chip, es->bob_freq); + es->count = 0; + es->hwptr = 0; + snd_es1968_pcm_start(chip, es); + spin_lock_irqsave(&chip->substream_lock, flags); + list_add(&es->list, &chip->substream_list); + spin_unlock_irqrestore(&chip->substream_lock, flags); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (! es->running) + return 0; + snd_es1968_pcm_stop(chip, es); + spin_lock_irqsave(&chip->substream_lock, flags); + list_del(&es->list); + spin_unlock_irqrestore(&chip->substream_lock, flags); + snd_es1968_bob_dec(chip); + break; + } + return 0; +} + +static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + unsigned int ptr; + + ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; + + return bytes_to_frames(substream->runtime, ptr % es->dma_size); +} + +static snd_pcm_hardware_t snd_es1968_playback = { + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 256, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_es1968_capture = { + info: (SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 65536, + period_bytes_min: 256, + period_bytes_max: 65536, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* ************************* + * DMA memory management * + *************************/ + +/* Because the Maestro can only take adresses relative to the PCM base adress + register :( */ + +static int calc_available_memory_size(es1968_t *chip) +{ + struct list_head *p; + int max_size = 0; + + down(&chip->memory_mutex); + list_for_each(p, &chip->buf_list) { + esm_memory_t *buf = list_entry(p, esm_memory_t, list); + if (buf->empty && buf->size > max_size) + max_size = buf->size; + } + up(&chip->memory_mutex); + if (max_size >= 128*1024) + max_size = 127*1024; + return max_size; +} + +/* allocate a new memory chunk with the specified size */ +static esm_memory_t *snd_es1968_new_memory(es1968_t *chip, int size) +{ + esm_memory_t *buf; + struct list_head *p; + + down(&chip->memory_mutex); + list_for_each(p, &chip->buf_list) { + buf = list_entry(p, esm_memory_t, list); + if (buf->empty && buf->size >= size) + goto __found; + } + up(&chip->memory_mutex); + return NULL; + +__found: + if (buf->size > size) { + esm_memory_t *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) + return NULL; + chunk->size = buf->size - size; + chunk->buf = buf->buf + size; + chunk->addr = buf->addr + size; + chunk->empty = 1; + buf->size = size; + list_add(&chunk->list, &buf->list); + } + buf->empty = 0; + up(&chip->memory_mutex); + return buf; +} + +/* free a memory chunk */ +static void snd_es1968_free_memory(es1968_t *chip, esm_memory_t *buf) +{ + esm_memory_t *chunk; + + down(&chip->memory_mutex); + buf->empty = 1; + if (buf->list.prev != &chip->buf_list) { + chunk = list_entry(buf->list.prev, esm_memory_t, list); + if (chunk->empty) { + chunk->size += buf->size; + list_del(&buf->list); + kfree(buf); + buf = chunk; + } + } + if (buf->list.next != &chip->buf_list) { + chunk = list_entry(buf->list.next, esm_memory_t, list); + if (chunk->empty) { + buf->size += chunk->size; + list_del(&chunk->list); + kfree(chunk); + } + } + up(&chip->memory_mutex); +} + +static void snd_es1968_free_dmabuf(es1968_t *chip) +{ + struct list_head *p; + + if (! chip->dma_buf) + return; + snd_free_pci_pages(chip->pci, chip->dma_buf_size, chip->dma_buf, chip->dma_buf_addr); + while ((p = chip->buf_list.next) != &chip->buf_list) { + esm_memory_t *chunk = list_entry(p, esm_memory_t, list); + list_del(p); + kfree(chunk); + } +} + +static int __devinit +snd_es1968_init_dmabuf(es1968_t *chip) +{ + esm_memory_t *chunk; + chip->dma_buf = snd_malloc_pci_pages_fallback(chip->pci, chip->total_bufsize, + &chip->dma_buf_addr, &chip->dma_buf_size); + //snd_printd("es1968: allocated buffer size %ld at %p\n", chip->dma_buf_size, chip->dma_buf); + if (chip->dma_buf == NULL) { + snd_printk("es1968: can't allocate dma pages for size %d\n", + chip->total_bufsize); + return -ENOMEM; + } + if ((chip->dma_buf_addr + chip->dma_buf_size - 1) & ~((1 << 28) - 1)) { + snd_es1968_free_dmabuf(chip); + snd_printk("es1968: DMA buffer beyond 256MB.\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&chip->buf_list); + /* allocate an empty chunk */ + chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) { + snd_es1968_free_dmabuf(chip); + return -ENOMEM; + } + memset(chip->dma_buf, 0, 512); + chunk->buf = chip->dma_buf + 512; + chunk->addr = chip->dma_buf_addr + 512; + chunk->size = chip->dma_buf_size - 512; + chunk->empty = 1; + list_add(&chunk->list, &chip->buf_list); + + return 0; +} + +/* setup the dma_areas */ +/* buffer is extracted from the pre-allocated memory chunk */ +static int snd_es1968_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + int size = params_buffer_bytes(hw_params); + + if (chan->memory) { + if (chan->memory->size >= size) { + runtime->dma_bytes = size; + return 0; + } + snd_es1968_free_memory(chip, chan->memory); + } + chan->memory = snd_es1968_new_memory(chip, size); + if (chan->memory == NULL) { + // snd_printd("cannot allocate dma buffer: size = %d\n", size); + return -ENOMEM; + } + runtime->dma_bytes = size; + runtime->dma_area = chan->memory->buf; + runtime->dma_addr = chan->memory->addr; + return 1; /* area was changed */ +} + +/* remove dma areas if allocated */ +static int snd_es1968_hw_free(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *chan; + + if (runtime->private_data == NULL) + return 0; + chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + if (chan->memory) { + snd_es1968_free_memory(chip, chan->memory); + chan->memory = NULL; + } + return 0; +} + + +/* + * allocate APU pair + */ +static int snd_es1968_alloc_apu_pair(es1968_t *chip, int type) +{ + int apu; + + for (apu = 0; apu < NR_APUS; apu += 2) { + if (chip->apu[apu] == ESM_APU_FREE && + chip->apu[apu + 1] == ESM_APU_FREE) { + chip->apu[apu] = chip->apu[apu + 1] = type; + return apu; + } + } + return -EBUSY; +} + +/* + * release APU pair + */ +static void snd_es1968_free_apu_pair(es1968_t *chip, int apu) +{ + chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE; +} + + +/****************** + * PCM open/close * + ******************/ + +static int snd_es1968_playback_open(snd_pcm_substream_t *substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es; + int apu1; + + /* search 2 APUs */ + apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY); + if (apu1 < 0) + return apu1; + + es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + if (!es) { + snd_es1968_free_apu_pair(chip, apu1); + return -ENOMEM; + } + + es->apu[0] = apu1; + es->apu[1] = apu1 + 1; + es->apu_mode[0] = 0; + es->apu_mode[1] = 0; + es->running = 0; + es->substream = substream; + es->mode = ESM_MODE_PLAY; + INIT_LIST_HEAD(&es->list); + + runtime->private_data = es; + runtime->hw = snd_es1968_playback; + runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = + calc_available_memory_size(chip); + + return 0; +} + +static int snd_es1968_capture_copy(snd_pcm_substream_t *substream, + int channel, snd_pcm_uframes_t pos, + void *buf, snd_pcm_uframes_t count) +{ + //es1968_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + char *src = runtime->dma_area; + + if (runtime->channels == 1) + return copy_to_user(buf, src + pos, count) ? -EFAULT : 0; + else { + count /= 2; + pos /= 2; + if (copy_to_user(buf, src + pos, count)) + return -EFAULT; + if (copy_to_user(buf + count, src + pos + es->dma_size/2, count)) + return -EFAULT; + return 0; + } +} + +static int snd_es1968_capture_open(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + int apu1, apu2; + + apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE); + if (apu1 < 0) + return apu1; + apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV); + if (apu2 < 0) { + snd_es1968_free_apu_pair(chip, apu1); + return apu2; + } + + es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + if (!es) { + snd_es1968_free_apu_pair(chip, apu1); + snd_es1968_free_apu_pair(chip, apu2); + return -ENOMEM; + } + + es->apu[0] = apu1; + es->apu[1] = apu1 + 1; + es->apu[2] = apu2; + es->apu[3] = apu2 + 1; + es->apu_mode[0] = 0; + es->apu_mode[1] = 0; + es->apu_mode[2] = 0; + es->apu_mode[3] = 0; + es->running = 0; + es->substream = substream; + es->mode = ESM_MODE_CAPTURE; + INIT_LIST_HEAD(&es->list); + + /* get mixbuffer */ + if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) { + snd_es1968_free_apu_pair(chip, apu1); + snd_es1968_free_apu_pair(chip, apu2); + snd_magic_kfree(es); + return -ENOMEM; + } + + runtime->private_data = es; + runtime->hw = snd_es1968_capture; + runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max = + calc_available_memory_size(chip) - 1024; + + return 0; +} + +static int snd_es1968_playback_close(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + if (substream->runtime->private_data == NULL) + return 0; + es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + snd_es1968_free_apu_pair(chip, es->apu[0]); + snd_magic_kfree(es); + + return 0; +} + +static int snd_es1968_capture_close(snd_pcm_substream_t * substream) +{ + es1968_t *chip = snd_pcm_substream_chip(substream); + esschan_t *es; + if (substream->runtime->private_data == NULL) + return 0; + es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + snd_es1968_free_memory(chip, es->mixbuf); + snd_es1968_free_apu_pair(chip, es->apu[0]); + snd_es1968_free_apu_pair(chip, es->apu[2]); + snd_magic_kfree(es); + + return 0; +} + +static snd_pcm_ops_t snd_es1968_playback_ops = { + open: snd_es1968_playback_open, + close: snd_es1968_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es1968_hw_params, + hw_free: snd_es1968_hw_free, + prepare: snd_es1968_pcm_prepare, + trigger: snd_es1968_pcm_trigger, + pointer: snd_es1968_pcm_pointer, +}; + +static snd_pcm_ops_t snd_es1968_capture_ops = { + open: snd_es1968_capture_open, + close: snd_es1968_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_es1968_hw_params, + hw_free: snd_es1968_hw_free, + prepare: snd_es1968_pcm_prepare, + trigger: snd_es1968_pcm_trigger, + pointer: snd_es1968_pcm_pointer, + copy: snd_es1968_capture_copy, +}; + + +/* + * measure clock + */ +#define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */ + +static void __devinit es1968_measure_clock(es1968_t *chip) +{ + int i, apu; + unsigned int pa, offset, t; + esm_memory_t *memory; + unsigned long flags; + struct timeval start_time, stop_time; + + if (chip->clock == 0) + chip->clock = 48000; /* default clock value */ + + /* search 2 APUs (although one apu is enough) */ + if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) { + snd_printk("Hmm, cannot find empty APU pair!?\n"); + return; + } + if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) { + snd_printk("cannot allocate dma buffer - using default clock %d\n", chip->clock); + snd_es1968_free_apu_pair(chip, apu); + return; + } + + memset(memory->buf, 0, CLOCK_MEASURE_BUFSIZE); + + wave_set_register(chip, apu << 3, (memory->addr - 0x10) & 0xfff8); + + pa = (unsigned int)((memory->addr - chip->dma_buf_addr) >> 1); + pa |= 0x00400000; /* System RAM (Bit 22) */ + + /* initialize apu */ + for (i = 0; i < 16; i++) + apu_set_register(chip, apu, i, 0x0000); + + apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8); + apu_set_register(chip, apu, 5, pa & 0xffff); + apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff); + apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2); + apu_set_register(chip, apu, 8, 0x0000); + apu_set_register(chip, apu, 9, 0xD000); + apu_set_register(chip, apu, 10, 0x8F08); + apu_set_register(chip, apu, 11, 0x0000); + spin_lock_irqsave(&chip->reg_lock, flags); + outw(1, chip->io_port + 0x04); /* clear WP interupts */ + outw(inw(chip->io_port + 0x18) | 4, chip->io_port + 0x18); /* enable WP ints */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */ + + spin_lock_irqsave(&chip->reg_lock, flags); + __apu_set_register(chip, apu, 5, pa & 0xffff); + snd_es1968_trigger_apu(chip, apu, 0x10); /* 16bit mono */ + do_gettimeofday(&start_time); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); /* 50 msec */ +#else + /* FIXME: + * schedule() above may be too inaccurate and the pointer can + * overlap the boundary.. + */ + mdelay(50); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + offset = __apu_get_register(chip, apu, 5); + do_gettimeofday(&stop_time); + snd_es1968_trigger_apu(chip, apu, 0); /* stop */ + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* check the current position */ + offset -= (pa & 0xffff); + offset &= 0xfffe; + + t = stop_time.tv_sec - start_time.tv_sec; + t *= 1000000; + if (stop_time.tv_usec < start_time.tv_usec) + t -= start_time.tv_usec - stop_time.tv_usec; + else + t += stop_time.tv_usec - start_time.tv_usec; + if (t == 0) { + snd_printk("?? calculation error..\n"); + } else { + offset *= 1000; + offset = (offset / t) * 1000 + ((offset % t) * 1000) / t; + if (offset < 47500 || offset > 48500) { + if (offset >= 40000 && offset <= 50000) + chip->clock = (chip->clock * offset) / 48000; + } + printk(KERN_INFO "es1968: clocking to %d\n", chip->clock); + } + snd_es1968_free_memory(chip, memory); + snd_es1968_free_apu_pair(chip, apu); +} + + +/* + */ + +static void snd_es1968_pcm_free(snd_pcm_t *pcm) +{ + es1968_t *esm = snd_magic_cast(es1968_t, pcm->private_data, return); + snd_es1968_free_dmabuf(esm); + esm->pcm = NULL; +} + +static int __devinit +snd_es1968_pcm(es1968_t *chip, int device) +{ + snd_pcm_t *pcm; + int err; + + /* get DMA buffer */ + if ((err = snd_es1968_init_dmabuf(chip)) < 0) + return err; + + /* set PCMBAR */ + wave_set_register(chip, 0x01FC, chip->dma_buf_addr >> 12); + wave_set_register(chip, 0x01FD, chip->dma_buf_addr >> 12); + wave_set_register(chip, 0x01FE, chip->dma_buf_addr >> 12); + wave_set_register(chip, 0x01FF, chip->dma_buf_addr >> 12); + + if ((err = snd_pcm_new(chip->card, "ESS Maestro", device, + chip->playback_streams, + chip->capture_streams, &pcm)) < 0) + return err; + + pcm->private_data = chip; + pcm->private_free = snd_es1968_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops); + + pcm->info_flags = 0; + + strcpy(pcm->name, "ESS Maestro"); + + chip->pcm = pcm; + + return 0; +} + +/* + * update pointer + */ +static void snd_es1968_update_pcm(es1968_t *chip, esschan_t *es) +{ + unsigned int hwptr; + unsigned int diff; + snd_pcm_substream_t *subs = es->substream; + + if (subs == NULL || !es->running) + return; + + hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; + hwptr %= es->dma_size; + + diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size; + + es->hwptr = hwptr; + es->count += diff; + + while (es->count > es->frag_size) { + spin_unlock(&chip->substream_lock); + snd_pcm_period_elapsed(subs); + spin_lock(&chip->substream_lock); + es->count -= es->frag_size; + } +} + +/* + */ +static void es1968_update_hw_volume(unsigned long private_data) +{ + es1968_t *chip = snd_magic_cast(es1968_t, (void*)private_data, return); + int x, val; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(chip->io_port + 0x1c); + /* Reset the volume control registers. */ + outb(0x88, chip->io_port + 0x1c); + outb(0x88, chip->io_port + 0x1d); + outb(0x88, chip->io_port + 0x1e); + outb(0x88, chip->io_port + 0x1f); + + if (! chip->master_switch || ! chip->master_volume) + return; + + /* FIXME: more clean up is needed.. */ + val = chip->ac97->regs[AC97_MASTER]; + if (x & 1) { + /* mute */ + snd_ac97_write_cache(chip->ac97, 0, val ^ 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_switch->id); + } else { + val &= 0x7fff; + if (((x>>1) & 7) > 4) { + /* volume up */ + if ((val & 0xff) > 0) + val--; + if ((val & 0xff00) > 0x100) + val -= 0x0100; + } else { + /* volume down */ + if ((val & 0xff) < 0x1f) + val++; + if ((val & 0xff00) < 0x1f00) + val += 0x0100; + } + snd_ac97_write_cache(chip->ac97, 0, val); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->master_volume->id); + } +} + +/* + * interrupt handler + */ +static void snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + es1968_t *chip = snd_magic_cast(es1968_t, dev_id, return); + u32 event; + + if (!(event = inb(chip->io_port + 0x1A))) + return; + + outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); + + if (event & ESM_HWVOL_IRQ) + tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */ + + /* else ack 'em all, i imagine */ + outb(0xFF, chip->io_port + 0x1A); + + if ((event & ESM_MPU401_IRQ) && chip->rmidi) { + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } + + if (event & ESM_SOUND_IRQ) { + struct list_head *p, *n; + spin_lock(&chip->substream_lock); + /* we need to use list_for_each_safe here since the substream + * can be deleted in period_elapsed(). + */ + list_for_each_safe(p, n, &chip->substream_list) { + esschan_t *es = list_entry(p, esschan_t, list); + snd_es1968_update_pcm(chip, es); + } + spin_unlock(&chip->substream_lock); + } +} + +/* + * Mixer stuff + */ + +static int __devinit +snd_es1968_mixer(es1968_t *chip) +{ + ac97_t ac97; + snd_ctl_elem_id_t id; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_es1968_ac97_write; + ac97.read = snd_es1968_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + /* attach master switch / volumes for h/w volume control */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Switch"); + chip->master_switch = snd_ctl_find_id(chip->card, &id); + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Master Playback Volume"); + chip->master_volume = snd_ctl_find_id(chip->card, &id); + + return 0; +} + +/* + * reset ac97 codec + */ + +static void snd_es1968_ac97_reset(es1968_t *chip) +{ + unsigned long ioaddr = chip->io_port; + + unsigned short save_ringbus_a; + unsigned short save_68; + unsigned short w; + unsigned int vend; + + /* save configuration */ + save_ringbus_a = inw(ioaddr + 0x36); + + //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */ + /* set command/status address i/o to 1st codec */ + outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* disable ac link */ + outw(0x0000, ioaddr + 0x36); + save_68 = inw(ioaddr + 0x68); + pci_read_config_word(chip->pci, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if (w & 1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* unmask gpio 0 */ + outw(0x0001, ioaddr + 0x68); /* gpio write */ + outw(0x0000, ioaddr + 0x60); /* write 0 to gpio 0 */ + udelay(20); + outw(0x0001, ioaddr + 0x60); /* write 1 to gpio 1 */ + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38); + outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a); + outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c); + + /* now the second codec */ + /* disable ac link */ + outw(0x0000, ioaddr + 0x36); + outw(0xfff7, ioaddr + 0x64); /* unmask gpio 3 */ + save_68 = inw(ioaddr + 0x68); + outw(0x0009, ioaddr + 0x68); /* gpio write 0 & 3 ?? */ + outw(0x0001, ioaddr + 0x60); /* write 1 to gpio */ + udelay(20); + outw(0x0009, ioaddr + 0x60); /* write 9 to gpio */ + mdelay(500); /* .. ouch.. */ + //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + snd_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80 | 0x7c, ioaddr + 0x30); + for (w = 0;; w++) { + if ((inw(ioaddr + 0x30) & 1) == 0) { + if (inb(ioaddr + 0x32) != 0) + break; + + outb(0x80 | 0x7d, ioaddr + 0x30); + if (((inw(ioaddr + 0x30) & 1) == 0) + && (inb(ioaddr + 0x32) != 0)) + break; + outb(0x80 | 0x7f, ioaddr + 0x30); + if (((inw(ioaddr + 0x30) & 1) == 0) + && (inb(ioaddr + 0x32) != 0)) + break; + } + + if (w > 10000) { + outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb(inb(ioaddr + 0x37) & ~0x08, + ioaddr + 0x37); + udelay(1); + outw(0x80, ioaddr + 0x30); + for (w = 0; w < 10000; w++) { + if ((inw(ioaddr + 0x30) & 1) == 0) + break; + } + } + } +#endif + if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } + + /* restore.. */ + outw(save_ringbus_a, ioaddr + 0x36); + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); +} + +static void snd_es1968_reset(es1968_t *chip) +{ + /* Reset */ + outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND, + chip->io_port + ESM_PORT_HOST_IRQ); + udelay(10); + outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ); + udelay(10); +} + +/* + * power management + */ +static void snd_es1968_set_acpi(es1968_t *chip, int state) +{ + u16 active_mask = acpi_state_mask[state]; + + pci_set_power_state(chip->pci, state); + /* make sure the units we care about are on + XXX we might want to do this before state flipping? */ + pci_write_config_word(chip->pci, 0x54, ~ active_mask); + pci_write_config_word(chip->pci, 0x56, ~ active_mask); +} + + +/* + * initialize maestro chip + */ +static void snd_es1968_chip_init(es1968_t *chip) +{ + struct pci_dev *pci = chip->pci; + int i; + unsigned long iobase = chip->io_port; + u16 w; + u32 n; + + /* We used to muck around with pci config space that + * we had no business messing with. We don't know enough + * about the machine to know which DMA mode is appropriate, + * etc. We were guessing wrong on some machines and making + * them unhappy. We now trust in the BIOS to do things right, + * which almost certainly means a new host of problems will + * arise with broken BIOS implementations. screw 'em. + * We're already intolerant of machines that don't assign + * IRQs. + */ + + /* do config work at full power */ + snd_es1968_set_acpi(chip, ACPI_D0); + + /* Config Reg A */ + pci_read_config_word(pci, ESM_CONFIG_A, &w); + + /* Use TDMA for now. TDMA works on all boards, so while its + * not the most efficient its the simplest. */ + w &= ~DMA_CLEAR; /* Clear DMA bits */ + w |= DMA_TDMA; /* TDMA on */ + w &= ~(PIC_SNOOP1 | PIC_SNOOP2); /* Clear Pic Snoop Mode Bits */ + w &= ~SAFEGUARD; /* Safeguard off */ + w |= POST_WRITE; /* Posted write */ + w |= ISA_TIMING; /* ISA timing on */ + /* XXX huh? claims to be reserved.. */ + w &= ~SWAP_LR; /* swap left/right + seems to only have effect on SB + Emulation */ + w &= ~SUBTR_DECODE; /* Subtractive decode off */ + + pci_write_config_word(pci, ESM_CONFIG_A, w); + + /* Config Reg B */ + + pci_read_config_word(pci, ESM_CONFIG_B, &w); + + w &= ~(1 << 15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w &= ~(1 << 14); /* External clock */ + + w &= ~SPDIF_CONFB; /* disable S/PDIF output */ + w |= HWV_CONFB; /* HWV on */ + w |= DEBOUNCE; /* Debounce off: easier to push the HW buttons */ + w &= ~GPIO_CONFB; /* GPIO 4:5 */ + w |= CHI_CONFB; /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ + w &= ~IDMA_CONFB; /* IDMA off (undocumented) */ + w &= ~MIDI_FIX; /* MIDI fix off (undoc) */ + w &= ~(1 << 1); /* reserved, always write 0 */ + w &= ~IRQ_TO_ISA; /* IRQ to ISA off (undoc) */ + + pci_write_config_word(pci, ESM_CONFIG_B, w); + + /* DDMA off */ + + pci_read_config_word(pci, ESM_DDMA, &w); + w &= ~(1 << 0); + pci_write_config_word(pci, ESM_DDMA, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w); + + w &= ~ESS_ENABLE_AUDIO; /* Disable Legacy Audio */ + w &= ~ESS_ENABLE_SERIAL_IRQ; /* Disable SIRQ */ + w &= ~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w); + + /* Set up 978 docking control chip. */ + pci_read_config_word(pci, 0x58, &w); + w|=1<<2; /* Enable 978. */ + w|=1<<3; /* Turn on 978 hardware volume control. */ + w&=~(1<<11); /* Turn on 978 mixer volume control. */ + pci_write_config_word(pci, 0x58, w); + + /* Sound Reset */ + + snd_es1968_reset(chip); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */ + udelay(20); + + /* + * Reset the CODEC + */ + + snd_es1968_ac97_reset(chip); + + /* Ring Bus Control B */ + + n = inl(iobase + ESM_RING_BUS_CONTR_B); + n &= ~RINGB_EN_SPDIF; /* SPDIF off */ + //w |= RINGB_EN_2CODEC; /* enable 2nd codec */ + outl(n, iobase + ESM_RING_BUS_CONTR_B); + + /* Set hardware volume control registers to midpoints. + We can tell which button was pushed based on how they change. */ + outb(0x88, iobase+0x1c); + outb(0x88, iobase+0x1d); + outb(0x88, iobase+0x1e); + outb(0x88, iobase+0x1f); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of wether we use the assp or not. */ + + outb(0, iobase + ASSP_CONTROL_B); + outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */ + outb(0, iobase + ASSP_CONTROL_C); /* M: Disable ASSP, ASSP IRQ's and FM Port */ + + /* Enable IRQ's */ + w = ESM_HIRQ_DSIE | ESM_HIRQ_MPU401; + outw(w, iobase + ESM_PORT_HOST_IRQ); + + /* + * set up wavecache + */ + for (i = 0; i < 16; i++) { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0 + i, iobase + WC_INDEX); + outw(0x0000, iobase + WC_DATA); + + /* The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too.*/ + outw(0x01D0 + i, iobase + WC_INDEX); + outw(0x0000, iobase + WC_DATA); + } + wave_set_register(chip, IDR7_WAVE_ROMRAM, + (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00)); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200); + wave_set_register(chip, IDR7_WAVE_ROMRAM, + wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400); + + + maestro_write(chip, IDR2_CRAM_DATA, 0x0000); + /* Now back to the DirectSound stuff */ + /* audio serial configuration.. ? */ + maestro_write(chip, 0x08, 0xB004); + maestro_write(chip, 0x09, 0x001B); + maestro_write(chip, 0x0A, 0x8000); + maestro_write(chip, 0x0B, 0x3F37); + maestro_write(chip, 0x0C, 0x0098); + + /* parallel in, has something to do with recording :) */ + maestro_write(chip, 0x0C, + (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000); + /* parallel out */ + maestro_write(chip, 0x0C, + (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500); + + maestro_write(chip, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + w = inw(iobase + WC_CONTROL); + + w &= ~0xFA00; /* Seems to be reserved? I don't know */ + w |= 0xA000; /* reserved... I don't know */ + w &= ~0x0200; /* Channels 56,57,58,59 as Extra Play,Rec Channel enable + Seems to crash the Computer if enabled... */ + w |= 0x0100; /* Wave Cache Operation Enabled */ + w |= 0x0080; /* Channels 60/61 as Placback/Record enabled */ + w &= ~0x0060; /* Clear Wavtable Size */ + w |= 0x0020; /* Wavetable Size : 1MB */ + /* Bit 4 is reserved */ + w &= ~0x000C; /* DMA Stuff? I don't understand what the datasheet means */ + /* Bit 1 is reserved */ + w &= ~0x0001; /* Test Mode off */ + + outw(w, iobase + WC_CONTROL); + + /* Now clear the APU control ram */ + for (i = 0; i < NR_APUS; i++) { + for (w = 0; w < NR_APU_REGS; w++) + apu_set_register(chip, i, w, 0); + + } +} + +#ifdef CONFIG_PM +/* + * PM support + */ +static void es1968_suspend(es1968_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + snd_es1968_bob_stop(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void es1968_resume(es1968_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* restore all our config */ + pci_enable_device(chip->pci); + snd_es1968_chip_init(chip); + + /* need to restore the base pointers.. */ + if (chip->dma_buf_addr) { + /* set PCMBAR */ + wave_set_register(chip, 0x01FC, chip->dma_buf_addr >> 12); + } + + /* restore ac97 state */ + snd_ac97_resume(chip->ac97); + + /* start timer again */ + if (atomic_read(&chip->bobclient)) + snd_es1968_bob_start(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_es1968_suspend(struct pci_dev *dev, u32 state) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO); + es1968_suspend(chip); + return 0; +} +static int snd_es1968_resume(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return -ENXIO); + es1968_resume(chip); + return 0; +} +#else +static void snd_es1968_suspend(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return); + es1968_suspend(chip); +} +static void snd_es1968_resume(struct pci_dev *dev) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(dev), return); + es1968_resume(chip); +} +#endif + +/* callback */ +static int snd_es1968_set_power_state(snd_card_t *card, unsigned int power_state) +{ + es1968_t *chip = snd_magic_cast(es1968_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + es1968_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + es1968_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_es1968_free(es1968_t *chip) +{ + snd_es1968_set_acpi(chip, ACPI_D3); + chip->master_switch = NULL; + chip->master_volume = NULL; + if (chip->res_io_port) { + release_resource(chip->res_io_port); + kfree_nocheck(chip->res_io_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_es1968_dev_free(snd_device_t *device) +{ + es1968_t *chip = snd_magic_cast(es1968_t, device->device_data, return -ENXIO); + return snd_es1968_free(chip); +} + +static int __devinit snd_es1968_create(snd_card_t * card, + struct pci_dev *pci, + int total_bufsize, + int play_streams, + int capt_streams, + es1968_t **chip_ret) +{ + static snd_device_ops_t ops = { + dev_free: snd_es1968_dev_free, + }; + es1968_t *chip; + int i, err; + + *chip_ret = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = (es1968_t *) snd_magic_kcalloc(es1968_t, 0, GFP_KERNEL); + if (! chip) + return -ENOMEM; + + /* Set Vars */ + chip->type = (pci->vendor << 16) | pci->device; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->substream_lock); + spin_lock_init(&chip->bob_lock); + INIT_LIST_HEAD(&chip->buf_list); + INIT_LIST_HEAD(&chip->substream_list); + init_MUTEX(&chip->memory_mutex); + tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->total_bufsize = total_bufsize; /* in bytes */ + chip->playback_streams = play_streams; + chip->capture_streams = capt_streams; + + chip->io_port = pci_resource_start(pci, 0); + if ((chip->res_io_port = request_region(chip->io_port, 0x100, "ESS Maestro")) == NULL) { + snd_es1968_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 0x100 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ, + "ESS Maestro", (void*)chip)) { + snd_es1968_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + /* Clear Maestro_map */ + for (i = 0; i < 32; i++) + chip->maestro_map[i] = 0; + + /* Clear Apu Map */ + for (i = 0; i < NR_APUS; i++) + chip->apu[i] = ESM_APU_FREE; + + atomic_set(&chip->bobclient, 0); + + /* just to be sure */ + pci_set_master(pci); + + snd_es1968_chip_init(chip); + +#ifdef CONFIG_PM + card->set_power_state = snd_es1968_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_es1968_free(chip); + return err; + } + + *chip_ret = chip; + + return 0; +} + + +/* + * joystick + */ + +static int snd_es1968_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_es1968_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + es1968_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &val); + ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0; + return 0; +} + +static int snd_es1968_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + es1968_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &oval); + val = oval & ~0x04; + if (ucontrol->value.integer.value[0]) + val |= 0x04; + if (val != oval); { + pci_write_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, val); + return 1; + } + return 0; +} + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_es1968_control_switches[] __devinitdata = { + { + name: "Joystick", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_es1968_joystick_info, + get: snd_es1968_joystick_get, + put: snd_es1968_joystick_put, + } +}; + +/* + */ +static int __devinit snd_es1968_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + es1968_t *chip; + int i, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (!card) + return -ENOMEM; + + if (snd_total_bufsize[dev] < 128) + snd_total_bufsize[dev] = 128; + if (snd_total_bufsize[dev] > 4096) + snd_total_bufsize[dev] = 4096; + if ((err = snd_es1968_create(card, pci, + snd_total_bufsize[dev] * 1024, /* in bytes */ + snd_pcm_substreams_p[dev], + snd_pcm_substreams_c[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + switch (chip->type) { + case CARD_TYPE_ESS_ES1978: + strcpy(card->driver, "ES1978"); + strcpy(card->shortname, "ESS ES1978 (Maestro 2E)"); + break; + case CARD_TYPE_ESS_ES1968: + strcpy(card->driver, "ES1968"); + strcpy(card->shortname, "ESS ES1968 (Maestro 2)"); + break; + case CARD_TYPE_ESS_ESOLDM1: + strcpy(card->driver, "ESM1"); + strcpy(card->shortname, "ESS Maestro 1"); + break; + } + + if ((err = snd_es1968_pcm(chip, 0)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_es1968_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + chip->io_port + ESM_MPU401_PORT, 1, + chip->irq, 0, &chip->rmidi)) < 0) { + printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n"); + } + + /* card switches */ + for (i = 0; i < num_controls(snd_es1968_control_switches); i++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_es1968_control_switches[i], chip)); + if (err < 0) { + snd_card_free(card); + return err; + } + } + + chip->clock = snd_clock[dev]; + if (! chip->clock) + es1968_measure_clock(chip); + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->io_port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_es1968_remove(struct pci_dev *pci) +{ + es1968_t *chip = snd_magic_cast(es1968_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ES1968 (ESS Maestro)", + id_table: snd_es1968_ids, + probe: snd_es1968_probe, + remove: __devexit_p(snd_es1968_remove), +#ifdef CONFIG_PM + suspend: snd_es1968_suspend, + resume: snd_es1968_resume, +#endif +}; + +#if 0 // do we really need this? +static int snd_es1968_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + pci_unregister_driver(&driver); + return NOTIFY_OK; +} + +static struct notifier_block snd_es1968_nb = {snd_es1968_notifier, NULL, 0}; +#endif + +static int __init alsa_card_es1968_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ESS Maestro soundcard not found or device busy\n"); +#endif + return err; + } +#if 0 // do we really need this? + /* If this driver is not shutdown cleanly at reboot, it can + leave the speaking emitting an annoying noise, so we catch + shutdown events. */ + if (register_reboot_notifier(&snd_es1968_nb)) { + printk(KERN_ERR "reboot notifier registration failed; may make noise at shutdown.\n"); + } +#endif + return 0; +} + +static void __exit alsa_card_es1968_exit(void) +{ +#if 0 // do we really need this? + unregister_reboot_notifier(&snd_es1968_nb); +#endif + pci_unregister_driver(&driver); +} + +module_init(alsa_card_es1968_init) +module_exit(alsa_card_es1968_exit) + +#ifndef MODULE + +/* format is: snd-es1968=snd_enable,snd_index,snd_id, + snd_total_bufsize, + snd_pcm_substreams_p, + snd_pcm_substreams_c, + snd_clock +*/ + +static int __init alsa_card_es1968_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_total_bufsize[nr_dev]) == 2 && + get_option(&str,&snd_pcm_substreams_p[nr_dev]) == 2 && + get_option(&str,&snd_pcm_substreams_c[nr_dev]) == 2 && + get_option(&str,&snd_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-es1968=", alsa_card_es1968_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/fm801.c linux-2.4.19-pre5-mjc/sound/pci/fm801.c --- linux/sound/pci/fm801.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/fm801.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1141 @@ +/* + * The driver for the ForteMedia FM801 based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define chip_t fm801_t + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ForteMedia FM801"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ForteMedia,FM801}," + "{Genius,SoundMaker Live 5.1}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the FM801 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the FM801 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable FM801 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +/* + * Direct registers + */ + +#define FM801_REG(chip, reg) (chip->port + FM801_##reg) + +#define FM801_PCM_VOL 0x00 /* PCM Output Volume */ +#define FM801_FM_VOL 0x02 /* FM Output Volume */ +#define FM801_I2S_VOL 0x04 /* I2S Volume */ +#define FM801_REC_SRC 0x06 /* Record Source */ +#define FM801_PLY_CTRL 0x08 /* Playback Control */ +#define FM801_PLY_COUNT 0x0a /* Playback Count */ +#define FM801_PLY_BUF1 0x0c /* Playback Bufer I */ +#define FM801_PLY_BUF2 0x10 /* Playback Buffer II */ +#define FM801_CAP_CTRL 0x14 /* Capture Control */ +#define FM801_CAP_COUNT 0x16 /* Capture Count */ +#define FM801_CAP_BUF1 0x18 /* Capture Buffer I */ +#define FM801_CAP_BUF2 0x1c /* Capture Buffer II */ +#define FM801_CODEC_CTRL 0x22 /* Codec Control */ +#define FM801_I2S_MODE 0x24 /* I2S Mode Control */ +#define FM801_VOLUME 0x26 /* Volume Up/Down/Mute Status */ +#define FM801_I2C_CTRL 0x29 /* I2C Control */ +#define FM801_AC97_CMD 0x2a /* AC'97 Command */ +#define FM801_AC97_DATA 0x2c /* AC'97 Data */ +#define FM801_MPU401_DATA 0x30 /* MPU401 Data */ +#define FM801_MPU401_CMD 0x31 /* MPU401 Command */ +#define FM801_GPIO_CTRL 0x52 /* General Purpose I/O Control */ +#define FM801_GEN_CTRL 0x54 /* General Control */ +#define FM801_IRQ_MASK 0x56 /* Interrupt Mask */ +#define FM801_IRQ_STATUS 0x5a /* Interrupt Status */ +#define FM801_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ +#define FM801_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ +#define FM801_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ +#define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ +#define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */ + +#define FM801_AC97_ADDR_SHIFT 10 + +/* playback and record control register bits */ +#define FM801_BUF1_LAST (1<<1) +#define FM801_BUF2_LAST (1<<2) +#define FM801_START (1<<5) +#define FM801_PAUSE (1<<6) +#define FM801_IMMED_STOP (1<<7) +#define FM801_RATE_SHIFT 8 +#define FM801_RATE_MASK (15 << FM801_RATE_SHIFT) +#define FM801_CHANNELS_4 (1<<12) /* playback only */ +#define FM801_CHANNELS_6 (2<<12) /* playback only */ +#define FM801_CHANNELS_6MS (3<<12) /* playback only */ +#define FM801_CHANNELS_MASK (3<<12) +#define FM801_16BIT (1<<14) +#define FM801_STEREO (1<<15) + +/* IRQ status bits */ +#define FM801_IRQ_PLAYBACK (1<<8) +#define FM801_IRQ_CAPTURE (1<<9) +#define FM801_IRQ_VOLUME (1<<14) +#define FM801_IRQ_MPU (1<<15) + +/* + + */ + +typedef struct _snd_fm801 fm801_t; + +struct _snd_fm801 { + int irq; + + unsigned long port; /* I/O port number */ + struct resource *res_port; + unsigned int multichannel: 1, /* multichannel support */ + secondary: 1; /* secondary codec */ + unsigned char secondary_addr; /* addres of the secondary codec */ + + unsigned short ply_ctrl; /* playback control */ + unsigned short cap_ctrl; /* capture control */ + + unsigned long ply_buffer; + unsigned int ply_buf; + unsigned int ply_count; + unsigned int ply_size; + unsigned int ply_pos; + + unsigned long cap_buffer; + unsigned int cap_buf; + unsigned int cap_count; + unsigned int cap_size; + unsigned int cap_pos; + + ac97_t *ac97; + ac97_t *ac97_sec; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_rawmidi_t *rmidi; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + unsigned int p_dma_size; + unsigned int c_dma_size; + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; +}; + +static struct pci_device_id snd_fm801_ids[] __devinitdata = { + { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* FM801 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_fm801_ids); + +/* + * common I/O routines + */ + +static int snd_fm801_update_bits(fm801_t *chip, unsigned short reg, + unsigned short mask, unsigned short value) +{ + int change; + unsigned short old, new; + + spin_lock(&chip->reg_lock); + old = inw(chip->port + reg); + new = (old & ~mask) | value; + change = old != new; + if (change) + outw(new, chip->port + reg); + spin_unlock(&chip->reg_lock); + return change; +} + +static void snd_fm801_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + int idx; + + /* + * Wait until the codec interface is not ready.. + */ + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok1; + udelay(10); + } + snd_printk("AC'97 interface is busy (1)\n"); + return; + + ok1: + /* write data and address */ + outw(val, FM801_REG(chip, AC97_DATA)); + outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + /* + * Wait until the write command is not completed.. + */ + for (idx = 0; idx < 1000; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + return; + udelay(10); + } + snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num); +} + +static unsigned short snd_fm801_codec_read(ac97_t *ac97, unsigned short reg) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return -ENXIO); + int idx; + + /* + * Wait until the codec interface is not ready.. + */ + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok1; + udelay(10); + } + snd_printk("AC'97 interface is busy (1)\n"); + return 0; + + ok1: + /* read command */ + outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD)); + for (idx = 0; idx < 100; idx++) { + if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) + goto ok2; + udelay(10); + } + snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num); + return 0; + + ok2: + for (idx = 0; idx < 1000; idx++) { + if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8)) + goto ok3; + udelay(10); + } + snd_printk("AC'97 interface #%d is not valid (2)\n", ac97->num); + return 0; + + ok3: + return inw(FM801_REG(chip, AC97_DATA)); +} + +static unsigned int rates[] = { + 5500, 8000, 9600, 11025, + 16000, 19200, 22050, 32000, + 38400, 44100, 48000 +}; + +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: RATES, + list: rates, + mask: 0, +}; + +static unsigned int channels[] = { + 2, 4, 6 +}; + +#define CHANNELS sizeof(channels) / sizeof(channels[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels = { + count: CHANNELS, + list: channels, + mask: 0, +}; + +/* + * Sample rate routines + */ + +static unsigned short snd_fm801_rate_bits(int rate) +{ + unsigned int idx; + + for (idx = 0; idx < 11; idx++) + if (rates[idx] == rate) + return idx; + snd_BUG(); + return RATES - 1; +} + +/* + * PCM part + */ + +static int snd_fm801_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->ply_ctrl &= ~(FM801_BUF1_LAST | + FM801_BUF2_LAST | + FM801_PAUSE); + chip->ply_ctrl |= FM801_START | + FM801_IMMED_STOP; + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + chip->ply_ctrl &= ~FM801_START; + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + } else { + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_fm801_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock(&chip->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + chip->cap_ctrl &= ~(FM801_BUF1_LAST | + FM801_BUF2_LAST | + FM801_PAUSE); + chip->cap_ctrl |= FM801_START | + FM801_IMMED_STOP; + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + chip->cap_ctrl &= ~FM801_START; + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + } else { + result = -EINVAL; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_fm801_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_fm801_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_fm801_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->ply_size = snd_pcm_lib_buffer_bytes(substream); + chip->ply_count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->ply_ctrl &= ~(FM801_START | FM801_16BIT | + FM801_STEREO | FM801_RATE_MASK | + FM801_CHANNELS_MASK); + if (snd_pcm_format_width(runtime->format) == 16) + chip->ply_ctrl |= FM801_16BIT; + if (runtime->channels > 1) { + chip->ply_ctrl |= FM801_STEREO; + if (runtime->channels == 4) + chip->ply_ctrl |= FM801_CHANNELS_4; + else if (runtime->channels == 6) + chip->ply_ctrl |= FM801_CHANNELS_6; + } + chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; + chip->ply_buf = 0; + outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL)); + outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT)); + chip->ply_buffer = runtime->dma_addr; + chip->ply_pos = 0; + outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1)); + outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_fm801_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->cap_size = snd_pcm_lib_buffer_bytes(substream); + chip->cap_count = snd_pcm_lib_period_bytes(substream); + spin_lock_irqsave(&chip->reg_lock, flags); + chip->cap_ctrl &= ~(FM801_START | FM801_16BIT | + FM801_STEREO | FM801_RATE_MASK); + if (snd_pcm_format_width(runtime->format) == 16) + chip->cap_ctrl |= FM801_16BIT; + if (runtime->channels > 1) + chip->cap_ctrl |= FM801_STEREO; + chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; + chip->cap_buf = 0; + outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL)); + outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT)); + chip->cap_buffer = runtime->dma_addr; + chip->cap_pos = 0; + outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1)); + outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_fm801_playback_pointer(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + size_t ptr; + + if (!(chip->ply_ctrl & FM801_START)) + return 0; + spin_lock_irqsave(&chip->reg_lock, flags); + ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT)); + if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) { + ptr += chip->ply_count; + ptr %= chip->ply_size; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_fm801_capture_pointer(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + size_t ptr; + + if (!(chip->cap_ctrl & FM801_START)) + return 0; + spin_lock_irqsave(&chip->reg_lock, flags); + ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT)); + if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) { + ptr += chip->cap_count; + ptr %= chip->cap_size; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return bytes_to_frames(substream->runtime, ptr); +} + +static void snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + fm801_t *chip = snd_magic_cast(fm801_t, dev_id, return); + unsigned short status; + unsigned int tmp; + + status = inw(FM801_REG(chip, IRQ_STATUS)); + if ((status & (FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME)) == 0) + return; + if (chip->pcm && (status & FM801_IRQ_PLAYBACK)) { + spin_lock(&chip->reg_lock); + chip->ply_buf++; + chip->ply_pos += chip->ply_count; + chip->ply_pos %= chip->ply_size; + tmp = chip->ply_pos + chip->ply_count; + tmp %= chip->ply_size; + outl(chip->ply_buffer + tmp, + (chip->ply_buf & 1) ? + FM801_REG(chip, PLY_BUF1) : + FM801_REG(chip, PLY_BUF2)); + outw(FM801_IRQ_PLAYBACK, FM801_REG(chip, IRQ_STATUS)); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->playback_substream); + } + if (chip->pcm && (status & FM801_IRQ_CAPTURE)) { + spin_lock(&chip->reg_lock); + chip->cap_buf++; + chip->cap_pos += chip->cap_count; + chip->cap_pos %= chip->cap_size; + tmp = chip->cap_pos + chip->cap_count; + tmp %= chip->cap_size; + outl(chip->cap_buffer + tmp, + (chip->cap_buf & 1) ? + FM801_REG(chip, CAP_BUF1) : + FM801_REG(chip, CAP_BUF2)); + outw(FM801_IRQ_CAPTURE, FM801_REG(chip, IRQ_STATUS)); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->capture_substream); + } + if ((status & FM801_IRQ_MPU) && chip->rmidi != NULL) { + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + outw(FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); + } + if (status & FM801_IRQ_VOLUME) { + /* TODO */ + outw(FM801_IRQ_VOLUME, FM801_REG(chip, IRQ_STATUS)); + } +} + +static snd_pcm_hardware_t snd_fm801_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_fm801_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, + rate_min: 5500, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_fm801_playback_open(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback_substream = substream; + runtime->hw = snd_fm801_playback; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (chip->multichannel) { + runtime->hw.channels_max = 6; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_fm801_capture_open(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture_substream = substream; + runtime->hw = snd_fm801_capture; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_fm801_playback_close(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + chip->playback_substream = NULL; + return 0; +} + +static int snd_fm801_capture_close(snd_pcm_substream_t * substream) +{ + fm801_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_fm801_playback_ops = { + open: snd_fm801_playback_open, + close: snd_fm801_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_fm801_hw_params, + hw_free: snd_fm801_hw_free, + prepare: snd_fm801_playback_prepare, + trigger: snd_fm801_playback_trigger, + pointer: snd_fm801_playback_pointer, +}; + +static snd_pcm_ops_t snd_fm801_capture_ops = { + open: snd_fm801_capture_open, + close: snd_fm801_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_fm801_hw_params, + hw_free: snd_fm801_hw_free, + prepare: snd_fm801_capture_prepare, + trigger: snd_fm801_capture_trigger, + pointer: snd_fm801_capture_pointer, +}; + +static void snd_fm801_pcm_free(snd_pcm_t *pcm) +{ + fm801_t *chip = snd_magic_cast(fm801_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_fm801_pcm(fm801_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_fm801_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "FM801"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer routines + */ + +#define FM801_SINGLE(xname, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_single, \ + get: snd_fm801_get_single, put: snd_fm801_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_fm801_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_fm801_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + return snd_fm801_update_bits(chip, reg, mask << shift, val << shift); +} + +#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_double, \ + get: snd_fm801_get_double, put: snd_fm801_put_double, \ + private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } + +static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_fm801_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock(&chip->reg_lock); + ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask; + ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask; + spin_unlock(&chip->reg_lock); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_fm801_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift_left = (kcontrol->private_value >> 8) & 0x0f; + int shift_right = (kcontrol->private_value >> 12) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + return snd_fm801_update_bits(chip, reg, + (mask << shift_left) | (mask << shift_right), + (val1 << shift_left ) | (val2 << shift_right)); +} + +static int snd_fm801_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[5] = { + "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + if (uinfo->value.enumerated.item > 4) + uinfo->value.enumerated.item = 4; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_fm801_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short val; + + val = inw(FM801_REG(chip, REC_SRC)) & 7; + if (val > 4) + val = 4; + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int snd_fm801_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + fm801_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short val; + + if ((val = ucontrol->value.enumerated.item[0]) > 4) + return -EINVAL; + return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); +} + +#define FM801_CONTROLS (sizeof(snd_fm801_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_fm801_controls[] __devinitdata = { +FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1), +FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1), +FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1), +FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1), +FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1), +FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Digital Capture Source", + info: snd_fm801_info_mux, + get: snd_fm801_get_mux, + put: snd_fm801_put_mux, +} +}; + +#define FM801_CONTROLS_MULTI (sizeof(snd_fm801_controls_multi)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = { +FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0), +FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0), +FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0), +FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0), +FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0), +}; + +static void snd_fm801_mixer_free_ac97(ac97_t *ac97) +{ + fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + if (ac97->num == 0) { + chip->ac97 = NULL; + } else { + chip->ac97_sec = NULL; + } +} + +static int __init snd_fm801_mixer(fm801_t *chip) +{ + ac97_t ac97; + int err, i; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_fm801_codec_write; + ac97.read = snd_fm801_codec_read; + ac97.private_data = chip; + ac97.private_free = snd_fm801_mixer_free_ac97; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + if (chip->secondary) { + ac97.num = 1; + ac97.addr = chip->secondary_addr; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97_sec)) < 0) + return err; + } + for (i = 0; i < FM801_CONTROLS; i++) + snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip)); + if (chip->multichannel) { + for (i = 0; i < FM801_CONTROLS_MULTI; i++) + snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip)); + } + return 0; +} + +/* + * initialization routines + */ + +static int snd_fm801_free(fm801_t *chip) +{ + unsigned short cmdw; + + if (chip->irq < 0) + goto __end_hw; + + /* interrupt setup - mask everything */ + cmdw = inw(FM801_REG(chip, IRQ_MASK)); + cmdw |= 0x00c3; + outw(cmdw, FM801_REG(chip, IRQ_MASK)); + + __end_hw: + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_fm801_dev_free(snd_device_t *device) +{ + fm801_t *chip = snd_magic_cast(fm801_t, device->device_data, return -ENXIO); + return snd_fm801_free(chip); +} + +static int __devinit snd_fm801_create(snd_card_t * card, + struct pci_dev * pci, + fm801_t ** rchip) +{ + fm801_t *chip; + unsigned char rev, id; + unsigned short cmdw; + signed long timeout; + int err; + static snd_device_ops_t ops = { + dev_free: snd_fm801_dev_free, + }; + + *rchip = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + chip = snd_magic_kcalloc(fm801_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 0x80, "FM801")) == NULL) { + snd_fm801_free(chip); + snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->port, chip->port + 0x80 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) { + snd_fm801_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); + + pci_read_config_byte(pci, PCI_REVISION_ID, &rev); + if (rev >= 0xb1) /* FM801-AU */ + chip->multichannel = 1; + + /* codec cold reset + AC'97 warm reset */ + outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL)); + udelay(100); + outw(0, FM801_REG(chip, CODEC_CTRL)); + + timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ + + outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) + goto __ac97_secondary; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + snd_printk("Primary AC'97 codec not found\n"); + snd_fm801_free(chip); + return -EIO; + + __ac97_secondary: + if (!chip->multichannel) /* lookup is not required */ + goto __ac97_ok; + for (id = 3; id > 0; id--) { /* my card has the secondary codec */ + /* at address #3, so the loop is inverted */ + + if ((timeout - (signed long)jiffies) < HZ / 20) + timeout = jiffies + HZ / 20; + + outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) { + cmdw = inw(FM801_REG(chip, AC97_DATA)); + if (cmdw != 0xffff && cmdw != 0) { + chip->secondary = 1; + chip->secondary_addr = id; + goto __ac97_ok; + } + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + } + + /* the recovery phase, it seems that probing for non-existing codec might */ + /* cause timeout problems */ + timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ + + outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); + udelay(5); + do { + if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) + goto __ac97_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while ((timeout - (signed long)jiffies) > 0); + snd_printk("Primary AC'97 codec not responding\n"); + snd_fm801_free(chip); + return -EIO; + + __ac97_ok: + + /* init volume */ + outw(0x0808, FM801_REG(chip, PCM_VOL)); + outw(0x9f1f, FM801_REG(chip, FM_VOL)); + outw(0x8808, FM801_REG(chip, I2S_VOL)); + + /* I2S control - I2S mode */ + outw(0x0003, FM801_REG(chip, I2S_MODE)); + + /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */ + cmdw = inw(FM801_REG(chip, IRQ_MASK)); + cmdw &= ~0x0083; + outw(cmdw, FM801_REG(chip, IRQ_MASK)); + + /* interrupt clear */ + outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_fm801_free(chip); + return err; + } + + *rchip = chip; + return 0; +} + +static int __devinit snd_card_fm801_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + fm801_t *chip; + opl3_t *opl3; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + if ((err = snd_fm801_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_fm801_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, + FM801_REG(chip, MPU401_DATA), 1, + chip->irq, 0, &chip->rmidi)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0), + FM801_REG(chip, OPL3_BANK1), + OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "FM801"); + strcpy(card->shortname, "ForteMedia FM801-"); + strcat(card->shortname, chip->multichannel ? "AU" : "AS"); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_card_fm801_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "FM801", + id_table: snd_fm801_ids, + probe: snd_card_fm801_probe, + remove: __devexit_p(snd_card_fm801_remove), +}; + +static int __init alsa_card_fm801_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ForteMedia FM801 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_fm801_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_fm801_init) +module_exit(alsa_card_fm801_exit) + +#ifndef MODULE + +/* format is: snd-fm801=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_fm801_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-fm801=", alsa_card_fm801_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/ice1712.c linux-2.4.19-pre5-mjc/sound/pci/ice1712.c --- linux/sound/pci/ice1712.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ice1712.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,4366 @@ +/* + * ALSA driver for ICEnsemble ICE1712 (Envy24) + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + NOTES: + - spdif nonaudio consumer mode does not work (at least with my + Sony STR-DB830) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#include + +#define SND_CS8403 +#define SND_CS8404 +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Hoontech SoundTrack DSP 24}," + "{MidiMan M Audio,Delta 1010}," + "{MidiMan M Audio,Delta DiO 2496}," + "{MidiMan M Audio,Delta 66}," + "{MidiMan M Audio,Delta 44}," + "{MidiMan M Audio,Audiophile 24/96}," + "{TerraTec,EWX 24/96}," + "{TerraTec,EWS 88MT}," + "{TerraTec,EWS 88D}," + "{TerraTec,DMX 6Fire}," + "{ICEnsemble,Generic ICE1712}," + "{ICEnsemble,Generic Envy24}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_omni[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; /* Delta44 & 66 Omni I/O support */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for ICE1712 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for ICE1712 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable ICE1712 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_omni, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_omni, "Enable Midiman M-Audio Delta Omni I/O support."); +MODULE_PARM_SYNTAX(snd_omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); + +#ifndef PCI_VENDOR_ID_ICE +#define PCI_VENDOR_ID_ICE 0x1412 +#endif +#ifndef PCI_DEVICE_ID_ICE_1712 +#define PCI_DEVICE_ID_ICE_1712 0x1712 +#endif + +#define ICE1712_SUBDEVICE_STDSP24 0x12141217 /* Hoontech SoundTrack Audio DSP 24 */ +#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6 +#define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6 +#define ICE1712_SUBDEVICE_DELTA66 0x121432d6 +#define ICE1712_SUBDEVICE_DELTA44 0x121433d6 +#define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6 +#define ICE1712_SUBDEVICE_EWX2496 0x3b153011 +#define ICE1712_SUBDEVICE_EWS88MT 0x3b151511 +#define ICE1712_SUBDEVICE_EWS88D 0x3b152b11 +#define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811 + +/* + * Direct registers + */ + +#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x) + +#define ICE1712_REG_CONTROL 0x00 /* byte */ +#define ICE1712_RESET 0x80 /* reset whole chip */ +#define ICE1712_SERR_LEVEL 0x04 /* SERR# level otherwise edge */ +#define ICE1712_NATIVE 0x01 /* native mode otherwise SB */ +#define ICE1712_REG_IRQMASK 0x01 /* byte */ +#define ICE1712_IRQ_MPU1 0x80 +#define ICE1712_IRQ_TIMER 0x40 +#define ICE1712_IRQ_MPU2 0x20 +#define ICE1712_IRQ_PROPCM 0x10 +#define ICE1712_IRQ_FM 0x08 /* FM/MIDI - legacy */ +#define ICE1712_IRQ_PBKDS 0x04 /* playback DS channels */ +#define ICE1712_IRQ_CONCAP 0x02 /* consumer capture */ +#define ICE1712_IRQ_CONPBK 0x01 /* consumer playback */ +#define ICE1712_REG_IRQSTAT 0x02 /* byte */ +/* look to ICE1712_IRQ_* */ +#define ICE1712_REG_INDEX 0x03 /* byte - indirect CCIxx regs */ +#define ICE1712_REG_DATA 0x04 /* byte - indirect CCIxx regs */ +#define ICE1712_REG_NMI_STAT1 0x05 /* byte */ +#define ICE1712_REG_NMI_DATA 0x06 /* byte */ +#define ICE1712_REG_NMI_INDEX 0x07 /* byte */ +#define ICE1712_REG_AC97_INDEX 0x08 /* byte */ +#define ICE1712_REG_AC97_CMD 0x09 /* byte */ +#define ICE1712_AC97_COLD 0x80 /* cold reset */ +#define ICE1712_AC97_WARM 0x40 /* warm reset */ +#define ICE1712_AC97_WRITE 0x20 /* W: write, R: write in progress */ +#define ICE1712_AC97_READ 0x10 /* W: read, R: read in progress */ +#define ICE1712_AC97_READY 0x08 /* codec ready status bit */ +#define ICE1712_AC97_PBK_VSR 0x02 /* playback VSR */ +#define ICE1712_AC97_CAP_VSR 0x01 /* capture VSR */ +#define ICE1712_REG_AC97_DATA 0x0a /* word (little endian) */ +#define ICE1712_REG_MPU1_CTRL 0x0c /* byte */ +#define ICE1712_REG_MPU1_DATA 0x0d /* byte */ +#define ICE1712_REG_I2C_DEV_ADDR 0x10 /* byte */ +#define ICE1712_I2C_WRITE 0x01 /* write direction */ +#define ICE1712_REG_I2C_BYTE_ADDR 0x11 /* byte */ +#define ICE1712_REG_I2C_DATA 0x12 /* byte */ +#define ICE1712_REG_I2C_CTRL 0x13 /* byte */ +#define ICE1712_I2C_EEPROM 0x80 /* EEPROM exists */ +#define ICE1712_I2C_BUSY 0x01 /* busy bit */ +#define ICE1712_REG_CONCAP_ADDR 0x14 /* dword - consumer capture */ +#define ICE1712_REG_CONCAP_COUNT 0x18 /* word - current/base count */ +#define ICE1712_REG_SERR_SHADOW 0x1b /* byte */ +#define ICE1712_REG_MPU2_CTRL 0x1c /* byte */ +#define ICE1712_REG_MPU2_DATA 0x1d /* byte */ +#define ICE1712_REG_TIMER 0x1e /* word */ + +/* + * Indirect registers + */ + +#define ICE1712_IREG_PBK_COUNT_HI 0x00 +#define ICE1712_IREG_PBK_COUNT_LO 0x01 +#define ICE1712_IREG_PBK_CTRL 0x02 +#define ICE1712_IREG_PBK_LEFT 0x03 /* left volume */ +#define ICE1712_IREG_PBK_RIGHT 0x04 /* right volume */ +#define ICE1712_IREG_PBK_SOFT 0x05 /* soft volume */ +#define ICE1712_IREG_PBK_RATE_LO 0x06 +#define ICE1712_IREG_PBK_RATE_MID 0x07 +#define ICE1712_IREG_PBK_RATE_HI 0x08 +#define ICE1712_IREG_CAP_COUNT_HI 0x10 +#define ICE1712_IREG_CAP_COUNT_LO 0x11 +#define ICE1712_IREG_CAP_CTRL 0x12 +#define ICE1712_IREG_GPIO_DATA 0x20 +#define ICE1712_IREG_GPIO_WRITE_MASK 0x21 +#define ICE1712_IREG_GPIO_DIRECTION 0x22 +#define ICE1712_IREG_CONSUMER_POWERDOWN 0x30 +#define ICE1712_IREG_PRO_POWERDOWN 0x31 + +/* + * Consumer section direct DMA registers + */ + +#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x) + +#define ICE1712_DS_INTMASK 0x00 /* word - interrupt mask */ +#define ICE1712_DS_INTSTAT 0x02 /* word - interrupt status */ +#define ICE1712_DS_DATA 0x04 /* dword - channel data */ +#define ICE1712_DS_INDEX 0x08 /* dword - channel index */ + +/* + * Consumer section channel registers + */ + +#define ICE1712_DSC_ADDR0 0x00 /* dword - base address 0 */ +#define ICE1712_DSC_COUNT0 0x01 /* word - count 0 */ +#define ICE1712_DSC_ADDR1 0x02 /* dword - base address 1 */ +#define ICE1712_DSC_COUNT1 0x03 /* word - count 1 */ +#define ICE1712_DSC_CONTROL 0x04 /* byte - control & status */ +#define ICE1712_BUFFER1 0x80 /* buffer1 is active */ +#define ICE1712_BUFFER1_AUTO 0x40 /* buffer1 auto init */ +#define ICE1712_BUFFER0_AUTO 0x20 /* buffer0 auto init */ +#define ICE1712_FLUSH 0x10 /* flush FIFO */ +#define ICE1712_STEREO 0x08 /* stereo */ +#define ICE1712_16BIT 0x04 /* 16-bit data */ +#define ICE1712_PAUSE 0x02 /* pause */ +#define ICE1712_START 0x01 /* start */ +#define ICE1712_DSC_RATE 0x05 /* dword - rate */ +#define ICE1712_DSC_VOLUME 0x06 /* word - volume control */ + +/* + * Professional multi-track direct control registers + */ + +#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x) + +#define ICE1712_MT_IRQ 0x00 /* byte - interrupt mask */ +#define ICE1712_MULTI_CAPTURE 0x80 /* capture IRQ */ +#define ICE1712_MULTI_PLAYBACK 0x40 /* playback IRQ */ +#define ICE1712_MULTI_CAPSTATUS 0x02 /* capture IRQ status */ +#define ICE1712_MULTI_PBKSTATUS 0x01 /* playback IRQ status */ +#define ICE1712_MT_RATE 0x01 /* byte - sampling rate select */ +#define ICE1712_SPDIF_MASTER 0x10 /* S/PDIF input is master clock */ +#define ICE1712_MT_I2S_FORMAT 0x02 /* byte - I2S data format */ +#define ICE1712_MT_AC97_INDEX 0x04 /* byte - AC'97 index */ +#define ICE1712_MT_AC97_CMD 0x05 /* byte - AC'97 command & status */ +/* look to ICE1712_AC97_* */ +#define ICE1712_MT_AC97_DATA 0x06 /* word - AC'97 data */ +#define ICE1712_MT_PLAYBACK_ADDR 0x10 /* dword - playback address */ +#define ICE1712_MT_PLAYBACK_SIZE 0x14 /* word - playback size */ +#define ICE1712_MT_PLAYBACK_COUNT 0x16 /* word - playback count */ +#define ICE1712_MT_PLAYBACK_CONTROL 0x18 /* byte - control */ +#define ICE1712_CAPTURE_START_SHADOW 0x04 /* capture start */ +#define ICE1712_PLAYBACK_PAUSE 0x02 /* playback pause */ +#define ICE1712_PLAYBACK_START 0x01 /* playback start */ +#define ICE1712_MT_CAPTURE_ADDR 0x20 /* dword - capture address */ +#define ICE1712_MT_CAPTURE_SIZE 0x24 /* word - capture size */ +#define ICE1712_MT_CAPTURE_COUNT 0x26 /* word - capture count */ +#define ICE1712_MT_CAPTURE_CONTROL 0x28 /* byte - control */ +#define ICE1712_CAPTURE_START 0x01 /* capture start */ +#define ICE1712_MT_ROUTE_PSDOUT03 0x30 /* word */ +#define ICE1712_MT_ROUTE_SPDOUT 0x32 /* word */ +#define ICE1712_MT_ROUTE_CAPTURE 0x34 /* dword */ +#define ICE1712_MT_MONITOR_VOLUME 0x38 /* word */ +#define ICE1712_MT_MONITOR_INDEX 0x3a /* byte */ +#define ICE1712_MT_MONITOR_RATE 0x3b /* byte */ +#define ICE1712_MT_MONITOR_ROUTECTRL 0x3c /* byte */ +#define ICE1712_ROUTE_AC97 0x01 /* route digital mixer output to AC'97 */ +#define ICE1712_MT_MONITOR_PEAKINDEX 0x3e /* byte */ +#define ICE1712_MT_MONITOR_PEAKDATA 0x3f /* byte */ + +/* + * Codec configuration bits + */ + +/* PCI[60] System Configuration */ +#define ICE1712_CFG_CLOCK 0xc0 +#define ICE1712_CFG_CLOCK512 0x00 /* 22.5692Mhz, 44.1kHz*512 */ +#define ICE1712_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */ +#define ICE1712_CFG_EXT 0x80 /* external clock */ +#define ICE1712_CFG_2xMPU401 0x20 /* two MPU401 UARTs */ +#define ICE1712_CFG_NO_CON_AC97 0x10 /* consumer AC'97 codec is not present */ +#define ICE1712_CFG_ADC_MASK 0x0c /* one, two, three, four stereo ADCs */ +#define ICE1712_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */ +/* PCI[61] AC-Link Configuration */ +#define ICE1712_CFG_PRO_I2S 0x80 /* multitrack converter: I2S or AC'97 */ +#define ICE1712_CFG_AC97_PACKED 0x01 /* split or packed mode - AC'97 */ +/* PCI[62] I2S Features */ +#define ICE1712_CFG_I2S_VOLUME 0x80 /* volume/mute capability */ +#define ICE1712_CFG_I2S_96KHZ 0x40 /* supports 96kHz sampling */ +#define ICE1712_CFG_I2S_RESMASK 0x30 /* resolution mask, 16,18,20,24-bit */ +#define ICE1712_CFG_I2S_OTHER 0x0f /* other I2S IDs */ +/* PCI[63] S/PDIF Configuration */ +#define ICE1712_CFG_I2S_CHIPID 0xfc /* I2S chip ID */ +#define ICE1712_CFG_SPDIF_IN 0x02 /* S/PDIF input is present */ +#define ICE1712_CFG_SPDIF_OUT 0x01 /* S/PDIF output is present */ + +/* + * MidiMan M-Audio Delta GPIO definitions + */ + +/* MidiMan M-Audio Delta1010 */ +#define ICE1712_DELTA_DFS 0x01 /* fast/slow sample rate mode */ + /* (>48kHz must be 1) */ +#define ICE1712_DELTA_SPDIF_IN_STAT 0x02 + /* S/PDIF input status */ + /* 0 = valid signal is present */ + /* all except Delta44 */ + /* look to CS8414 datasheet */ +#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04 + /* S/PDIF output status clock */ + /* (writting on rising edge - 0->1) */ + /* all except Delta44 */ + /* look to CS8404A datasheet */ +#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08 + /* S/PDIF output status data */ + /* all except Delta44 */ + /* look to CS8404A datasheet */ +/* MidiMan M-Audio DeltaDiO */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10 + /* coaxial (0), optical (1) */ + /* S/PDIF input select*/ + +/* MidiMan M-Audio Delta1010 */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10 + /* 1 - clock are taken from S/PDIF input */ + /* 0 - clock are taken from Word Clock input */ + /* affected SPMCLKIN pin of Envy24 */ +#define ICE1712_DELTA_WORD_CLOCK_STATUS 0x20 + /* 0 = valid word clock signal is present */ + +/* MidiMan M-Audio Delta66 */ +/* 0x01 = DFS */ +/* 0x02 = SPDIF_IN_STAT */ +/* 0x04 = SPDIF_OUT_STAT_CLOCK */ +/* 0x08 = SPDIF_OUT_STAT_DATA */ +#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10 + /* AKM4524 serial data */ +#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20 + /* AKM4524 serial clock */ + /* (writting on rising edge - 0->1 */ +#define ICE1712_DELTA_CODEC_CHIP_A 0x40 +#define ICE1712_DELTA_CODEC_CHIP_B 0x80 + /* 1 - select chip A or B */ + +/* MidiMan M-Audio Delta44 */ +/* 0x01 = DFS */ +/* 0x10 = CODEC_SERIAL_DATA */ +/* 0x20 = CODEC_SERIAL_CLOCK */ +/* 0x40 = CODEC_CHIP_A */ +/* 0x80 = CODEC_CHIP_B */ + +/* MidiMan M-Audio Audiophile definitions */ +/* 0x01 = DFS */ +#define ICE1712_DELTA_AP_CCLK 0x02 /* SPI clock */ + /* (clocking on rising edge - 0->1) */ +#define ICE1712_DELTA_AP_DIN 0x04 /* data input */ +#define ICE1712_DELTA_AP_DOUT 0x08 /* data output */ +#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */ + /* low signal = select */ +#define ICE1712_DELTA_AP_CS_CODEC 0x20 /* AK4528 chip select */ + /* low signal = select */ + +/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */ + +#define ICE1712_STDSP24_0_BOX(r, x) r[0] = ((r[0] & ~3) | ((x)&3)) +#define ICE1712_STDSP24_0_DAREAR(r, x) r[0] = ((r[0] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_1_CHN1(r, x) r[1] = ((r[1] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_1_CHN2(r, x) r[1] = ((r[1] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_1_CHN3(r, x) r[1] = ((r[1] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_2_CHN4(r, x) r[2] = ((r[2] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_2_MIDIIN(r, x) r[2] = ((r[2] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_2_MIDI1(r, x) r[2] = ((r[2] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_3_MIDI2(r, x) r[3] = ((r[3] & ~1) | ((x)&1)) +#define ICE1712_STDSP24_3_MUTE(r, x) r[3] = ((r[3] & ~2) | (((x)&1)<<1)) +#define ICE1712_STDSP24_3_INSEL(r, x) r[3] = ((r[3] & ~4) | (((x)&1)<<2)) +#define ICE1712_STDSP24_SET_ADDR(r, a) r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3)) +#define ICE1712_STDSP24_CLOCK(r, a, c) r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5)) +#define ICE1712_STDSP24_CLOCK_BIT (1<<5) + +/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */ + +#define ICE1712_STDSP24_DAREAR (1<<0) +#define ICE1712_STDSP24_MUTE (1<<1) +#define ICE1712_STDSP24_INSEL (1<<2) + +#define ICE1712_STDSP24_BOX_CHN1 (1<<0) /* input channel 1 */ +#define ICE1712_STDSP24_BOX_CHN2 (1<<1) /* input channel 2 */ +#define ICE1712_STDSP24_BOX_CHN3 (1<<2) /* input channel 3 */ +#define ICE1712_STDSP24_BOX_CHN4 (1<<3) /* input channel 4 */ +#define ICE1712_STDSP24_BOX_MIDI1 (1<<8) +#define ICE1712_STDSP24_BOX_MIDI2 (1<<9) + +/* TerraTec EWX 24/96 configuration definitions */ + +#define ICE1712_EWX2496_AK4524_CS 0x01 /* AK4524 chip select; low = active */ +#define ICE1712_EWX2496_AIN_SEL 0x02 /* input sensitivity switch; high = louder */ +#define ICE1712_EWX2496_AOUT_SEL 0x04 /* output sensitivity switch; high = louder */ +#define ICE1712_EWX2496_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_EWX2496_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_EWX2496_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_EWX2496_TX2 0x40 /* MIDI2 (not used) */ +#define ICE1712_EWX2496_RX2 0x80 /* MIDI2 (not used) */ + +/* TerraTec EWS 88MT/D configuration definitions */ +/* RW, SDA snd SCLK are identical with EWX24/96 */ +#define ICE1712_EWS88_CS8414_RATE 0x07 /* CS8414 sample rate: gpio 0-2 */ +#define ICE1712_EWS88_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_EWS88_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_EWS88_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_EWS88_TX2 0x40 /* MIDI2 (only on 88D) */ +#define ICE1712_EWS88_RX2 0x80 /* MIDI2 (only on 88D) */ + +/* i2c address */ +#define ICE1712_EWS88MT_CS8404_ADDR (0x40>>1) +#define ICE1712_EWS88MT_INPUT_ADDR (0x46>>1) +#define ICE1712_EWS88MT_OUTPUT_ADDR (0x48>>1) +#define ICE1712_EWS88MT_OUTPUT_SENSE 0x40 /* mask */ +#define ICE1712_EWS88D_PCF_ADDR (0x40>>1) + +/* TerraTec DMX 6Fire configuration definitions */ +#define ICE1712_6FIRE_AK4524_CS_MASK 0x07 /* AK4524 chip select #1-#3 */ +#define ICE1712_6FIRE_RW 0x08 /* read/write switch for i2c; high = write */ +#define ICE1712_6FIRE_SERIAL_DATA 0x10 /* i2c & ak4524 data */ +#define ICE1712_6FIRE_SERIAL_CLOCK 0x20 /* i2c & ak4524 clock */ +#define ICE1712_6FIRE_TX2 0x40 /* MIDI2 */ +#define ICE1712_6FIRE_RX2 0x80 /* MIDI2 */ + +#define ICE1712_6FIRE_CS8427_ADDR (0x22>>1) /* ?? */ + +/* + * DMA mode values + * identical with DMA_XXX on i386 architecture. + */ +#define ICE1712_DMA_MODE_WRITE 0x48 +#define ICE1712_DMA_AUTOINIT 0x10 + + +/* + * + */ + +typedef struct _snd_ice1712 ice1712_t; + +typedef struct { + unsigned int subvendor; /* PCI[2c-2f] */ + unsigned char size; /* size of EEPROM image in bytes */ + unsigned char version; /* must be 1 */ + unsigned char codec; /* codec configuration PCI[60] */ + unsigned char aclink; /* ACLink configuration PCI[61] */ + unsigned char i2sID; /* PCI[62] */ + unsigned char spdif; /* S/PDIF configuration PCI[63] */ + unsigned char gpiomask; /* GPIO initial mask, 0 = write, 1 = don't */ + unsigned char gpiostate; /* GPIO initial state */ + unsigned char gpiodir; /* GPIO direction state */ + unsigned short ac97main; + unsigned short ac97pcm; + unsigned short ac97rec; + unsigned char ac97recsrc; + unsigned char dacID[4]; /* I2S IDs for DACs */ + unsigned char adcID[4]; /* I2S IDs for ADCs */ + unsigned char extra[4]; +} ice1712_eeprom_t; + +struct _snd_ice1712 { + unsigned long conp_dma_size; + unsigned long conc_dma_size; + unsigned long prop_dma_size; + unsigned long proc_dma_size; + int irq; + + unsigned long port; + struct resource *res_port; + unsigned long ddma_port; + struct resource *res_ddma_port; + unsigned long dmapath_port; + struct resource *res_dmapath_port; + unsigned long profi_port; + struct resource *res_profi_port; + + unsigned int config; /* system configuration */ + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm_ds; + snd_pcm_t *pcm_pro; + snd_pcm_substream_t *playback_con_substream; + snd_pcm_substream_t *playback_con_substream_ds[6]; + snd_pcm_substream_t *capture_con_substream; + snd_pcm_substream_t *playback_pro_substream; + snd_pcm_substream_t *capture_pro_substream; + unsigned int playback_pro_size; + unsigned int capture_pro_size; + unsigned int playback_con_virt_addr[6]; + unsigned int playback_con_active_buf[6]; + unsigned int capture_con_virt_addr; + unsigned int ac97_ext_id; + ac97_t *ac97; + snd_rawmidi_t *rmidi[2]; + + spinlock_t reg_lock; + struct semaphore gpio_mutex; + snd_info_entry_t *proc_entry; + + ice1712_eeprom_t eeprom; + + unsigned int pro_volumes[20]; + int ak4528: 1, /* AK4524 or AK4528 */ + omni: 1; /* Delta Omni I/O */ + int num_adcs; /* AK4524 or AK4528 ADCs */ + int num_dacs; /* AK4524 or AK4528 DACs */ + int num_total_dacs; /* total DACs */ + unsigned char ak4524_images[4][8]; + unsigned char ak4524_ipga_gain[4][2]; + unsigned char hoontech_boxbits[4]; + unsigned int hoontech_config; + unsigned short hoontech_boxconfig[4]; + + snd_i2c_bus_t *i2c; /* I2C bus */ + snd_i2c_device_t *cs8404; /* CS8404A I2C device */ + snd_i2c_device_t *cs8427; /* CS8427 I2C device */ + snd_i2c_device_t *pcf8574[2]; /* PCF8574 Output/Input (EWS88MT) */ + snd_i2c_device_t *pcf8575; /* PCF8575 (EWS88D) */ + + unsigned char cs8403_spdif_bits; + unsigned char cs8403_spdif_stream_bits; + snd_kcontrol_t *spdif_stream_ctl; + + unsigned char gpio_direction, gpio_write_mask; +}; + +#define chip_t ice1712_t + +static struct pci_device_id snd_ice1712_ids[] __devinitdata = { + { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ICE1712 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_ice1712_ids); + +static int snd_ice1712_build_pro_mixer(ice1712_t *ice); +static int snd_ice1712_build_controls(ice1712_t *ice); + +/* + * Basic I/O + */ + +static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data) +{ + outb(addr, ICEREG(ice, INDEX)); + outb(data, ICEREG(ice, DATA)); +} + +static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr) +{ + outb(addr, ICEREG(ice, INDEX)); + return inb(ICEREG(ice, DATA)); +} + +static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data) +{ + outb((channel << 4) | addr, ICEDS(ice, INDEX)); + outl(data, ICEDS(ice, DATA)); +} + +static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr) +{ + outb((channel << 4) | addr, ICEDS(ice, INDEX)); + return inl(ICEDS(ice, DATA)); +} + +static void snd_ice1712_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEREG(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEREG(ice, AC97_INDEX)); + outw(val, ICEREG(ice, AC97_DATA)); + old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); + outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) + break; +} + +static unsigned short snd_ice1712_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEREG(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEREG(ice, AC97_INDEX)); + outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) + break; + if (tm >= 0x10000) /* timeout */ + return ~0; + return inw(ICEREG(ice, AC97_DATA)); +} + +/* + * pro ac97 section + */ + +static void snd_ice1712_pro_ac97_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEMT(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEMT(ice, AC97_INDEX)); + outw(val, ICEMT(ice, AC97_DATA)); + old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR); + outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0) + break; +} + + +static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + ice1712_t *ice = (ice1712_t *)ac97->private_data; + int tm; + unsigned char old_cmd = 0; + + for (tm = 0; tm < 0x10000; tm++) { + old_cmd = inb(ICEMT(ice, AC97_CMD)); + if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ)) + continue; + if (!(old_cmd & ICE1712_AC97_READY)) + continue; + break; + } + outb(reg, ICEMT(ice, AC97_INDEX)); + outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD)); + for (tm = 0; tm < 0x10000; tm++) + if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0) + break; + if (tm >= 0x10000) /* timeout */ + return ~0; + return inw(ICEMT(ice, AC97_DATA)); +} + +static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0; + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char val, nval; + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + val = inb(ICEMT(ice, MONITOR_ROUTECTRL)); + nval = val & ~ICE1712_ROUTE_AC97; + if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97; + outb(nval, ICEMT(ice, MONITOR_ROUTECTRL)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Digital Mixer To AC97", + info: snd_ice1712_digmix_route_ac97_info, + get: snd_ice1712_digmix_route_ac97_get, + put: snd_ice1712_digmix_route_ac97_put, +}; + + +/* + */ + +static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits) +{ + unsigned char tmp, mask1, mask2; + int idx; + /* send byte to transmitter */ + mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK; + mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA; + down(&ice->gpio_mutex); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + for (idx = 7; idx >= 0; idx--) { + tmp &= ~(mask1 | mask2); + if (bits & (1 << idx)) + tmp |= mask2; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(100); + tmp |= mask1; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(100); + } + tmp &= ~mask1; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + up(&ice->gpio_mutex); +} + + +/* + * set gpio direction, write mask and data + */ +static void snd_ice1712_gpio_write_bits(ice1712_t *ice, int mask, int bits) +{ + ice->gpio_direction |= mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, mask & bits); +} + +/* + */ +static void save_gpio_status(ice1712_t *ice, unsigned char *tmp) +{ + down(&ice->gpio_mutex); + tmp[0] = ice->gpio_direction; + tmp[1] = ice->gpio_write_mask; +} + +static void restore_gpio_status(ice1712_t *ice, unsigned char *tmp) +{ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, tmp[0]); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, tmp[1]); + ice->gpio_direction = tmp[0]; + ice->gpio_write_mask = tmp[1]; + up(&ice->gpio_mutex); +} + +/* + * CS8427 via SPI mode (for Audiophile), emulated I2C + */ + +/* send 8 bits */ +static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp) +{ + int idx; + + for (idx = 7; idx >= 0; idx--) { + tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK); + if (data & (1 << idx)) + tmp |= ICE1712_DELTA_AP_DOUT; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + tmp |= ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + } +} + +/* read 8 bits */ +static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp) +{ + unsigned char data = 0; + int idx; + + for (idx = 7; idx >= 0; idx--) { + tmp &= ~ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN) + data |= 1 << idx; + tmp |= ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + } + return data; +} + +/* assert chip select */ +static unsigned char ap_cs8427_codec_select(ice1712_t *ice) +{ + unsigned char tmp; + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC; + tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + return tmp; +} + +/* deassert chip select */ +static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp) +{ + tmp |= ICE1712_DELTA_AP_CS_DIGITAL; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); +} + +/* sequential write */ +static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + int res = count; + unsigned char tmp; + + down(&ice->gpio_mutex); + tmp = ap_cs8427_codec_select(ice); + ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */ + while (count-- > 0) + ap_cs8427_write_byte(ice, *bytes++, tmp); + ap_cs8427_codec_deassert(ice, tmp); + up(&ice->gpio_mutex); + return res; +} + +/* sequential read */ +static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + int res = count; + unsigned char tmp; + + down(&ice->gpio_mutex); + tmp = ap_cs8427_codec_select(ice); + ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */ + while (count-- > 0) + *bytes++ = ap_cs8427_read_byte(ice, tmp); + ap_cs8427_codec_deassert(ice, tmp); + up(&ice->gpio_mutex); + return res; +} + +static int ap_cs8427_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) +{ + if (addr == 0x10) + return 1; + return -ENOENT; +} + +static snd_i2c_ops_t ap_cs8427_i2c_ops = { + sendbytes: ap_cs8427_sendbytes, + readbytes: ap_cs8427_readbytes, + probeaddr: ap_cs8427_probeaddr, +}; + +/* + * access via i2c mode (for EWX 24/96, EWS 88MT&D) + */ + +/* send SDA and SCL */ +static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char tmp = 0; + if (clk) + tmp |= ICE1712_EWX2496_SERIAL_CLOCK; + if (data) + tmp |= ICE1712_EWX2496_SERIAL_DATA; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); +} + +static int ewx_i2c_getclock(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0; +} + +static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + int bit; + /* set RW pin to low */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0); + if (ack) + udelay(5); + bit = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA ? 1 : 0; + /* set RW pin to high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW); + /* reset write mask */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK); + return bit; +} + +static void ewx_i2c_start(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char mask; + + save_gpio_status(ice, (unsigned char *)&bus->private_value); + /* set RW high */ + mask = ICE1712_EWX2496_RW; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */ + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + mask |= ICE1712_6FIRE_AK4524_CS_MASK; /* CS high also */ + break; + } + snd_ice1712_gpio_write_bits(ice, mask, mask); +} + +static void ewx_i2c_stop(snd_i2c_bus_t *bus) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + restore_gpio_status(ice, (unsigned char *)&bus->private_value); +} + +static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + unsigned char mask = 0; + + if (clock) + mask |= ICE1712_EWX2496_SERIAL_CLOCK; /* write SCL */ + if (data) + mask |= ICE1712_EWX2496_SERIAL_DATA; /* write SDA */ + ice->gpio_direction &= ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA); + ice->gpio_direction |= mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask); +} + +static snd_i2c_bit_ops_t snd_ice1712_ewx_cs8427_bit_ops = { + start: ewx_i2c_start, + stop: ewx_i2c_stop, + direction: ewx_i2c_direction, + setlines: ewx_i2c_setlines, + getclock: ewx_i2c_getclock, + getdata: ewx_i2c_getdata, +}; + +/* AK4524 chip select; address 0x48 bit 0-3 */ +static void snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask) +{ + unsigned char data, ndata; + + snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return); + snd_i2c_lock(ice->i2c); + snd_runtime_check(snd_i2c_readbytes(ice->pcf8574[1], &data, 1) == 1, snd_i2c_unlock(ice->i2c); return); + ndata = (data & 0xf0) | chip_mask; + if (ndata != data) + snd_runtime_check(snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) == 1, snd_i2c_unlock(ice->i2c); return); + snd_i2c_unlock(ice->i2c); +} + +/* + * write AK4524 register + */ +static void snd_ice1712_ak4524_write(ice1712_t *ice, int chip, + unsigned char addr, unsigned char data) +{ + unsigned char tmp, data_mask, clk_mask, saved[2]; + unsigned char codecs_mask; + int idx, cif; + unsigned int addrdata; + + snd_assert(chip >= 0 && chip < 4, return); + + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) { + /* assert AK4524 CS */ + snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f); + //snd_ice1712_ews88mt_chip_select(ice, 0x0f); + } + + cif = 0; /* the default level of the CIF pin from AK4524 */ + save_gpio_status(ice, saved); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + data_mask = ICE1712_DELTA_AP_DOUT; + clk_mask = ICE1712_DELTA_AP_CCLK; + codecs_mask = ICE1712_DELTA_AP_CS_CODEC; /* select AK4528 codec */ + tmp |= ICE1712_DELTA_AP_CS_DIGITAL; /* assert digital high */ + break; + case ICE1712_SUBDEVICE_EWX2496: + data_mask = ICE1712_EWX2496_SERIAL_DATA; + clk_mask = ICE1712_EWX2496_SERIAL_CLOCK; + codecs_mask = ICE1712_EWX2496_AK4524_CS; + tmp |= ICE1712_EWX2496_RW; /* set rw bit high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio_direction | data_mask | clk_mask | + codecs_mask | ICE1712_EWX2496_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, + ~(data_mask | clk_mask | + codecs_mask | ICE1712_EWX2496_RW)); + cif = 1; /* CIF high */ + break; + case ICE1712_SUBDEVICE_EWS88MT: + data_mask = ICE1712_EWS88_SERIAL_DATA; + clk_mask = ICE1712_EWS88_SERIAL_CLOCK; + codecs_mask = 0; /* no chip select on gpio */ + tmp |= ICE1712_EWS88_RW; /* set rw bit high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio_direction | data_mask | clk_mask | + codecs_mask | ICE1712_EWS88_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, + ~(data_mask | clk_mask | + codecs_mask | ICE1712_EWS88_RW)); + cif = 1; /* CIF high */ + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + data_mask = ICE1712_6FIRE_SERIAL_DATA; + clk_mask = ICE1712_6FIRE_SERIAL_CLOCK; + codecs_mask = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK; + tmp |= ICE1712_6FIRE_RW; /* set rw bit high */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, + ice->gpio_direction | data_mask | clk_mask | + codecs_mask | ICE1712_6FIRE_RW); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, + ~(data_mask | clk_mask | + codecs_mask | ICE1712_6FIRE_RW)); + cif = 1; /* CIF high */ + break; + default: + data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA; + clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK; + codecs_mask = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A : ICE1712_DELTA_CODEC_CHIP_B; + break; + } + + if (cif) { + tmp |= codecs_mask; /* start without chip select */ + } else { + tmp &= ~codecs_mask; /* chip select low */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + } + + addr &= 0x07; + /* build I2C address + data byte */ + addrdata = 0xa000 | (addr << 8) | data; + for (idx = 15; idx >= 0; idx--) { + tmp &= ~(data_mask|clk_mask); + if (addrdata & (1 << idx)) + tmp |= data_mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + //udelay(200); + udelay(1); + tmp |= clk_mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + } + + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) { + restore_gpio_status(ice, saved); + //snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f); + udelay(1); + snd_ice1712_ews88mt_chip_select(ice, 0x0f); + } else { + if (cif) { + /* assert a cs pulse to trigger */ + tmp &= ~codecs_mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + } + tmp |= codecs_mask; /* chip select high to trigger */ + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(1); + restore_gpio_status(ice, saved); + } + + if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0) + ice->ak4524_images[chip][addr] = data; + else + ice->ak4524_ipga_gain[chip][addr-4] = data; +} + +static void snd_ice1712_ak4524_reset(ice1712_t *ice, int state) +{ + int chip; + unsigned char reg; + + for (chip = 0; chip < ice->num_dacs/2; chip++) { + snd_ice1712_ak4524_write(ice, chip, 0x01, state ? 0x00 : 0x03); + if (state) + continue; + for (reg = 0x04; reg < (ice->ak4528 ? 0x06 : 0x08); reg++) + snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_images[chip][reg]); + if (ice->ak4528) + continue; + for (reg = 0x04; reg < 0x06; reg++) + snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_ipga_gain[chip][reg-4]); + } +} + +static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits) +{ + unsigned char bytes[2]; + + snd_i2c_lock(ice->i2c); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWS88MT: + snd_runtime_check(snd_i2c_sendbytes(ice->cs8404, &bits, 1) == 1, snd_i2c_unlock(ice->i2c); return); + break; + case ICE1712_SUBDEVICE_EWS88D: + snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return); + if (bits != bytes[1]) { + bytes[1] = bits; + snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return); + } + break; + } + snd_i2c_unlock(ice->i2c); +} + + +/* + * change the input clock selection + * spdif_clock = 1 - IEC958 input, 0 - Envy24 + */ +static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock) +{ + unsigned char reg[2] = { 0x80 | 4, 0 }; /* CS8427 auto increment | register number 4 + data */ + unsigned char val, nval; + snd_i2c_lock(ice->i2c); + if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + nval = val & 0xf0; + if (spdif_clock) + nval |= 0x01; + else + nval |= 0x04; + if (val != nval) { + if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + return 1; + } + snd_i2c_unlock(ice->i2c); + return 0; +} + +/* + */ +static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substream) +{ + unsigned long flags; + unsigned int rate; + unsigned char val, tmp; + + spin_lock_irqsave(&ice->reg_lock, flags); + if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW| + ICE1712_PLAYBACK_PAUSE| + ICE1712_PLAYBACK_START)) + goto __end; + if (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) + goto __end; + rate = substream->runtime->rate; + switch (rate) { + case 8000: val = 6; break; + case 9600: val = 3; break; + case 11025: val = 10; break; + case 12000: val = 2; break; + case 16000: val = 5; break; + case 22050: val = 9; break; + case 24000: val = 1; break; + case 32000: val = 4; break; + case 44100: val = 8; break; + case 48000: val = 0; break; + case 64000: val = 15; break; + case 88200: val = 11; break; + case 96000: val = 7; break; + default: + snd_BUG(); + val = 0; + break; + } + outb(val, ICEMT(ice, RATE)); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_AUDIOPHILE: + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_ak4524_reset(ice, 1); + down(&ice->gpio_mutex); + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + if (val == 15 || val == 11 || val == 7) { + tmp |= ICE1712_DELTA_DFS; + } else { + tmp &= ~ICE1712_DELTA_DFS; + } + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + up(&ice->gpio_mutex); + snd_ice1712_ak4524_reset(ice, 0); + return; + } + __end: + spin_unlock_irqrestore(&ice->reg_lock, flags); +} + +/* + * Interrupt handler + */ + +static void snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return); + unsigned char status; + + while (1) { + status = inb(ICEREG(ice, IRQSTAT)); + if (status == 0) + break; + if (status & ICE1712_IRQ_MPU1) { + if (ice->rmidi[0]) + snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs); + outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT)); + status &= ~ICE1712_IRQ_MPU1; + } + if (status & ICE1712_IRQ_TIMER) + outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT)); + if (status & ICE1712_IRQ_MPU2) { + if (ice->rmidi[1]) + snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs); + outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT)); + status &= ~ICE1712_IRQ_MPU2; + } + if (status & ICE1712_IRQ_PROPCM) { + unsigned char mtstat = inb(ICEMT(ice, IRQ)); + if (mtstat & ICE1712_MULTI_PBKSTATUS) { + if (ice->playback_pro_substream) + snd_pcm_period_elapsed(ice->playback_pro_substream); + outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ)); + } + if (mtstat & ICE1712_MULTI_CAPSTATUS) { + if (ice->capture_pro_substream) + snd_pcm_period_elapsed(ice->capture_pro_substream); + outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ)); + } + } + if (status & ICE1712_IRQ_FM) + outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT)); + if (status & ICE1712_IRQ_PBKDS) { + u32 idx; + u16 pbkstatus; + snd_pcm_substream_t *substream; + pbkstatus = inw(ICEDS(ice, INTSTAT)); + //printk("pbkstatus = 0x%x\n", pbkstatus); + for (idx = 0; idx < 6; idx++) { + if ((pbkstatus & (3 << (idx * 2))) == 0) + continue; + if ((substream = ice->playback_con_substream_ds[idx]) != NULL) + snd_pcm_period_elapsed(substream); + outw(3 << (idx * 2), ICEDS(ice, INTSTAT)); + } + outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT)); + } + if (status & ICE1712_IRQ_CONCAP) { + if (ice->capture_con_substream) + snd_pcm_period_elapsed(ice->capture_con_substream); + outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT)); + } + if (status & ICE1712_IRQ_CONPBK) { + if (ice->playback_con_substream) + snd_pcm_period_elapsed(ice->playback_con_substream); + outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT)); + } + } +} + +/* + * PCM part - misc + */ + +static int snd_ice1712_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ice1712_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* + * PCM part - consumer I/O + */ + +static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u32 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) { + tmp |= 2; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) { + tmp &= ~2; + } else { + result = -EINVAL; + } + snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u32 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) { + tmp |= 2; + } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) { + tmp &= ~2; + } else { + result = -EINVAL; + } + snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + int result = 0; + u8 tmp; + + spin_lock(&ice->reg_lock); + tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL); + if (cmd == SNDRV_PCM_TRIGGER_START) { + tmp |= 1; + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + tmp &= ~1; + } else { + result = -EINVAL; + } + snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp); + spin_unlock(&ice->reg_lock); + return result; +} + +static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size, rate, tmp; + + period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x0000; + if (snd_pcm_format_width(runtime->format) == 16) + tmp |= 0x10; + if (runtime->channels == 2) + tmp |= 0x08; + rate = (runtime->rate * 8192) / 375; + if (rate > 0x000fffff) + rate = 0x000fffff; + spin_lock_irqsave(&ice->reg_lock, flags); + outb(0, ice->ddma_port + 15); + outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b); + outl(runtime->dma_addr, ice->ddma_port + 0); + outw(buf_size, ice->ddma_port + 4); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp); + snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8); + snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0); + snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size, rate, tmp, chn; + + period_size = snd_pcm_lib_period_bytes(substream) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x0064; + if (snd_pcm_format_width(runtime->format) == 16) + tmp &= ~0x04; + if (runtime->channels == 2) + tmp |= 0x08; + rate = (runtime->rate * 8192) / 375; + if (rate > 0x000fffff) + rate = 0x000fffff; + ice->playback_con_active_buf[substream->number] = 0; + ice->playback_con_virt_addr[substream->number] = runtime->dma_addr; + chn = substream->number * 2; + spin_lock_irqsave(&ice->reg_lock, flags); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0)); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0); + snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp); + if (runtime->channels == 2) { + snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate); + snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0); + } + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + u32 period_size, buf_size; + u8 tmp; + + period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; + buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; + tmp = 0x06; + if (snd_pcm_format_width(runtime->format) == 16) + tmp &= ~0x04; + if (runtime->channels == 2) + tmp &= ~0x02; + spin_lock_irqsave(&ice->reg_lock, flags); + outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR)); + outw(buf_size, ICEREG(ice, CONCAP_COUNT)); + snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8); + snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff); + snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp); + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + return 0; +} + +static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + size_t ptr; + + if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1)) + return 0; + ptr = runtime->buffer_size - inw(ice->ddma_port + 4); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + u8 addr; + size_t ptr; + + if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1)) + return 0; + if (ice->playback_con_active_buf[substream->number]) + addr = ICE1712_DSC_ADDR1; + else + addr = ICE1712_DSC_ADDR0; + ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) - + ice->playback_con_virt_addr[substream->number]; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1)) + return 0; + ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_ice1712_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (64*1024), + period_bytes_min: 64, + period_bytes_max: (64*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ice1712_playback_ds = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ice1712_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (64*1024), + period_bytes_min: 64, + period_bytes_max: (64*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ice1712_playback_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_con_substream = substream; + runtime->hw = snd_ice1712_playback; + return 0; +} + +static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned long flags; + u32 tmp; + + ice->playback_con_substream_ds[substream->number] = substream; + runtime->hw = snd_ice1712_playback_ds; + spin_lock_irqsave(&ice->reg_lock, flags); + tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2)); + outw(tmp, ICEDS(ice, INTMASK)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_capture_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_con_substream = substream; + runtime->hw = snd_ice1712_capture; + runtime->hw.rates = ice->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + return 0; +} + +static int snd_ice1712_playback_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_con_substream = NULL; + return 0; +} + +static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&ice->reg_lock, flags); + tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2)); + outw(tmp, ICEDS(ice, INTMASK)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + ice->playback_con_substream_ds[substream->number] = NULL; + return 0; +} + +static int snd_ice1712_capture_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + /* disable ADC power */ + snd_ac97_update_bits(ice->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + ice->capture_con_substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_ice1712_playback_ops = { + open: snd_ice1712_playback_open, + close: snd_ice1712_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_playback_prepare, + trigger: snd_ice1712_playback_trigger, + pointer: snd_ice1712_playback_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_playback_ds_ops = { + open: snd_ice1712_playback_ds_open, + close: snd_ice1712_playback_ds_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_playback_ds_prepare, + trigger: snd_ice1712_playback_ds_trigger, + pointer: snd_ice1712_playback_ds_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_capture_ops = { + open: snd_ice1712_capture_open, + close: snd_ice1712_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_capture_prepare, + trigger: snd_ice1712_capture_trigger, + pointer: snd_ice1712_capture_pointer, +}; + +static void snd_ice1712_pcm_free(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 consumer"); + ice->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + + if (rpcm) + *rpcm = pcm; + + printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n"); + + return 0; +} + +static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm_ds = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_free_ds; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 consumer (DS)"); + ice->pcm_ds = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +/* + * PCM code - professional part (multitrack) + */ + +static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000 }; + +#define RATES sizeof(rates) / sizeof(rates[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + count: RATES, + list: rates, + mask: 0, +}; + +#if 0 + +static int snd_ice1712_playback_pro_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_IOCTL1_DIG_INFO: + { + snd_pcm_dig_info_t *info = arg; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + return snd_ice1712_dig_cs8403_info(substream, info); + } + } + case SNDRV_PCM_IOCTL1_DIG_PARAMS: + { + snd_pcm_dig_params_t *params = arg; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + return snd_ice1712_dig_cs8403_params(substream, params); + } + } + default: + break; + } + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +#endif + +static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + unsigned int what; + unsigned int old; + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + what = ICE1712_PLAYBACK_PAUSE; + snd_pcm_trigger_done(substream, substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT(ice, PLAYBACK_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + old |= what; + else + old &= ~what; + outl(old, ICEMT(ice, PLAYBACK_CONTROL)); + spin_unlock(&ice->reg_lock); + break; + } + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + unsigned int old; + snd_pcm_substream_t *s = substream; + do { + if (s == ice->playback_pro_substream) { + what |= ICE1712_PLAYBACK_START; + snd_pcm_trigger_done(s, substream); + } else if (s == ice->capture_pro_substream) { + what |= ICE1712_CAPTURE_START_SHADOW; + snd_pcm_trigger_done(s, substream); + } + s = s->link_next; + } while (s != substream); + spin_lock(&ice->reg_lock); + old = inl(ICEMT(ice, PLAYBACK_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_START) + old |= what; + else + old &= ~what; + outl(old, ICEMT(ice, PLAYBACK_CONTROL)); + spin_unlock(&ice->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned int tmp; + int change; + + ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream); + snd_ice1712_set_pro_rate(ice, substream); + spin_lock_irqsave(&ice->reg_lock, flags); + outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR)); + outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE)); + outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT)); + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + /* setup S/PDIF */ + tmp = ice->cs8403_spdif_stream_bits; + if (tmp & 0x01) /* consumer */ + tmp &= (tmp & 0x01) ? ~0x06 : ~0x18; + switch (substream->runtime->rate) { + case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break; + case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break; + case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break; + default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break; + } + change = ice->cs8403_spdif_stream_bits != tmp; + ice->cs8403_spdif_stream_bits = tmp; + spin_unlock_irqrestore(&ice->reg_lock, flags); + if (change) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id); + snd_ice1712_delta_cs8403_spdif_write(ice, tmp); + return 0; + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_AUDIOPHILE: + snd_cs8427_iec958_pcm(ice->cs8427, substream->runtime->rate); + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + /* setup S/PDIF */ + tmp = ice->cs8403_spdif_stream_bits; + if (tmp & 0x10) /* consumer */ + tmp &= (tmp & 0x01) ? ~0x06 : ~0x60; + switch (substream->runtime->rate) { + case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break; + case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break; + case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break; + default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break; + } + change = ice->cs8403_spdif_stream_bits != tmp; + ice->cs8403_spdif_stream_bits = tmp; + spin_unlock_irqrestore(&ice->reg_lock, flags); + if (change) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id); + snd_ice1712_ews_cs8404_spdif_write(ice, tmp); + return 0; + } + + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream); + snd_ice1712_set_pro_rate(ice, substream); + spin_lock_irqsave(&ice->reg_lock, flags); + outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR)); + outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE)); + outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START)) + return 0; + ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW)) + return 0; + ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_ice1712_playback_pro = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + rate_min: 4000, + rate_max: 96000, + channels_min: 10, + channels_max: 10, + buffer_bytes_max: (256*1024), + period_bytes_min: 10 * 4 * 2, + period_bytes_max: 131040, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ice1712_capture_pro = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000, + rate_min: 4000, + rate_max: 96000, + channels_min: 12, + channels_max: 12, + buffer_bytes_max: (256*1024), + period_bytes_min: 12 * 4 * 2, + period_bytes_max: 131040, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_pro_substream = substream; + runtime->hw = snd_ice1712_playback_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + + ice->cs8403_spdif_stream_bits = ice->cs8403_spdif_bits; + if (ice->cs8427) + snd_cs8427_iec958_active(ice->cs8427, 1); + + return 0; +} + +static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + ice->capture_pro_substream = substream; + runtime->hw = snd_ice1712_capture_pro; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + return 0; +} + +static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->playback_pro_substream = NULL; + if (ice->cs8427) + snd_cs8427_iec958_active(ice->cs8427, 0); + + return 0; +} + +static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + ice->capture_pro_substream = NULL; + return 0; +} + +static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice->pcm_pro = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static snd_pcm_ops_t snd_ice1712_playback_pro_ops = { + open: snd_ice1712_playback_pro_open, + close: snd_ice1712_playback_pro_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_playback_pro_prepare, + trigger: snd_ice1712_pro_trigger, + pointer: snd_ice1712_playback_pro_pointer, +}; + +static snd_pcm_ops_t snd_ice1712_capture_pro_ops = { + open: snd_ice1712_capture_pro_open, + close: snd_ice1712_capture_pro_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ice1712_hw_params, + hw_free: snd_ice1712_hw_free, + prepare: snd_ice1712_capture_pro_prepare, + trigger: snd_ice1712_pro_trigger, + pointer: snd_ice1712_capture_pro_pointer, +}; + +static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops); + + pcm->private_data = ice; + pcm->private_free = snd_ice1712_pcm_profi_free; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1712 multi"); + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + + ice->pcm_pro = pcm; + if (rpcm) + *rpcm = pcm; + + if (ice->cs8427) { + /* assign channels to iec958 */ + err = snd_cs8427_iec958_build(ice->cs8427, + pcm->streams[0].substream, + pcm->streams[1].substream); + if (err < 0) + return err; + } + + if ((err = snd_ice1712_build_pro_mixer(ice)) < 0) + return err; + return 0; +} + +/* + * Mixer section + */ + +static void snd_ice1712_update_volume(ice1712_t *ice, int index) +{ + unsigned int vol = ice->pro_volumes[index]; + unsigned short val = 0; + + val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f; + val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8; + outb(index, ICEMT(ice, MONITOR_INDEX)); + outw(val, ICEMT(ice, MONITOR_VOLUME)); +} + +static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int index = kcontrol->private_value; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1); + ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + unsigned int nval, change; + + nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) | + (ucontrol->value.integer.value[1] ? 0 : 0x80000000); + spin_lock_irqsave(&ice->reg_lock, flags); + nval |= ice->pro_volumes[index] & ~0x80008000; + change = nval != ice->pro_volumes[index]; + ice->pro_volumes[index] = nval; + snd_ice1712_update_volume(ice, index); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return change; +} + +static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 96; + return 0; +} + +static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int index = kcontrol->private_value; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127; + ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127; + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int index = kcontrol->private_value; + unsigned int nval, change; + + nval = (ucontrol->value.integer.value[0] & 127) | + ((ucontrol->value.integer.value[1] & 127) << 16); + spin_lock_irqsave(&ice->reg_lock, flags); + nval |= ice->pro_volumes[index] & ~0x007f007f; + change = nval != ice->pro_volumes[index]; + ice->pro_volumes[index] = nval; + snd_ice1712_update_volume(ice, index); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return change; +} + +static int snd_ice1712_ak4524_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_ice1712_ak4524_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + ucontrol->value.integer.value[0] = ice->ak4524_images[chip][addr]; + return 0; +} + +static int snd_ice1712_ak4524_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + unsigned char nval = ucontrol->value.integer.value[0]; + int change = ice->ak4524_images[chip][addr] != nval; + if (change) + snd_ice1712_ak4524_write(ice, chip, addr, nval); + return change; +} + +static int snd_ice1712_ak4524_ipga_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 36; + return 0; +} + +static int snd_ice1712_ak4524_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + ucontrol->value.integer.value[0] = ice->ak4524_ipga_gain[chip][addr-4] & 0x7f; + return 0; +} + +static int snd_ice1712_ak4524_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->private_value / 8; + int addr = kcontrol->private_value % 8; + unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80; + int change = ice->ak4524_ipga_gain[chip][addr] != nval; + if (change) + snd_ice1712_ak4524_write(ice, chip, addr, nval); + return change; +} + +static int snd_ice1712_ak4524_deemphasis_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[4] = { + "44.1kHz", "Off", "48kHz", "32kHz", + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_ak4524_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->id.index; + ucontrol->value.enumerated.item[0] = ice->ak4524_images[chip][3] & 3; + return 0; +} + +static int snd_ice1712_ak4524_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int chip = kcontrol->id.index; + unsigned char nval = ucontrol->value.enumerated.item[0]; + int change; + nval |= (nval & 3) | (ice->ak4524_images[chip][3] & ~3); + change = ice->ak4524_images[chip][3] != nval; + if (change) + snd_ice1712_ak4524_write(ice, chip, 3, nval); + return change; +} + +static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice) +{ + snd_card_t * card = ice->card; + snd_kcontrol_t ctl; + int idx, err; + + /* PCM playback */ + for (idx = 0; idx < 10; idx++) { + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Playback Switch"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_switch_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_switch_get; + ctl.put = snd_ice1712_pro_mixer_switch_put; + ctl.private_value = idx; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Playback Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_volume_get; + ctl.put = snd_ice1712_pro_mixer_volume_put; + ctl.private_value = idx; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + } + + /* PCM capture */ + for (idx = 0; idx < 10; idx++) { + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Capture Switch"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_switch_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_switch_get; + ctl.put = snd_ice1712_pro_mixer_switch_put; + ctl.private_value = idx + 10; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Multi Capture Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_pro_mixer_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_pro_mixer_volume_get; + ctl.put = snd_ice1712_pro_mixer_volume_put; + ctl.private_value = idx + 10; + ctl.private_data = ice; + if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0) + return err; + } + + /* initialize volumes */ + for (idx = 0; idx < 20; idx++) { + ice->pro_volumes[idx] = 0x80008000; /* mute */ + snd_ice1712_update_volume(ice, idx); + } + return 0; +} + +static void snd_ice1712_ac97_init(ac97_t *ac97) +{ + // ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return); + + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_ice1712_mixer_free_ac97(ac97_t *ac97) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return); + ice->ac97 = NULL; +} + +static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) +{ + int err; + + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) { + ac97_t ac97; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ice1712_ac97_write; + ac97.read = snd_ice1712_ac97_read; + ac97.init = snd_ice1712_ac97_init; + ac97.private_data = ice; + ac97.private_free = snd_ice1712_mixer_free_ac97; + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { + printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n"); + // return err; + } else { + if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0) + return err; + } + return 0; + } + /* hmm.. can we have both consumer and pro ac97 mixers? */ + if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) { + ac97_t ac97; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ice1712_pro_ac97_write; + ac97.read = snd_ice1712_pro_ac97_read; + ac97.init = snd_ice1712_ac97_init; + ac97.private_data = ice; + ac97.private_free = snd_ice1712_mixer_free_ac97; + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { + printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n"); + // return err; + } + return 0; + } + /* I2S mixer only */ + strcat(ice->card->mixername, "ICE1712 - multitrack"); + return 0; +} + +/* + * + */ + +static void snd_ice1712_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return); + unsigned int idx; + + snd_iprintf(buffer, "ICE1712\n\n"); + snd_iprintf(buffer, "EEPROM:\n"); + snd_iprintf(buffer, " Subvendor : 0x%x\n", ice->eeprom.subvendor); + snd_iprintf(buffer, " Size : %i bytes\n", ice->eeprom.size); + snd_iprintf(buffer, " Version : %i\n", ice->eeprom.version); + snd_iprintf(buffer, " Codec : 0x%x\n", ice->eeprom.codec); + snd_iprintf(buffer, " ACLink : 0x%x\n", ice->eeprom.aclink); + snd_iprintf(buffer, " I2S ID : 0x%x\n", ice->eeprom.i2sID); + snd_iprintf(buffer, " S/PDIF : 0x%x\n", ice->eeprom.spdif); + snd_iprintf(buffer, " GPIO mask : 0x%x\n", ice->eeprom.gpiomask); + snd_iprintf(buffer, " GPIO state : 0x%x\n", ice->eeprom.gpiostate); + snd_iprintf(buffer, " GPIO direction : 0x%x\n", ice->eeprom.gpiodir); + snd_iprintf(buffer, " AC'97 main : 0x%x\n", ice->eeprom.ac97main); + snd_iprintf(buffer, " AC'97 pcm : 0x%x\n", ice->eeprom.ac97pcm); + snd_iprintf(buffer, " AC'97 record : 0x%x\n", ice->eeprom.ac97rec); + snd_iprintf(buffer, " AC'97 record src : 0x%x\n", ice->eeprom.ac97recsrc); + for (idx = 0; idx < 4; idx++) + snd_iprintf(buffer, " DAC ID #%i : 0x%x\n", idx, ice->eeprom.dacID[idx]); + for (idx = 0; idx < 4; idx++) + snd_iprintf(buffer, " ADC ID #%i : 0x%x\n", idx, ice->eeprom.adcID[idx]); + for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++) + snd_iprintf(buffer, " Extra #%02i : 0x%x\n", idx, ice->eeprom.extra[idx - 0x1c]); +} + +static void __devinit snd_ice1712_proc_init(ice1712_t * ice) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = ice; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 2048; + entry->c.text.read = snd_ice1712_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + ice->proc_entry = entry; +} + +static void snd_ice1712_proc_done(ice1712_t * ice) +{ + if (ice->proc_entry) { + snd_info_unregister(ice->proc_entry); + ice->proc_entry = NULL; + } +} + +/* + * + */ + +static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 32; + return 0; +} + +static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + memcpy(ucontrol->value.bytes.data, &ice->eeprom, 32); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_eeprom __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_CARD, + name: "ICE1712 EEPROM", + access: SNDRV_CTL_ELEM_ACCESS_READ, + info: snd_ice1712_eeprom_info, + get: snd_ice1712_eeprom_get +}; + +/* + */ +static int snd_ice1712_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits); + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits); + break; + } + return 0; +} + +static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_bits != val; + ice->cs8403_spdif_bits = val; + if (change && ice->playback_pro_substream == NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_delta_cs8403_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_bits != val; + ice->cs8403_spdif_bits = val; + if (change && ice->playback_pro_substream == NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_ews_cs8404_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + default: + change = 0; + } + return change; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_ice1712_spdif_default_info, + get: snd_ice1712_spdif_default_get, + put: snd_ice1712_spdif_default_put +}; + +static int snd_ice1712_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_NOT_COPYRIGHT | + IEC958_AES0_CON_EMPHASIS; + ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL | + IEC958_AES1_CON_CATEGORY; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS; + break; + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + break; + } + return 0; +} + +static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_FS | + IEC958_AES0_PRO_EMPHASIS; + ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE; + break; + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + break; + } + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_ice1712_spdif_mask_info, + get: snd_ice1712_spdif_maskc_get, +}; + +static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_ice1712_spdif_mask_info, + get: snd_ice1712_spdif_maskp_get, +}; + +static int snd_ice1712_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits); + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits); + break; + } + return 0; +} + +static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_stream_bits != val; + ice->cs8403_spdif_stream_bits = val; + if (change && ice->playback_pro_substream != NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_delta_cs8403_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958); + spin_lock_irqsave(&ice->reg_lock, flags); + change = ice->cs8403_spdif_stream_bits != val; + ice->cs8403_spdif_stream_bits = val; + if (change && ice->playback_pro_substream != NULL) { + spin_unlock_irqrestore(&ice->reg_lock, flags); + snd_ice1712_ews_cs8404_spdif_write(ice, val); + } else { + spin_unlock_irqrestore(&ice->reg_lock, flags); + } + break; + default: + change = 0; + } + return change; +} + +static snd_kcontrol_new_t snd_ice1712_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_ice1712_spdif_stream_info, + get: snd_ice1712_spdif_stream_get, + put: snd_ice1712_spdif_stream_put +}; + +#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \ +{ iface: xiface, name: xname, access: xaccess, info: snd_ice1712_gpio_info, \ + get: snd_ice1712_gpio_get, put: snd_ice1712_gpio_put, \ + private_value: mask | (invert << 24) } + +static int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0; + unsigned char saved[2]; + + save_gpio_status(ice, saved); + ucontrol->value.integer.value[0] = (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0) ^ invert; + restore_gpio_status(ice, saved); + return 0; +} + +static int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value & (1<<24)) ? mask : 0; + unsigned char saved[2]; + int val, nval; + + if (kcontrol->private_value & (1 << 31)) + return -EPERM; + nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert; + save_gpio_status(ice, saved); + val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + nval |= val & ~mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval); + restore_gpio_status(ice, saved); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0); +static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); +static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0); +static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata = +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); + +static int snd_ice1712_pro_spdif_master_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_pro_spdif_master_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER ? 1 : 0; + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_spdif_master_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int nval, change; + + nval = ucontrol->value.integer.value[0] ? ICE1712_SPDIF_MASTER : 0; + spin_lock_irqsave(&ice->reg_lock, flags); + nval |= inb(ICEMT(ice, RATE)) & ~ICE1712_SPDIF_MASTER; + change = inb(ICEMT(ice, RATE)) != nval; + outb(nval, ICEMT(ice, RATE)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + + if (ice->cs8427) { + /* change CS8427 clock source too */ + snd_ice1712_cs8427_set_input_clock(ice, ucontrol->value.integer.value[0]); + } + + return change; +} + +static snd_kcontrol_new_t snd_ice1712_pro_spdif_master __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Multi Track IEC958 Master", + info: snd_ice1712_pro_spdif_master_info, + get: snd_ice1712_pro_spdif_master_get, + put: snd_ice1712_pro_spdif_master_put +}; + +/* + * routing + */ +static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[] = { + "PCM Out", /* 0 */ + "H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */ + "H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */ + "IEC958 In L", "IEC958 In R", /* 9-10 */ + "Digital Mixer", /* 11 - optional */ + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + unsigned int val, cval; + val = inw(ICEMT(ice, ROUTE_PSDOUT03)); + val >>= ((idx % 2) * 8) + ((idx / 2) * 2); + val &= 3; + cval = inl(ICEMT(ice, ROUTE_CAPTURE)); + cval >>= ((idx / 2) * 8) + ((idx % 2) * 4); + if (val == 1 && idx < 2) + ucontrol->value.enumerated.item[0] = 11; + else if (val == 2) + ucontrol->value.enumerated.item[0] = (cval & 7) + 1; + else if (val == 3) + ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9; + else + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change, shift; + int idx = kcontrol->id.index; + unsigned int val, old_val, nval; + + /* update PSDOUT */ + if (ucontrol->value.enumerated.item[0] >= 11) + nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */ + else if (ucontrol->value.enumerated.item[0] >= 9) + nval = 3; /* spdif in */ + else if (ucontrol->value.enumerated.item[0] >= 1) + nval = 2; /* analog in */ + else + nval = 0; /* pcm */ + shift = ((idx % 2) * 8) + ((idx / 2) * 2); + val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03)); + val &= ~(0x03 << shift); + val |= nval << shift; + change = val != old_val; + if (change) + outw(val, ICEMT(ice, ROUTE_PSDOUT03)); + if (nval < 2) /* dig mixer of pcm */ + return change; + + /* update CAPTURE */ + val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE)); + shift = ((idx / 2) * 8) + ((idx % 2) * 4); + if (nval == 2) { /* analog in */ + nval = ucontrol->value.enumerated.item[0] - 1; + val &= ~(0x07 << shift); + val |= nval << shift; + } else { /* spdif in */ + nval = (ucontrol->value.enumerated.item[0] - 9) << 3; + val &= ~(0x08 << shift); + val |= nval << shift; + } + if (val != old_val) { + change = 1; + outl(val, ICEMT(ice, ROUTE_CAPTURE)); + } + return change; +} + +static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->id.index; + unsigned int val, cval; + val = inw(ICEMT(ice, ROUTE_SPDOUT)); + cval = (val >> (idx * 4 + 8)) & 0x0f; + val = (val >> (idx * 2)) & 0x03; + if (val == 1) + ucontrol->value.enumerated.item[0] = 11; + else if (val == 2) + ucontrol->value.enumerated.item[0] = (cval & 7) + 1; + else if (val == 3) + ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9; + else + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change, shift; + int idx = kcontrol->id.index; + unsigned int val, old_val, nval; + + /* update SPDOUT */ + val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT)); + if (ucontrol->value.enumerated.item[0] >= 11) + nval = 1; + else if (ucontrol->value.enumerated.item[0] >= 9) + nval = 3; + else if (ucontrol->value.enumerated.item[0] >= 1) + nval = 2; + else + nval = 0; + shift = idx * 2; + val &= ~(0x03 << shift); + val |= nval << shift; + shift = idx * 4 + 8; + if (nval == 2) { + nval = ucontrol->value.enumerated.item[0] - 1; + val &= ~(0x07 << shift); + val |= nval << shift; + } else if (nval == 3) { + nval = (ucontrol->value.enumerated.item[0] - 9) << 3; + val &= ~(0x08 << shift); + val |= nval << shift; + } + change = val != old_val; + if (change) + outw(val, ICEMT(ice, ROUTE_SPDOUT)); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "H/W Playback Route", + info: snd_ice1712_pro_route_info, + get: snd_ice1712_pro_route_analog_get, + put: snd_ice1712_pro_route_analog_put, +}; + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "IEC958 Playback Route", + info: snd_ice1712_pro_route_info, + get: snd_ice1712_pro_route_spdif_get, + put: snd_ice1712_pro_route_spdif_put, +}; + + +static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int change; + + spin_lock_irqsave(&ice->reg_lock, flags); + change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0]; + outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE)); + spin_unlock_irqrestore(&ice->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Multi Track Volume Rate", + info: snd_ice1712_pro_volume_rate_info, + get: snd_ice1712_pro_volume_rate_get, + put: snd_ice1712_pro_volume_rate_put +}; + +static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 22; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int idx; + + spin_lock_irqsave(&ice->reg_lock, flags); + for (idx = 0; idx < 22; idx++) { + outb(idx, ICEMT(ice, MONITOR_PEAKINDEX)); + ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA)); + } + spin_unlock_irqrestore(&ice->reg_lock, flags); + return 0; +} + +static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Multi Track Peak", + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + info: snd_ice1712_pro_peak_info, + get: snd_ice1712_pro_peak_get +}; + +/* + * EWX 24/96 + */ + +static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ + + static char *texts[4] = { + "+4dBu", "-10dBV", + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + unsigned char saved[2]; + + save_gpio_status(ice, saved); + ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0; + restore_gpio_status(ice, saved); + return 0; +} + +static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char mask = kcontrol->private_value & 0xff; + unsigned char saved[2]; + int val, nval; + + if (kcontrol->private_value & (1 << 31)) + return -EPERM; + nval = ucontrol->value.enumerated.item[0] ? mask : 0; + save_gpio_status(ice, saved); + val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + nval |= val & ~mask; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval); + restore_gpio_status(ice, saved); + return val != nval; +} + +static snd_kcontrol_new_t snd_ice1712_ewx_input_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Input Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ewx_io_sense_get, + put: snd_ice1712_ewx_io_sense_put, + private_value: ICE1712_EWX2496_AIN_SEL, +}; + +static snd_kcontrol_new_t snd_ice1712_ewx_output_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Output Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ewx_io_sense_get, + put: snd_ice1712_ewx_io_sense_put, + private_value: ICE1712_EWX2496_AOUT_SEL, +}; + + +/* + * EWS88MT + */ +/* analog output sensitivity;; address 0x48 bit 6 */ +static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char data; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */ + return 0; +} + +/* analog output sensitivity;; address 0x48 bit 6 */ +static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char data, ndata; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0); + if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return ndata != data; +} + +/* analog input sensitivity; address 0x46 */ +static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int channel = kcontrol->id.index; + unsigned char data; + + snd_assert(channel >= 0 && channel <= 7, return 0); + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + /* reversed; high = +4dBu, low = -10dBV */ + ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1; + return 0; +} + +/* analog output sensitivity; address 0x46 */ +static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int channel = kcontrol->id.index; + unsigned char data, ndata; + + snd_assert(channel >= 0 && channel <= 7, return 0); + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel)); + if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[0], &ndata, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return ndata != data; +} + +static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Input Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ews88mt_input_sense_get, + put: snd_ice1712_ews88mt_input_sense_put, +}; + +static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Output Sensitivity Switch", + info: snd_ice1712_ewx_io_sense_info, + get: snd_ice1712_ews88mt_output_sense_get, + put: snd_ice1712_ews88mt_output_sense_put, +}; + + +/* + * EWS88D controls + */ + +static int snd_ice1712_ews88d_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + unsigned char data[2]; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + data[0] = (data[shift >> 3] >> (shift & 7)) & 0x01; + if (invert) + data[0] ^= 0x01; + ucontrol->value.integer.value[0] = data[0]; + return 0; +} + +static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + unsigned char data[2], ndata[2]; + int change; + + snd_i2c_lock(ice->i2c); + if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + ndata[shift >> 3] = data[shift >> 3] & ~(1 << (shift & 7)); + if (invert) { + if (! ucontrol->value.integer.value[0]) + ndata[shift >> 3] |= (1 << (shift & 7)); + } else { + if (ucontrol->value.integer.value[0]) + ndata[shift >> 3] |= (1 << (shift & 7)); + } + change = (data[shift >> 3] != ndata[shift >> 3]); + if (change && snd_i2c_sendbytes(ice->pcf8575, data, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return change; +} + +#define EWS88D_CONTROL(xiface, xname, xshift, xinvert, xaccess) \ +{ iface: xiface,\ + name: xname,\ + access: xaccess,\ + info: snd_ice1712_ews88d_control_info,\ + get: snd_ice1712_ews88d_control_get,\ + put: snd_ice1712_ews88d_control_put,\ + private_value: xshift | (xinvert << 8),\ +} + +static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = { + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */ + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 3, 0, 0), + EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 4, 1, 0), +}; + + +/* + * DMX 6Fire controls + */ + +#if 0 // XXX not working yet +static int snd_ice1712_6fire_read_pca(ice1712_t *ice) +{ + unsigned char byte; + snd_i2c_lock(ice->i2c); + byte = 0; /* read port */ + snd_i2c_sendbytes(ice->pcf8575, &byte, 1); + if (snd_i2c_readbytes(ice->pcf8575, &byte, 1) != 1) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return byte; +} + +static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char data) +{ + unsigned char bytes[2]; + snd_i2c_lock(ice->i2c); + bytes[0] = 1; /* write port */ + bytes[1] = data; + if (snd_i2c_sendbytes(ice->pcf8575, bytes, 2) != 2) { + snd_i2c_unlock(ice->i2c); + return -EREMOTE; + } + snd_i2c_unlock(ice->i2c); + return 0; +} + +static int snd_ice1712_6fire_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int data; + + if ((data = snd_ice1712_6fire_read_pca(ice)) < 0) + return data; + data = (data >> shift) & 1; + if (invert) + data ^= 1; + ucontrol->value.integer.value[0] = data; + return 0; +} + +static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int shift = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 8) & 1; + int data, ndata; + + if ((data = snd_ice1712_6fire_read_pca(ice)) < 0) + return data; + ndata = data & ~(1 << shift); + if (ucontrol->value.integer.value[0]) + ndata |= (1 << shift); + if (invert) + ndata ^= (1 << shift); + if (data != ndata) { + snd_ice1712_6fire_write_pca(ice, (unsigned char)ndata); + return 1; + } + return 0; +} + +#define DMX6FIRE_CONTROL(xiface, xname, xshift, xinvert, xaccess) \ +{ iface: xiface,\ + name: xname,\ + access: xaccess,\ + info: snd_ice1712_6fire_control_info,\ + get: snd_ice1712_6fire_control_get,\ + put: snd_ice1712_6fire_control_put,\ + private_value: xshift | (xinvert << 8),\ +} + +static snd_kcontrol_new_t snd_ice1712_6fire_led __devinitdata = +DMX6FIRE_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Breakbox LED", 6, 0, 0); + +#endif // XXX not working yet + + +/* + * + */ + +static unsigned char __devinit snd_ice1712_read_i2c(ice1712_t *ice, + unsigned char dev, + unsigned char addr) +{ + long t = 0x10000; + + outb(addr, ICEREG(ice, I2C_BYTE_ADDR)); + outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR)); + while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ; + return inb(ICEREG(ice, I2C_DATA)); +} + +static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice) +{ + int dev = 0xa0; /* EEPROM device address */ + unsigned int idx; + + if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) { + snd_printk("ICE1712 has not detected EEPROM\n"); + return -EIO; + } + ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | + (snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | + (snd_ice1712_read_i2c(ice, dev, 0x03) << 24); + ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04); + if (ice->eeprom.size < 28) { + snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size); + return -EIO; + } + ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05); + if (ice->eeprom.version != 1) { + snd_printk("invalid EEPROM version %i\n", ice->eeprom.version); + return -EIO; + } + ice->eeprom.codec = snd_ice1712_read_i2c(ice, dev, 0x06); + ice->eeprom.aclink = snd_ice1712_read_i2c(ice, dev, 0x07); + ice->eeprom.i2sID = snd_ice1712_read_i2c(ice, dev, 0x08); + ice->eeprom.spdif = snd_ice1712_read_i2c(ice, dev, 0x09); + ice->eeprom.gpiomask = snd_ice1712_read_i2c(ice, dev, 0x0a); + ice->eeprom.gpiostate = snd_ice1712_read_i2c(ice, dev, 0x0b); + ice->eeprom.gpiodir = snd_ice1712_read_i2c(ice, dev, 0x0c); + ice->eeprom.ac97main = (snd_ice1712_read_i2c(ice, dev, 0x0d) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x0e) << 8); + ice->eeprom.ac97pcm = (snd_ice1712_read_i2c(ice, dev, 0x0f) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x10) << 8); + ice->eeprom.ac97rec = (snd_ice1712_read_i2c(ice, dev, 0x11) << 0) | + (snd_ice1712_read_i2c(ice, dev, 0x12) << 8); + ice->eeprom.ac97recsrc = snd_ice1712_read_i2c(ice, dev, 0x13) << 0; + for (idx = 0; idx < 4; idx++) { + ice->eeprom.dacID[idx] = snd_ice1712_read_i2c(ice, dev, 0x14 + idx); + ice->eeprom.adcID[idx] = snd_ice1712_read_i2c(ice, dev, 0x18 + idx); + } + for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++) + ice->eeprom.extra[idx - 0x1c] = snd_ice1712_read_i2c(ice, dev, idx); + return 0; +} + +static void __devinit snd_ice1712_ak4524_init(ice1712_t *ice) +{ + static unsigned char inits[] = { + 0x00, 0x07, /* 0: all power up */ + 0x01, 0x00, /* 1: ADC/DAC reset */ + 0x02, 0x60, /* 2: 24bit I2S */ + 0x03, 0x19, /* 3: deemphasis off */ + 0x01, 0x03, /* 1: ADC/DAC enable */ + 0x04, 0x00, /* 4: ADC left muted */ + 0x05, 0x00, /* 5: ADC right muted */ + 0x04, 0x80, /* 4: ADC IPGA gain 0dB */ + 0x05, 0x80, /* 5: ADC IPGA gain 0dB */ + 0x06, 0x00, /* 6: DAC left muted */ + 0x07, 0x00, /* 7: DAC right muted */ + 0xff, 0xff + }; + int chip, idx; + unsigned char *ptr, reg, data; + + for (chip = idx = 0; chip < ice->num_dacs/2; chip++) { + ptr = inits; + while (*ptr != 0xff) { + reg = *ptr++; + data = *ptr++; + if (ice->ak4528) { + if (reg > 5) + continue; + if (reg >= 4 && (data & 0x80)) + continue; + } + if (reg == 0x03 && ice->ak4528) + data = 0x0d; /* deemphasis off, turn LR highpass filters on */ + snd_ice1712_ak4524_write(ice, chip, reg, data); + } + } +} + +static void __devinit snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte) +{ + byte |= ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); + byte &= ~ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); + byte |= ICE1712_STDSP24_CLOCK_BIT; + udelay(100); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte); +} + +static void __devinit snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate) +{ + down(&ice->gpio_mutex); + ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate) +{ + down(&ice->gpio_mutex); + + /* select box */ + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + + /* prepare for write */ + if (chn == 3) + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + udelay(100); + if (chn == 3) { + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + } else { + switch (chn) { + case 0: ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 0); break; + case 1: ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 0); break; + case 2: ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 0); break; + } + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + } + udelay(100); + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + udelay(100); + + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master, int slave) +{ + down(&ice->gpio_mutex); + + /* select box */ + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, master); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]); + + udelay(100); + + /* MIDI2 is direct */ + ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, slave); + snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]); + + up(&ice->gpio_mutex); +} + +static void __devinit snd_ice1712_stdsp24_init(ice1712_t *ice) +{ + int box, chn; + + ice->hoontech_boxbits[0] = + ice->hoontech_boxbits[1] = + ice->hoontech_boxbits[2] = + ice->hoontech_boxbits[3] = 0; /* should be already */ + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 0, 1); + ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 1, 1); + ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 2); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 2, 1); + ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 3); + ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 3, 1); + ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, 0); + ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, 1); + ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, 0); + + /* let's go - activate only functions in first box */ + ice->hoontech_config = 0; + /* ICE1712_STDSP24_MUTE | + ICE1712_STDSP24_INSEL | + ICE1712_STDSP24_DAREAR; */ + ice->hoontech_boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | + ICE1712_STDSP24_BOX_CHN2 | + ICE1712_STDSP24_BOX_CHN3 | + ICE1712_STDSP24_BOX_CHN4 | + ICE1712_STDSP24_BOX_MIDI1 | + ICE1712_STDSP24_BOX_MIDI2; + ice->hoontech_boxconfig[1] = + ice->hoontech_boxconfig[2] = + ice->hoontech_boxconfig[3] = 0; + snd_ice1712_stdsp24_darear(ice, (ice->hoontech_config & ICE1712_STDSP24_DAREAR) ? 1 : 0); + snd_ice1712_stdsp24_mute(ice, (ice->hoontech_config & ICE1712_STDSP24_MUTE) ? 1 : 0); + snd_ice1712_stdsp24_insel(ice, (ice->hoontech_config & ICE1712_STDSP24_INSEL) ? 1 : 0); + for (box = 0; box < 4; box++) { + for (chn = 0; chn < 4; chn++) + snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->hoontech_boxconfig[box] & (1 << chn)) ? 1 : 0); + snd_ice1712_stdsp24_box_midi(ice, box, + (ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0, + (ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) ? 1 : 0); + } +} + +static int __devinit snd_ice1712_chip_init(ice1712_t *ice) +{ + int err, has_i2c = 0; + + outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL)); + udelay(200); + outb(ICE1712_NATIVE, ICEREG(ice, CONTROL)); + udelay(200); + pci_write_config_byte(ice->pci, 0x60, ice->eeprom.codec); + pci_write_config_byte(ice->pci, 0x61, ice->eeprom.aclink); + pci_write_config_byte(ice->pci, 0x62, ice->eeprom.i2sID); + pci_write_config_byte(ice->pci, 0x63, ice->eeprom.spdif); + if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) { + ice->gpio_write_mask = ice->eeprom.gpiomask; + ice->gpio_direction = ice->eeprom.gpiodir; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate); + } else { + ice->gpio_write_mask = 0xc0; + ice->gpio_direction = 0xff; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff); + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT); + } + snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0); + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) { + outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD)); + udelay(100); + outb(0, ICEREG(ice, AC97_CMD)); + udelay(200); + snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0); + } + + /* determine I2C, DACs and ADCs */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + ice->ak4528 = 1; + /* follow thru */ + case ICE1712_SUBDEVICE_EWX2496: + has_i2c = 1; + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 2; + break; + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_DELTA66: + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 4; + if (ice->omni) + ice->num_total_dacs = 8; + break; + case ICE1712_SUBDEVICE_EWS88MT: + has_i2c = 1; + /* follow thru */ + case ICE1712_SUBDEVICE_DELTA1010: + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 8; + break; + case ICE1712_SUBDEVICE_EWS88D: + has_i2c = 1; + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + has_i2c = 1; + ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 6; + break; + } + + if (has_i2c) { + if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { + snd_printk("unable to create I2C bus\n"); + return err; + } + ice->i2c->private_data = ice; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + ice->i2c->ops = &ap_cs8427_i2c_ops; + break; + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + case ICE1712_SUBDEVICE_DMX6FIRE: + ice->i2c->hw_ops.bit = &snd_ice1712_ewx_cs8427_bit_ops; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + if ((err = snd_cs8427_create(ice->i2c, CS8427_BASE_ADDR, &ice->cs8427)) < 0) { + snd_printk("CS8427 initialization failed\n"); + return err; + } + break; + case ICE1712_SUBDEVICE_DMX6FIRE: +#if 0 // XXX not working yet + if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", 0x40>>1, &ice->pcf8575)) < 0) + return err; + if ((err = snd_cs8427_create(ice->i2c, 0x11, &ice->cs8427)) < 0) { + snd_printk("CS8427 initialization failed\n"); + return err; + } +#endif // XXX not working yet + break; + case ICE1712_SUBDEVICE_EWS88MT: + if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0) + return err; + if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->pcf8574[0])) < 0) + return err; + if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->pcf8574[1])) < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88D: + if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->pcf8575)) < 0) + return err; + break; + } + } + /* second stage of initialization, analog parts and others */ + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_DMX6FIRE: + snd_ice1712_ak4524_init(ice); + break; + case ICE1712_SUBDEVICE_STDSP24: + snd_ice1712_stdsp24_init(ice); + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + /* Set spdif defaults */ + snd_ice1712_delta_cs8403_spdif_write(ice, ice->cs8403_spdif_bits); + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + /* Set spdif defaults */ + snd_ice1712_ews_cs8404_spdif_write(ice, ice->cs8403_spdif_bits); + break; + } + return 0; +} + +static int __init snd_ice1712_build_controls(ice1712_t *ice) +{ + unsigned int idx; + snd_kcontrol_t *kctl; + int err; + + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_spdif_master, ice)); + if (err < 0) + return err; + for (idx = 0; idx < ice->num_total_dacs; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_analog_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + for (idx = 0; idx < 2; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice); + if (kctl == NULL) + return -ENOMEM; + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice)); + if (err < 0) + return err; + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_DELTADIO2496: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice)); + if (err < 0) + return err; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_EWS88D: + snd_assert(ice->pcm_pro != NULL, return -EIO); + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice)); + if (err < 0) + return err; + kctl->id.device = ice->pcm_pro->device; + ice->spdif_stream_ctl = kctl; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_DELTA1010: + case ICE1712_SUBDEVICE_DELTADIO2496: + case ICE1712_SUBDEVICE_DELTA66: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice)); + if (err < 0) + return err; + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + case ICE1712_SUBDEVICE_AUDIOPHILE: + case ICE1712_SUBDEVICE_DELTA44: + case ICE1712_SUBDEVICE_DELTA66: + case ICE1712_SUBDEVICE_EWS88MT: + case ICE1712_SUBDEVICE_DMX6FIRE: + for (idx = 0; idx < ice->num_dacs; ++idx) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "DAC Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_volume_get; + ctl.put = snd_ice1712_ak4524_volume_put; + if (ice->ak4528) + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */ + else + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 6; /* register 6 & 7 */ + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + for (idx = 0; idx < ice->num_adcs && !ice->ak4528; ++idx) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "ADC Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_volume_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_volume_get; + ctl.put = snd_ice1712_ak4524_volume_put; + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */ + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "IPGA Analog Capture Volume"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_ipga_gain_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_ipga_gain_get; + ctl.put = snd_ice1712_ak4524_ipga_gain_put; + ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */ + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + for (idx = 0; idx < ice->num_dacs/2; idx++) { + snd_kcontrol_t ctl; + memset(&ctl, 0, sizeof(ctl)); + strcpy(ctl.id.name, "Deemphasis"); + ctl.id.index = idx; + ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl.info = snd_ice1712_ak4524_deemphasis_info; + ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl.get = snd_ice1712_ak4524_deemphasis_get; + ctl.put = snd_ice1712_ak4524_deemphasis_put; + ctl.private_data = ice; + if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0) + return err; + } + break; + } + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_EWX2496: + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_input_sense, ice)); + if (err < 0) + return err; + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_output_sense, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88MT: + for (idx = 0; idx < 8; idx++) { + kctl = snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice); + kctl->id.index = idx; + err = snd_ctl_add(ice->card, kctl); + if (err < 0) + return err; + } + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice)); + if (err < 0) + return err; + break; + case ICE1712_SUBDEVICE_EWS88D: + for (idx = 0; idx < sizeof(snd_ice1712_ews88d_controls)/sizeof(snd_ice1712_ews88d_controls[0]); idx++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_controls[idx], ice)); + if (err < 0) + return err; + } + break; + case ICE1712_SUBDEVICE_DMX6FIRE: +#if 0 // XXX not working yet + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_led, ice)); + if (err < 0) + return err; +#endif + break; + } + + return 0; +} + +static int snd_ice1712_free(ice1712_t *ice) +{ + if (ice->res_port == NULL) + goto __hw_end; + /* mask all interrupts */ + outb(0xc0, ICEMT(ice, IRQ)); + outb(0xff, ICEREG(ice, IRQMASK)); + /* --- */ + __hw_end: + snd_ice1712_proc_done(ice); + synchronize_irq(); + if (ice->irq) + free_irq(ice->irq, (void *) ice); + if (ice->res_port) { + release_resource(ice->res_port); + kfree_nocheck(ice->res_port); + } + if (ice->res_ddma_port) { + release_resource(ice->res_ddma_port); + kfree_nocheck(ice->res_ddma_port); + } + if (ice->res_dmapath_port) { + release_resource(ice->res_dmapath_port); + kfree_nocheck(ice->res_dmapath_port); + } + if (ice->res_profi_port) { + release_resource(ice->res_profi_port); + kfree_nocheck(ice->res_profi_port); + } + snd_magic_kfree(ice); + return 0; +} + +static int snd_ice1712_dev_free(snd_device_t *device) +{ + ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO); + return snd_ice1712_free(ice); +} + +static int __devinit snd_ice1712_create(snd_card_t * card, + struct pci_dev *pci, + int omni, + ice1712_t ** r_ice1712) +{ + ice1712_t *ice; + int err; + static snd_device_ops_t ops = { + dev_free: snd_ice1712_dev_free, + }; + + *r_ice1712 = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL); + if (ice == NULL) + return -ENOMEM; + ice->omni = omni ? 1 : 0; + spin_lock_init(&ice->reg_lock); + init_MUTEX(&ice->gpio_mutex); + ice->cs8403_spdif_bits = + ice->cs8403_spdif_stream_bits = (0x01 | /* consumer format */ + 0x10 | /* no emphasis */ + 0x20); /* PCM encoder/decoder */ + ice->card = card; + ice->pci = pci; + ice->irq = -1; + ice->port = pci_resource_start(pci, 0); + ice->ddma_port = pci_resource_start(pci, 1); + ice->dmapath_port = pci_resource_start(pci, 2); + ice->profi_port = pci_resource_start(pci, 3); + pci_set_master(pci); + pci_write_config_word(ice->pci, 0x40, 0x807f); + pci_write_config_word(ice->pci, 0x42, 0x0006); + snd_ice1712_proc_init(ice); + synchronize_irq(); + + if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1); + return -EIO; + } + if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1); + return -EIO; + } + if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1); + return -EIO; + } + if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) { + snd_ice1712_free(ice); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1); + return -EIO; + } + if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) { + snd_ice1712_free(ice); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EIO; + } + ice->irq = pci->irq; + + if (snd_ice1712_read_eeprom(ice) < 0) { + snd_ice1712_free(ice); + return -EIO; + } + if (snd_ice1712_chip_init(ice) < 0) { + snd_ice1712_free(ice); + return -EIO; + } + + /* unmask used interrupts */ + outb((ice->eeprom.codec & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 | + (ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0, + ICEREG(ice, IRQMASK)); + outb(0x00, ICEMT(ice, IRQ)); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) { + snd_ice1712_free(ice); + return err; + } + + *r_ice1712 = ice; + return 0; +} + +static int __devinit snd_ice1712_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ice1712_t *ice; + int pcm_dev = 0, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ice1712_create(card, pci, snd_omni[dev], &ice)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) + if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_ac97_mixer(ice)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ice1712_build_controls(ice)) < 0) { + snd_card_free(card); + return err; + } + + if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) + if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "ICE1712"); + strcpy(card->shortname, "ICEnsemble ICE1712"); + + switch (ice->eeprom.subvendor) { + case ICE1712_SUBDEVICE_STDSP24: + strcpy(card->shortname, "Hoontech SoundTrack Audio DSP24"); + break; + case ICE1712_SUBDEVICE_DELTA1010: + strcpy(card->shortname, "M Audio Delta 1010"); + break; + case ICE1712_SUBDEVICE_DELTADIO2496: + strcpy(card->shortname, "M Audio Delta DiO 2496"); + goto __no_mpu401; + case ICE1712_SUBDEVICE_DELTA66: + strcpy(card->shortname, "M Audio Delta 66"); + goto __no_mpu401; + case ICE1712_SUBDEVICE_DELTA44: + strcpy(card->shortname, "M Audio Delta 44"); + goto __no_mpu401; + case ICE1712_SUBDEVICE_AUDIOPHILE: + strcpy(card->shortname, "M Audio Audiophile 24/96"); + break; + case ICE1712_SUBDEVICE_EWX2496: + strcpy(card->shortname, "TerraTec EWX 24/96"); + break; + case ICE1712_SUBDEVICE_EWS88MT: + strcpy(card->shortname, "TerraTec EWS 88MT"); + break; + case ICE1712_SUBDEVICE_EWS88D: + strcpy(card->shortname, "TerraTec EWS 88D"); + break; + case ICE1712_SUBDEVICE_DMX6FIRE: + strcpy(card->shortname, "TerraTec DMX 6Fire"); + break; + } + + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, + ICEREG(ice, MPU1_CTRL), 1, + ice->irq, 0, + &ice->rmidi[0])) < 0) { + snd_card_free(card); + return err; + } + + if (ice->eeprom.codec & ICE1712_CFG_2xMPU401) + if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712, + ICEREG(ice, MPU2_CTRL), 1, + ice->irq, 0, + &ice->rmidi[1])) < 0) { + snd_card_free(card); + return err; + } + + __no_mpu401: + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, ice->port, ice->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_ice1712_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "ICE1712", + id_table: snd_ice1712_ids, + probe: snd_ice1712_probe, + remove: __devexit_p(snd_ice1712_remove), +}; + +static int __init alsa_card_ice1712_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "ICE1712 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ice1712_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ice1712_init) +module_exit(alsa_card_ice1712_exit) + +#ifndef MODULE + +/* format is: snd-ice1712=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_ice1712_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ice1712=", alsa_card_ice1712_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/intel8x0.c linux-2.4.19-pre5-mjc/sound/pci/intel8x0.c --- linux/sound/pci/intel8x0.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/intel8x0.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1598 @@ +/* + * ALSA driver for Intel ICH (i8x0) chipsets + * + * Copyright (c) 2000 Jaroslav Kysela + * + * + * This code also contains alpha support for SiS 735 chipsets provided + * by Mike Pieper . We have no datasheet + * for SiS735, so the code is not fully functional. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,MX440; SiS 7012"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Intel,82801AA}," + "{Intel,82901AB}," + "{Intel,82801BA}," + "{Intel,ICH3}," + "{Intel,MX440}," + "{SiS,SI7012}," + "{NVidia,NForce Audio}}"); + +#define SUPPORT_JOYSTICK 1 +#define SUPPORT_MIDI 1 + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +#ifdef SUPPORT_JOYSTICK +static int snd_joystick_port[SNDRV_CARDS] = +#ifdef CONFIG_ISA + {0x200}; /* enable as default */ +#else + {0}; /* disabled */ +#endif +#endif +#ifdef SUPPORT_MIDI +static int snd_mpu_port[SNDRV_CARDS]; /* disabled */ +#endif + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Intel i8x0 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (0 = auto-detect)."); +MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:0"); +#ifdef SUPPORT_JOYSTICK +MODULE_PARM(snd_joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_joystick_port, "Joystick port address for Intel i8x0 soundcard. (0 = disabled)"); +MODULE_PARM_SYNTAX(snd_joystick_port, SNDRV_ENABLED ",allows:{{0},{0x200}},dialog:list"); +#endif +#ifdef SUPPORT_MIDI +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mpu_port, "MPU401 port # for Intel i8x0 driver."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog:list"); +#endif + +/* + * Direct registers + */ + +#ifndef PCI_DEVICE_ID_INTEL_82801 +#define PCI_DEVICE_ID_INTEL_82801 0x2415 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82901 +#define PCI_DEVICE_ID_INTEL_82901 0x2425 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82801BA +#define PCI_DEVICE_ID_INTEL_82801BA 0x2445 +#endif +#ifndef PCI_DEVICE_ID_INTEL_440MX +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#endif +#ifndef PCI_DEVICE_ID_INTEL_ICH3 +#define PCI_DEVICE_ID_INTEL_ICH3 0x2485 +#endif +#ifndef PCI_DEVICE_ID_SI_7012 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO +#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1 +#endif + +#define DEVICE_INTEL 0 +#define DEVICE_SIS 1 + +#define ICHREG(ice, x) ((ice)->bmport + ICH_REG_##x) +#define ICHREG2(ice, x) ((ice)->bmport + x) + +/* capture block */ +#define ICH_REG_PI_BDBAR 0x00 /* dword - buffer descriptor list base address */ +#define ICH_REG_PI_CIV 0x04 /* byte - current index value */ +#define ICH_REG_PI_LVI 0x05 /* byte - last valid index */ +#define ICH_REG_LVI_MASK 0x1f +#define ICH_REG_PI_SR 0x06 /* byte - status register */ +#define ICH_FIFOE 0x10 /* FIFO error */ +#define ICH_BCIS 0x08 /* buffer completion interrupt status */ +#define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */ +#define ICH_CELV 0x02 /* current equals last valid */ +#define ICH_DCH 0x01 /* DMA controller halted */ +#define ICH_REG_PI_PICB 0x08 /* word - position in current buffer */ +#define ICH_REG_PI_PIV 0x0a /* byte - prefetched index value */ +#define ICH_REG_PIV_MASK 0x1f /* mask */ +#define ICH_REG_PI_CR 0x0b /* byte - control register */ +#define ICH_IOCE 0x10 /* interrupt on completion enable */ +#define ICH_FEIE 0x08 /* fifo error interrupt enable */ +#define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */ +#define ICH_RESETREGS 0x02 /* reset busmaster registers */ +#define ICH_STARTBM 0x01 /* start busmaster operation */ +/* playback block */ +#define ICH_REG_PO_BDBAR 0x10 /* dword - buffer descriptor list base address */ +#define ICH_REG_PO_CIV 0x14 /* byte - current index value */ +#define ICH_REG_PO_LVI 0x15 /* byte - last valid command */ +#define ICH_REG_PO_SR 0x16 /* byte - status register */ +#define ICH_REG_PO_PICB 0x18 /* word - position in current buffer */ +#define ICH_REG_PO_PIV 0x1a /* byte - prefetched index value */ +#define ICH_REG_PO_CR 0x1b /* byte - control register */ +/* mic capture block */ +#define ICH_REG_MC_BDBAR 0x20 /* dword - buffer descriptor list base address */ +#define ICH_REG_MC_CIV 0x24 /* byte - current index value */ +#define ICH_REG_MC_LVI 0x25 /* byte - last valid command */ +#define ICH_REG_MC_SR 0x26 /* byte - status register */ +#define ICH_REG_MC_PICB 0x28 /* word - position in current buffer */ +#define ICH_REG_MC_PIV 0x2a /* byte - prefetched index value */ +#define ICH_REG_MC_CR 0x2b /* byte - control register */ +/* global block */ +#define ICH_REG_GLOB_CNT 0x2c /* dword - global control */ +#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */ +#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */ +#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */ +#define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */ +#define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */ +#define ICH_PRIE 0x00000010 /* primary resume interrupt enable */ +#define ICH_ACLINK 0x00000008 /* AClink shut off */ +#define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */ +#define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */ +#define ICH_GIE 0x00000001 /* GPI interrupt enable */ +#define ICH_REG_GLOB_STA 0x30 /* dword - global status */ +#define ICH_MD3 0x00020000 /* modem power down semaphore */ +#define ICH_AD3 0x00010000 /* audio power down semaphore */ +#define ICH_RCS 0x00008000 /* read completion status */ +#define ICH_BIT3 0x00004000 /* bit 3 slot 12 */ +#define ICH_BIT2 0x00002000 /* bit 2 slot 12 */ +#define ICH_BIT1 0x00001000 /* bit 1 slot 12 */ +#define ICH_SRI 0x00000800 /* secondary resume interrupt */ +#define ICH_PRI 0x00000400 /* primary resume interrupt */ +#define ICH_SCR 0x00000200 /* secondary codec ready */ +#define ICH_PCR 0x00000100 /* primary codec ready */ +#define ICH_MCINT 0x00000080 /* MIC capture interrupt */ +#define ICH_POINT 0x00000040 /* playback interrupt */ +#define ICH_PIINT 0x00000020 /* capture interrupt */ +#define ICH_MOINT 0x00000004 /* modem playback interrupt */ +#define ICH_MIINT 0x00000002 /* modem capture interrupt */ +#define ICH_GSCI 0x00000001 /* GPI status change interrupt */ +#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */ +#define ICH_CAS 0x01 /* codec access semaphore */ + +#define ICH_MAX_FRAGS 32 /* max hw frags */ + +/* + * + */ + +typedef struct { + unsigned long reg_offset; + u32 *bdbar; /* CPU address (32bit) */ + unsigned int bdbar_addr; /* PCI bus address (32bit) */ + snd_pcm_substream_t *substream; + unsigned int physbuf; /* physical address (32bit) */ + unsigned int size; + unsigned int fragsize; + unsigned int fragsize1; + unsigned int position; + int frags; + int lvi; + int lvi_frag; + int ack; + int ack_reload; +#ifdef CONFIG_PM + unsigned char civ_saved; + unsigned char piv_saved; + unsigned short picb_saved; +#endif +} ichdev_t; + +typedef struct _snd_intel8x0 intel8x0_t; +#define chip_t intel8x0_t + +struct _snd_intel8x0 { + unsigned int device_type; + + unsigned long dma_playback_size; + unsigned long dma_capture_size; + unsigned long dma_mic_size; + + int irq; + + unsigned long port; + struct resource *res_port; + unsigned long bmport; + struct resource *res_bmport; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + snd_pcm_t *pcm_mic; + ichdev_t playback; + ichdev_t capture; + ichdev_t capture_mic; + + int multi4: 1, + multi6: 1; + int in_ac97_init: 1; + + ac97_t *ac97; + ac97_t *ac97sec; + + snd_rawmidi_t *rmidi; + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; + + u32 *bdbars; + dma_addr_t bdbars_addr; + + unsigned int reg_pi_sr; + unsigned int reg_pi_picb; + unsigned int reg_po_sr; + unsigned int reg_po_picb; + unsigned int reg_mc_sr; + unsigned int reg_mc_picb; + +#ifdef CONFIG_PM + int in_suspend; +#endif +}; + +static struct pci_device_id snd_intel8x0_ids[] __devinitdata = { + { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */ + { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */ + { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ + { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ + { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ + { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ + { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* NFORCE */ + { 0x764d, 0x1022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids); + +/* + * Basic I/O + */ +static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec) +{ + int time; + + /* codec ready ? */ + if ((inl(ICHREG(chip, GLOB_STA)) & (codec ? ICH_SCR : ICH_PCR)) == 0) + return -EIO; + + /* Anyone holding a semaphore for 1 msec should be shot... */ + time = 100; + do { + if (!(inb(ICHREG(chip, ACC_SEMA)) & ICH_CAS)) + return 0; + udelay(10); + } while (time--); + + /* access to some forbidden (non existant) ac97 registers will not + * reset the semaphore. So even if you don't get the semaphore, still + * continue the access. We don't need the semaphore anyway. */ + snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n", + inb(ICHREG(chip, ACC_SEMA)), inl(ICHREG(chip, GLOB_STA))); + inw(chip->port); /* clear semaphore flag */ + /* I don't care about the semaphore */ + return -EBUSY; +} + +static void snd_intel8x0_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { + if (! chip->in_ac97_init) + snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); + } + outw(val, chip->port + reg + ac97->num * 0x80); + spin_unlock(&chip->ac97_lock); +} + +static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, + unsigned short reg) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0); + unsigned short res; + unsigned int tmp; + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { + if (! chip->in_ac97_init) + snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg); + res = 0xffff; + } else { + res = inw(chip->port + reg + ac97->num * 0x80); + if ((tmp = inl(ICHREG(chip, GLOB_STA))) & ICH_RCS) { + /* reset RCS and preserve other R/WC bits */ + outl(tmp & ~(ICH_SRI|ICH_PRI|ICH_GSCI), ICHREG(chip, GLOB_STA)); + if (! chip->in_ac97_init) + snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg); + res = 0xffff; + } + } + spin_unlock(&chip->ac97_lock); + return res; +} + +static int snd_intel8x0_trigger(intel8x0_t *chip, ichdev_t *ichdev, int cmd) +{ + unsigned char val = 0; + unsigned long port = chip->bmport + ichdev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + val = ICH_IOCE | ICH_STARTBM; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + val = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = ICH_IOCE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = ICH_IOCE | ICH_STARTBM; + break; + default: + return -EINVAL; + } + outb(val, port + ICH_REG_PI_CR); + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + /* reset whole DMA things */ + while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) ; + outb(ICH_RESETREGS, port + ICH_REG_PI_CR); + } + return 0; +} + +static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) +{ + int idx; + u32 *bdbar = ichdev->bdbar; + unsigned long port = chip->bmport + ichdev->reg_offset; + int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1; + + outl(ichdev->bdbar_addr, port + ICH_REG_PI_BDBAR); + if (ichdev->size == ichdev->fragsize) { + ichdev->ack_reload = ichdev->ack = 2; + ichdev->fragsize1 = ichdev->fragsize >> 1; + for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) { + bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf); + bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize1 >> shiftlen); + bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1)); + bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize1 >> shiftlen); + } + ichdev->frags = 2; + } else { + ichdev->ack_reload = ichdev->ack = 1; + ichdev->fragsize1 = ichdev->fragsize; + for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) { + bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size)); + bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */ + ichdev->fragsize >> shiftlen); + // printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]); + } + ichdev->frags = ichdev->size / ichdev->fragsize; + } + outb(ichdev->lvi = ICH_REG_LVI_MASK, port + ICH_REG_PI_LVI); + ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags; + ichdev->position = 0; +#if 0 + printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n", + ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1); +#endif + /* clear interrupts */ + outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr); +} + +/* + * Interrupt handler + */ + +static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev) +{ + unsigned long port = chip->bmport + ichdev->reg_offset; + int ack = 0; + + spin_lock(&chip->reg_lock); + ichdev->position += ichdev->fragsize1; + ichdev->position %= ichdev->size; + ichdev->lvi++; + ichdev->lvi &= ICH_REG_LVI_MASK; + outb(ichdev->lvi, port + ICH_REG_PI_LVI); + ichdev->lvi_frag++; + ichdev->lvi_frag %= ichdev->frags; + ichdev->bdbar[ichdev->lvi * 2] = ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1; + // printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_PI_PIV + port), inl(port + 4), inb(port + ICH_REG_PI_CR)); + if ((ack = (--ichdev->ack == 0)) != 0) + ichdev->ack = ichdev->ack_reload; + spin_unlock(&chip->reg_lock); + if (ack && ichdev->substream) + snd_pcm_period_elapsed(ichdev->substream); + outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr); +} + +static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return); + unsigned int status; + + status = inl(ICHREG(chip, GLOB_STA)); + if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0) + return; + if (status & ICH_POINT) + snd_intel8x0_update(chip, &chip->playback); + if (status & ICH_PIINT) + snd_intel8x0_update(chip, &chip->capture); + if (status & ICH_MCINT) + snd_intel8x0_update(chip, &chip->capture_mic); +} + +/* + * PCM part + */ + +static int snd_intel8x0_playback_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + int result; + result = snd_pcm_lib_ioctl(substream, cmd, arg); + if (result < 0) + return result; + return 0; +} + +static int snd_intel8x0_capture_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + int result; + result = snd_pcm_lib_ioctl(substream, cmd, arg); + if (result < 0) + return result; + return 0; +} + +static int snd_intel8x0_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_trigger(chip, &chip->playback, cmd); +} + +static int snd_intel8x0_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_trigger(chip, &chip->capture, cmd); +} + +static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels) +{ + unsigned int cnt = inl(ICHREG(chip, GLOB_CNT)) & ~ICH_PCM_246_MASK; + if (chip->multi4 && channels == 4) + cnt |= ICH_PCM_4; + else if (chip->multi6 && channels == 6) + cnt |= ICH_PCM_6; + outl(cnt, ICHREG(chip, GLOB_CNT)); +} + +static int snd_intel8x0_playback_prepare(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->playback.physbuf = runtime->dma_addr; + chip->playback.size = snd_pcm_lib_buffer_bytes(substream); + chip->playback.fragsize = snd_pcm_lib_period_bytes(substream); + spin_lock(&chip->reg_lock); + snd_intel8x0_setup_multi_channels(chip, runtime->channels); + spin_unlock(&chip->reg_lock); + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_intel8x0_setup_periods(chip, &chip->playback); + return 0; +} + +static int snd_intel8x0_capture_prepare(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture.physbuf = runtime->dma_addr; + chip->capture.size = snd_pcm_lib_buffer_bytes(substream); + chip->capture.fragsize = snd_pcm_lib_period_bytes(substream); + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_intel8x0_setup_periods(chip, &chip->capture); + return 0; +} + +static snd_pcm_uframes_t snd_intel8x0_playback_pointer(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + ptr = chip->playback.fragsize1; + if (chip->device_type == DEVICE_SIS) + ptr -= inw(ICHREG2(chip,chip->reg_po_picb)); + else + ptr -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1; + ptr += chip->playback.position; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_intel8x0_capture_pointer(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + ptr = chip->capture.fragsize1; + if (chip->device_type == DEVICE_SIS) + ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)); + else + ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)) << 1; + ptr += chip->capture.position; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_intel8x0_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_intel8x0_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static unsigned int channels4[] = { + 2, 4, +}; + +#define CHANNELS4 sizeof(channels4) / sizeof(channels4[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = { + count: CHANNELS4, + list: channels4, + mask: 0, +}; + +static unsigned int channels6[] = { + 2, 4, 6, +}; + +#define CHANNELS6 sizeof(channels6) / sizeof(channels6[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = { + count: CHANNELS6, + list: channels6, + mask: 0, +}; + +static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback.substream = substream; + runtime->hw = snd_intel8x0_playback; + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if (chip->device_type == DEVICE_SIS) { + runtime->hw.buffer_bytes_max = 64*1024; + runtime->hw.period_bytes_max = 64*1024; + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + if (chip->multi6) { + runtime->hw.channels_max = 6; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6); + } else if (chip->multi4) { + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels4); + } + return 0; +} + +static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture.substream = substream; + runtime->hw = snd_intel8x0_capture; + runtime->hw.rates = chip->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if (chip->device_type == DEVICE_SIS) { + runtime->hw.buffer_bytes_max = 64*1024; + runtime->hw.period_bytes_max = 64*1024; + } + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->playback.substream = NULL; + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->capture.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + return 0; +} + +static snd_pcm_ops_t snd_intel8x0_playback_ops = { + open: snd_intel8x0_playback_open, + close: snd_intel8x0_playback_close, + ioctl: snd_intel8x0_playback_ioctl, + hw_params: snd_intel8x0_hw_params, + hw_free: snd_intel8x0_hw_free, + prepare: snd_intel8x0_playback_prepare, + trigger: snd_intel8x0_playback_trigger, + pointer: snd_intel8x0_playback_pointer, +}; + +static snd_pcm_ops_t snd_intel8x0_capture_ops = { + open: snd_intel8x0_capture_open, + close: snd_intel8x0_capture_close, + ioctl: snd_intel8x0_capture_ioctl, + hw_params: snd_intel8x0_hw_params, + hw_free: snd_intel8x0_hw_free, + prepare: snd_intel8x0_capture_prepare, + trigger: snd_intel8x0_capture_trigger, + pointer: snd_intel8x0_capture_pointer, +}; + +static void snd_intel8x0_pcm_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_intel8x0_pcm(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * PCM code - MIC + */ + +static int snd_intel8x0_capture_mic_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + int result; + result = snd_pcm_lib_ioctl(substream, cmd, arg); + if (result < 0) + return result; + return 0; +} + +static int snd_intel8x0_capture_mic_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + return snd_intel8x0_trigger(chip, &chip->capture_mic, cmd); +} + +static int snd_intel8x0_capture_mic_prepare(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_mic.physbuf = runtime->dma_addr; + chip->capture_mic.size = snd_pcm_lib_buffer_bytes(substream); + chip->capture_mic.fragsize = snd_pcm_lib_period_bytes(substream); + snd_ac97_set_rate(chip->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate); + snd_intel8x0_setup_periods(chip, &chip->capture_mic); + return 0; +} + +static snd_pcm_uframes_t snd_intel8x0_capture_mic_pointer(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + size_t ptr; + + ptr = chip->capture_mic.fragsize1; + if (chip->device_type == DEVICE_SIS) + ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)); + else + ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)) << 1; + ptr += chip->capture_mic.position; + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_intel8x0_capture_mic = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 1, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_intel8x0_capture_mic_open(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->capture_mic.substream = substream; + runtime->hw = snd_intel8x0_capture_mic; + runtime->hw.rates = chip->ac97->rates_mic_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if (chip->device_type == DEVICE_SIS) { + runtime->hw.buffer_bytes_max = 64*1024; + runtime->hw.period_bytes_max = 64*1024; + } + return 0; +} + +static int snd_intel8x0_capture_mic_close(snd_pcm_substream_t * substream) +{ + intel8x0_t *chip = snd_pcm_substream_chip(substream); + + chip->capture_mic.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x4000, 0x4000); + return 0; +} + +static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = { + open: snd_intel8x0_capture_mic_open, + close: snd_intel8x0_capture_mic_close, + ioctl: snd_intel8x0_capture_mic_ioctl, + hw_params: snd_intel8x0_hw_params, + hw_free: snd_intel8x0_hw_free, + prepare: snd_intel8x0_capture_mic_prepare, + trigger: snd_intel8x0_capture_mic_trigger, + pointer: snd_intel8x0_capture_mic_pointer, +}; + +static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return); + chip->pcm_mic = NULL; +} + +static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic_ops); + + pcm->private_data = chip; + pcm->private_free = snd_intel8x0_pcm_mic_free; + pcm->info_flags = 0; + sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname); + + chip->pcm_mic = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer part + */ + +static void snd_intel8x0_codec_init(ac97_t *ac97) +{ + // intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + + /* disable DAC & ADC power */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300); + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + if (ac97->num == 0) { + chip->ac97 = NULL; + } else { + chip->ac97sec = NULL; + } +} + +static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) +{ + ac97_t ac97; + int err; + + chip->in_ac97_init = 1; + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_intel8x0_codec_write; + ac97.read = snd_intel8x0_codec_read; + ac97.init = snd_intel8x0_codec_init; + ac97.private_data = chip; + ac97.private_free = snd_intel8x0_mixer_free_ac97; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + ac97.clock = ac97_clock; + else + ac97.clock = 48000; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; +#if 0 /* it seems that SDIN signals are mixed together (at least for AD CNR boards) */ + if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) { + ac97.num = 1; + ac97.addr = 1; + snd_ac97_mixer(chip->card, &ac97, &chip->ac97sec); + } +#endif + if ((inl(ICHREG(chip, GLOB_STA)) & (ICH_PCM_4|ICH_PCM_6)) != (ICH_PCM_4|ICH_PCM_6)) + return 0; + if ((chip->ac97->scaps & AC97_SCAP_SURROUND_DAC) || + (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_SURROUND_DAC))) { + chip->multi4 = 1; + if ((chip->ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) || + (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_CENTER_LFE_DAC))) + chip->multi6 = 1; + } + chip->in_ac97_init = 0; + return 0; +} + + +/* + * + */ + +static int snd_intel8x0_chip_init(intel8x0_t *chip) +{ + signed long end_time; + unsigned int cnt; + + /* put logic to right state */ + /* first clear status bits */ + cnt = inl(ICHREG(chip, GLOB_STA)); + outl(cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA)); + + /* ACLink on, 2 channels */ + cnt = inl(ICHREG(chip, GLOB_CNT)); + cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); + /* finish cold or do warm reset */ + cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; + outl(cnt, ICHREG(chip, GLOB_CNT)); + end_time = (jiffies + (HZ / 4)) + 1; + do { + if ((inl(ICHREG(chip, GLOB_CNT)) & ICH_AC97WARM) == 0) + goto __ok; +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay(10); + continue; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 warm reset still in progress? [0x%x]\n", inl(ICHREG(chip, GLOB_CNT))); + return -EIO; + + __ok: + /* wait for primary codec ready status. + * Once it becomes ready it should remain ready + * as long as we do not disable the ac97 link. + */ + end_time = jiffies + HZ; + do { + if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR) + goto __ok1; +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay(10); + continue; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("codec_ready: primary codec is not ready [0x%x]\n", inl(ICHREG(chip, GLOB_STA))); + return -EIO; + + __ok1: + /* wait for secondary codec ready status. No secondary codec? , ok */ + /* the end_time variable is not initialized again */ + do { + if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) + break; +#ifdef CONFIG_PM + if (chip->in_suspend) { + mdelay(10); + continue; + } +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + + inw(chip->port); /* clear semaphore flag */ + + /* disable interrupts */ + outb(0x00, ICHREG(chip, PI_CR)); + outb(0x00, ICHREG(chip, PO_CR)); + outb(0x00, ICHREG(chip, MC_CR)); + /* reset channels */ + outb(ICH_RESETREGS, ICHREG(chip, PI_CR)); + outb(ICH_RESETREGS, ICHREG(chip, PO_CR)); + outb(ICH_RESETREGS, ICHREG(chip, MC_CR)); + /* initialize Buffer Descriptor Lists */ + outl(chip->playback.bdbar_addr, ICHREG(chip, PO_BDBAR)); + outl(chip->capture.bdbar_addr, ICHREG(chip, PI_BDBAR)); + outl(chip->capture_mic.bdbar_addr, ICHREG(chip, MC_BDBAR)); + return 0; +} + +static int snd_intel8x0_free(intel8x0_t *chip) +{ + if (chip->irq < 0) + goto __hw_end; + /* disable interrupts */ + outb(0x00, ICHREG(chip, PI_CR)); + outb(0x00, ICHREG(chip, PO_CR)); + outb(0x00, ICHREG(chip, MC_CR)); + /* reset channels */ + outb(ICH_RESETREGS, ICHREG(chip, PI_CR)); + outb(ICH_RESETREGS, ICHREG(chip, PO_CR)); + outb(ICH_RESETREGS, ICHREG(chip, MC_CR)); + /* --- */ + synchronize_irq(); + __hw_end: + if (chip->bdbars) + snd_free_pci_pages(chip->pci, 3 * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->res_bmport) { + release_resource(chip->res_bmport); + kfree_nocheck(chip->res_bmport); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +#ifdef CONFIG_PM +/* + * power management + */ +static void intel8x0_suspend(intel8x0_t *chip) +{ + snd_card_t *card = chip->card; + + chip->in_suspend = 1; + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + if (chip->pcm_mic) + snd_pcm_suspend_all(chip->pcm_mic); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void intel8x0_resume(intel8x0_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + pci_enable_device(chip->pci); + snd_intel8x0_chip_init(chip); + snd_ac97_resume(chip->ac97); + + chip->in_suspend = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_intel8x0_suspend(struct pci_dev *dev, u32 state) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO); + intel8x0_suspend(chip); + return 0; +} +static int snd_intel8x0_resume(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return -ENXIO); + intel8x0_resume(chip); + return 0; +} +#else +static void snd_intel8x0_suspend(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return); + intel8x0_suspend(chip); +} +static void snd_intel8x0_resume(struct pci_dev *dev) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(dev), return); + intel8x0_resume(chip); +} +#endif + +/* callback */ +static int snd_intel8x0_set_power_state(snd_card_t *card, unsigned int power_state) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + intel8x0_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + intel8x0_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ + +static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) +{ + snd_pcm_substream_t *subs; + unsigned long port; + unsigned long pos, t; + unsigned long flags; + struct timeval start_time, stop_time; + + if (chip->ac97->clock != 48000) + return; /* specified in module option */ + + subs = chip->pcm->streams[0].substream; + if (! subs || subs->dma_bytes < INTEL8X0_TESTBUF_SIZE) { + snd_printk("no playback buffer allocated - aborting measure ac97 clock\n"); + return; + } + chip->playback.physbuf = subs->dma_addr; + chip->playback.size = chip->playback.fragsize = INTEL8X0_TESTBUF_SIZE; + chip->playback.substream = NULL; /* don't process interrupts */ + + spin_lock_irqsave(&chip->reg_lock, flags); + /* set rate */ + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 48000); + snd_intel8x0_setup_periods(chip, &chip->playback); + port = chip->bmport + chip->playback.reg_offset; + outb(ICH_IOCE | ICH_STARTBM, port + ICH_REG_PI_CR); /* trigger */ + do_gettimeofday(&start_time); + spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); +#else + /* FIXME: schedule() can take too long time and overlap the boundary.. */ + mdelay(50); +#endif + spin_lock_irqsave(&chip->reg_lock, flags); + /* check the position */ + pos = chip->playback.fragsize1; + if (chip->device_type == DEVICE_SIS) + pos -= inw(ICHREG2(chip,chip->reg_po_picb)); + else + pos -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1; + pos += chip->playback.position; + do_gettimeofday(&stop_time); + outb(0, port + ICH_REG_PI_CR); /* stop */ + /* reset whole DMA things */ + while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) + ; + outb(ICH_RESETREGS, port + ICH_REG_PI_CR); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + t = stop_time.tv_sec - start_time.tv_sec; + t *= 1000000; + if (stop_time.tv_usec < start_time.tv_usec) + t -= start_time.tv_usec - stop_time.tv_usec; + else + t += stop_time.tv_usec - start_time.tv_usec; + if (t == 0) { + snd_printk("?? calculation error..\n"); + return; + } + pos = (pos / 4) * 1000; + pos = (pos / t) * 1000 + ((pos % t) * 1000) / t; + if ((pos > 40000 && pos < 47500) || + (pos > 48500 && pos < 50000)) { + chip->ac97->clock = (chip->ac97->clock * 48000) / pos; + printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97->clock); + } +} + +static int snd_intel8x0_dev_free(snd_device_t *device) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, device->device_data, return -ENXIO); + return snd_intel8x0_free(chip); +} + +static int __devinit snd_intel8x0_create(snd_card_t * card, + struct pci_dev *pci, + unsigned long device_type, + intel8x0_t ** r_intel8x0) +{ + intel8x0_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_intel8x0_dev_free, + }; + char name[32]; + + *r_intel8x0 = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(intel8x0_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + chip->device_type = device_type; + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + sprintf(name, "%s - AC'97", card->shortname); + if ((chip->res_port = request_region(chip->port, 256, name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + sprintf(name, "%s - Controller", card->shortname); + chip->bmport = pci_resource_start(pci, 1); + if ((chip->res_bmport = request_region(chip->bmport, 64, name)) == NULL) { + snd_intel8x0_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmport, chip->bmport + 64 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) { + snd_intel8x0_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); + synchronize_irq(); + + /* initialize offsets */ + chip->reg_pi_sr = ICH_REG_PI_SR; + chip->reg_pi_picb = ICH_REG_PI_PICB; + chip->reg_po_sr = ICH_REG_PO_SR; + chip->reg_po_picb = ICH_REG_PO_PICB; + chip->reg_mc_sr = ICH_REG_MC_SR; + chip->reg_mc_picb = ICH_REG_MC_PICB; + if (device_type == DEVICE_SIS) { + chip->reg_pi_sr = ICH_REG_PI_PICB; + chip->reg_pi_picb = ICH_REG_PI_SR; + chip->reg_po_sr = ICH_REG_PO_PICB; + chip->reg_po_picb = ICH_REG_PO_SR; + chip->reg_mc_sr = ICH_REG_MC_PICB; + chip->reg_mc_picb = ICH_REG_MC_SR; + } + chip->playback.reg_offset = 0x10; + chip->capture.reg_offset = 0; + chip->capture_mic.reg_offset = 0x20; + + /* allocate buffer descriptor lists */ + /* the start of each lists must be aligned to 8 bytes */ + chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); + if (chip->bdbars == NULL) { + snd_intel8x0_free(chip); + return -ENOMEM; + } + /* tables must be aligned to 8 bytes here, but the kernel pages + are much bigger, so we don't care (on i386) */ +#ifndef __i386__ + /* .. not sure on other architectures, so we check now. */ + if (chip->bdbars_addr & ~((dma_addr_t)0xffffffff | 0x07)) { + snd_printk("invalid i/o port address %lx\n", (unsigned long)chip->bdbars_addr); + snd_intel8x0_free(chip); + return -ENOMEM; + } +#endif + chip->playback.bdbar = chip->bdbars; /* crop to 32bit */ + chip->playback.bdbar_addr = (unsigned int)chip->bdbars_addr; + chip->capture.bdbar = chip->playback.bdbar + ICH_MAX_FRAGS * 2; + chip->capture.bdbar_addr = chip->playback.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2; + chip->capture_mic.bdbar = chip->capture.bdbar + ICH_MAX_FRAGS * 2; + chip->capture_mic.bdbar_addr = chip->capture.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2; + + if ((err = snd_intel8x0_chip_init(chip)) < 0) { + snd_intel8x0_free(chip); + return err; + } + +#ifdef CONFIG_PM + card->set_power_state = snd_intel8x0_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_intel8x0_free(chip); + return err; + } + + *r_intel8x0 = chip; + return 0; +} + +static struct shortname_table { + unsigned int id; + const char *s; +} shortnames[] __devinitdata = { + { PCI_DEVICE_ID_INTEL_82801, "Intel ICH 82801AA" }, + { PCI_DEVICE_ID_INTEL_82901, "Intel ICH 82901AB" }, + { PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" }, + { PCI_DEVICE_ID_INTEL_ICH3, "Intel ICH3" }, + { PCI_DEVICE_ID_SI_7012, "SiS SI7012" }, + { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" }, + { 0x1022, "AMD-8111" }, + { 0, 0 }, +}; + +static int __devinit snd_intel8x0_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + intel8x0_t *chip; + int pcm_dev = 0, err; + struct shortname_table *name; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "ICH"); + strcpy(card->shortname, "Intel ICH"); + for (name = shortnames; name->id; name++) { + if (pci->device == name->id) { + strcpy(card->shortname, name->s); + break; + } + } + + if ((err = snd_intel8x0_create(card, pci, id->driver_data, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_intel8x0_mixer(chip, snd_ac97_clock[dev])) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_intel8x0_pcm(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (chip->ac97->ext_id & 0x0008) { /* MIC VRM */ + if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + + if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, + snd_mpu_port[dev], 0, + -1, 0, &chip->rmidi)) < 0) { + printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", snd_mpu_port[dev]); + snd_mpu_port[dev] = 0; + } + } else + snd_mpu_port[dev] = 0; + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, chip->port, chip->irq); + + if (! snd_ac97_clock[dev]) + intel8x0_measure_ac97_clock(chip); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_intel8x0_remove(struct pci_dev *pci) +{ + intel8x0_t *chip = snd_magic_cast(intel8x0_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Intel ICH", + id_table: snd_intel8x0_ids, + probe: snd_intel8x0_probe, + remove: __devexit_p(snd_intel8x0_remove), +#ifdef CONFIG_PM + suspend: snd_intel8x0_suspend, + resume: snd_intel8x0_resume, +#endif +}; + + +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) +/* + * initialize joystick/midi addresses + */ + +static int __devinit snd_intel8x0_joystick_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + if (snd_joystick_port[dev] > 0 || snd_mpu_port[dev] > 0) { + u16 val; + pci_read_config_word(pci, 0xe6, &val); + if (snd_joystick_port[dev] > 0) + val |= 0x100; + if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) + val |= 0x20; + pci_write_config_word(pci, 0xe6, val | 0x100); + + if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) { + u8 b; + pci_read_config_byte(pci, 0xe2, &b); + if (snd_mpu_port[dev] == 0x300) + b |= 0x08; + else + b &= ~0x08; + pci_write_config_byte(pci, 0xe2, b); + } + } + return 0; +} + +static struct pci_device_id snd_intel8x0_joystick_ids[] __devinitdata = { + { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 82801AA */ + { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 82901AB */ + { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2 */ + { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2M */ + { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH3 */ + // { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 440MX */ + // { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SI7012 */ + { 0x10de, 0x01b2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* NFORCE */ + { 0, } +}; + +static struct pci_driver joystick_driver = { + name: "Intel ICH Joystick", + id_table: snd_intel8x0_joystick_ids, + probe: snd_intel8x0_joystick_probe, +}; +#endif + +static int __init alsa_card_intel8x0_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Intel ICH soundcard not found or device busy\n"); +#endif + return err; + } +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) + pci_module_init(&joystick_driver); +#endif + return 0; + +} + +static void __exit alsa_card_intel8x0_exit(void) +{ + pci_unregister_driver(&driver); +#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI) + pci_unregister_driver(&joystick_driver); +#endif +} + +module_init(alsa_card_intel8x0_init) +module_exit(alsa_card_intel8x0_exit) + +#ifndef MODULE + +/* format is: snd-intel8x0=snd_enable,snd_index,snd_id,snd_ac97_clock */ + +static int __init alsa_card_intel8x0_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-intel8x0=", alsa_card_intel8x0_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/korg1212/Makefile linux-2.4.19-pre5-mjc/sound/pci/korg1212/Makefile --- linux/sound/pci/korg1212/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/korg1212/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _korg1212.o + +list-multi := snd-korg1212.o + +snd-korg1212-objs := korg1212.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o + +include $(TOPDIR)/Rules.make + +snd-korg1212.o: $(snd-korg1212-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-korg1212-objs) diff -Nru linux/sound/pci/korg1212/korg1212-firmware.h linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212-firmware.h --- linux/sound/pci/korg1212/korg1212-firmware.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212-firmware.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,987 @@ +static char dspCode [] = { +0x01,0xff,0x18,0xff,0xf5,0xff,0xcf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x26,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x38,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,0x67,0xff,0x40,0xff,0xff,0xff,0xc0,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x0c,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff, +0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x72,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x02,0xff,0x91,0xff,0xff,0xff,0x80,0xff, +0x02,0xff,0x91,0xff,0xff,0xff,0x90,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0xff,0xff,0x47,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x17,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x17,0xff, +0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff, +0x02,0xff,0x34,0xff,0xff,0xff,0x4a,0xff,0x02,0xff,0x38,0xff,0xff,0xff,0x4a,0xff, +0x01,0xff,0x34,0xff,0xff,0xff,0x2b,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x2b,0xff, +0x80,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x81,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x60,0xff, +0x84,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x85,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x82,0xff,0x37,0xff,0xff,0xff,0x81,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x88,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8c,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8a,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x8e,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x82,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0x80,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x07,0xff, +0x40,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xbb,0xff, +0x02,0xff,0x41,0xff,0xff,0xff,0x82,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xcb,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0xe2,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xdb,0xff, +0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0x82,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0x9b,0xff,0x03,0xff,0x41,0xff,0xff,0xff,0x02,0xff, +0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xa2,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x21,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x40,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x85,0xff,0x0a,0xff,0x14,0xff,0xff,0xff,0xae,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff, +0x0a,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff, +0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x0b,0xff,0x14,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x01,0xff, +0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x03,0xff,0x35,0xff,0xff,0xff,0x01,0xff, +0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x5b,0xff,0x40,0xff,0xff,0xff,0xf0,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0xdf,0xff,0x40,0xff,0xff,0xff,0xf0,0xff, +0xfe,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0xc1,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x04,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x23,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x59,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x0c,0xff,0x14,0xff,0xff,0xff,0xe4,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x24,0xff, +0x00,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0x30,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x84,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x76,0xff,0x1c,0xff,0xff,0xff,0x9f,0xff, +0x86,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0x64,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0x81,0xff, +0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x61,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,0x77,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff, +0x63,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff, +0x0f,0xff,0x14,0xff,0xff,0xff,0x6e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0xe0,0xff, +0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff, +0x10,0xff,0x14,0xff,0xff,0xff,0x0e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x79,0xff,0x1c,0xff,0xff,0xff,0xff,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x80,0xff,0x35,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x11,0xff,0x14,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff, +0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x63,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x83,0xff,0x37,0xff,0xff,0xff,0x80,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x83,0xff,0x43,0xff,0xff,0xff,0x00,0xff, +0x87,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x40,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x90,0xff, +0x80,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xa0,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x13,0xff,0x14,0xff,0xff,0xff,0xbe,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x43,0xff,0xff,0xff,0x01,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0xe1,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x72,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff, +0x38,0xff,0x42,0xff,0xff,0xff,0x50,0xff,0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x20,0xff,0x1c,0xff,0xff,0xff,0xcf,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff, +0x79,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff, +0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x19,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff, +0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff, +0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff, +0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff, +0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x01,0xff, +0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff, +0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x8b,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x40,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff, +0x1b,0xff,0x14,0xff,0xff,0xff,0xce,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe1,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x9b,0xff,0xff,0xff,0xa0,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x1d,0xff,0x14,0xff,0xff,0xff,0x3e,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff, +0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x51,0xff, +0x79,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x14,0xff,0xff,0xff,0xd5,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff, +0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff, +0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff, +0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff, +0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x8f,0xff,0xff,0xff,0xc5,0xff,0x20,0xff,0x14,0xff,0xff,0xff,0xae,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff, +0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,0x21,0xff,0x14,0xff,0xff,0xff,0x5e,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff, +0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0c,0xff,0x18,0xff,0xff,0xff,0x90,0xff, +0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff, +0x98,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xa5,0xff, +0x24,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff, +0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff, +0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff, +0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff, +0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x58,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x01,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x26,0xff,0x18,0xff,0xff,0xff,0x94,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x80,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x16,0xff,0x0f,0xff,0xff,0xff,0x02,0xff, +0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff, +0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff, +0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,0x27,0xff,0x18,0xff,0xff,0xff,0xd2,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x29,0xff,0x18,0xff,0xff,0xff,0x92,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x03,0xff, +0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x31,0xff,0x18,0xff,0xff,0xff,0x12,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x34,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x55,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x18,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x48,0xff,0x10,0xff,0x0f,0xff,0xff,0xff,0xfe,0xff, +0x87,0xff,0x93,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x2e,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x06,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa1,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x28,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x38,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff, +0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff, +0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff, +0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x32,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x44,0xff, +0x08,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x8a,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x5a,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x35,0xff,0x18,0xff,0xff,0xff,0xd2,0xff, +0x00,0xff,0x4c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff, +0x35,0xff,0x1c,0xff,0xff,0xff,0x24,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x33,0xff,0x18,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xc0,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x34,0xff,0x18,0xff,0xff,0xff,0x85,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x0d,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0xff,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x90,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x37,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x30,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x40,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x50,0xff, +0x89,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x39,0xff,0x18,0xff,0xff,0xff,0x22,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x1c,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x41,0xff,0x18,0xff,0xff,0xff,0x30,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xe4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x42,0xff,0x18,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xd4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x47,0xff,0x18,0xff,0xff,0xff,0xa0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xc4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xb4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x48,0xff,0x18,0xff,0xff,0xff,0xe0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4a,0xff,0x18,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x94,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4c,0xff,0x18,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x84,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4d,0xff,0x18,0xff,0xff,0xff,0xe0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x74,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0x20,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x64,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xf4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x44,0xff,0x18,0xff,0xff,0xff,0x40,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xe4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x45,0xff,0x18,0xff,0xff,0xff,0x50,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3d,0xff,0x18,0xff,0xff,0xff,0xd0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x10,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x80,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x34,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x40,0xff,0x18,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x21,0xff,0x40,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x25,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0xe9,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0xed,0xff,0x41,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf1,0xff,0x41,0xff,0xff,0xff,0x80,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x50,0xff,0xff,0xff,0x23,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0xe2,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf2,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0xe3,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff, +0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x32,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff, +0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x05,0xff,0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xc7,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff, +0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe7,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff, +0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x51,0xff,0x14,0xff,0xff,0xff,0x81,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff, +0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff, +0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff, +0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff, +0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x18,0xff,0xff,0xff,0x00,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x54,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff, +0x54,0xff,0x14,0xff,0xff,0xff,0x1e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb3,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x55,0xff,0x14,0xff,0xff,0xff,0x21,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x31,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x54,0xff,0x18,0xff,0xff,0xff,0xd5,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x82,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0xf1,0xff,0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x56,0xff,0x18,0xff,0xff,0xff,0xb4,0xff, +0x54,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x14,0xff,0xff,0xff,0x91,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff, +0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff, +0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff, +0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff, +0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff, +0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff, +0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x57,0xff,0x18,0xff,0xff,0xff,0x10,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x5a,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff, +0x5a,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x59,0xff,0x18,0xff,0xff,0xff,0xd3,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff, +0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x5b,0xff,0x14,0xff,0xff,0xff,0x61,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff, +0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x5e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x5b,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x05,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x5e,0xff,0x1c,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x50,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x5f,0xff,0x1c,0xff,0xff,0xff,0x54,0xff, +0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0xa8,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x08,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x90,0xff, +0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0xb6,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xfa,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff, +0x90,0xff,0x80,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe2,0xff, +0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0xe8,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0xac,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0xd4,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff, +0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xa8,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xd8,0xff, +0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff, +0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff, +0x01,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x01,0xff,0x42,0xff,0xff,0xff,0x80,0xff, +0x00,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x07,0xff,0x42,0xff,0xff,0xff,0x80,0xff, +0x03,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x02,0xff,0x42,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x08,0xff,0x42,0xff,0xff,0xff,0x00,0xff, +0x03,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x01,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff, +0x04,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x04,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff, +0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff, +0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,0x65,0xff,0x14,0xff,0xff,0xff,0x9e,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff, +0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff, +0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x80,0xff,0xff,0xff,0x48,0xff, +0x10,0xff,0x0f,0xff,0xff,0xff,0x03,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0x74,0xff,0x18,0xff,0xff,0xff,0x54,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0xc0,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff, +0x86,0xff,0x83,0xff,0xff,0xff,0x80,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x8a,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xd0,0xff, +0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x8b,0xff,0xff,0xff,0xb0,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x44,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x55,0xff, +0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xa0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0x24,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff, +0x89,0xff,0x83,0xff,0xff,0xff,0x35,0xff,0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff, +0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff, +0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff, +0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x6a,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x01,0xff,0x34,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,0x87,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x88,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x01,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x6a,0xff,0x14,0xff,0xff,0xff,0x9e,0xff, +0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff, +0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x6a,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x82,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xf4,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x86,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff, +0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x64,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff, +0x01,0xff,0x34,0xff,0xff,0xff,0x96,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x65,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x96,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x49,0xff, +0x02,0xff,0x38,0xff,0xff,0xff,0x49,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff, +0x10,0xff,0x40,0xff,0xff,0xff,0x02,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6d,0xff,0x14,0xff,0xff,0xff,0xbe,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc5,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xc6,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff, +0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff, +0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6e,0xff,0x14,0xff,0xff,0xff,0x8e,0xff, +0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff, +0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc4,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xe9,0xff, +0x01,0xff,0x38,0xff,0xff,0xff,0x28,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x29,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x66,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x75,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff, +0x00,0xff,0x41,0xff,0xff,0xff,0x07,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff, +0x98,0xff,0x70,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff, +0x70,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x41,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff, +0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x46,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x45,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x41,0xff, +0x63,0xff,0x6a,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff, +0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x44,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0xf0,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff, +0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff, +0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff, +0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x44,0xff, +0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff, +0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff, +0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff, +0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x72,0xff,0x14,0xff,0xff,0xff,0x35,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x30,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0x10,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x10,0xff,0x90,0xff,0xff,0xff,0xb0,0xff,0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff, +0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x40,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x49,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x10,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff, +0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x10,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff, +0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff, +0x00,0xff,0x48,0xff,0xff,0xff,0x20,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff, +0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x48,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xb0,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xc0,0xff, +0x86,0xff,0x97,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0x90,0xff, +0x00,0xff,0x70,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xb0,0xff, +0x88,0xff,0x37,0xff,0xff,0xff,0x03,0xff,0x8c,0xff,0x3b,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff, +0x82,0xff,0x43,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x60,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x0c,0xff,0x0d,0xff,0xff,0xff,0x80,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xa0,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x80,0xff,0x37,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff, +0x76,0xff,0x14,0xff,0xff,0xff,0xde,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff, +0x84,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff, +0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,0x77,0xff,0x14,0xff,0xff,0xff,0x2e,0xff, +0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff, +0x77,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x64,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x77,0xff,0x14,0xff,0xff,0xff,0xe5,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff, +0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x78,0xff,0x14,0xff,0xff,0xff,0x35,0xff, +0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff, +0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x56,0xff, +0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x00,0xff, +0x01,0xff,0x40,0xff,0xff,0xff,0xc1,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x15,0xff, +0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x45,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xc8,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff, +0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff, +0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0xf0,0xff, +0x86,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff, +0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff, +0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x01,0xff, +0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff, +0x7e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xb1,0xff, +0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xc5,0xff, +0x7a,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0x46,0xff, +0xe1,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x7a,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff, +0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xa7,0xff, +0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0x00,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff }; diff -Nru linux/sound/pci/korg1212/korg1212.c linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212.c --- linux/sound/pci/korg1212/korg1212.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/korg1212/korg1212.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2350 @@ +/* + * Driver for the Korg 1212 IO PCI card + * + * Copyright (c) 2001 Haroldo Gamal + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define DEBUG 1 +//#define LARGEALLOC 1 +#define PRINTK printk + +// ---------------------------------------------------------------------------- +// the following enum defines the valid states of the Korg 1212 I/O card. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_STATE_NONEXISTENT, // there is no card here + K1212_STATE_UNINITIALIZED, // the card is awaiting DSP download + K1212_STATE_DSP_IN_PROCESS, // the card is currently downloading its DSP code + K1212_STATE_DSP_COMPLETE, // the card has finished the DSP download + K1212_STATE_READY, // the card can be opened by an application. Any application + // requests prior to this state should fail. Only an open + // request can be made at this state. + K1212_STATE_OPEN, // an application has opened the card + K1212_STATE_SETUP, // the card has been setup for play + K1212_STATE_PLAYING, // the card is playing + K1212_STATE_MONITOR, // the card is in the monitor mode + K1212_STATE_CALIBRATING, // the card is currently calibrating + K1212_STATE_ERRORSTOP, // the card has stopped itself because of an error and we + // are in the process of cleaning things up. + K1212_STATE_MAX_STATE // state values of this and beyond are invalid +} CardState; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants written to the card's +// host-to-card doorbell to initiate a command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_DB_RequestForData = 0, // sent by the card to request a buffer fill. + K1212_DB_TriggerPlay = 1, // starts playback/record on the card. + K1212_DB_SelectPlayMode = 2, // select monitor, playback setup, or stop. + K1212_DB_ConfigureBufferMemory = 3, // tells card where the host audio buffers are. + K1212_DB_RequestAdatTimecode = 4, // asks the card for the latest ADAT timecode value. + K1212_DB_SetClockSourceRate = 5, // sets the clock source and rate for the card. + K1212_DB_ConfigureMiscMemory = 6, // tells card where other buffers are. + K1212_DB_TriggerFromAdat = 7, // tells card to trigger from Adat at a specific + // timecode value. + K1212_DB_RebootCard = 0xA0, // instructs the card to reboot. + K1212_DB_BootFromDSPPage4 = 0xA4, // instructs the card to boot from the DSP microcode + // on page 4 (local page to card). + K1212_DB_DSPDownloadDone = 0xAE, // sent by the card to indicate the download has + // completed. + K1212_DB_StartDSPDownload = 0xAF // tells the card to download its DSP firmware. +} korg1212_dbcnst_t; + +#define K1212_ISRCODE_DMAERROR 0x80 +#define K1212_ISRCODE_CARDSTOPPED 0x81 + +// ---------------------------------------------------------------------------- +// The following enumeration defines return codes for DeviceIoControl() calls +// to the Korg 1212 I/O driver. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_CMDRET_Success = 0, // command was successfully placed + K1212_CMDRET_DIOCFailure, // the DeviceIoControl call failed + K1212_CMDRET_PMFailure, // the protected mode call failed + K1212_CMDRET_FailUnspecified, // unspecified failure + K1212_CMDRET_FailBadState, // the specified command can not be given in + // the card's current state. (or the wave device's + // state) + K1212_CMDRET_CardUninitialized, // the card is uninitialized and cannot be used + K1212_CMDRET_BadIndex, // an out of range card index was specified + K1212_CMDRET_BadHandle, // an invalid card handle was specified + K1212_CMDRET_NoFillRoutine, // a play request has been made before a fill routine set + K1212_CMDRET_FillRoutineInUse, // can't set a new fill routine while one is in use + K1212_CMDRET_NoAckFromCard, // the card never acknowledged a command + K1212_CMDRET_BadParams, // bad parameters were provided by the caller + + // -------------------------------------------------------------- + // the following return errors are specific to the wave device + // driver interface. These will not be encountered by users of + // the 32 bit DIOC interface (a.k.a. custom or native API). + // -------------------------------------------------------------- + K1212_CMDRET_BadDevice, // the specified wave device was out of range + K1212_CMDRET_BadFormat // the specified wave format is unsupported +} snd_korg1212rc; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants used to select the play +// mode for the card in the SelectPlayMode command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_MODE_SetupPlay = 0x00000001, // provides card with pre-play information + K1212_MODE_MonitorOn = 0x00000002, // tells card to turn on monitor mode + K1212_MODE_MonitorOff = 0x00000004, // tells card to turn off monitor mode + K1212_MODE_StopPlay = 0x00000008 // stops playback on the card +} PlayModeSelector; + +// ---------------------------------------------------------------------------- +// The following enumeration defines the constants used to select the monitor +// mode for the card in the SetMonitorMode command. +// ---------------------------------------------------------------------------- +typedef enum { + K1212_MONMODE_Off = 0, // tells card to turn off monitor mode + K1212_MONMODE_On // tells card to turn on monitor mode +} MonitorModeSelector; + +#define MAILBOX0_OFFSET 0x40 // location of mailbox 0 relative to base address +#define MAILBOX1_OFFSET 0x44 // location of mailbox 1 relative to base address +#define MAILBOX2_OFFSET 0x48 // location of mailbox 2 relative to base address +#define MAILBOX3_OFFSET 0x4c // location of mailbox 3 relative to base address +#define OUT_DOORBELL_OFFSET 0x60 // location of PCI to local doorbell " +#define IN_DOORBELL_OFFSET 0x64 // location of local to PCI doorbell " +#define STATUS_REG_OFFSET 0x68 // location of interrupt control/status register " +#define PCI_CONTROL_OFFSET 0x6c // location of the EEPROM, PCI, User I/O, init control + // register +#define SENS_CONTROL_OFFSET 0x6e // location of the input sensitivity setting register. + // this is the upper word of the PCI control reg. +#define DEV_VEND_ID_OFFSET 0x70 // location of the device and vendor ID register + +#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement + // from the card after sending a command. +#define INTERCOMMAND_DELAY 40 +#define MAX_COMMAND_RETRIES 5 // maximum number of times the driver will attempt + // to send a command before giving up. +#define COMMAND_ACK_MASK 0x8000 // the MSB is set in the command acknowledgment from + // the card. +#define DOORBELL_VAL_MASK 0x00FF // the doorbell value is one byte + +#define CARD_BOOT_DELAY_IN_MS 10 + +#define DSP_BOOT_DELAY_IN_MS 200 + +#define kNumBuffers 8 +#define k1212MaxCards 4 +#define k1212NumWaveDevices 6 +#define k16BitChannels 10 +#define k32BitChannels 2 +#define kAudioChannels (k16BitChannels + k32BitChannels) +#define kPlayBufferFrames 1024 + +#define K1212_CHANNELS 16 +#define K1212_FRAME_SIZE (sizeof(KorgAudioFrame)) +#define K1212_MAX_SAMPLES (kPlayBufferFrames*kNumBuffers) +#define K1212_PERIODS (K1212_BUF_SIZE/K1212_BLOCK_SIZE) +#define K1212_PERIOD_BYTES (K1212_BLOCK_SIZE) +#define K1212_BLOCK_SIZE (K1212_FRAME_SIZE*kPlayBufferFrames) +#define K1212_BUF_SIZE (K1212_BLOCK_SIZE*kNumBuffers) + +#define k1212MinADCSens 0x7f +#define k1212MaxADCSens 0x00 +#define k1212MaxVolume 0x7fff +#define k1212MaxWaveVolume 0xffff +#define k1212MinVolume 0x0000 +#define k1212MaxVolInverted 0x8000 + +// ----------------------------------------------------------------- +// the following bits are used for controlling interrupts in the +// interrupt control/status reg +// ----------------------------------------------------------------- +#define PCI_INT_ENABLE_BIT 0x00000100 +#define PCI_DOORBELL_INT_ENABLE_BIT 0x00000200 +#define LOCAL_INT_ENABLE_BIT 0x00010000 +#define LOCAL_DOORBELL_INT_ENABLE_BIT 0x00020000 +#define LOCAL_DMA1_INT_ENABLE_BIT 0x00080000 + +// ----------------------------------------------------------------- +// the following bits are defined for the PCI command register +// ----------------------------------------------------------------- +#define PCI_CMD_MEM_SPACE_ENABLE_BIT 0x0002 +#define PCI_CMD_IO_SPACE_ENABLE_BIT 0x0001 +#define PCI_CMD_BUS_MASTER_ENABLE_BIT 0x0004 + +// ----------------------------------------------------------------- +// the following bits are defined for the PCI status register +// ----------------------------------------------------------------- +#define PCI_STAT_PARITY_ERROR_BIT 0x8000 +#define PCI_STAT_SYSTEM_ERROR_BIT 0x4000 +#define PCI_STAT_MASTER_ABORT_RCVD_BIT 0x2000 +#define PCI_STAT_TARGET_ABORT_RCVD_BIT 0x1000 +#define PCI_STAT_TARGET_ABORT_SENT_BIT 0x0800 + +// ------------------------------------------------------------------------ +// the following constants are used in setting the 1212 I/O card's input +// sensitivity. +// ------------------------------------------------------------------------ +#define SET_SENS_LOCALINIT_BITPOS 15 +#define SET_SENS_DATA_BITPOS 10 +#define SET_SENS_CLOCK_BITPOS 8 +#define SET_SENS_LOADSHIFT_BITPOS 0 + +#define SET_SENS_LEFTCHANID 0x00 +#define SET_SENS_RIGHTCHANID 0x01 + +#define K1212SENSUPDATE_DELAY_IN_MS 50 + +// -------------------------------------------------------------------------- +// WaitRTCTicks +// +// This function waits the specified number of real time clock ticks. +// According to the DDK, each tick is ~0.8 microseconds. +// The defines following the function declaration can be used for the +// numTicksToWait parameter. +// -------------------------------------------------------------------------- +#define ONE_RTC_TICK 1 +#define SENSCLKPULSE_WIDTH 4 +#define LOADSHIFT_DELAY 4 +#define INTERCOMMAND_DELAY 40 +#define STOPCARD_DELAY 300 // max # RTC ticks for the card to stop once we write + // the command register. (could be up to 180 us) +#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement + // from the card after sending a command. + +#include "korg1212-firmware.h" + +typedef struct _snd_korg1212 korg1212_t; + +typedef u16 K1212Sample; // channels 0-9 use 16 bit samples +typedef u32 K1212SpdifSample; // channels 10-11 use 32 bits - only 20 are sent + // across S/PDIF. +typedef u32 K1212TimeCodeSample; // holds the ADAT timecode value + +typedef enum { + K1212_CLKIDX_AdatAt44_1K = 0, // selects source as ADAT at 44.1 kHz + K1212_CLKIDX_AdatAt48K, // selects source as ADAT at 48 kHz + K1212_CLKIDX_WordAt44_1K, // selects source as S/PDIF at 44.1 kHz + K1212_CLKIDX_WordAt48K, // selects source as S/PDIF at 48 kHz + K1212_CLKIDX_LocalAt44_1K, // selects source as local clock at 44.1 kHz + K1212_CLKIDX_LocalAt48K, // selects source as local clock at 48 kHz + K1212_CLKIDX_Invalid // used to check validity of the index +} ClockSourceIndex; + +typedef enum { + K1212_CLKIDX_Adat = 0, // selects source as ADAT + K1212_CLKIDX_Word, // selects source as S/PDIF + K1212_CLKIDX_Local // selects source as local clock +} ClockSourceType; + +typedef struct KorgAudioFrame { + K1212Sample frameData16[k16BitChannels]; + K1212SpdifSample frameData32[k32BitChannels]; + K1212TimeCodeSample timeCodeVal; +} KorgAudioFrame; + +typedef struct KorgAudioBuffer { + KorgAudioFrame bufferData[kPlayBufferFrames]; /* buffer definition */ +} KorgAudioBuffer; + +typedef struct KorgSharedBuffer { +#ifdef LARGEALLOC + KorgAudioBuffer playDataBufs[kNumBuffers]; + KorgAudioBuffer recordDataBufs[kNumBuffers]; +#endif + short volumeData[kAudioChannels]; + u32 cardCommand; + u16 routeData [kAudioChannels]; + u32 AdatTimeCode; // ADAT timecode value +} KorgSharedBuffer; + +typedef struct SensBits { + union { + struct { + unsigned int leftChanVal:8; + unsigned int leftChanId:8; + } v; + u16 leftSensBits; + } l; + union { + struct { + unsigned int rightChanVal:8; + unsigned int rightChanId:8; + } v; + u16 rightSensBits; + } r; +} SensBits; + +struct _snd_korg1212 { + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm16; + int irq; + + spinlock_t lock; + + wait_queue_head_t wait; + + unsigned long iomem; + unsigned long ioport; + unsigned long iomem2; + unsigned long irqcount; + unsigned long inIRQ; + unsigned long iobase; + + struct resource *res_iomem; + struct resource *res_ioport; + struct resource *res_iomem2; + + u32 dspCodeSize; + u32 dspMemPhy; // DSP memory block handle (Physical Address) + void * dspMemPtr; // block memory (Virtual Address) + + u32 DataBufsSize; + + KorgAudioBuffer * playDataBufsPtr; + KorgAudioBuffer * recordDataBufsPtr; + + KorgSharedBuffer * sharedBufferPtr; + u32 RecDataPhy; + u32 PlayDataPhy; + unsigned long sharedBufferPhy; + u32 VolumeTablePhy; + u32 RoutingTablePhy; + u32 AdatTimeCodePhy; + + u32 * statusRegPtr; // address of the interrupt status/control register + u32 * outDoorbellPtr; // address of the host->card doorbell register + u32 * inDoorbellPtr; // address of the card->host doorbell register + u32 * mailbox0Ptr; // address of mailbox 0 on the card + u32 * mailbox1Ptr; // address of mailbox 1 on the card + u32 * mailbox2Ptr; // address of mailbox 2 on the card + u32 * mailbox3Ptr; // address of mailbox 3 on the card + u32 * controlRegPtr; // address of the EEPROM, PCI, I/O, Init ctrl reg + u16 * sensRegPtr; // address of the sensitivity setting register + u32 * idRegPtr; // address of the device and vendor ID registers + + + size_t periodsize; + size_t currentBuffer; + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_info_entry_t * proc_entry; + + CardState cardState; + int running; + int idleMonitorOn; // indicates whether the card is in idle monitor mode. + u32 cmdRetryCount; // tracks how many times we have retried sending to the card. + + ClockSourceIndex clkSrcRate; // sample rate and clock source + + ClockSourceType clkSource; // clock source + int clkRate; // clock rate + + int volumePhase[kAudioChannels]; + + u16 leftADCInSens; // ADC left channel input sensitivity + u16 rightADCInSens; // ADC right channel input sensitivity +}; + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("korg1212"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{KORG,korg1212}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Korg 1212 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_AUTHOR("Haroldo Gamal "); + +static struct pci_device_id snd_korg1212_ids[] __devinitdata = { + { 0x10b5, 0x906d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +static char* stateName[] = { + "Non-existent", + "Uninitialized", + "DSP download in process", + "DSP download complete", + "Ready", + "Open", + "Setup for play", + "Playing", + "Monitor mode on", + "Calibrating" + "Invalid" +}; + +static char* clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" }; + +static char* clockSourceName[] = { + "ADAT at 44.1 kHz", + "ADAT at 48 kHz", + "S/PDIF at 44.1 kHz", + "S/PDIF at 48 kHz", + "local clock at 44.1 kHz", + "local clock at 48 kHz" +}; + +static char* channelName[] = { + "ADAT-1", + "ADAT-2", + "ADAT-3", + "ADAT-4", + "ADAT-5", + "ADAT-6", + "ADAT-7", + "ADAT-8", + "Analog-L", + "Analog-R", + "SPDIF-L", + "SPDIF-R", +}; + +u16 ClockSourceSelector[] = {0x8000, // selects source as ADAT at 44.1 kHz + 0x0000, // selects source as ADAT at 48 kHz + 0x8001, // selects source as S/PDIF at 44.1 kHz + 0x0001, // selects source as S/PDIF at 48 kHz + 0x8002, // selects source as local clock at 44.1 kHz + 0x0002 // selects source as local clock at 48 kHz + }; + +snd_korg1212rc rc; + + +MODULE_DEVICE_TABLE(pci, snd_korg1212_ids); + +typedef union swap_u32 { unsigned char c[4]; u32 i; } swap_u32; + +#ifdef SNDRV_BIG_ENDIAN +static u32 LowerWordSwap(u32 swappee) +#else +static u32 UpperWordSwap(u32 swappee) +#endif +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[2] = swapper.c[3]; + retVal.c[3] = swapper.c[2]; + retVal.c[1] = swapper.c[1]; + retVal.c[0] = swapper.c[0]; + + return retVal.i; +} + +#ifdef SNDRV_BIG_ENDIAN +static u32 UpperWordSwap(u32 swappee) +#else +static u32 LowerWordSwap(u32 swappee) +#endif +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[2] = swapper.c[2]; + retVal.c[3] = swapper.c[3]; + retVal.c[1] = swapper.c[0]; + retVal.c[0] = swapper.c[1]; + + return retVal.i; +} + +#if 0 /* not used */ + +static u32 EndianSwap(u32 swappee) +{ + swap_u32 retVal, swapper; + + swapper.i = swappee; + retVal.c[0] = swapper.c[3]; + retVal.c[1] = swapper.c[2]; + retVal.c[2] = swapper.c[1]; + retVal.c[3] = swapper.c[0]; + + return retVal.i; +} + +#endif /* not used */ + +void TickDelay(int time) +{ + udelay(time); +} + +#define SetBitInWord(theWord,bitPosition) (*theWord) |= (0x0001 << bitPosition) +#define SetBitInDWord(theWord,bitPosition) (*theWord) |= (0x00000001 << bitPosition) +#define ClearBitInWord(theWord,bitPosition) (*theWord) &= ~(0x0001 << bitPosition) +#define ClearBitInDWord(theWord,bitPosition) (*theWord) &= ~(0x00000001 << bitPosition) + +static snd_korg1212rc snd_korg1212_Send1212Command(korg1212_t *korg1212, korg1212_dbcnst_t doorbellVal, + u32 mailBox0Val, u32 mailBox1Val, u32 mailBox2Val, u32 mailBox3Val) +{ + u32 retryCount; + u16 mailBox3Lo; + + if (korg1212->outDoorbellPtr) { +#ifdef DEBUG + PRINTK("DEBUG: Card <- 0x%08x 0x%08x [%s]\n", doorbellVal, mailBox0Val, stateName[korg1212->cardState]); +#endif + for (retryCount = 0; retryCount < MAX_COMMAND_RETRIES; retryCount++) { + + writel(mailBox3Val, korg1212->mailbox3Ptr); + writel(mailBox2Val, korg1212->mailbox2Ptr); + writel(mailBox1Val, korg1212->mailbox1Ptr); + writel(mailBox0Val, korg1212->mailbox0Ptr); + writel(doorbellVal, korg1212->outDoorbellPtr); // interrupt the card + + // -------------------------------------------------------------- + // the reboot command will not give an acknowledgement. + // -------------------------------------------------------------- + switch (doorbellVal) { + case K1212_DB_RebootCard: + case K1212_DB_BootFromDSPPage4: + case K1212_DB_StartDSPDownload: + return K1212_CMDRET_Success; + default: + break; + } + + // -------------------------------------------------------------- + // See if the card acknowledged the command. Wait a bit, then + // read in the low word of mailbox3. If the MSB is set and the + // low byte is equal to the doorbell value, then it ack'd. + // -------------------------------------------------------------- + TickDelay(COMMAND_ACK_DELAY); + mailBox3Lo = readl(korg1212->mailbox3Ptr); + if (mailBox3Lo & COMMAND_ACK_MASK) { + if ((mailBox3Lo & DOORBELL_VAL_MASK) == (doorbellVal & DOORBELL_VAL_MASK)) { + korg1212->cmdRetryCount += retryCount; + return K1212_CMDRET_Success; + } + } + } + korg1212->cmdRetryCount += retryCount; + return K1212_CMDRET_NoAckFromCard; + } else { + return K1212_CMDRET_CardUninitialized; + } +} + +static void snd_korg1212_WaitForCardStopAck(korg1212_t *korg1212) +{ + unsigned long endtime = jiffies + 20 * HZ; + +#ifdef DEBUG + PRINTK("DEBUG: WaitForCardStopAck [%s]\n", stateName[korg1212->cardState]); +#endif // DEBUG + + if (korg1212->inIRQ) + return; + + do { + if (readl(&korg1212->sharedBufferPtr->cardCommand) == 0) + return; + if (!korg1212->inIRQ) + schedule(); + } while (jiffies < endtime); + + writel(0, &korg1212->sharedBufferPtr->cardCommand); +} + +static void snd_korg1212_TurnOnIdleMonitor(korg1212_t *korg1212) +{ + TickDelay(INTERCOMMAND_DELAY); + korg1212->idleMonitorOn = 1; + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); +} + +static void snd_korg1212_TurnOffIdleMonitor(korg1212_t *korg1212) +{ + if (korg1212->idleMonitorOn) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + korg1212->idleMonitorOn = 0; + } +} + +static void snd_korg1212_setCardState(korg1212_t * korg1212, CardState csState) +{ + switch (csState) { + case K1212_STATE_READY: + snd_korg1212_TurnOnIdleMonitor(korg1212); + break; + + case K1212_STATE_OPEN: + snd_korg1212_TurnOffIdleMonitor(korg1212); + break; + + default: + break; + } + + korg1212->cardState = csState; +} + +static int snd_korg1212_OpenCard(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: OpenCard [%s]\n", stateName[korg1212->cardState]); +#endif + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + return 1; +} + +static int snd_korg1212_CloseCard(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: CloseCard [%s]\n", stateName[korg1212->cardState]); +#endif + + if (korg1212->cardState == K1212_STATE_SETUP) { + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_StopPlay, 0, 0, 0); +#ifdef DEBUG + if (rc) PRINTK("DEBUG: CloseCard - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + if (rc != K1212_CMDRET_Success) + return 0; + } else if (korg1212->cardState > K1212_STATE_SETUP) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + } + + if (korg1212->cardState > K1212_STATE_READY) + snd_korg1212_setCardState(korg1212, K1212_STATE_READY); + + return 0; +} + +static int snd_korg1212_SetupForPlay(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: SetupForPlay [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_korg1212_setCardState(korg1212, K1212_STATE_SETUP); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_SetupPlay, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: SetupForPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + if (rc != K1212_CMDRET_Success) { + return 0; + } + return 1; +} + +static int snd_korg1212_TriggerPlay(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: TriggerPlay [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_korg1212_setCardState(korg1212, K1212_STATE_PLAYING); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_TriggerPlay, 0, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: TriggerPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + if (rc != K1212_CMDRET_Success) { + return 0; + } + return 1; +} + +static int snd_korg1212_StopPlay(korg1212_t * korg1212) +{ +#ifdef DEBUG + PRINTK("DEBUG: StopPlay [%s]\n", stateName[korg1212->cardState]); +#endif + + if (korg1212->cardState != K1212_STATE_ERRORSTOP) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + } + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + return 1; +} + +static void snd_korg1212_EnableCardInterrupts(korg1212_t * korg1212) +{ + * korg1212->statusRegPtr = PCI_INT_ENABLE_BIT | + PCI_DOORBELL_INT_ENABLE_BIT | + LOCAL_INT_ENABLE_BIT | + LOCAL_DOORBELL_INT_ENABLE_BIT | + LOCAL_DMA1_INT_ENABLE_BIT; +} + +#if 0 /* not used */ + +static int snd_korg1212_SetMonitorMode(korg1212_t *korg1212, MonitorModeSelector mode) +{ +#ifdef DEBUG + PRINTK("DEBUG: SetMonitorMode [%s]\n", stateName[korg1212->cardState]); +#endif + + switch (mode) { + case K1212_MONMODE_Off: + if (korg1212->cardState != K1212_STATE_MONITOR) { + return 0; + } else { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + snd_korg1212_WaitForCardStopAck(korg1212); + snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN); + } + break; + + case K1212_MONMODE_On: + if (korg1212->cardState != K1212_STATE_OPEN) { + return 0; + } else { + snd_korg1212_setCardState(korg1212, K1212_STATE_MONITOR); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); + if (rc != K1212_CMDRET_Success) { + return 0; + } + } + break; + + default: + return 0; + } + + return 1; +} + +#endif /* not used */ + +static int snd_korg1212_SetRate(korg1212_t *korg1212, int rate) +{ + static ClockSourceIndex s44[] = { K1212_CLKIDX_AdatAt44_1K, + K1212_CLKIDX_WordAt44_1K, + K1212_CLKIDX_LocalAt44_1K }; + static ClockSourceIndex s48[] = { + K1212_CLKIDX_AdatAt48K, + K1212_CLKIDX_WordAt48K, + K1212_CLKIDX_LocalAt48K }; + int parm; + + switch(rate) { + case 44100: + parm = s44[korg1212->clkSource]; + break; + + case 48000: + parm = s48[korg1212->clkSource]; + break; + + default: + return -EINVAL; + } + + korg1212->clkSrcRate = parm; + korg1212->clkRate = rate; + + TickDelay(INTERCOMMAND_DELAY); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate, + ClockSourceSelector[korg1212->clkSrcRate], + 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + return 0; +} + +static int snd_korg1212_SetClockSource(korg1212_t *korg1212, int source) +{ + + if (source<0 || source >2) + return -EINVAL; + + korg1212->clkSource = source; + + snd_korg1212_SetRate(korg1212, korg1212->clkRate); + + return 0; +} + +static void snd_korg1212_DisableCardInterrupts(korg1212_t *korg1212) +{ + * korg1212->statusRegPtr = 0; +} + +static int snd_korg1212_WriteADCSensitivity(korg1212_t *korg1212) +{ + SensBits sensVals; + int bitPosition; + int channel; + int clkIs48K; + int monModeSet; + u16 controlValue; // this keeps the current value to be written to + // the card's eeprom control register. + u16 count; + +#ifdef DEBUG + PRINTK("DEBUG: WriteADCSensivity [%s]\n", stateName[korg1212->cardState]); +#endif + + // ---------------------------------------------------------------------------- + // initialize things. The local init bit is always set when writing to the + // card's control register. + // ---------------------------------------------------------------------------- + controlValue = 0; + SetBitInWord(&controlValue, SET_SENS_LOCALINIT_BITPOS); // init the control value + + // ---------------------------------------------------------------------------- + // make sure the card is not in monitor mode when we do this update. + // ---------------------------------------------------------------------------- + if (korg1212->cardState == K1212_STATE_MONITOR || korg1212->idleMonitorOn) { + writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand); + monModeSet = 1; + snd_korg1212_WaitForCardStopAck(korg1212); + } else + monModeSet = 0; + + // ---------------------------------------------------------------------------- + // we are about to send new values to the card, so clear the new values queued + // flag. Also, clear out mailbox 3, so we don't lockup. + // ---------------------------------------------------------------------------- + writel(0, korg1212->mailbox3Ptr); + TickDelay(LOADSHIFT_DELAY); + + // ---------------------------------------------------------------------------- + // determine whether we are running a 48K or 44.1K clock. This info is used + // later when setting the SPDIF FF after the volume has been shifted in. + // ---------------------------------------------------------------------------- + switch (korg1212->clkSrcRate) { + case K1212_CLKIDX_AdatAt44_1K: + case K1212_CLKIDX_WordAt44_1K: + case K1212_CLKIDX_LocalAt44_1K: + clkIs48K = 0; + break; + + case K1212_CLKIDX_WordAt48K: + case K1212_CLKIDX_AdatAt48K: + case K1212_CLKIDX_LocalAt48K: + default: + clkIs48K = 1; + break; + } + + // ---------------------------------------------------------------------------- + // start the update. Setup the bit structure and then shift the bits. + // ---------------------------------------------------------------------------- + sensVals.l.v.leftChanId = SET_SENS_LEFTCHANID; + sensVals.r.v.rightChanId = SET_SENS_RIGHTCHANID; + sensVals.l.v.leftChanVal = korg1212->leftADCInSens; + sensVals.r.v.rightChanVal = korg1212->rightADCInSens; + + // ---------------------------------------------------------------------------- + // now start shifting the bits in. Start with the left channel then the right. + // ---------------------------------------------------------------------------- + for (channel = 0; channel < 2; channel++) { + + // ---------------------------------------------------------------------------- + // Bring the load/shift line low, then wait - the spec says >150ns from load/ + // shift low to the first rising edge of the clock. + // ---------------------------------------------------------------------------- + ClearBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS); + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // load/shift goes low + TickDelay(LOADSHIFT_DELAY); + + for (bitPosition = 15; bitPosition >= 0; bitPosition--) { // for all the bits + if (channel == 0) { + if (sensVals.l.leftSensBits & (0x0001 << bitPosition)) { + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high + } else { + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low + } + } else { + if (sensVals.r.rightSensBits & (0x0001 << bitPosition)) { + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set high + } else { + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); // data bit set low + } + } + + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes low + TickDelay(SENSCLKPULSE_WIDTH); + SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes high + TickDelay(SENSCLKPULSE_WIDTH); + } + + // ---------------------------------------------------------------------------- + // finish up SPDIF for left. Bring the load/shift line high, then write a one + // bit if the clock rate is 48K otherwise write 0. + // ---------------------------------------------------------------------------- + ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + SetBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // load shift goes high - clk low + TickDelay(SENSCLKPULSE_WIDTH); + + if (clkIs48K) + SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS); + + writew(controlValue, korg1212->sensRegPtr); // set/clear data bit + TickDelay(ONE_RTC_TICK); + SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes high + TickDelay(SENSCLKPULSE_WIDTH); + ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS); + writew(controlValue, korg1212->sensRegPtr); // clock goes low + TickDelay(SENSCLKPULSE_WIDTH); + } + + // ---------------------------------------------------------------------------- + // The update is complete. Set a timeout. This is the inter-update delay. + // Also, if the card was in monitor mode, restore it. + // ---------------------------------------------------------------------------- + for (count = 0; count < 10; count++) + TickDelay(SENSCLKPULSE_WIDTH); + + if (monModeSet) { + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode, + K1212_MODE_MonitorOn, 0, 0, 0); +#ifdef DEBUG + if (rc) PRINTK("DEBUG: WriteADCSensivity - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + } + + return 1; +} + +static void snd_korg1212_OnDSPDownloadComplete(korg1212_t *korg1212) +{ + int channel; + +#ifdef DEBUG + PRINTK("DEBUG: DSP download is complete. [%s]\n", stateName[korg1212->cardState]); +#endif + + // ---------------------------------------------------- + // tell the card to boot + // ---------------------------------------------------- + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_BootFromDSPPage4, 0, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Boot from Page 4 - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + mdelay(DSP_BOOT_DELAY_IN_MS); + + // -------------------------------------------------------------------------------- + // Let the card know where all the buffers are. + // -------------------------------------------------------------------------------- + rc = snd_korg1212_Send1212Command(korg1212, + K1212_DB_ConfigureBufferMemory, + LowerWordSwap(korg1212->PlayDataPhy), + LowerWordSwap(korg1212->RecDataPhy), + ((kNumBuffers * kPlayBufferFrames) / 2), // size given to the card + // is based on 2 buffers + 0 + ); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Configure Buffer Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + TickDelay(INTERCOMMAND_DELAY); + + rc = snd_korg1212_Send1212Command(korg1212, + K1212_DB_ConfigureMiscMemory, + LowerWordSwap(korg1212->VolumeTablePhy), + LowerWordSwap(korg1212->RoutingTablePhy), + LowerWordSwap(korg1212->AdatTimeCodePhy), + 0 + ); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Configure Misc Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + + // -------------------------------------------------------------------------------- + // Initialize the routing and volume tables, then update the card's state. + // -------------------------------------------------------------------------------- + TickDelay(INTERCOMMAND_DELAY); + + for (channel = 0; channel < kAudioChannels; channel++) { + korg1212->sharedBufferPtr->volumeData[channel] = k1212MaxVolume; + //korg1212->sharedBufferPtr->routeData[channel] = channel; + korg1212->sharedBufferPtr->routeData[channel] = 8 + (channel & 1); + } + + snd_korg1212_WriteADCSensitivity(korg1212); + + TickDelay(INTERCOMMAND_DELAY); + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate, + ClockSourceSelector[korg1212->clkSrcRate], + 0, 0, 0); +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + snd_korg1212_setCardState(korg1212, K1212_STATE_READY); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Set Monitor On - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + wake_up_interruptible(&korg1212->wait); +} + + +static void snd_korg1212_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 doorbellValue; + korg1212_t *korg1212 = snd_magic_cast(korg1212_t, dev_id, return); + + if(irq != korg1212->irq) + return; + + doorbellValue = readl(korg1212->inDoorbellPtr); + + if (!doorbellValue) + return; + + writel(doorbellValue, korg1212->inDoorbellPtr); + + korg1212->irqcount++; + + korg1212->inIRQ++; + + + switch (doorbellValue) { + case K1212_DB_DSPDownloadDone: +#ifdef DEBUG + PRINTK("DEBUG: IRQ DNLD count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + if (korg1212->cardState == K1212_STATE_DSP_IN_PROCESS) { + snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_COMPLETE); + snd_korg1212_OnDSPDownloadComplete(korg1212); + } + break; + + // ------------------------------------------------------------------------ + // an error occurred - stop the card + // ------------------------------------------------------------------------ + case K1212_ISRCODE_DMAERROR: +#ifdef DEBUG + PRINTK("DEBUG: IRQ DMAE count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + writel(0, &korg1212->sharedBufferPtr->cardCommand); + break; + + // ------------------------------------------------------------------------ + // the card has stopped by our request. Clear the command word and signal + // the semaphore in case someone is waiting for this. + // ------------------------------------------------------------------------ + case K1212_ISRCODE_CARDSTOPPED: +#ifdef DEBUG + PRINTK("DEBUG: IRQ CSTP count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]); +#endif + writel(0, &korg1212->sharedBufferPtr->cardCommand); + break; + + default: +#ifdef XDEBUG + PRINTK("DEBUG: IRQ DFLT count - %ld, %x, cpos=%d [%s].\n", korg1212->irqcount, doorbellValue, + korg1212->currentBuffer, stateName[korg1212->cardState]); +#endif + if ((korg1212->cardState > K1212_STATE_SETUP) || korg1212->idleMonitorOn) { + korg1212->currentBuffer++; + + if (korg1212->currentBuffer >= kNumBuffers) + korg1212->currentBuffer = 0; + + if (!korg1212->running) + break; + + if (korg1212->capture_substream) { + snd_pcm_period_elapsed(korg1212->capture_substream); + } + + if (korg1212->playback_substream) { + snd_pcm_period_elapsed(korg1212->playback_substream); + } + } + break; + } + + korg1212->inIRQ--; +} + +static int snd_korg1212_downloadDSPCode(korg1212_t *korg1212) +{ + +#ifdef DEBUG + PRINTK("DEBUG: DSP download is starting... [%s]\n", stateName[korg1212->cardState]); +#endif + + // --------------------------------------------------------------- + // verify the state of the card before proceeding. + // --------------------------------------------------------------- + if (korg1212->cardState >= K1212_STATE_DSP_IN_PROCESS) { + return 1; + } + + snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS); + + memcpy(korg1212->dspMemPtr, dspCode, korg1212->dspCodeSize); + + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload, + UpperWordSwap(korg1212->dspMemPhy), + 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Start DSP Download RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + interruptible_sleep_on_timeout(&korg1212->wait, HZ * 4); + + return 0; +} + +static snd_pcm_hardware_t snd_korg1212_playback_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: K1212_CHANNELS, + channels_max: K1212_CHANNELS, + buffer_bytes_max: K1212_BUF_SIZE, + period_bytes_min: K1212_PERIOD_BYTES, + period_bytes_max: K1212_PERIOD_BYTES, + periods_min: K1212_PERIODS, + periods_max: K1212_PERIODS, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_korg1212_capture_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: K1212_CHANNELS, + channels_max: K1212_CHANNELS, + buffer_bytes_max: K1212_BUF_SIZE, + period_bytes_min: K1212_PERIOD_BYTES, + period_bytes_max: K1212_PERIOD_BYTES, + periods_min: K1212_PERIODS, + periods_max: K1212_PERIODS, + fifo_size: 0, +}; + +static void snd_korg1212_free_pcm(snd_pcm_t *pcm) +{ + korg1212_t *korg1212 = (korg1212_t *) pcm->private_data; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_free_pcm [%s]\n", stateName[korg1212->cardState]); +#endif + + korg1212->pcm16 = NULL; +} + +static unsigned int period_bytes[] = { K1212_PERIOD_BYTES }; + +#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { + count: PERIOD_BYTES, + list: period_bytes, + mask: 0 +}; + +static int snd_korg1212_playback_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_open [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_OpenCard(korg1212); + + snd_pcm_set_sync(substream); // ??? + + runtime->hw = snd_korg1212_playback_info; + runtime->dma_area = (char *) korg1212->playDataBufsPtr; + runtime->dma_bytes = K1212_BUF_SIZE; + + korg1212->playback_substream = substream; + korg1212->periodsize = K1212_PERIODS; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int snd_korg1212_capture_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_capture_open [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_OpenCard(korg1212); + + snd_pcm_set_sync(substream); // ??? + + runtime->hw = snd_korg1212_capture_info; + runtime->dma_area = (char *) korg1212->recordDataBufsPtr; + runtime->dma_bytes = K1212_BUF_SIZE; + + korg1212->capture_substream = substream; + korg1212->periodsize = K1212_PERIODS; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int snd_korg1212_playback_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_close [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + korg1212->playback_substream = NULL; + korg1212->periodsize = 0; + + snd_korg1212_CloseCard(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_capture_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_capture_close [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + korg1212->capture_substream = NULL; + korg1212->periodsize = 0; + + snd_korg1212_CloseCard(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_channel_info(snd_pcm_substream_t *substream, + snd_pcm_channel_info_t *info) +{ + int chn = info->channel; + + // snd_assert(info->channel < kAudioChannels + 1, return -EINVAL); + + info->offset = 0; + // if (chn < k16BitChannels) { + info->first = chn * 16; + // } else { + // info->first = k16BitChannels * 16 + (chn - k16BitChannels - 1) * 32; + // } + info->step = sizeof(KorgAudioFrame) * 8; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_channel_info %d:, offset=%ld, first=%d, step=%d\n", chn, info->offset, info->first, info->step); +#endif + + return 0; +} + +static int snd_korg1212_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_ioctl: cmd=%d\n", cmd); +#endif + if (cmd == SNDRV_PCM_IOCTL1_CHANNEL_INFO ) { + snd_pcm_channel_info_t *info = arg; + return snd_korg1212_channel_info(substream, info); + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_korg1212_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + int err; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_hw_params [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) { + spin_unlock_irqrestore(&korg1212->lock, flags); + return err; + } + + if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { + spin_unlock_irqrestore(&korg1212->lock, flags); + return -EINVAL; + } + + korg1212->periodsize = K1212_BLOCK_SIZE; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_prepare(snd_pcm_substream_t *substream) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + unsigned long flags; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_prepare [%s]\n", stateName[korg1212->cardState]); +#endif + + spin_lock_irqsave(&korg1212->lock, flags); + + snd_korg1212_SetupForPlay(korg1212); + + korg1212->currentBuffer = -1; + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_trigger [%s] cmd=%d\n", stateName[korg1212->cardState], cmd); +#endif + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + korg1212->running = 1; + snd_korg1212_TriggerPlay(korg1212); + break; + + case SNDRV_PCM_TRIGGER_STOP: + korg1212->running = 0; + snd_korg1212_StopPlay(korg1212); + break; + + default: + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t snd_korg1212_pointer(snd_pcm_substream_t *substream) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + snd_pcm_uframes_t pos; + + if (korg1212->currentBuffer < 0) + return 0; + + pos = korg1212->currentBuffer * kPlayBufferFrames; + +#ifdef XDEBUG + PRINTK("DEBUG: snd_korg1212_pointer [%s] %ld\n", stateName[korg1212->cardState], pos); +#endif + + return pos; +} + +static int snd_korg1212_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); +#endif + + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + copy_from_user(dst, src, count * K1212_FRAME_SIZE); + + return 0; +} + +static int snd_korg1212_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); +#endif + + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + copy_to_user(dst, src, count * K1212_FRAME_SIZE); + + return 0; +} + +static int snd_korg1212_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; + +#ifdef DEBUG + PRINTK("DEBUG: snd_korg1212_playback_silence [%s]\n", stateName[korg1212->cardState]); +#endif + + snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); + + memset(dst, 0, count * K1212_FRAME_SIZE); + + return 0; +} + +static snd_pcm_ops_t snd_korg1212_playback_ops = { + open: snd_korg1212_playback_open, + close: snd_korg1212_playback_close, + ioctl: snd_korg1212_ioctl, + hw_params: snd_korg1212_hw_params, + prepare: snd_korg1212_prepare, + trigger: snd_korg1212_trigger, + pointer: snd_korg1212_pointer, + copy: snd_korg1212_playback_copy, + silence: snd_korg1212_playback_silence, +}; + +static snd_pcm_ops_t snd_korg1212_capture_ops = { + open: snd_korg1212_capture_open, + close: snd_korg1212_capture_close, + ioctl: snd_korg1212_ioctl, + hw_params: snd_korg1212_hw_params, + prepare: snd_korg1212_prepare, + trigger: snd_korg1212_trigger, + pointer: snd_korg1212_pointer, + copy: snd_korg1212_capture_copy, +}; + +/* + * Control Interface + */ + +static int snd_korg1212_control_phase_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + return 0; +} + +static int snd_korg1212_control_phase_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i = kcontrol->private_value; + + spin_lock_irqsave(&korg1212->lock, flags); + + u->value.integer.value[0] = korg1212->volumePhase[i]; + + if (i >= 8) + u->value.integer.value[1] = korg1212->volumePhase[i+1]; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_phase_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + int i, val; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + korg1212->volumePhase[i] = u->value.integer.value[0]; + + val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value]; + + if ((u->value.integer.value[0] > 0) != (val < 0)) { + val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1); + korg1212->sharedBufferPtr->volumeData[i] = val; + change = 1; + } + + if (i >= 8) { + korg1212->volumePhase[i+1] = u->value.integer.value[1]; + + val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1]; + + if ((u->value.integer.value[1] > 0) != (val < 0)) { + val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1); + korg1212->sharedBufferPtr->volumeData[i+1] = val; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + uinfo->value.integer.min = k1212MinVolume; + uinfo->value.integer.max = k1212MaxVolume; + return 0; +} + +static int snd_korg1212_control_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]); + + if (i >= 8) + u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]); + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + int i; + int val; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) { + val = korg1212->volumePhase[i] > 0 ? -1 : 1; + val *= u->value.integer.value[0]; + korg1212->sharedBufferPtr->volumeData[i] = val; + change = 1; + } + + if (i >= 8) { + if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) { + val = korg1212->volumePhase[i+1] > 0 ? -1 : 1; + val *= u->value.integer.value[1]; + korg1212->sharedBufferPtr->volumeData[i+1] = val; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; + uinfo->value.enumerated.items = kAudioChannels; + if (uinfo->value.enumerated.item > kAudioChannels-1) { + uinfo->value.enumerated.item = kAudioChannels-1; + } + strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_korg1212_control_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i]; + + if (i >= 8) + u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1]; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0, i; + + spin_lock_irqsave(&korg1212->lock, flags); + + i = kcontrol->private_value; + + if (u->value.enumerated.item[0] != korg1212->sharedBufferPtr->volumeData[i]) { + korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0]; + change = 1; + } + + if (i >= 8) { + if (u->value.enumerated.item[1] != korg1212->sharedBufferPtr->volumeData[i+1]) { + korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1]; + change = 1; + } + } + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_analog_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = k1212MaxADCSens; + uinfo->value.integer.max = k1212MinADCSens; + return 0; +} + +static int snd_korg1212_control_analog_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&korg1212->lock, flags); + + u->value.integer.value[0] = korg1212->leftADCInSens; + u->value.integer.value[1] = korg1212->rightADCInSens; + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return 0; +} + +static int snd_korg1212_control_analog_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + + spin_lock_irqsave(&korg1212->lock, flags); + + if (u->value.integer.value[0] != korg1212->leftADCInSens) { + korg1212->leftADCInSens = u->value.integer.value[0]; + change = 1; + } + if (u->value.integer.value[1] != korg1212->rightADCInSens) { + korg1212->rightADCInSens = u->value.integer.value[1]; + change = 1; + } + + if (change) + snd_korg1212_WriteADCSensitivity(korg1212); + + spin_unlock_irqrestore(&korg1212->lock, flags); + + return change; +} + +static int snd_korg1212_control_sync_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) { + uinfo->value.enumerated.item = 2; + } + strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_korg1212_control_sync_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&korg1212->lock, flags); + + ucontrol->value.enumerated.item[0] = korg1212->clkSource; + + spin_unlock_irqrestore(&korg1212->lock, flags); + return 0; +} + +static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&korg1212->lock, flags); + change = val != korg1212->clkSource; + snd_korg1212_SetClockSource(korg1212, val); + spin_unlock_irqrestore(&korg1212->lock, flags); + return change; +} + +#define MON_MIXER(ord,c_name) \ + { \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: c_name " Monitor Volume", \ + info: snd_korg1212_control_volume_info, \ + get: snd_korg1212_control_volume_get, \ + put: snd_korg1212_control_volume_put, \ + private_value: ord, \ + }, \ + { \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, \ + name: c_name " Monitor Route", \ + info: snd_korg1212_control_route_info, \ + get: snd_korg1212_control_route_get, \ + put: snd_korg1212_control_route_put, \ + private_value: ord, \ + }, \ + { \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ + iface: SNDRV_CTL_ELEM_IFACE_PCM, \ + name: c_name " Monitor Phase Invert", \ + info: snd_korg1212_control_phase_info, \ + get: snd_korg1212_control_phase_get, \ + put: snd_korg1212_control_phase_put, \ + private_value: ord, \ + } + +static snd_kcontrol_new_t snd_korg1212_controls[] = { + MON_MIXER(8, "Analog"), + MON_MIXER(10, "SPDIF"), + MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"), + MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"), + { + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Sync Source", + info: snd_korg1212_control_sync_info, + get: snd_korg1212_control_sync_get, + put: snd_korg1212_control_sync_put, + }, + { + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "ADC Attenuation", + info: snd_korg1212_control_analog_info, + get: snd_korg1212_control_analog_get, + put: snd_korg1212_control_analog_put, + } +}; + +#define K1212_CONTROL_ELEMENTS (sizeof(snd_korg1212_controls) / sizeof(snd_korg1212_controls[0])) + +/* + * proc interface + */ + +static void snd_korg1212_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + int n; + korg1212_t *korg1212 = (korg1212_t *)entry->private_data; + + snd_iprintf(buffer, korg1212->card->longname); + snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1); + snd_iprintf(buffer, "\nGeneral settings\n"); + snd_iprintf(buffer, " period size: %d bytes\n", K1212_BLOCK_SIZE); + snd_iprintf(buffer, " clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] ); + snd_iprintf(buffer, " left ADC Sens: %d\n", korg1212->leftADCInSens ); + snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens ); + snd_iprintf(buffer, " Volume Info:\n"); + for (n=0; n %s [%d]\n", n, + channelName[n], + channelName[korg1212->sharedBufferPtr->routeData[n]], + korg1212->sharedBufferPtr->volumeData[n]); + snd_iprintf(buffer, "\nGeneral status\n"); + snd_iprintf(buffer, " ADAT Time Code: %d\n", korg1212->sharedBufferPtr->AdatTimeCode); + snd_iprintf(buffer, " Card State: %s\n", stateName[korg1212->cardState]); + snd_iprintf(buffer, "Idle mon. State: %d\n", korg1212->idleMonitorOn); + snd_iprintf(buffer, "Cmd retry count: %d\n", korg1212->cmdRetryCount); + snd_iprintf(buffer, " Irq count: %ld\n", korg1212->irqcount); +} + +static void __init snd_korg1212_proc_init(korg1212_t *korg1212) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(korg1212->card, "korg1212", korg1212->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = korg1212; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_korg1212_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + korg1212->proc_entry = entry; +} + +static void snd_korg1212_proc_done(korg1212_t * korg1212) +{ + if (korg1212->proc_entry) { + snd_info_unregister(korg1212->proc_entry); + korg1212->proc_entry = NULL; + } +} + +static int __init snd_korg1212_create(korg1212_t *korg1212) +{ + struct pci_dev *pci = korg1212->pci; + int err; + int i; + unsigned ioport_size, iomem_size, iomem2_size; + dma_addr_t phys_addr; + + korg1212->irq = -1; + korg1212->clkSource = K1212_CLKIDX_Local; + korg1212->clkRate = 44100; + korg1212->inIRQ = 0; + korg1212->running = 0; + snd_korg1212_setCardState(korg1212, K1212_STATE_UNINITIALIZED); + korg1212->idleMonitorOn = 0; + korg1212->clkSrcRate = K1212_CLKIDX_LocalAt44_1K; + korg1212->leftADCInSens = k1212MaxADCSens; + korg1212->rightADCInSens = k1212MaxADCSens; + + for (i=0; ivolumePhase[i] = 0; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + korg1212->iomem = pci_resource_start(korg1212->pci, 0); + korg1212->ioport = pci_resource_start(korg1212->pci, 1); + korg1212->iomem2 = pci_resource_start(korg1212->pci, 2); + + iomem_size = pci_resource_len(korg1212->pci, 0); + ioport_size = pci_resource_len(korg1212->pci, 1); + iomem2_size = pci_resource_len(korg1212->pci, 2); + +#ifdef DEBUG + PRINTK("DEBUG: resources:\n" + " iomem = 0x%lx (%d)\n" + " ioport = 0x%lx (%d)\n" + " iomem = 0x%lx (%d)\n" + " [%s]\n", + korg1212->iomem, iomem_size, + korg1212->ioport, ioport_size, + korg1212->iomem2, iomem2_size, + stateName[korg1212->cardState]); +#endif + + korg1212->res_iomem = request_mem_region(korg1212->iomem, iomem_size, "korg1212"); + if (korg1212->res_iomem == NULL) { + snd_printk("unable to grab region 0x%lx-0x%lx\n", + korg1212->iomem, korg1212->iomem + iomem_size - 1); + return -EBUSY; + } + + korg1212->res_ioport = request_region(korg1212->ioport, ioport_size, "korg1212"); + if (korg1212->res_ioport == NULL) { + snd_printk("unable to grab region 0x%lx-0x%lx\n", + korg1212->ioport, korg1212->ioport + ioport_size - 1); + return -EBUSY; + } + + korg1212->res_iomem2 = request_mem_region(korg1212->iomem2, iomem2_size, "korg1212"); + if (korg1212->res_iomem2 == NULL) { + snd_printk("unable to grab region 0x%lx-0x%lx\n", + korg1212->iomem2, korg1212->iomem2 + iomem2_size - 1); + return -EBUSY; + } + + if ((korg1212->iobase = (unsigned long) ioremap(korg1212->iomem, iomem_size)) == 0) { + snd_printk("unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase, + korg1212->iobase + iomem_size - 1); + return -EBUSY; + } + + err = request_irq(pci->irq, snd_korg1212_interrupt, + SA_INTERRUPT|SA_SHIRQ, + "korg1212", (void *) korg1212); + + if (err) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + + korg1212->irq = pci->irq; + + init_waitqueue_head(&korg1212->wait); + spin_lock_init(&korg1212->lock); + pci_set_master(korg1212->pci); + + korg1212->statusRegPtr = (u32 *) (korg1212->iobase + STATUS_REG_OFFSET); + korg1212->outDoorbellPtr = (u32 *) (korg1212->iobase + OUT_DOORBELL_OFFSET); + korg1212->inDoorbellPtr = (u32 *) (korg1212->iobase + IN_DOORBELL_OFFSET); + korg1212->mailbox0Ptr = (u32 *) (korg1212->iobase + MAILBOX0_OFFSET); + korg1212->mailbox1Ptr = (u32 *) (korg1212->iobase + MAILBOX1_OFFSET); + korg1212->mailbox2Ptr = (u32 *) (korg1212->iobase + MAILBOX2_OFFSET); + korg1212->mailbox3Ptr = (u32 *) (korg1212->iobase + MAILBOX3_OFFSET); + korg1212->controlRegPtr = (u32 *) (korg1212->iobase + PCI_CONTROL_OFFSET); + korg1212->sensRegPtr = (u16 *) (korg1212->iobase + SENS_CONTROL_OFFSET); + korg1212->idRegPtr = (u32 *) (korg1212->iobase + DEV_VEND_ID_OFFSET); + +#ifdef DEBUG + PRINTK("DEBUG: card registers:\n" + " Status register = 0x%p\n" + " OutDoorbell = 0x%p\n" + " InDoorbell = 0x%p\n" + " Mailbox0 = 0x%p\n" + " Mailbox1 = 0x%p\n" + " Mailbox2 = 0x%p\n" + " Mailbox3 = 0x%p\n" + " ControlReg = 0x%p\n" + " SensReg = 0x%p\n" + " IDReg = 0x%p\n" + " [%s]\n", + korg1212->statusRegPtr, + korg1212->outDoorbellPtr, + korg1212->inDoorbellPtr, + korg1212->mailbox0Ptr, + korg1212->mailbox1Ptr, + korg1212->mailbox2Ptr, + korg1212->mailbox3Ptr, + korg1212->controlRegPtr, + korg1212->sensRegPtr, + korg1212->idRegPtr, + stateName[korg1212->cardState]); +#endif + + korg1212->sharedBufferPtr = (KorgSharedBuffer *) snd_malloc_pci_pages(korg1212->pci, sizeof(KorgSharedBuffer), &phys_addr); + korg1212->sharedBufferPhy = (unsigned long)phys_addr; + + if (korg1212->sharedBufferPtr == NULL) { + snd_printk("can not allocate shared buffer memory (%d bytes)\n", sizeof(KorgSharedBuffer)); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer)); +#endif + +#ifndef LARGEALLOC + + korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers; + + korg1212->playDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); + korg1212->PlayDataPhy = (u32)phys_addr; + + if (korg1212->playDataBufsPtr == NULL) { + snd_printk("can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n", + korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize); +#endif + + korg1212->recordDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); + korg1212->RecDataPhy = (u32)phys_addr; + + if (korg1212->recordDataBufsPtr == NULL) { + snd_printk("can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n", + korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize); +#endif + +#else // LARGEALLOC + + korg1212->recordDataBufsPtr = korg1212->sharedBufferPtr->recordDataBufs; + korg1212->playDataBufsPtr = korg1212->sharedBufferPtr->playDataBufs; + korg1212->PlayDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->playDataBufs; + korg1212->RecDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->recordDataBufs; + +#endif // LARGEALLOC + + korg1212->dspCodeSize = sizeof (dspCode); + + korg1212->VolumeTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->volumeData; + korg1212->RoutingTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->routeData; + korg1212->AdatTimeCodePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->AdatTimeCode; + + korg1212->dspMemPtr = snd_malloc_pci_pages(korg1212->pci, korg1212->dspCodeSize, &phys_addr); + korg1212->dspMemPhy = (u32)phys_addr; + + if (korg1212->dspMemPtr == NULL) { + snd_printk("can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); + return -ENOMEM; + } + +#ifdef DEBUG + PRINTK("DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", + korg1212->dspMemPtr, korg1212->dspMemPhy, korg1212->dspCodeSize, + stateName[korg1212->cardState]); +#endif + + rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0); + +#ifdef DEBUG + if (rc) PRINTK("DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]); +#endif + + snd_korg1212_EnableCardInterrupts(korg1212); + + mdelay(CARD_BOOT_DELAY_IN_MS); + + if (snd_korg1212_downloadDSPCode(korg1212)) + return -EBUSY; + + printk(KERN_INFO "dspMemPhy = %08x U[%08x]\n" + "PlayDataPhy = %08x L[%08x]\n" + "RecDataPhy = %08x L[%08x]\n" + "VolumeTablePhy = %08x L[%08x]\n" + "RoutingTablePhy = %08x L[%08x]\n" + "AdatTimeCodePhy = %08x L[%08x]\n", + korg1212->dspMemPhy, UpperWordSwap(korg1212->dspMemPhy), + korg1212->PlayDataPhy, LowerWordSwap(korg1212->PlayDataPhy), + korg1212->RecDataPhy, LowerWordSwap(korg1212->RecDataPhy), + korg1212->VolumeTablePhy, LowerWordSwap(korg1212->VolumeTablePhy), + korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy), + korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy)); + + if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm16)) < 0) + return err; + + korg1212->pcm16->private_data = korg1212; + korg1212->pcm16->private_free = snd_korg1212_free_pcm; + strcpy(korg1212->pcm16->name, "korg1212"); + + snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops); + snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_CAPTURE, &snd_korg1212_capture_ops); + + korg1212->pcm16->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + for (i = 0; i < K1212_CONTROL_ELEMENTS; i++) { + err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212)); + if (err < 0) + return err; + } + + snd_korg1212_proc_init(korg1212); + + return 0; + +} + +static void +snd_korg1212_free(void *private_data) +{ + korg1212_t *korg1212 = (korg1212_t *)private_data; + + if (korg1212 == NULL) { + return; + } + + snd_korg1212_proc_done(korg1212); + + snd_korg1212_TurnOffIdleMonitor(korg1212); + + snd_korg1212_DisableCardInterrupts(korg1212); + + if (korg1212->irq >= 0) { + free_irq(korg1212->irq, (void *)korg1212); + korg1212->irq = -1; + } + if (korg1212->iobase != 0) { + iounmap((void *)korg1212->iobase); + korg1212->iobase = 0; + } + if (korg1212->res_iomem != NULL) { + release_resource(korg1212->res_iomem); + kfree_nocheck(korg1212->res_iomem); + korg1212->res_iomem = NULL; + } + if (korg1212->res_ioport != NULL) { + release_resource(korg1212->res_ioport); + kfree_nocheck(korg1212->res_ioport); + korg1212->res_ioport = NULL; + } + if (korg1212->res_iomem2 != NULL) { + release_resource(korg1212->res_iomem2); + kfree_nocheck(korg1212->res_iomem2); + korg1212->res_iomem2 = NULL; + } + + // ---------------------------------------------------- + // free up memory resources used for the DSP download. + // ---------------------------------------------------- + if (korg1212->dspMemPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->dspCodeSize, + korg1212->dspMemPtr, (dma_addr_t)korg1212->dspMemPhy); + korg1212->dspMemPhy = 0; + korg1212->dspMemPtr = 0; + korg1212->dspCodeSize = 0; + } + +#ifndef LARGEALLOC + + // ------------------------------------------------------ + // free up memory resources used for the Play/Rec Buffers + // ------------------------------------------------------ + if (korg1212->playDataBufsPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, + korg1212->playDataBufsPtr, (dma_addr_t)korg1212->PlayDataPhy); + korg1212->PlayDataPhy = 0; + korg1212->playDataBufsPtr = NULL; + } + + if (korg1212->recordDataBufsPtr) { + snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, + korg1212->recordDataBufsPtr, (dma_addr_t)korg1212->RecDataPhy); + korg1212->RecDataPhy = 0; + korg1212->recordDataBufsPtr = NULL; + } + +#endif + + // ---------------------------------------------------- + // free up memory resources used for the Shared Buffers + // ---------------------------------------------------- + if (korg1212->sharedBufferPtr) { + snd_free_pci_pages(korg1212->pci, (u32) sizeof(KorgSharedBuffer), + korg1212->sharedBufferPtr, (dma_addr_t)korg1212->sharedBufferPhy); + korg1212->sharedBufferPhy = 0; + korg1212->sharedBufferPtr = NULL; + } +} + +/* + * Card initialisation + */ + +static void snd_korg1212_card_free(snd_card_t *card) +{ +#ifdef DEBUG + PRINTK("DEBUG: Freeing card\n"); +#endif + snd_korg1212_free(card->private_data); +} + +static int __devinit +snd_korg1212_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + korg1212_t *korg1212; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) { + return -ENODEV; + } + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(korg1212_t))) == NULL) + return -ENOMEM; + + card->private_free = snd_korg1212_card_free; + korg1212 = (korg1212_t *)card->private_data; + korg1212->card = card; + korg1212->pci = pci; + + if ((err = snd_korg1212_create(korg1212)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "korg1212"); + strcpy(card->shortname, "korg1212"); + sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, + korg1212->iomem, korg1212->irq); + +#ifdef DEBUG + PRINTK("DEBUG: %s\n", card->longname); +#endif + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_korg1212_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "korg1212", + id_table: snd_korg1212_ids, + probe: snd_korg1212_probe, + remove: __devexit_p(snd_korg1212_remove), +}; + +static int __init alsa_card_korg1212_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "No Korg 1212IO cards found\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_korg1212_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_korg1212_init) +module_exit(alsa_card_korg1212_exit) + +#ifndef MODULE + +/* format is: snd-korg1212=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_korg1212_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-korg1212=", alsa_card_korg1212_setup); + +#endif /* ifndef MODULE */ + diff -Nru linux/sound/pci/maestro3.c linux-2.4.19-pre5-mjc/sound/pci/maestro3.c --- linux/sound/pci/maestro3.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/maestro3.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2715 @@ +/* + * Driver for ESS Maestro3/Allegro soundcards. + * Copyright (c) 2000 by Zach Brown + * Takashi Iwai + * + * Most of the hardware init stuffs are based on maestro3 driver for + * OSS/Free by Zach Brown. Many thanks to Zach! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * ChangeLog: + * Aug. 27, 2001 + * - Fixed deadlock on capture + * - Added Canyon3D-2 support by Rob Riggs + * + */ + +#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2" +#define DRIVER_NAME "Maestro3" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Zach Brown , Takashi Iwai "); +MODULE_DESCRIPTION("ESS Maestro3 PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{ESS,Maestro3 PCI}," + "{ESS,Allegro PCI}," + "{ESS,Allegro-1 PCI}," + "{ESS,Canyon3D-2/LE PCI}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */ +static int snd_external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; +static int snd_amp_gpio[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_external_amp, "Enable external amp for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); +MODULE_PARM(snd_amp_gpio, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_amp_gpio, "GPIO pin number for external amp. (default = -1)"); +MODULE_PARM_SYNTAX(snd_amp_gpio, SNDRV_ENABLED); + +#define MAX_PLAYBACKS 2 +#define MAX_CAPTURES 1 +#define NR_DSPS (MAX_PLAYBACKS + MAX_CAPTURES) + + +/* + * maestro3 registers + */ + +/* Allegro PCI configuration registers */ +#define PCI_LEGACY_AUDIO_CTRL 0x40 +#define SOUND_BLASTER_ENABLE 0x00000001 +#define FM_SYNTHESIS_ENABLE 0x00000002 +#define GAME_PORT_ENABLE 0x00000004 +#define MPU401_IO_ENABLE 0x00000008 +#define MPU401_IRQ_ENABLE 0x00000010 +#define ALIAS_10BIT_IO 0x00000020 +#define SB_DMA_MASK 0x000000C0 +#define SB_DMA_0 0x00000040 +#define SB_DMA_1 0x00000040 +#define SB_DMA_R 0x00000080 +#define SB_DMA_3 0x000000C0 +#define SB_IRQ_MASK 0x00000700 +#define SB_IRQ_5 0x00000000 +#define SB_IRQ_7 0x00000100 +#define SB_IRQ_9 0x00000200 +#define SB_IRQ_10 0x00000300 +#define MIDI_IRQ_MASK 0x00003800 +#define SERIAL_IRQ_ENABLE 0x00004000 +#define DISABLE_LEGACY 0x00008000 + +#define PCI_ALLEGRO_CONFIG 0x50 +#define SB_ADDR_240 0x00000004 +#define MPU_ADDR_MASK 0x00000018 +#define MPU_ADDR_330 0x00000000 +#define MPU_ADDR_300 0x00000008 +#define MPU_ADDR_320 0x00000010 +#define MPU_ADDR_340 0x00000018 +#define USE_PCI_TIMING 0x00000040 +#define POSTED_WRITE_ENABLE 0x00000080 +#define DMA_POLICY_MASK 0x00000700 +#define DMA_DDMA 0x00000000 +#define DMA_TDMA 0x00000100 +#define DMA_PCPCI 0x00000200 +#define DMA_WBDMA16 0x00000400 +#define DMA_WBDMA4 0x00000500 +#define DMA_WBDMA2 0x00000600 +#define DMA_WBDMA1 0x00000700 +#define DMA_SAFE_GUARD 0x00000800 +#define HI_PERF_GP_ENABLE 0x00001000 +#define PIC_SNOOP_MODE_0 0x00002000 +#define PIC_SNOOP_MODE_1 0x00004000 +#define SOUNDBLASTER_IRQ_MASK 0x00008000 +#define RING_IN_ENABLE 0x00010000 +#define SPDIF_TEST_MODE 0x00020000 +#define CLK_MULT_MODE_SELECT_2 0x00040000 +#define EEPROM_WRITE_ENABLE 0x00080000 +#define CODEC_DIR_IN 0x00100000 +#define HV_BUTTON_FROM_GD 0x00200000 +#define REDUCED_DEBOUNCE 0x00400000 +#define HV_CTRL_ENABLE 0x00800000 +#define SPDIF_ENABLE 0x01000000 +#define CLK_DIV_SELECT 0x06000000 +#define CLK_DIV_BY_48 0x00000000 +#define CLK_DIV_BY_49 0x02000000 +#define CLK_DIV_BY_50 0x04000000 +#define CLK_DIV_RESERVED 0x06000000 +#define PM_CTRL_ENABLE 0x08000000 +#define CLK_MULT_MODE_SELECT 0x30000000 +#define CLK_MULT_MODE_SHIFT 28 +#define CLK_MULT_MODE_0 0x00000000 +#define CLK_MULT_MODE_1 0x10000000 +#define CLK_MULT_MODE_2 0x20000000 +#define CLK_MULT_MODE_3 0x30000000 +#define INT_CLK_SELECT 0x40000000 +#define INT_CLK_MULT_RESET 0x80000000 + +/* M3 */ +#define INT_CLK_SRC_NOT_PCI 0x00100000 +#define INT_CLK_MULT_ENABLE 0x80000000 + +#define PCI_ACPI_CONTROL 0x54 +#define PCI_ACPI_D0 0x00000000 +#define PCI_ACPI_D1 0xB4F70000 +#define PCI_ACPI_D2 0xB4F7B4F7 + +#define PCI_USER_CONFIG 0x58 +#define EXT_PCI_MASTER_ENABLE 0x00000001 +#define SPDIF_OUT_SELECT 0x00000002 +#define TEST_PIN_DIR_CTRL 0x00000004 +#define AC97_CODEC_TEST 0x00000020 +#define TRI_STATE_BUFFER 0x00000080 +#define IN_CLK_12MHZ_SELECT 0x00000100 +#define MULTI_FUNC_DISABLE 0x00000200 +#define EXT_MASTER_PAIR_SEL 0x00000400 +#define PCI_MASTER_SUPPORT 0x00000800 +#define STOP_CLOCK_ENABLE 0x00001000 +#define EAPD_DRIVE_ENABLE 0x00002000 +#define REQ_TRI_STATE_ENABLE 0x00004000 +#define REQ_LOW_ENABLE 0x00008000 +#define MIDI_1_ENABLE 0x00010000 +#define MIDI_2_ENABLE 0x00020000 +#define SB_AUDIO_SYNC 0x00040000 +#define HV_CTRL_TEST 0x00100000 +#define SOUNDBLASTER_TEST 0x00400000 + +#define PCI_USER_CONFIG_C 0x5C + +#define PCI_DDMA_CTRL 0x60 +#define DDMA_ENABLE 0x00000001 + + +/* Allegro registers */ +#define HOST_INT_CTRL 0x18 +#define SB_INT_ENABLE 0x0001 +#define MPU401_INT_ENABLE 0x0002 +#define ASSP_INT_ENABLE 0x0010 +#define RING_INT_ENABLE 0x0020 +#define HV_INT_ENABLE 0x0040 +#define CLKRUN_GEN_ENABLE 0x0100 +#define HV_CTRL_TO_PME 0x0400 +#define SOFTWARE_RESET_ENABLE 0x8000 + +/* + * should be using the above defines, probably. + */ +#define REGB_ENABLE_RESET 0x01 +#define REGB_STOP_CLOCK 0x10 + +#define HOST_INT_STATUS 0x1A +#define SB_INT_PENDING 0x01 +#define MPU401_INT_PENDING 0x02 +#define ASSP_INT_PENDING 0x10 +#define RING_INT_PENDING 0x20 +#define HV_INT_PENDING 0x40 + +#define HARDWARE_VOL_CTRL 0x1B +#define SHADOW_MIX_REG_VOICE 0x1C +#define HW_VOL_COUNTER_VOICE 0x1D +#define SHADOW_MIX_REG_MASTER 0x1E +#define HW_VOL_COUNTER_MASTER 0x1F + +#define CODEC_COMMAND 0x30 +#define CODEC_READ_B 0x80 + +#define CODEC_STATUS 0x30 +#define CODEC_BUSY_B 0x01 + +#define CODEC_DATA 0x32 + +#define RING_BUS_CTRL_A 0x36 +#define RAC_PME_ENABLE 0x0100 +#define RAC_SDFS_ENABLE 0x0200 +#define LAC_PME_ENABLE 0x0400 +#define LAC_SDFS_ENABLE 0x0800 +#define SERIAL_AC_LINK_ENABLE 0x1000 +#define IO_SRAM_ENABLE 0x2000 +#define IIS_INPUT_ENABLE 0x8000 + +#define RING_BUS_CTRL_B 0x38 +#define SECOND_CODEC_ID_MASK 0x0003 +#define SPDIF_FUNC_ENABLE 0x0010 +#define SECOND_AC_ENABLE 0x0020 +#define SB_MODULE_INTF_ENABLE 0x0040 +#define SSPE_ENABLE 0x0040 +#define M3I_DOCK_ENABLE 0x0080 + +#define SDO_OUT_DEST_CTRL 0x3A +#define COMMAND_ADDR_OUT 0x0003 +#define PCM_LR_OUT_LOCAL 0x0000 +#define PCM_LR_OUT_REMOTE 0x0004 +#define PCM_LR_OUT_MUTE 0x0008 +#define PCM_LR_OUT_BOTH 0x000C +#define LINE1_DAC_OUT_LOCAL 0x0000 +#define LINE1_DAC_OUT_REMOTE 0x0010 +#define LINE1_DAC_OUT_MUTE 0x0020 +#define LINE1_DAC_OUT_BOTH 0x0030 +#define PCM_CLS_OUT_LOCAL 0x0000 +#define PCM_CLS_OUT_REMOTE 0x0040 +#define PCM_CLS_OUT_MUTE 0x0080 +#define PCM_CLS_OUT_BOTH 0x00C0 +#define PCM_RLF_OUT_LOCAL 0x0000 +#define PCM_RLF_OUT_REMOTE 0x0100 +#define PCM_RLF_OUT_MUTE 0x0200 +#define PCM_RLF_OUT_BOTH 0x0300 +#define LINE2_DAC_OUT_LOCAL 0x0000 +#define LINE2_DAC_OUT_REMOTE 0x0400 +#define LINE2_DAC_OUT_MUTE 0x0800 +#define LINE2_DAC_OUT_BOTH 0x0C00 +#define HANDSET_OUT_LOCAL 0x0000 +#define HANDSET_OUT_REMOTE 0x1000 +#define HANDSET_OUT_MUTE 0x2000 +#define HANDSET_OUT_BOTH 0x3000 +#define IO_CTRL_OUT_LOCAL 0x0000 +#define IO_CTRL_OUT_REMOTE 0x4000 +#define IO_CTRL_OUT_MUTE 0x8000 +#define IO_CTRL_OUT_BOTH 0xC000 + +#define SDO_IN_DEST_CTRL 0x3C +#define STATUS_ADDR_IN 0x0003 +#define PCM_LR_IN_LOCAL 0x0000 +#define PCM_LR_IN_REMOTE 0x0004 +#define PCM_LR_RESERVED 0x0008 +#define PCM_LR_IN_BOTH 0x000C +#define LINE1_ADC_IN_LOCAL 0x0000 +#define LINE1_ADC_IN_REMOTE 0x0010 +#define LINE1_ADC_IN_MUTE 0x0020 +#define MIC_ADC_IN_LOCAL 0x0000 +#define MIC_ADC_IN_REMOTE 0x0040 +#define MIC_ADC_IN_MUTE 0x0080 +#define LINE2_DAC_IN_LOCAL 0x0000 +#define LINE2_DAC_IN_REMOTE 0x0400 +#define LINE2_DAC_IN_MUTE 0x0800 +#define HANDSET_IN_LOCAL 0x0000 +#define HANDSET_IN_REMOTE 0x1000 +#define HANDSET_IN_MUTE 0x2000 +#define IO_STATUS_IN_LOCAL 0x0000 +#define IO_STATUS_IN_REMOTE 0x4000 + +#define SPDIF_IN_CTRL 0x3E +#define SPDIF_IN_ENABLE 0x0001 + +#define GPIO_DATA 0x60 +#define GPIO_DATA_MASK 0x0FFF +#define GPIO_HV_STATUS 0x3000 +#define GPIO_PME_STATUS 0x4000 + +#define GPIO_MASK 0x64 +#define GPIO_DIRECTION 0x68 +#define GPO_PRIMARY_AC97 0x0001 +#define GPI_LINEOUT_SENSE 0x0004 +#define GPO_SECONDARY_AC97 0x0008 +#define GPI_VOL_DOWN 0x0010 +#define GPI_VOL_UP 0x0020 +#define GPI_IIS_CLK 0x0040 +#define GPI_IIS_LRCLK 0x0080 +#define GPI_IIS_DATA 0x0100 +#define GPI_DOCKING_STATUS 0x0100 +#define GPI_HEADPHONE_SENSE 0x0200 +#define GPO_EXT_AMP_SHUTDOWN 0x1000 + +#define GPO_EXT_AMP_M3 1 /* default m3 amp */ +#define GPO_EXT_AMP_ALLEGRO 8 /* default allegro amp */ + +/* M3 */ +#define GPO_M3_EXT_AMP_SHUTDN 0x0002 + +#define ASSP_INDEX_PORT 0x80 +#define ASSP_MEMORY_PORT 0x82 +#define ASSP_DATA_PORT 0x84 + +#define MPU401_DATA_PORT 0x98 +#define MPU401_STATUS_PORT 0x99 + +#define CLK_MULT_DATA_PORT 0x9C + +#define ASSP_CONTROL_A 0xA2 +#define ASSP_0_WS_ENABLE 0x01 +#define ASSP_CTRL_A_RESERVED1 0x02 +#define ASSP_CTRL_A_RESERVED2 0x04 +#define ASSP_CLK_49MHZ_SELECT 0x08 +#define FAST_PLU_ENABLE 0x10 +#define ASSP_CTRL_A_RESERVED3 0x20 +#define DSP_CLK_36MHZ_SELECT 0x40 + +#define ASSP_CONTROL_B 0xA4 +#define RESET_ASSP 0x00 +#define RUN_ASSP 0x01 +#define ENABLE_ASSP_CLOCK 0x00 +#define STOP_ASSP_CLOCK 0x10 +#define RESET_TOGGLE 0x40 + +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOST_INT_ENABLE 0x01 +#define FM_ADDR_REMAP_DISABLE 0x02 +#define HOST_WRITE_PORT_ENABLE 0x08 + +#define ASSP_HOST_INT_STATUS 0xAC +#define DSP2HOST_REQ_PIORECORD 0x01 +#define DSP2HOST_REQ_I2SRATE 0x02 +#define DSP2HOST_REQ_TIMER 0x04 + +/* AC97 registers */ +/* XXX fix this crap up */ +/*#define AC97_RESET 0x00*/ + +#define AC97_VOL_MUTE_B 0x8000 +#define AC97_VOL_M 0x1F +#define AC97_LEFT_VOL_S 8 + +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_MASTER_MONO_VOL 0x06 +#define AC97_PC_BEEP_VOL 0x0A +#define AC97_PC_BEEP_VOL_M 0x0F +#define AC97_SROUND_MASTER_VOL 0x38 +#define AC97_PC_BEEP_VOL_S 1 + +/*#define AC97_PHONE_VOL 0x0C +#define AC97_MIC_VOL 0x0E*/ +#define AC97_MIC_20DB_ENABLE 0x40 + +/*#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16*/ +#define AC97_PCM_OUT_VOL 0x18 +/*#define AC97_RECORD_SELECT 0x1A*/ +#define AC97_RECORD_MIC 0x00 +#define AC97_RECORD_CD 0x01 +#define AC97_RECORD_VIDEO 0x02 +#define AC97_RECORD_AUX 0x03 +#define AC97_RECORD_MONO_MUX 0x02 +#define AC97_RECORD_DIGITAL 0x03 +#define AC97_RECORD_LINE 0x04 +#define AC97_RECORD_STEREO 0x05 +#define AC97_RECORD_MONO 0x06 +#define AC97_RECORD_PHONE 0x07 + +/*#define AC97_RECORD_GAIN 0x1C*/ +#define AC97_RECORD_VOL_M 0x0F + +/*#define AC97_GENERAL_PURPOSE 0x20*/ +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_ADC_READY 0x0001 +#define AC97_DAC_READY 0x0002 +#define AC97_ANALOG_READY 0x0004 +#define AC97_VREF_ON 0x0008 +#define AC97_PR0 0x0100 +#define AC97_PR1 0x0200 +#define AC97_PR2 0x0400 +#define AC97_PR3 0x0800 +#define AC97_PR4 0x1000 + +#define AC97_RESERVED1 0x28 + +#define AC97_VENDOR_TEST 0x5A + +#define AC97_CLOCK_DELAY 0x5C +#define AC97_LINEOUT_MUX_SEL 0x0001 +#define AC97_MONO_MUX_SEL 0x0002 +#define AC97_CLOCK_DELAY_SEL 0x1F +#define AC97_DAC_CDS_SHIFT 6 +#define AC97_ADC_CDS_SHIFT 11 + +#define AC97_MULTI_CHANNEL_SEL 0x74 + +/*#define AC97_VENDOR_ID1 0x7C +#define AC97_VENDOR_ID2 0x7E*/ + +/* + * ASSP control regs + */ +#define DSP_PORT_TIMER_COUNT 0x06 + +#define DSP_PORT_MEMORY_INDEX 0x80 + +#define DSP_PORT_MEMORY_TYPE 0x82 +#define MEMTYPE_INTERNAL_CODE 0x0002 +#define MEMTYPE_INTERNAL_DATA 0x0003 +#define MEMTYPE_MASK 0x0003 + +#define DSP_PORT_MEMORY_DATA 0x84 + +#define DSP_PORT_CONTROL_REG_A 0xA2 +#define DSP_PORT_CONTROL_REG_B 0xA4 +#define DSP_PORT_CONTROL_REG_C 0xA6 + +#define REV_A_CODE_MEMORY_BEGIN 0x0000 +#define REV_A_CODE_MEMORY_END 0x0FFF +#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) + +#define REV_B_CODE_MEMORY_BEGIN 0x0000 +#define REV_B_CODE_MEMORY_END 0x0BFF +#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) + +#define REV_A_DATA_MEMORY_BEGIN 0x1000 +#define REV_A_DATA_MEMORY_END 0x2FFF +#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) + +#define REV_B_DATA_MEMORY_BEGIN 0x1000 +#define REV_B_DATA_MEMORY_END 0x2BFF +#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) + + +#define NUM_UNITS_KERNEL_CODE 16 +#define NUM_UNITS_KERNEL_DATA 2 + +#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 +#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 + +/* + * Kernel data layout + */ + +#define DP_SHIFT_COUNT 7 + +#define KDATA_BASE_ADDR 0x1000 +#define KDATA_BASE_ADDR2 0x1080 + +#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) +#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) +#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) +#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) +#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) +#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) +#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) +#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) +#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) + +#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) +#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) + +#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) +#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) +#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) +#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) +#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) +#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) +#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) +#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) +#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) +#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) + +#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) +#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) + +#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) +#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) + +#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) +#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) + +#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) +#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) +#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) + +#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) +#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) +#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) +#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) +#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) + +#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) +#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) +#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) + +#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) +#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) +#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) + +#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) +#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) +#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) +#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) +#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) +#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) +#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) +#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) +#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) +#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) + +#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) +#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) +#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) + +#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) +#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) + +#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) +#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) +#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) + +#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) +#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) +#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) +#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) +#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) +#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) + +#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) +#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) +#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) +#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) +#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) +#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) + +#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) +#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) +#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) +#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) +#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) +#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) + +#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) +#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) +#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) +#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) + +#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) +#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) + +#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) +#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) + +#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) +#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) +#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) +#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) +#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) + +#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) +#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) + +#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) +#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) +#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) + +#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) +#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) + +#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) + +#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) +#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) +#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) +#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) +#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) +#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) +#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) +#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) +#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) +#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) +#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) +#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) + +#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) +#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) +#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) +#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) + +#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) +#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) + +#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) +#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) +#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) +#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) + +#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) +#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) +#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) +#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) +#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) + +/* + * second 'segment' (?) reserved for mixer + * buffers.. + */ + +#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) +#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) +#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) +#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) +#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) +#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) +#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) +#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) +#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) +#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) +#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) +#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) +#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) +#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) +#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) +#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) + +#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) +#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) +#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) +#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) +#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) +#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) +#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) +#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) +#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) +#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) +#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) + +#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) +#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) +#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) +#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) +#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) +#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) + +#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) +#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) +#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) +#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) + +/* + * client data area offsets + */ +#define CDATA_INSTANCE_READY 0x00 + +#define CDATA_HOST_SRC_ADDRL 0x01 +#define CDATA_HOST_SRC_ADDRH 0x02 +#define CDATA_HOST_SRC_END_PLUS_1L 0x03 +#define CDATA_HOST_SRC_END_PLUS_1H 0x04 +#define CDATA_HOST_SRC_CURRENTL 0x05 +#define CDATA_HOST_SRC_CURRENTH 0x06 + +#define CDATA_IN_BUF_CONNECT 0x07 +#define CDATA_OUT_BUF_CONNECT 0x08 + +#define CDATA_IN_BUF_BEGIN 0x09 +#define CDATA_IN_BUF_END_PLUS_1 0x0A +#define CDATA_IN_BUF_HEAD 0x0B +#define CDATA_IN_BUF_TAIL 0x0C +#define CDATA_OUT_BUF_BEGIN 0x0D +#define CDATA_OUT_BUF_END_PLUS_1 0x0E +#define CDATA_OUT_BUF_HEAD 0x0F +#define CDATA_OUT_BUF_TAIL 0x10 + +#define CDATA_DMA_CONTROL 0x11 +#define CDATA_RESERVED 0x12 + +#define CDATA_FREQUENCY 0x13 +#define CDATA_LEFT_VOLUME 0x14 +#define CDATA_RIGHT_VOLUME 0x15 +#define CDATA_LEFT_SUR_VOL 0x16 +#define CDATA_RIGHT_SUR_VOL 0x17 + +#define CDATA_HEADER_LEN 0x18 + +#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN +#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) +#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) +#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) +#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) +#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) +#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) +#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) + +#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) +#define MINISRC_BIQUAD_STAGE 2 +#define MINISRC_COEF_LOC 0x175 + +#define DMACONTROL_BLOCK_MASK 0x000F +#define DMAC_BLOCK0_SELECTOR 0x0000 +#define DMAC_BLOCK1_SELECTOR 0x0001 +#define DMAC_BLOCK2_SELECTOR 0x0002 +#define DMAC_BLOCK3_SELECTOR 0x0003 +#define DMAC_BLOCK4_SELECTOR 0x0004 +#define DMAC_BLOCK5_SELECTOR 0x0005 +#define DMAC_BLOCK6_SELECTOR 0x0006 +#define DMAC_BLOCK7_SELECTOR 0x0007 +#define DMAC_BLOCK8_SELECTOR 0x0008 +#define DMAC_BLOCK9_SELECTOR 0x0009 +#define DMAC_BLOCKA_SELECTOR 0x000A +#define DMAC_BLOCKB_SELECTOR 0x000B +#define DMAC_BLOCKC_SELECTOR 0x000C +#define DMAC_BLOCKD_SELECTOR 0x000D +#define DMAC_BLOCKE_SELECTOR 0x000E +#define DMAC_BLOCKF_SELECTOR 0x000F +#define DMACONTROL_PAGE_MASK 0x00F0 +#define DMAC_PAGE0_SELECTOR 0x0030 +#define DMAC_PAGE1_SELECTOR 0x0020 +#define DMAC_PAGE2_SELECTOR 0x0010 +#define DMAC_PAGE3_SELECTOR 0x0000 +#define DMACONTROL_AUTOREPEAT 0x1000 +#define DMACONTROL_STOPPED 0x2000 +#define DMACONTROL_DIRECTION 0x0100 + +/* + * an arbitrary volume we set the internal + * volume settings to so that the ac97 volume + * range is a little less insane. 0x7fff is + * max. + */ +#define ARB_VOLUME ( 0x6800 ) + +/* + */ + +typedef struct snd_m3_dma m3_dma_t; +typedef struct snd_m3 m3_t; +#define chip_t m3_t + + +struct m3_list { + int curlen; + int mem_addr; + int max; +}; + +struct snd_m3_dma { + + int number; + m3_t *chip; + snd_pcm_substream_t *substream; + + struct assp_instance { + unsigned short code, data; + } inst; + + int running; + int opened; + + unsigned long buffer_addr; + int dma_size; + int period_size; + unsigned int hwptr; + int count; + + int index[3]; + struct m3_list *index_list[3]; + + int in_lists; + + struct list_head list; + +}; + +struct snd_m3 { + + snd_card_t *card; + + unsigned long iobase; + struct resource *iobase_res; + + int irq; + int allegro_flag : 1; + + ac97_t *ac97; + + snd_pcm_t *pcm; + + struct pci_dev *pci; + /* pci_dev in 2.2 kernel doesn't have them, so we keep them private */ + u16 subsystem_vendor; + u16 subsystem_device; + + int dacs_active; + int timer_users; + + struct m3_list msrc_list; + struct m3_list mixer_list; + struct m3_list adc1_list; + struct m3_list dma_list; + + /* for storing reset state..*/ + u8 reset_state; + + int external_amp; + int amp_gpio; + + /* pcm streams */ + int num_substreams; + m3_dma_t *substreams; + + spinlock_t reg_lock; + +#ifdef CONFIG_PM + u16 *suspend_mem; +#endif +}; + +/* + * pci ids + */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125D +#endif +#ifndef PCI_DEVICE_ID_ESS_ALLEGRO_1 +#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 +#endif +#ifndef PCI_DEVICE_ID_ESS_ALLEGRO +#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 +#endif +#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2LE +#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 +#endif +#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2 +#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3 +#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_1 +#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_HW +#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a +#endif +#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_2 +#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b +#endif + +static struct pci_device_id snd_m3_ids[] __devinitdata = { + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2LE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_HW, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, snd_m3_ids); + +/* + * lowlevel functions + */ + +inline static void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg) +{ + outw(value, chip->iobase + reg); +} + +inline static u16 snd_m3_inw(m3_t *chip, unsigned long reg) +{ + return inw(chip->iobase + reg); +} + +inline static void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg) +{ + outb(value, chip->iobase + reg); +} + +inline static u8 snd_m3_inb(m3_t *chip, unsigned long reg) +{ + return inb(chip->iobase + reg); +} + +/* + * access 16bit words to the code or data regions of the dsp's memory. + * index addresses 16bit words. + */ +static u16 snd_m3_assp_read(m3_t *chip, u16 region, u16 index) +{ + snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX); + return snd_m3_inw(chip, DSP_PORT_MEMORY_DATA); +} + +static void snd_m3_assp_write(m3_t *chip, u16 region, u16 index, u16 data) +{ + snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX); + snd_m3_outw(chip, data, DSP_PORT_MEMORY_DATA); +} + +static void snd_m3_assp_halt(m3_t *chip) +{ + chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; + mdelay(10); + snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +static void snd_m3_assp_continue(m3_t *chip) +{ + snd_m3_outb(chip, chip->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + + +/* + * This makes me sad. the maestro3 has lists + * internally that must be packed.. 0 terminates, + * apparently, or maybe all unused entries have + * to be 0, the lists have static lengths set + * by the binary code images. + */ + +static int snd_m3_add_list(m3_t *chip, struct m3_list *list, u16 val) +{ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + list->curlen, + val); + return list->curlen++; +} + +static void snd_m3_remove_list(m3_t *chip, struct m3_list *list, int index) +{ + u16 val; + int lastindex = list->curlen - 1; + + if (index != lastindex) { + val = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + index, + val); + } + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex, + 0); + + list->curlen--; +} + +static void snd_m3_inc_timer_users(m3_t *chip) +{ + chip->timer_users++; + if (chip->timer_users != 1) + return; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 240); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 240); + + snd_m3_outw(chip, + snd_m3_inw(chip, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +} + +static void snd_m3_dec_timer_users(m3_t *chip) +{ + chip->timer_users--; + if (chip->timer_users > 0) + return; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 0); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 0); + + snd_m3_outw(chip, + snd_m3_inw(chip, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +} + +/* + * start/stop + */ + +/* spinlock held! */ +static int snd_m3_pcm_start(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + if (! s || ! subs) + return -EINVAL; + + snd_m3_inc_timer_users(chip); + switch (subs->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + chip->dacs_active++; + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 1); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + chip->dacs_active); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_m3_assp_write(s->chip, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 1); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 1); + break; + } + return 0; +} + +/* spinlock held! */ +static int snd_m3_pcm_stop(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + if (! s || ! subs) + return -EINVAL; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_INSTANCE_READY, 0); + snd_m3_dec_timer_users(chip); + switch (subs->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + chip->dacs_active--; + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + chip->dacs_active); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 0); + break; + } + return 0; +} + +static int +snd_m3_pcm_trigger(snd_pcm_substream_t *subs, int cmd) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data; + unsigned long flags; + int err = -EINVAL; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (s->running) + err = -EBUSY; + else { + s->running = 1; + err = snd_m3_pcm_start(chip, s, subs); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (! s->running) + err = 0; /* should return error? */ + else { + s->running = 0; + err = snd_m3_pcm_stop(chip, s, subs); + } + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + +/* + * setup + */ +static void +snd_m3_pcm_setup1(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + int dsp_in_size, dsp_out_size, dsp_in_buffer, dsp_out_buffer; + snd_pcm_runtime_t *runtime = subs->runtime; + + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); + } else { + dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x10 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); + } + dsp_in_buffer = s->inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + + s->dma_size = frames_to_bytes(runtime, runtime->buffer_size); + s->period_size = frames_to_bytes(runtime, runtime->period_size); + s->hwptr = 0; + s->count = 0; + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_ADDRL, + LO(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_ADDRH, + HI(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(s->buffer_addr + s->dma_size)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(s->buffer_addr + s->dma_size)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTL, + LO(s->buffer_addr)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH, + HI(s->buffer_addr)); +#undef LO +#undef HI + + /* dsp buffers */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); +} + +static void snd_m3_pcm_setup2(m3_t *chip, m3_dma_t *s, snd_pcm_runtime_t *runtime) +{ + u32 freq; + + /* + * put us in the lists if we're not already there + */ + if (! s->in_lists) { + s->index[0] = snd_m3_add_list(chip, s->index_list[0], + s->inst.data >> DP_SHIFT_COUNT); + s->index[1] = snd_m3_add_list(chip, s->index_list[1], + s->inst.data >> DP_SHIFT_COUNT); + s->index[2] = snd_m3_add_list(chip, s->index_list[2], + s->inst.data >> DP_SHIFT_COUNT); + s->in_lists = 1; + } + + /* write to 'mono' word */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 1, + runtime->channels == 2 ? 0 : 1); + /* write to '8bit' word */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 2, + snd_pcm_format_width(runtime->format) == 16 ? 0 : 1); + + /* set up dac/adc rate */ + freq = ((runtime->rate << 15) + 24000 ) / 48000; + if (freq) + freq--; + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_FREQUENCY, + freq); +} + + +static struct play_vals { + u16 addr, val; +} pv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 0} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ + {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ +}; + + +/* the mode passed should be already shifted and masked */ +static void +snd_m3_playback_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + int i; + + /* + * some per client initializers + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 12, + s->inst.data + 40 + 8); + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 19, + s->inst.code + MINISRC_COEF_LOC); + + /* enable or disable low pass filter? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 22, + subs->runtime->rate > 45000 ? 0xff : 0); + + /* tell it which way dma is going? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_DMA_CONTROL, + DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for (i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + pv[i].addr, pv[i].val); +} + +/* + * Native record driver + */ +static struct rec_vals { + u16 addr, val; +} rv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 1} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ + {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ + {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ +}; + +static void +snd_m3_capture_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + int i; + + /* + * some per client initializers + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + SRC3_DIRECTION_OFFSET + 12, + s->inst.data + 40 + 8); + + /* tell it which way dma is going? */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_DMA_CONTROL, + DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for (i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + rv[i].addr, rv[i].val); +} + +static int snd_m3_pcm_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + m3_dma_t *s = (m3_dma_t*) substream->runtime->private_data; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + /* set buffer address */ + s->buffer_addr = substream->runtime->dma_addr; + if (s->buffer_addr & 0x3) { + snd_printk("oh my, not aligned\n"); + s->buffer_addr = s->buffer_addr & ~0x3; + } + return 0; +} + +static int snd_m3_pcm_hw_free(snd_pcm_substream_t * substream) +{ + m3_dma_t *s; + + if (substream->runtime->private_data == NULL) + return 0; + s = (m3_dma_t*) substream->runtime->private_data; + snd_pcm_lib_free_pages(substream); + s->buffer_addr = 0; + return 0; +} + +static int +snd_m3_pcm_prepare(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + m3_dma_t *s = (m3_dma_t*)runtime->private_data; + unsigned long flags; + + snd_assert(s != NULL, return -ENXIO); + + if (runtime->format != SNDRV_PCM_FORMAT_U8 && + runtime->format != SNDRV_PCM_FORMAT_S16_LE) + return -EINVAL; + if (runtime->rate > 48000 || + runtime->rate < 8000) + return -EINVAL; + + spin_lock_irqsave(&chip->reg_lock, flags); + + snd_m3_pcm_setup1(chip, s, subs); + + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_m3_playback_setup(chip, s, subs); + else + snd_m3_capture_setup(chip, s, subs); + + snd_m3_pcm_setup2(chip, s, runtime); + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +/* + * get current pointer + */ +static unsigned int +snd_m3_get_pointer(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs) +{ + u16 hi = 0, lo = 0; + int retry = 10; + u32 addr; + + /* + * try and get a valid answer + */ + while (retry--) { + hi = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH); + + lo = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTL); + + if (hi == snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, + s->inst.data + CDATA_HOST_SRC_CURRENTH)) + break; + } + addr = lo | ((u32)hi<<16); + return (unsigned int)(addr - s->buffer_addr); +} + +static snd_pcm_uframes_t +snd_m3_pcm_pointer(snd_pcm_substream_t * subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data; + snd_assert(s != NULL, return 0); + return bytes_to_frames(subs->runtime, snd_m3_get_pointer(chip, s, subs)); +} + + +/* update pointer */ +/* spinlock held! */ +static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s) +{ + snd_pcm_substream_t *subs = s->substream; + unsigned int hwptr; + int diff; + + if (! s->running) + return; + + hwptr = snd_m3_get_pointer(chip, s, subs) % s->dma_size; + diff = (s->dma_size + hwptr - s->hwptr) % s->dma_size; + s->hwptr = hwptr; + s->count += diff; + while (s->count >= (signed)s->period_size) { + s->count -= s->period_size; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(subs); + spin_lock(&chip->reg_lock); + } +} + +static void +snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + m3_t *chip = snd_magic_cast(m3_t, dev_id, ); + u8 status; + int i; + + status = inb(chip->iobase + 0x1A); + + if (status == 0xff) + return; + + /* presumably acking the ints? */ + outw(status, chip->iobase + 0x1A); + + /*if (in_suspend) + return;*/ + + /* + * ack an assp int if its running + * and has an int pending + */ + if (status & ASSP_INT_PENDING) { + u8 ctl = inb(chip->iobase + ASSP_CONTROL_B); + if (!(ctl & STOP_ASSP_CLOCK)) { + ctl = inb(chip->iobase + ASSP_HOST_INT_STATUS); + if (ctl & DSP2HOST_REQ_TIMER) { + outb(DSP2HOST_REQ_TIMER, chip->iobase + ASSP_HOST_INT_STATUS); + /* update adc/dac info if it was a timer int */ + spin_lock(&chip->reg_lock); + for (i = 0; i < chip->num_substreams; i++) { + m3_dma_t *s = &chip->substreams[i]; + if (s->running) + snd_m3_update_ptr(chip, s); + } + spin_unlock(&chip->reg_lock); + } + } + } + + /* XXX is this needed? */ + if (status & 0x40) + outb(0x40, chip->iobase+0x1A); +} + + +/* + */ + +static snd_pcm_hardware_t snd_m3_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 1024, +}; + +static snd_pcm_hardware_t snd_m3_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (512*1024), + period_bytes_min: 64, + period_bytes_max: (512*1024), + periods_min: 1, + periods_max: 1024, +}; + + +/* + */ + +static int +snd_m3_substream_open(m3_t *chip, snd_pcm_substream_t *subs) +{ + int i; + m3_dma_t *s; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < chip->num_substreams; i++) { + s = &chip->substreams[i]; + if (! s->opened) + goto __found; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return -ENOMEM; +__found: + s->opened = 1; + s->running = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + subs->runtime->private_data = s; + s->substream = subs; + + /* set list owners */ + if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) { + s->index_list[0] = &chip->mixer_list; + } else + s->index_list[0] = &chip->adc1_list; + s->index_list[1] = &chip->msrc_list; + s->index_list[2] = &chip->dma_list; + + return 0; +} + +static void +snd_m3_substream_close(m3_t *chip, snd_pcm_substream_t *subs) +{ + m3_dma_t *s = (m3_dma_t*) subs->runtime->private_data; + unsigned long flags; + + if (s == NULL) + return; /* not opened properly */ + + spin_lock_irqsave(&chip->reg_lock, flags); + if (s->substream && s->running) + snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */ + if (s->in_lists) { + snd_m3_remove_list(chip, s->index_list[0], s->index[0]); + snd_m3_remove_list(chip, s->index_list[1], s->index[1]); + snd_m3_remove_list(chip, s->index_list[2], s->index[2]); + s->in_lists = 0; + } + s->running = 0; + s->opened = 0; + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static int +snd_m3_playback_open(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int err; + + if ((err = snd_m3_substream_open(chip, subs)) < 0) + return err; + + runtime->hw = snd_m3_playback; + snd_pcm_set_sync(subs); + + return 0; +} + +static int +snd_m3_playback_close(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + + snd_m3_substream_close(chip, subs); + return 0; +} + +static int +snd_m3_capture_open(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int err; + + if ((err = snd_m3_substream_open(chip, subs)) < 0) + return err; + + runtime->hw = snd_m3_capture; + snd_pcm_set_sync(subs); + + return 0; +} + +static int +snd_m3_capture_close(snd_pcm_substream_t *subs) +{ + m3_t *chip = snd_pcm_substream_chip(subs); + + snd_m3_substream_close(chip, subs); + return 0; +} + +/* + * create pcm instance + */ + +static snd_pcm_ops_t snd_m3_playback_ops = { + open: snd_m3_playback_open, + close: snd_m3_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_m3_pcm_hw_params, + hw_free: snd_m3_pcm_hw_free, + prepare: snd_m3_pcm_prepare, + trigger: snd_m3_pcm_trigger, + pointer: snd_m3_pcm_pointer, +}; + +static snd_pcm_ops_t snd_m3_capture_ops = { + open: snd_m3_capture_open, + close: snd_m3_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_m3_pcm_hw_params, + hw_free: snd_m3_pcm_hw_free, + prepare: snd_m3_pcm_prepare, + trigger: snd_m3_pcm_trigger, + pointer: snd_m3_pcm_pointer, +}; + +static int __devinit +snd_m3_pcm(m3_t * chip, int device) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(chip->card, chip->card->driver, device, + MAX_PLAYBACKS, MAX_CAPTURES, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_m3_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_m3_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->driver); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + + return 0; +} + + +/* + * ac97 interface + */ + +/* + * Wait for the ac97 serial bus to be free. + * return nonzero if the bus is still busy. + */ +static int snd_m3_ac97_wait(m3_t *chip) +{ + int i = 10000; + + do { + if (! (snd_m3_inb(chip, 0x30) & 1)) + return 0; + } while (i-- > 0); + + snd_printk("ac97 serial bus busy\n"); + return 1; +} + +static unsigned short +snd_m3_ac97_read(ac97_t *ac97, unsigned short reg) +{ + m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return -ENXIO); + unsigned short ret = 0; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_m3_ac97_wait(chip)) + goto __error; + snd_m3_outb(chip, 0x80 | (reg & 0x7f), 0x30); + if (snd_m3_ac97_wait(chip)) + goto __error; + ret = snd_m3_inw(chip, 0x32); +__error: + spin_unlock_irqrestore(&chip->reg_lock, flags); + return ret; +} + +static void +snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_m3_ac97_wait(chip)) + goto __error; + snd_m3_outw(chip, val, 0x32); + snd_m3_outb(chip, reg & 0x7f, 0x30); +__error: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + + +static void snd_m3_remote_codec_config(int io, int isremote) +{ + isremote = isremote ? 1 : 0; + + outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, + io + RING_BUS_CTRL_B); + outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, + io + SDO_OUT_DEST_CTRL); + outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, + io + SDO_IN_DEST_CTRL); +} + +/* + * hack, returns non zero on err + */ +static int snd_m3_try_read_vendor(m3_t *chip) +{ + u16 ret; + + if (snd_m3_ac97_wait(chip)) + return 1; + + snd_m3_outb(chip, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); + + if (snd_m3_ac97_wait(chip)) + return 1; + + ret = snd_m3_inw(chip, 0x32); + + return (ret == 0) || (ret == 0xffff); +} + +static void snd_m3_ac97_reset(m3_t *chip, int busywait) +{ + u16 dir; + int delay1 = 0, delay2 = 0, i; + int io = chip->iobase; + + if (chip->allegro_flag) { + /* + * the onboard codec on the allegro seems + * to want to wait a very long time before + * coming back to life + */ + delay1 = 50; + delay2 = 800; + } else { + /* maestro3 */ + delay1 = 20; + delay2 = 500; + } + + for (i = 0; i < 5; i++) { + dir = inw(io + GPIO_DIRECTION); + if (chip->subsystem_vendor == 0x1028 && + chip->subsystem_device == 0x00b0) /* Dell Inspiron 4000 */ + ; /* seems conflicting with IrDA */ + else + dir |= 0x10; /* assuming pci bus master? */ + + snd_m3_remote_codec_config(io, 0); + + outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); + udelay(20); + + outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); + outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); + outw(0, io + GPIO_DATA); + outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); + + if (busywait) { + mdelay(delay1); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay1 * HZ) / 1000); + } + + outw(GPO_PRIMARY_AC97, io + GPIO_DATA); + udelay(5); + /* ok, bring back the ac-link */ + outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); + outw(~0, io + GPIO_MASK); + + if (busywait) { + mdelay(delay2); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay2 * HZ) / 1000); + } + if (! snd_m3_try_read_vendor(chip)) + break; + + delay1 += 10; + delay2 += 100; + + snd_printd("maestro3: retrying codec reset with delays of %d and %d ms\n", + delay1, delay2); + } + +#if 0 + /* more gung-ho reset that doesn't + * seem to work anywhere :) + */ + tmp = inw(io + RING_BUS_CTRL_A); + outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); + mdelay(20); + outw(tmp, io + RING_BUS_CTRL_A); + mdelay(50); +#endif +} + +static int snd_m3_mixer(m3_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_m3_ac97_write; + ac97.read = snd_m3_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + /* seems ac97 PCM needs initialization.. hack hack.. */ + snd_ac97_write(chip->ac97, AC97_PCM, 0x8000 | (15 << 8) | 15); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 10); + snd_ac97_write(chip->ac97, AC97_PCM, 0); + + return 0; +} + + +/* + * DSP Code images + */ + +static u16 assp_kernel_image[] = { + 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, + 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, + 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, + 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, + 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, + 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, + 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, + 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, + 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, + 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, + 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, + 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, + 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, + 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, + 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, + 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, + 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, + 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, + 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, + 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, + 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, + 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, + 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, + 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, + 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, + 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, + 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, + 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, + 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, + 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, + 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, + 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, + 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, + 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, + 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, + 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, + 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, + 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, + 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, + 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, + 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, + 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, + 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, + 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, + 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, + 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, + 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, + 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, + 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, + 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, + 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, + 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, + 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, + 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, + 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, + 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, + 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, + 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, + 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, + 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, + 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, + 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, + 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, + 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, + 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, + 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, + 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, + 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, + 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, + 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, + 0xBE3A, +}; + +/* + * Mini sample rate converter code image + * that is to be loaded at 0x400 on the DSP. + */ +static u16 assp_minisrc_image[] = { + + 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, + 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, + 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, + 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, + 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, + 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, + 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, + 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, + 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, + 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, + 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, + 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, + 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, + 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, + 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, + 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, + 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, + 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, + 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, + 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, + 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, + 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, + 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, + 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, + 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, + 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, + 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, + 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, + 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, + 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, + 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, + 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + + +/* + * initialize ASSP + */ + +#define MINISRC_LPF_LEN 10 +static u16 minisrc_lpf[MINISRC_LPF_LEN] = { + 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, + 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F +}; + +static void snd_m3_assp_init(m3_t *chip) +{ + int i; + + /* zero kernel data */ + for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR + i, 0); + + /* zero mixer data? */ + for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR2 + i, 0); + + /* init dma pointer */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_CURRENT_DMA, + KDATA_DMA_XFER0); + + /* write kernel into code memory.. */ + for (i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + REV_B_CODE_MEMORY_BEGIN + i, + assp_kernel_image[i]); + } + + /* + * We only have this one client and we know that 0x400 + * is free in our kernel's mem map, so lets just + * drop it there. It seems that the minisrc doesn't + * need vectors, so we won't bother with them.. + */ + for (i = 0; i < sizeof(assp_minisrc_image) / 2; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + i, + assp_minisrc_image[i]); + } + + /* + * write the coefficients for the low pass filter? + */ + for (i = 0; i < MINISRC_LPF_LEN ; i++) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + i, + minisrc_lpf[i]); + } + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, + 0x8000); + + /* + * the minisrc is the only thing on + * our task list.. + */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_TASK0, + 0x400); + + /* + * init the mixer number.. + */ + + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER,0); + + /* + * EXTREME KERNEL MASTER VOLUME + */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); + + chip->mixer_list.curlen = 0; + chip->mixer_list.mem_addr = KDATA_MIXER_XFER0; + chip->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; + chip->adc1_list.curlen = 0; + chip->adc1_list.mem_addr = KDATA_ADC1_XFER0; + chip->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; + chip->dma_list.curlen = 0; + chip->dma_list.mem_addr = KDATA_DMA_XFER0; + chip->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; + chip->msrc_list.curlen = 0; + chip->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; + chip->msrc_list.max = MAX_INSTANCE_MINISRC; +} + + +static int snd_m3_assp_client_init(m3_t *chip, m3_dma_t *s, int index) +{ + int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + + MINISRC_IN_BUFFER_SIZE / 2 + + 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); + int address, i; + + /* + * the revb memory map has 0x1100 through 0x1c00 + * free. + */ + + /* + * align instance address to 256 bytes so that it's + * shifted list address is aligned. + * list address = (mem address >> 1) >> 7; + */ + data_bytes = (data_bytes + 255) & ~255; + address = 0x1100 + ((data_bytes/2) * index); + + if ((address + (data_bytes/2)) >= 0x1c00) { + snd_printk("no memory for %d bytes at ind %d (addr 0x%x)\n", + data_bytes, index, address); + return -ENOMEM; + } + + s->number = index; + s->inst.code = 0x400; + s->inst.data = address; + + for (i = data_bytes / 2; i > 0; address++, i--) { + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + address, 0); + } + + return 0; +} + + +/* + * this works for the reference board, have to find + * out about others + * + * this needs more magic for 4 speaker, but.. + */ +static void +snd_m3_amp_enable(m3_t *chip, int enable) +{ + int io = chip->iobase; + u16 gpo, polarity; + + if (! chip->external_amp) + return; + + polarity = enable ? 0 : 1; + polarity = polarity << chip->amp_gpio; + gpo = 1 << chip->amp_gpio; + + outw(~gpo, io + GPIO_MASK); + + outw(inw(io + GPIO_DIRECTION) | gpo, + io + GPIO_DIRECTION); + + outw((GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity), + io + GPIO_DATA); + + outw(0xffff, io + GPIO_MASK); +} + +static int +snd_m3_chip_init(m3_t *chip) +{ + struct pci_dev *pcidev = chip->pci; + u32 n; + u8 t; /* makes as much sense as 'n', no? */ + + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= REDUCED_DEBOUNCE; + n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + outb(RESET_ASSP, chip->iobase + ASSP_CONTROL_B); + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= ~INT_CLK_SELECT; + if (!chip->allegro_flag) { + n &= ~INT_CLK_MULT_ENABLE; + n |= INT_CLK_SRC_NOT_PCI; + } + n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + if (chip->allegro_flag) { + pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); + n |= IN_CLK_12MHZ_SELECT; + pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); + } + + t = inb(chip->iobase + ASSP_CONTROL_A); + t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); + t |= ASSP_CLK_49MHZ_SELECT; + t |= ASSP_0_WS_ENABLE; + outb(t, chip->iobase + ASSP_CONTROL_A); + + outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); + + return 0; +} + +static void +snd_m3_enable_ints(m3_t *chip) +{ + unsigned long io = chip->iobase; + + outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); + outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, + io + ASSP_CONTROL_C); +} + + +/* + */ + +static int snd_m3_free(m3_t *chip) +{ + unsigned long flags; + m3_dma_t *s; + int i; + + if (chip->substreams) { + spin_lock_irqsave(&chip->reg_lock, flags); + for (i = 0; i < chip->num_substreams; i++) { + s = &chip->substreams[i]; + /* check surviving pcms; this should not happen though.. */ + if (s->substream && s->running) + snd_m3_pcm_stop(chip, s, s->substream); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + kfree(chip->substreams); + } +#ifdef CONFIG_PM + if (chip->suspend_mem) + vfree(chip->suspend_mem); +#endif + + synchronize_irq(); + + if (chip->iobase_res) { + release_resource(chip->iobase_res); + kfree_nocheck(chip->iobase_res); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + snd_magic_kfree(chip); + return 0; +} + + +/* + * APM support + */ +#ifdef CONFIG_PM + +static void m3_suspend(m3_t *chip) +{ + snd_card_t *card = chip->card; + int i, index; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + + mdelay(10); /* give the assp a chance to idle.. */ + + snd_m3_assp_halt(chip); + + /* save dsp image */ + index = 0; + for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) + chip->suspend_mem[index++] = + snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i); + for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + chip->suspend_mem[index++] = + snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i); + + /* power down apci registers */ + snd_m3_outw(chip, 0xffff, 0x54); + snd_m3_outw(chip, 0xffff, 0x56); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void m3_resume(m3_t *chip) +{ + snd_card_t *card = chip->card; + int i, index; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* first lets just bring everything back. .*/ + snd_m3_outw(chip, 0, 0x54); + snd_m3_outw(chip, 0, 0x56); + + snd_m3_chip_init(chip); + snd_m3_assp_halt(chip); + snd_m3_ac97_reset(chip, 1); + + /* restore dsp image */ + index = 0; + for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i, + chip->suspend_mem[index++]); + for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i, + chip->suspend_mem[index++]); + + /* tell the dma engine to restart itself */ + snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, + KDATA_DMA_ACTIVE, 0); + + /* restore ac97 registers */ + snd_ac97_resume(chip->ac97); + + snd_m3_assp_continue(chip); + snd_m3_enable_ints(chip); + snd_m3_amp_enable(chip, 1); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_m3_suspend(struct pci_dev *pci, u32 state) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO); + m3_suspend(chip); + return 0; +} +static int snd_m3_resume(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return -ENXIO); + m3_resume(chip); + return 0; +} +#else +static void snd_m3_suspend(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + m3_suspend(chip); +} +static void snd_m3_resume(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + m3_resume(chip); +} +#endif + +/* callback */ +static int snd_m3_set_power_state(snd_card_t *card, unsigned int power_state) +{ + m3_t *chip = snd_magic_cast(m3_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + m3_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + m3_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + + +/* + */ + +static int snd_m3_dev_free(snd_device_t *device) +{ + m3_t *chip = snd_magic_cast(m3_t, device->device_data, return -ENXIO); + return snd_m3_free(chip); +} + +static int __devinit +snd_m3_create(snd_card_t *card, struct pci_dev *pci, + int enable_amp, + int amp_gpio, + m3_t **chip_ret) +{ + m3_t *chip; + int i, err; + static snd_device_ops_t ops = { + dev_free: snd_m3_dev_free, + }; + + *chip_ret = NULL; + + if (pci_enable_device(pci)) + return -EIO; + + /* check, if we can restrict PCI DMA transfers to 28 bits */ + if (!pci_dma_supported(pci, 0x0fffffff)) { + snd_printk("architecture does not support 28bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x0fffffff); + + chip = snd_magic_kcalloc(m3_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + spin_lock_init(&chip->reg_lock); + switch (pci->device) { + case PCI_DEVICE_ID_ESS_ALLEGRO: + case PCI_DEVICE_ID_ESS_ALLEGRO_1: + case PCI_DEVICE_ID_ESS_CANYON3D_2LE: + case PCI_DEVICE_ID_ESS_CANYON3D_2: + chip->allegro_flag = 1; + break; + } + +#ifndef LINUX_2_2 + chip->subsystem_vendor = pci->subsystem_vendor; + chip->subsystem_device = pci->subsystem_device; +#else + pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->subsystem_vendor); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->subsystem_device); +#endif + + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->external_amp = enable_amp; + if (amp_gpio >= 0 && amp_gpio <= 0x0f) + chip->amp_gpio = amp_gpio; + else if (chip->allegro_flag) { + /* panasonic CF-28 "toughbook" has different GPIO connection.. */ + if (chip->subsystem_vendor == 0x10f7 && + chip->subsystem_device == 0x833e) + chip->amp_gpio = 0x0d; + else + chip->amp_gpio = GPO_EXT_AMP_ALLEGRO; + } else + chip->amp_gpio = GPO_EXT_AMP_M3; /* presumably this is for all 'maestro3's.. */ + chip->num_substreams = NR_DSPS; + chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL); + if (chip->substreams == NULL) { + snd_magic_kfree(chip); + return -ENOMEM; + } + memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams); + + chip->iobase = pci_resource_start(pci, 0); + if ((chip->iobase_res = request_region(chip->iobase, 256, + card->driver)) == NULL) { + snd_m3_free(chip); + snd_printk("unable to grab i/o ports %ld\n", chip->iobase); + return -EBUSY; + } + + /* just to be sure */ + pci_set_master(pci); + + snd_m3_chip_init(chip); + snd_m3_assp_halt(chip); + + snd_m3_ac97_reset(chip, 0); + + if ((err = snd_m3_mixer(chip)) < 0) { + snd_m3_free(chip); + return err; + } + + snd_m3_assp_init(chip); + snd_m3_amp_enable(chip, 1); + + for (i = 0; i < chip->num_substreams; i++) { + m3_dma_t *s = &chip->substreams[i]; + s->chip = chip; + if ((err = snd_m3_assp_client_init(chip, s, i)) < 0) { + snd_m3_free(chip); + return err; + } + } + + if ((err = snd_m3_pcm(chip, 0)) < 0) { + snd_m3_free(chip); + return err; + } + + if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void *)chip)) { + snd_m3_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -ENOMEM; + } + chip->irq = pci->irq; + +#ifdef CONFIG_PM + chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH)); + if (chip->suspend_mem == NULL) + snd_printk("can't allocate apm buffer\n"); + card->set_power_state = snd_m3_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_m3_free(chip); + return err; + } + + snd_m3_enable_ints(chip); + snd_m3_assp_continue(chip); + + *chip_ret = chip; + + return 0; +} + +/* + */ +static int __devinit +snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + m3_t *chip; + int err; + + /* don't pick up modems */ + if (((pci->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return 0; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_ESS_ALLEGRO: + case PCI_DEVICE_ID_ESS_ALLEGRO_1: + strcpy(card->driver, "Allegro"); + break; + case PCI_DEVICE_ID_ESS_CANYON3D_2LE: + case PCI_DEVICE_ID_ESS_CANYON3D_2: + strcpy(card->driver, "Canyon3D-2"); + break; + default: + strcpy(card->driver, "Maestro3"); + break; + } + + if ((err = snd_m3_create(card, pci, + snd_external_amp[dev], + snd_amp_gpio[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "ESS %s PCI", card->driver); + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->iobase, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_m3_remove(struct pci_dev *pci) +{ + m3_t *chip = snd_magic_cast(m3_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Maestro3", + id_table: snd_m3_ids, + probe: snd_m3_probe, + remove: __devexit_p(snd_m3_remove), +#ifdef CONFIG_PM + suspend: snd_m3_suspend, + resume: snd_m3_resume, +#endif +}; + +static int __init alsa_card_m3_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Maestro3/Allegro soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_m3_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_m3_init) +module_exit(alsa_card_m3_exit) + +#ifndef MODULE + +/* format is: snd-maestro3=snd_enable,snd_index,snd_id,snd_external_amp,snd_amp_gpio */ + +static int __init alsa_card_maestro3_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_external_amp[nr_dev]) == 2 && + get_option(&str,&snd_amp_gpio[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-maestro3=", alsa_card_maestro3_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/nm256/Makefile linux-2.4.19-pre5-mjc/sound/pci/nm256/Makefile --- linux/sound/pci/nm256/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/nm256/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _nm256.o + +list-multi := snd-nm256.o + +snd-nm256-objs := nm256.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_NM256) += snd-nm256.o + +include $(TOPDIR)/Rules.make + +snd-nm256.o: $(snd-nm256-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-nm256-objs) diff -Nru linux/sound/pci/nm256/nm256.c linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256.c --- linux/sound/pci/nm256/nm256.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1696 @@ +/* + * Driver for NeoMagic 256AV and 256ZX chipsets. + * Copyright (c) 2000 by Takashi Iwai + * + * Based on nm256_audio.c OSS driver in linux kernel. + * The original author of OSS nm256 driver wishes to remain anonymous, + * so I just put my acknoledgment to him/her here. + * The original author's web page is found at + * http://www.uglx.org/sony.html + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +#define CARD_NAME "NeoMagic 256AV/ZX" +#define DRIVER_NAME "NM256" + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("NeoMagic NM256AV/ZX"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{NeoMagic,NM256AV}," + "{NeoMagic,NM256ZX}}"); + +/* + * some compile conditions. + */ + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int snd_playback_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16}; +static int snd_capture_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 16}; +static int snd_force_ac97[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled as default */ +static int snd_buffer_top[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* not specified */ +static int snd_use_cache[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */ +static int snd_vaio_hack[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* disabled */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_playback_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_playback_bufsize, SNDRV_ENABLED); +MODULE_PARM(snd_capture_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_capture_bufsize, SNDRV_ENABLED); +MODULE_PARM(snd_force_ac97, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_force_ac97, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(snd_buffer_top, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(snd_buffer_top, SNDRV_ENABLED); +MODULE_PARM(snd_use_cache, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_use_cache, "Enable the cache for coefficient table access."); +MODULE_PARM_SYNTAX(snd_use_cache, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_PARM(snd_vaio_hack, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_vaio_hack, "Enable workaround for Sony VAIO notebooks."); +MODULE_PARM_SYNTAX(snd_vaio_hack, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); + +/* + * hw definitions + */ + +/* The BIOS signature. */ +#define NM_SIGNATURE 0x4e4d0000 +/* Signature mask. */ +#define NM_SIG_MASK 0xffff0000 + +/* Size of the second memory area. */ +#define NM_PORT2_SIZE 4096 + +/* The base offset of the mixer in the second memory area. */ +#define NM_MIXER_OFFSET 0x600 + +/* The maximum size of a coefficient entry. */ +#define NM_MAX_PLAYBACK_COEF_SIZE 0x5000 +#define NM_MAX_RECORD_COEF_SIZE 0x1260 + +/* The interrupt register. */ +#define NM_INT_REG 0xa04 +/* And its bits. */ +#define NM_PLAYBACK_INT 0x40 +#define NM_RECORD_INT 0x100 +#define NM_MISC_INT_1 0x4000 +#define NM_MISC_INT_2 0x1 +#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1) + +/* The AV's "mixer ready" status bit and location. */ +#define NM_MIXER_STATUS_OFFSET 0xa04 +#define NM_MIXER_READY_MASK 0x0800 +#define NM_MIXER_PRESENCE 0xa06 +#define NM_PRESENCE_MASK 0x0050 +#define NM_PRESENCE_VALUE 0x0040 + +/* + * For the ZX. It uses the same interrupt register, but it holds 32 + * bits instead of 16. + */ +#define NM2_PLAYBACK_INT 0x10000 +#define NM2_RECORD_INT 0x80000 +#define NM2_MISC_INT_1 0x8 +#define NM2_MISC_INT_2 0x2 +#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X)) + +/* The ZX's "mixer ready" status bit and location. */ +#define NM2_MIXER_STATUS_OFFSET 0xa06 +#define NM2_MIXER_READY_MASK 0x0800 + +/* The playback registers start from here. */ +#define NM_PLAYBACK_REG_OFFSET 0x0 +/* The record registers start from here. */ +#define NM_RECORD_REG_OFFSET 0x200 + +/* The rate register is located 2 bytes from the start of the register area. */ +#define NM_RATE_REG_OFFSET 2 + +/* Mono/stereo flag, number of bits on playback, and rate mask. */ +#define NM_RATE_STEREO 1 +#define NM_RATE_BITS_16 2 +#define NM_RATE_MASK 0xf0 + +/* Playback enable register. */ +#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) +#define NM_PLAYBACK_ENABLE_FLAG 1 +#define NM_PLAYBACK_ONESHOT 2 +#define NM_PLAYBACK_FREERUN 4 + +/* Mutes the audio output. */ +#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) +#define NM_AUDIO_MUTE_LEFT 0x8000 +#define NM_AUDIO_MUTE_RIGHT 0x0080 + +/* Recording enable register. */ +#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) +#define NM_RECORD_ENABLE_FLAG 1 +#define NM_RECORD_FREERUN 2 + +/* coefficient buffer pointer */ +#define NM_COEFF_START_OFFSET 0x1c +#define NM_COEFF_END_OFFSET 0x20 + +/* DMA buffer offsets */ +#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) +#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) +#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) +#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) + +#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) +#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) +#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) +#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) + +/* + * type definitions + */ + +typedef struct snd_nm256 nm256_t; +typedef struct snd_nm256_stream nm256_stream_t; +#define chip_t nm256_t + +struct snd_nm256_stream { + + nm256_t *chip; + snd_pcm_substream_t *substream; + int running; + + u32 buf; /* offset from chip->buffer */ + int bufsize; /* buffer size in bytes */ + unsigned long bufptr; /* mapped pointer */ + unsigned long bufptr_addr; /* physical address of the mapped pointer */ + + int dma_size; /* buffer size of the substream in bytes */ + int period_size; /* period size in bytes */ + int periods; /* # of periods */ + int shift; /* bit shifts */ + int cur_period; /* current period # */ + +}; + +struct snd_nm256 { + + snd_card_t *card; + + unsigned long cport; /* control port */ + struct resource *res_cport; /* its resource */ + unsigned long cport_addr; /* physical address */ + + unsigned long buffer; /* buffer */ + struct resource *res_buffer; /* its resource */ + unsigned long buffer_addr; /* buffer phyiscal address */ + + u32 buffer_start; /* start offset from pci resource 0 */ + u32 buffer_end; /* end offset */ + u32 buffer_size; /* total buffer size */ + + u32 all_coeff_buf; /* coefficient buffer */ + u32 coeff_buf[2]; /* coefficient buffer for each stream */ + + int coeffs_current; /* coeff. table is loaded? */ + int use_cache; /* use one big coef. table */ + + int mixer_base; /* register offset of ac97 mixer */ + int mixer_status_offset; /* offset of mixer status reg. */ + int mixer_status_mask; /* bit mask to test the mixer status */ + + int irq; + void (*interrupt)(int, void *, struct pt_regs *); + int badintrcount; /* counter to check bogus interrupts */ + + nm256_stream_t streams[2]; + + ac97_t *ac97; + + snd_pcm_t *pcm; + + struct pci_dev *pci; + + spinlock_t reg_lock; + +}; + + +/* + * include coefficient table + */ +#include "nm256_coef.c" + + +/* + * PCI ids + */ + +#ifndef PCI_VENDOR_ID_NEOMAGIC +#define PCI_VENDOR_ID_NEOMEGIC 0x10c8 +#endif +#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#endif +#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +#endif + + +static struct pci_device_id snd_nm256_ids[] __devinitdata = { + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, snd_nm256_ids); + + +/* + * lowlvel stuffs + */ + +inline static u8 +snd_nm256_readb(nm256_t *chip, int offset) +{ + return readb(chip->cport + offset); +} + +inline static u16 +snd_nm256_readw(nm256_t *chip, int offset) +{ + return readw(chip->cport + offset); +} + +inline static u32 +snd_nm256_readl(nm256_t *chip, int offset) +{ + return readl(chip->cport + offset); +} + +inline static void +snd_nm256_writeb(nm256_t *chip, int offset, u8 val) +{ + writeb(val, chip->cport + offset); +} + +inline static void +snd_nm256_writew(nm256_t *chip, int offset, u16 val) +{ + writew(val, chip->cport + offset); +} + +inline static void +snd_nm256_writel(nm256_t *chip, int offset, u32 val) +{ + writel(val, chip->cport + offset); +} + +inline static void +snd_nm256_write_buffer(nm256_t *chip, void *src, int offset, int size) +{ + offset -= chip->buffer_start; +#ifdef SNDRV_CONFIG_DEBUG + if (offset < 0 || offset >= chip->buffer_size) { + snd_printk("write_buffer invalid offset = %d size = %d\n", offset, size); + return; + } +#endif + memcpy_toio(chip->buffer + offset, src, size); +} + +/* + * coefficient handlers -- what a magic! + */ + +static u16 +snd_nm256_get_start_offset(int which) +{ + u16 offset = 0; + while (which-- > 0) + offset += coefficient_sizes[which]; + return offset; +} + +static void +snd_nm256_load_one_coefficient(nm256_t *chip, int stream, u32 port, int which) +{ + u32 coeff_buf = chip->coeff_buf[stream]; + u16 offset = snd_nm256_get_start_offset(which); + u16 size = coefficient_sizes[which]; + + snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size); + snd_nm256_writel(chip, port, coeff_buf); + /* ??? Record seems to behave differently than playback. */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + size--; + snd_nm256_writel(chip, port + 4, coeff_buf + size); +} + +static void +snd_nm256_load_coefficient(nm256_t *chip, int stream, int number) +{ + /* The enable register for the specified engine. */ + u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG); + u32 addr = NM_COEFF_START_OFFSET; + + addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET); + + if (snd_nm256_readb(chip, poffset) & 1) { + snd_printd("NM256: Engine was enabled while loading coefficients!\n"); + return; + } + + /* The recording engine uses coefficient values 8-15. */ + number &= 7; + if (stream == SNDRV_PCM_STREAM_CAPTURE) + number += 8; + + if (! chip->use_cache) { + snd_nm256_load_one_coefficient(chip, stream, addr, number); + return; + } + if (! chip->coeffs_current) { + snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf, + NM_TOTAL_COEFF_COUNT * 4); + chip->coeffs_current = 1; + } else { + u32 base = chip->all_coeff_buf; + u32 offset = snd_nm256_get_start_offset(number); + u32 end_offset = offset + coefficient_sizes[number]; + snd_nm256_writel(chip, addr, base + offset); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + end_offset--; + snd_nm256_writel(chip, addr + 4, base + end_offset); + } +} + + +/* The actual rates supported by the card. */ +static int samplerates[8] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, +}; +#define NUM_SAMPLERATES (sizeof(samplerates) / sizeof(samplerates[0])) +static snd_pcm_hw_constraint_list_t constraints_rates = { + count: NUM_SAMPLERATES, + list: samplerates, + mask: 0, +}; + +/* + * return the index of the target rate + */ +static int +snd_nm256_fixed_rate(int rate) +{ + int i; + for (i = 0; i < NUM_SAMPLERATES; i++) { + if (rate == samplerates[i]) + return i; + } + snd_BUG(); + return 0; +} + +/* + * set sample rate and format + */ +static void +snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int rate_index = snd_nm256_fixed_rate(runtime->rate); + unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK; + + s->shift = 0; + if (snd_pcm_format_width(runtime->format) == 16) { + ratebits |= NM_RATE_BITS_16; + s->shift++; + } + if (runtime->channels > 1) { + ratebits |= NM_RATE_STEREO; + s->shift++; + } + + runtime->rate = samplerates[rate_index]; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */ + snd_nm256_writeb(chip, + NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + break; + case SNDRV_PCM_STREAM_CAPTURE: + snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */ + snd_nm256_writeb(chip, + NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, + ratebits); + break; + } +} + +/* + * start / stop + */ + +/* update the watermark (current period) */ +static void snd_nm256_pcm_mark(nm256_t *chip, nm256_stream_t *s, int reg) +{ + s->cur_period++; + s->cur_period %= s->periods; + snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size); +} + +#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK) +#define snd_nm256_capture_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK) + +static void +snd_nm256_playback_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + /* program buffer pointers */ + snd_nm256_writel(chip, NM_PBUFFER_START, s->buf); + snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift)); + snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf); + snd_nm256_playback_mark(chip, s); + + /* Enable playback engine and interrupts. */ + snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, + NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); + /* Enable both channels. */ + snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0); +} + +static void +snd_nm256_capture_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) +{ + /* program buffer pointers */ + snd_nm256_writel(chip, NM_RBUFFER_START, s->buf); + snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size); + snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf); + snd_nm256_capture_mark(chip, s); + + /* Enable playback engine and interrupts. */ + snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, + NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); +} + +/* Stop the play engine. */ +static void +snd_nm256_playback_stop(nm256_t *chip) +{ + /* Shut off sound from both channels. */ + snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, + NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); + /* Disable play engine. */ + snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0); +} + +static void +snd_nm256_capture_stop(nm256_t *chip) +{ + /* Disable recording engine. */ + snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0); +} + +static int +snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long flags; + int err = 0; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (! s->running) { + snd_nm256_playback_start(chip, s, substream); + s->running = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (s->running) { + snd_nm256_playback_stop(chip); + s->running = 0; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + +static int +snd_nm256_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long flags; + int err = 0; + + snd_assert(s != NULL, return -ENXIO); + + spin_lock_irqsave(&chip->reg_lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (! s->running) { + snd_nm256_capture_start(chip, s, substream); + s->running = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (s->running) { + snd_nm256_capture_stop(chip); + s->running = 0; + } + break; + default: + err = -EINVAL; + break; + } + spin_unlock_irqrestore(&chip->reg_lock, flags); + return err; +} + + +/* + * prepare playback/capture channel + */ +static int snd_nm256_pcm_prepare(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + unsigned long flags; + + snd_assert(s, return -ENXIO); + s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size); + s->period_size = frames_to_bytes(runtime, substream->runtime->period_size); + s->periods = substream->runtime->periods; + s->cur_period = 0; + + spin_lock_irqsave(&chip->reg_lock, flags); + s->running = 0; + snd_nm256_set_format(chip, s, substream); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + + +/* + * get the current pointer + */ +static snd_pcm_uframes_t +snd_nm256_playback_pointer(snd_pcm_substream_t * substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long curp; + + snd_assert(s, return 0); + curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf; + curp %= s->dma_size; + return bytes_to_frames(substream->runtime, curp); +} + +static snd_pcm_uframes_t +snd_nm256_capture_pointer(snd_pcm_substream_t * substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; + unsigned long curp; + + snd_assert(s != NULL, return 0); + curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf; + curp %= s->dma_size; + return bytes_to_frames(substream->runtime, curp); +} + +#ifndef __i386__ +/* FIXME: I/O space is not accessible via pointers on all architectures */ + +/* + * silence / copy for playback + */ +static int +snd_nm256_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + memset_io(s->bufptr + pos, 0, count); + return 0; +} + +static int +snd_nm256_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + if (copy_from_user_toio(s->bufptr + pos, src, count)) + return -EFAULT; + return 0; +} + +/* + * copy to user + */ +static int +snd_nm256_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; + count = frames_to_bytes(runtime, count); + pos = frames_to_bytes(runtime, pos); + if (copy_to_user_fromio(dst, s->bufptr + pos, count)) + return -EFAULT; + return 0; +} + +#endif /* !__i386__ */ + + +/* + * update playback/capture watermarks + */ + +/* spinlock held! */ +static void +snd_nm256_playback_update(nm256_t *chip) +{ + nm256_stream_t *s; + + s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK]; + if (s->running && s->substream) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&chip->reg_lock); + snd_nm256_playback_mark(chip, s); + } +} + +/* spinlock held! */ +static void +snd_nm256_capture_update(nm256_t *chip) +{ + nm256_stream_t *s; + + s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE]; + if (s->running && s->substream) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&chip->reg_lock); + snd_nm256_capture_mark(chip, s); + } +} + +/* + * hardware info + */ +static snd_pcm_hardware_t snd_nm256_playback = +{ + info: +#ifdef __i386__ + SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| +#endif + SNDRV_PCM_INFO_INTERLEAVED | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME, + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + periods_min: 2, + periods_max: 1024, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 256, + period_bytes_max: 128 * 1024, +}; + +static snd_pcm_hardware_t snd_nm256_capture = +{ + info: +#ifdef __i386__ + SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| +#endif + SNDRV_PCM_INFO_INTERLEAVED | + /*SNDRV_PCM_INFO_PAUSE |*/ + SNDRV_PCM_INFO_RESUME, + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + periods_min: 2, + periods_max: 1024, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 256, + period_bytes_max: 128 * 1024, +}; + + +/* set dma transfer size */ +static int snd_nm256_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) +{ + /* area and addr are already set and unchanged */ + substream->runtime->dma_bytes = params_buffer_bytes(hw_params); + return 0; +} + +/* + * open + */ +static void snd_nm256_setup_stream(nm256_t *chip, nm256_stream_t *s, + snd_pcm_substream_t *substream, + snd_pcm_hardware_t *hw_ptr) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + s->running = 0; + runtime->hw = *hw_ptr; + runtime->hw.buffer_bytes_max = s->bufsize; + runtime->hw.period_bytes_max = s->bufsize / 2; + runtime->dma_area = (void*) s->bufptr; + runtime->dma_addr = s->bufptr_addr; + runtime->dma_bytes = s->bufsize; + runtime->private_data = s; + s->substream = substream; + + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); +} + +static int +snd_nm256_playback_open(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], + substream, &snd_nm256_playback); + return 0; +} + +static int +snd_nm256_capture_open(snd_pcm_substream_t *substream) +{ + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], + substream, &snd_nm256_capture); + return 0; +} + +/* + * close - we don't have to do special.. + */ +static int +snd_nm256_playback_close(snd_pcm_substream_t *substream) +{ + return 0; +} + + +static int +snd_nm256_capture_close(snd_pcm_substream_t *substream) +{ + return 0; +} + +/* + * create a pcm instance + */ +static snd_pcm_ops_t snd_nm256_playback_ops = { + open: snd_nm256_playback_open, + close: snd_nm256_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_nm256_pcm_hw_params, + prepare: snd_nm256_pcm_prepare, + trigger: snd_nm256_playback_trigger, + pointer: snd_nm256_playback_pointer, +#ifndef __i386__ + copy: snd_nm256_playback_copy, + silence: snd_nm256_playback_silence, +#endif +}; + +static snd_pcm_ops_t snd_nm256_capture_ops = { + open: snd_nm256_capture_open, + close: snd_nm256_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_nm256_pcm_hw_params, + prepare: snd_nm256_pcm_prepare, + trigger: snd_nm256_capture_trigger, + pointer: snd_nm256_capture_pointer, +#ifndef __i386__ + copy: snd_nm256_capture_copy, +#endif +}; + +static int __init +snd_nm256_pcm(nm256_t *chip, int device) +{ + snd_pcm_t *pcm; + int i, err; + + for (i = 0; i < 2; i++) { + nm256_stream_t *s = &chip->streams[i]; + s->bufptr = chip->buffer + s->buf - chip->buffer_start; + s->bufptr_addr = chip->buffer_addr + s->buf - chip->buffer_start; + } + + err = snd_pcm_new(chip->card, chip->card->driver, device, + 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + chip->pcm = pcm; + + return 0; +} + + +/* + * Initialize the hardware. + */ +static void +snd_nm256_init_chip(nm256_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + /* Reset everything. */ + snd_nm256_writeb(chip, 0x0, 0x11); + snd_nm256_writew(chip, 0x214, 0); + /* stop sounds.. */ + //snd_nm256_playback_stop(chip); + //snd_nm256_capture_stop(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + + +inline static void +snd_nm256_intr_check(nm256_t *chip) +{ + if (chip->badintrcount++ > 1000) { + /* + * I'm not sure if the best thing is to stop the card from + * playing or just release the interrupt (after all, we're in + * a bad situation, so doing fancy stuff may not be such a good + * idea). + * + * I worry about the card engine continuing to play noise + * over and over, however--that could become a very + * obnoxious problem. And we know that when this usually + * happens things are fairly safe, it just means the user's + * inserted a PCMCIA card and someone's spamming us with IRQ 9s. + */ + if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) + snd_nm256_playback_stop(chip); + if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) + snd_nm256_capture_stop(chip); + chip->badintrcount = 0; + } +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * + * I don't like the cut-n-paste job here either between the two routines, + * but there are sufficient differences between the two interrupt handlers + * that parameterizing it isn't all that great either. (Could use a macro, + * I suppose...yucky bleah.) + */ + +static void +snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy) +{ + nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return); + u16 status; + u8 cbyte; + + status = snd_nm256_readw(chip, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + snd_nm256_intr_check(chip); + return; + } + + chip->badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + spin_lock(&chip->reg_lock); + if (status & NM_PLAYBACK_INT) { + status &= ~NM_PLAYBACK_INT; + NM_ACK_INT(chip, NM_PLAYBACK_INT); + snd_nm256_playback_update(chip); + } + + if (status & NM_RECORD_INT) { + status &= ~NM_RECORD_INT; + NM_ACK_INT(chip, NM_RECORD_INT); + snd_nm256_capture_update(chip); + } + + if (status & NM_MISC_INT_1) { + status &= ~NM_MISC_INT_1; + NM_ACK_INT(chip, NM_MISC_INT_1); + snd_printd("NM256: Got misc interrupt #1\n"); + snd_nm256_writew(chip, NM_INT_REG, 0x8000); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte | 2); + } + + if (status & NM_MISC_INT_2) { + status &= ~NM_MISC_INT_2; + NM_ACK_INT(chip, NM_MISC_INT_2); + snd_printd("NM256: Got misc interrupt #2\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM_ACK_INT(chip, status); + } + + spin_unlock(&chip->reg_lock); +} + +/* + * Handle a potential interrupt for the device referred to by DEV_ID. + * This handler is for the 256ZX, and is very similar to the non-ZX + * routine. + */ + +static void +snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy) +{ + nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return); + u32 status; + u8 cbyte; + + status = snd_nm256_readl(chip, NM_INT_REG); + + /* Not ours. */ + if (status == 0) { + snd_nm256_intr_check(chip); + return; + } + + chip->badintrcount = 0; + + /* Rather boring; check for individual interrupts and process them. */ + + spin_lock(&chip->reg_lock); + if (status & NM2_PLAYBACK_INT) { + status &= ~NM2_PLAYBACK_INT; + NM2_ACK_INT(chip, NM2_PLAYBACK_INT); + snd_nm256_playback_update(chip); + } + + if (status & NM2_RECORD_INT) { + status &= ~NM2_RECORD_INT; + NM2_ACK_INT(chip, NM2_RECORD_INT); + snd_nm256_capture_update(chip); + } + + if (status & NM2_MISC_INT_1) { + status &= ~NM2_MISC_INT_1; + NM2_ACK_INT(chip, NM2_MISC_INT_1); + snd_printd("NM256: Got misc interrupt #1\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte | 2); + } + + if (status & NM2_MISC_INT_2) { + status &= ~NM2_MISC_INT_2; + NM2_ACK_INT(chip, NM2_MISC_INT_2); + snd_printd("NM256: Got misc interrupt #2\n"); + cbyte = snd_nm256_readb(chip, 0x400); + snd_nm256_writeb(chip, 0x400, cbyte & ~2); + } + + /* Unknown interrupt. */ + if (status) { + snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", + status); + /* Pray. */ + NM2_ACK_INT(chip, status); + } + + spin_unlock(&chip->reg_lock); +} + +/* + * AC97 interface + */ + +/* + * Waits for the mixer to become ready to be written; returns a zero value + * if it timed out. + */ +static int +snd_nm256_ac97_ready(nm256_t *chip) +{ + int timeout = 10; + u32 testaddr; + u16 testb; + + testaddr = chip->mixer_status_offset; + testb = chip->mixer_status_mask; + + /* + * Loop around waiting for the mixer to become ready. + */ + while (timeout-- > 0) { + if ((snd_nm256_readw(chip, testaddr) & testb) == 0) + return 1; + udelay(100); + } + return 0; +} + +/* + */ +static unsigned short +snd_nm256_ac97_read(ac97_t *ac97, unsigned short reg) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return -ENXIO); + int res; + + if (reg >= 128) + return 0; + + if (! snd_nm256_ac97_ready(chip)) + return 0; + res = snd_nm256_readw(chip, chip->mixer_base + reg); + /* Magic delay. Bleah yucky. */ + udelay(1000); + return res; +} + +/* + */ +static void +snd_nm256_ac97_write(ac97_t *ac97, + unsigned short reg, unsigned short val) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); + int tries = 2; + u32 base; + + base = chip->mixer_base; + + snd_nm256_ac97_ready(chip); + + /* Wait for the write to take, too. */ + while (tries-- > 0) { + snd_nm256_writew(chip, base + reg, val); + udelay(1000); /* a little delay here seems better.. */ + if (snd_nm256_ac97_ready(chip)) + return; + } + snd_printd("nm256: ac97 codec not ready..\n"); +} + +/* initialize the ac97 into a known state */ +static void +snd_nm256_ac97_reset(ac97_t *ac97) +{ + nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + /* Reset the mixer. 'Tis magic! */ + snd_nm256_writeb(chip, 0x6c0, 1); +#if 0 /* Dell latitude LS will lock up by this */ + snd_nm256_writeb(chip, 0x6cc, 0x87); +#endif + snd_nm256_writeb(chip, 0x6cc, 0x80); + snd_nm256_writeb(chip, 0x6cc, 0x0); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* create an ac97 mixer interface */ +static int __init +snd_nm256_mixer(nm256_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.init = snd_nm256_ac97_reset; + ac97.write = snd_nm256_ac97_write; + ac97.read = snd_nm256_ac97_read; + ac97.private_data = chip; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + return 0; +} + +/* + * See if the signature left by the NM256 BIOS is intact; if so, we use + * the associated address as the end of our audio buffer in the video + * RAM. + */ + +static int __init +snd_nm256_peek_for_sig(nm256_t *chip) +{ + /* The signature is located 1K below the end of video RAM. */ + unsigned long temp; + /* Default buffer end is 5120 bytes below the top of RAM. */ + unsigned long pointer_found = chip->buffer_end - 0x1400; + u32 sig; + + temp = (unsigned long) ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16); + if (temp == 0) { + snd_printk("Unable to scan for card signature in video RAM\n"); + return -EBUSY; + } + + sig = readl(temp); + if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { + u32 pointer = readl(temp + 4); + + /* + * If it's obviously invalid, don't use it + */ + if (pointer == 0xffffffff || + pointer < chip->buffer_size || + pointer > chip->buffer_end) { + snd_printk("invalid signature found: 0x%x\n", pointer); + iounmap((void *)temp); + return -ENODEV; + } else { + pointer_found = pointer; + printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n", pointer); + } + } + + iounmap((void *)temp); + chip->buffer_end = pointer_found; + + return 0; +} + +#ifdef CONFIG_PM +/* + * APM event handler, so the card is properly reinitialized after a power + * event. + */ +static void nm256_suspend(nm256_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + snd_pcm_suspend_all(chip->pcm); + chip->coeffs_current = 0; + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void nm256_resume(nm256_t *chip) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + /* Perform a full reset on the hardware */ + pci_enable_device(chip->pci); + snd_nm256_init_chip(chip); + + /* restore ac97 */ + snd_ac97_resume(chip->ac97); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +#ifndef PCI_OLD_SUSPEND +static int snd_nm256_suspend(struct pci_dev *dev, u32 state) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO); + nm256_suspend(chip); + return 0; +} +static int snd_nm256_resume(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return -ENXIO); + nm256_resume(chip); + return 0; +} +#else +static void snd_nm256_suspend(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return); + nm256_suspend(chip); +} +static void snd_nm256_resume(struct pci_dev *dev) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(dev), return); + nm256_resume(chip); +} +#endif + +/* callback */ +static int snd_nm256_set_power_state(snd_card_t *card, unsigned int power_state) +{ + nm256_t *chip = snd_magic_cast(nm256_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + nm256_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + nm256_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +static int snd_nm256_free(nm256_t *chip) +{ + if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) + snd_nm256_playback_stop(chip); + if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) + snd_nm256_capture_stop(chip); + + synchronize_irq(); + + if (chip->cport) + iounmap((void *) chip->cport); + if (chip->buffer) + iounmap((void *) chip->buffer); + if (chip->res_cport) { + release_resource(chip->res_cport); + kfree_nocheck(chip->res_cport); + } + if (chip->res_buffer) { + release_resource(chip->res_buffer); + kfree_nocheck(chip->res_buffer); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void*)chip); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_nm256_dev_free(snd_device_t *device) +{ + nm256_t *chip = snd_magic_cast(nm256_t, device->device_data, return -ENXIO); + return snd_nm256_free(chip); +} + +static int __init +snd_nm256_create(snd_card_t *card, struct pci_dev *pci, + int play_bufsize, int capt_bufsize, + int force_load, + u32 buffertop, + int usecache, + nm256_t **chip_ret) +{ + nm256_t *chip; + int err, pval; + static snd_device_ops_t ops = { + dev_free: snd_nm256_dev_free, + }; + u32 addr; + + *chip_ret = NULL; + + chip = snd_magic_kcalloc(nm256_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + chip->pci = pci; + chip->use_cache = usecache; + spin_lock_init(&chip->reg_lock); + chip->irq = -1; + + chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize; + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize; + + /* + * The NM256 has two memory ports. The first port is nothing + * more than a chunk of video RAM, which is used as the I/O ring + * buffer. The second port has the actual juicy stuff (like the + * mixer and the playback engine control registers). + */ + + chip->buffer_addr = pci_resource_start(pci, 0); + chip->cport_addr = pci_resource_start(pci, 1); + + /* Init the memory port info. */ + /* remap control port (#2) */ + chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE, + card->driver); + if (chip->res_cport == NULL) { + snd_printk("memory region 0x%lx (size 0x%x) busy\n", + chip->cport_addr, NM_PORT2_SIZE); + err = -EBUSY; + goto __error; + } + chip->cport = (unsigned long) ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE); + if (chip->cport == 0) { + snd_printk("unable to map control port %lx\n", chip->cport_addr); + err = -ENOMEM; + goto __error; + } + + if (!strcmp(card->driver, "NM256AV")) { + /* Ok, try to see if this is a non-AC97 version of the hardware. */ + pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); + if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { + if (! force_load) { + printk(KERN_ERR "nm256: no ac97 is found!\n"); + printk(KERN_ERR " force the driver to load by passing in the module parameter\n"); + printk(KERN_ERR " snd_force_ac97=1\n"); + printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n"); + err = -ENXIO; + goto __error; + } + } + chip->buffer_end = 2560 * 1024; + chip->interrupt = snd_nm256_interrupt; + chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET; + chip->mixer_status_mask = NM_MIXER_READY_MASK; + } else { + /* Not sure if there is any relevant detect for the ZX or not. */ + if (snd_nm256_readb(chip, 0xa0b) != 0) + chip->buffer_end = 6144 * 1024; + else + chip->buffer_end = 4096 * 1024; + + chip->interrupt = snd_nm256_interrupt_zx; + chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; + chip->mixer_status_mask = NM2_MIXER_READY_MASK; + } + + chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; + if (chip->use_cache) + chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4; + else + chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE; + + if (buffertop >= chip->buffer_size && buffertop < chip->buffer_end) + chip->buffer_end = buffertop; + else { + /* get buffer end pointer from signature */ + if ((err = snd_nm256_peek_for_sig(chip)) < 0) + goto __error; + } + + chip->buffer_start = chip->buffer_end - chip->buffer_size; + chip->buffer_addr += chip->buffer_start; + + printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n", + chip->buffer_start, chip->buffer_end); + + chip->res_buffer = request_mem_region(chip->buffer_addr, + chip->buffer_size, + card->driver); + if (chip->res_buffer == NULL) { + snd_printk("nm256: buffer 0x%lx (size 0x%x) busy\n", + chip->buffer_addr, chip->buffer_size); + err = -EBUSY; + goto __error; + } + chip->buffer = (unsigned long) ioremap_nocache(chip->buffer_addr, chip->buffer_size); + if (chip->buffer == 0) { + err = -ENOMEM; + snd_printk("unable to map ring buffer at %lx\n", chip->buffer_addr); + goto __error; + } + + /* set offsets */ + addr = chip->buffer_start; + chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr; + addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize; + chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr; + addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; + if (chip->use_cache) { + chip->all_coeff_buf = addr; + } else { + chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr; + addr += NM_MAX_PLAYBACK_COEF_SIZE; + chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; + } + + /* acquire interrupt */ + if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void*)chip)) { + err = -EBUSY; + snd_printk("unable to grab IRQ %d\n", pci->irq); + goto __error; + } + chip->irq = pci->irq; + + /* Fixed setting. */ + chip->mixer_base = NM_MIXER_OFFSET; + + chip->coeffs_current = 0; + + snd_nm256_init_chip(chip); + + if ((err = snd_nm256_pcm(chip, 0)) < 0) + goto __error; + + if ((err = snd_nm256_mixer(chip) < 0)) + goto __error; + + // pci_set_master(pci); /* needed? */ + +#ifdef CONFIG_PM + card->set_power_state = snd_nm256_set_power_state; + card->power_state_private_data = chip; +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; + + *chip_ret = chip; + return 0; + +__error: + snd_nm256_free(chip); + return err; +} + + +static int __devinit snd_nm256_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + nm256_t *chip; + int err; + unsigned int buffer_top; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (pci->device) { + case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: + strcpy(card->driver, "NM256AV"); + break; + case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: + strcpy(card->driver, "NM256ZX"); + break; + default: + snd_printk("invalid device id 0x%x\n", pci->device); + snd_card_free(card); + return -EINVAL; + } + + if (snd_vaio_hack[dev]) + buffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */ + else + buffer_top = snd_buffer_top[dev]; + + if (snd_playback_bufsize[dev] < 4) + snd_playback_bufsize[dev] = 4; + if (snd_playback_bufsize[dev] > 128) + snd_playback_bufsize[dev] = 128; + if (snd_capture_bufsize[dev] < 4) + snd_capture_bufsize[dev] = 4; + if (snd_capture_bufsize[dev] > 128) + snd_capture_bufsize[dev] = 128; + if ((err = snd_nm256_create(card, pci, + snd_playback_bufsize[dev] * 1024, /* in bytes */ + snd_capture_bufsize[dev] * 1024, /* in bytes */ + snd_force_ac97[dev], + buffer_top, + snd_use_cache[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + sprintf(card->shortname, "NeoMagic %s", card->driver); + sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d", + card->shortname, + chip->buffer_addr, chip->cport_addr, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +static void __devexit snd_nm256_remove(struct pci_dev *pci) +{ + nm256_t *chip = snd_magic_cast(nm256_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + + +static struct pci_driver driver = { + name: "NeoMagic 256", + id_table: snd_nm256_ids, + probe: snd_nm256_probe, + remove: __devexit_p(snd_nm256_remove), +#ifdef CONFIG_PM + suspend: snd_nm256_suspend, + resume: snd_nm256_resume, +#endif +}; + + +static int __init alsa_card_nm256_init(void) +{ + int err; + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "NeoMagic 256 audio soundchip not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_nm256_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_nm256_init) +module_exit(alsa_card_nm256_exit) + +#ifndef MODULE + +/* format is: snd-nm256=snd_enable,snd_index,snd_id, + snd_playback_bufsize,snd_capture_bufsize, + snd_force_ac97,snd_buffer_top,snd_use_cache */ + +static int __init alsa_card_nm256_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_playback_bufsize[nr_dev]) == 2 && + get_option(&str,&snd_capture_bufsize[nr_dev]) == 2 && + get_option(&str,&snd_force_ac97[nr_dev]) == 2 && + get_option(&str,&snd_buffer_top[nr_dev]) == 2 && + get_option(&str,&snd_use_cache[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-nm256=", alsa_card_nm256_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/nm256/nm256_coef.c linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256_coef.c --- linux/sound/pci/nm256/nm256_coef.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/nm256/nm256_coef.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,4607 @@ +#define NM_TOTAL_COEFF_COUNT 0x3158 + +static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { + 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21, + 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01, + 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, + 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, + 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF, + 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, + 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, + 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9, + 0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C, + 0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00, + 0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, + 0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, + 0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00, + 0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1, + 0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, + 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, + 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD, + 0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38, + 0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, + 0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, + 0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06, + 0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, + 0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, + 0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA, + 0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9, + 0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68, + 0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32, + 0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, + 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, + 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53, + 0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04, + 0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01, + 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20, + 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43, + 0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1, + 0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD, + 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8, + 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5, + 0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04, + 0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04, + 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D, + 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE, + 0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD, + 0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, + 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2, + 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD, + 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, + 0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D, + 0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01, + 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, + 0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, + 0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, + 0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66, + 0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF, + 0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, + 0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, + 0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00, + 0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF, + 0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, + 0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, + 0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC, + 0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98, + 0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, + 0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, + 0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05, + 0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A, + 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, + 0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, + 0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8, + 0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40, + 0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85, + 0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36, + 0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D, + 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39, + 0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48, + 0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01, + 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F, + 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F, + 0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1, + 0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE, + 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A, + 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A, + 0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03, + 0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, + 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, + 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01, + 0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC, + 0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, + 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2, + 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF, + 0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48, + 0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01, + 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11, + 0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF, + 0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, + 0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, + 0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF, + 0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C, + 0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, + 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, + 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC, + 0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1, + 0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, + 0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, + 0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04, + 0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, + 0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, + 0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, + 0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7, + 0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3, + 0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9, + 0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A, + 0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, + 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, + 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, + 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A, + 0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48, + 0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, + 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91, + 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8, + 0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1, + 0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF, + 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA, + 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD, + 0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD, + 0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06, + 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF, + 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94, + 0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC, + 0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, + 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4, + 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF, + 0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A, + 0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01, + 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, + 0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, + 0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, + 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B, + 0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF, + 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, + 0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, + 0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF, + 0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC, + 0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, + 0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, + 0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC, + 0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE, + 0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, + 0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, + 0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03, + 0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91, + 0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, + 0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, + 0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5, + 0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20, + 0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3, + 0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E, + 0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00, + 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47, + 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96, + 0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46, + 0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01, + 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74, + 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B, + 0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2, + 0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF, + 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D, + 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A, + 0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE, + 0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, + 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, + 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF, + 0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81, + 0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC, + 0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, + 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5, + 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2, + 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF, + 0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED, + 0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01, + 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, + 0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25, + 0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, + 0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, + 0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF, + 0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2, + 0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, + 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, + 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, + 0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA, + 0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, + 0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, + 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02, + 0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB, + 0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, + 0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, + 0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4, + 0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85, + 0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02, + 0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41, + 0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, + 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, + 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, + 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA, + 0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44, + 0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01, + 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5, + 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A, + 0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3, + 0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00, + 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A, + 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C, + 0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00, + 0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06, + 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D, + 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0, + 0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC, + 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, + 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8, + 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7, + 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64, + 0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE, + 0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, + 0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, + 0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, + 0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, + 0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD, + 0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, + 0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, + 0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE, + 0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54, + 0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, + 0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, + 0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, + 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC, + 0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63, + 0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, + 0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, + 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00, + 0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34, + 0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, + 0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, + 0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3, + 0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0, + 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36, + 0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44, + 0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, + 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA, + 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00, + 0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4, + 0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42, + 0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01, + 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E, + 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31, + 0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4, + 0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01, + 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86, + 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D, + 0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01, + 0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, + 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, + 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88, + 0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF, + 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA, + 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92, + 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA, + 0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE, + 0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF, + 0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5, + 0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, + 0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, + 0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01, + 0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C, + 0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, + 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, + 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC, + 0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8, + 0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, + 0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, + 0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, + 0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF, + 0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99, + 0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, + 0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, + 0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2, + 0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F, + 0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D, + 0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46, + 0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, + 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, + 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, + 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, + 0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F, + 0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01, + 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A, + 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF, + 0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5, + 0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02, + 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9, + 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8, + 0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03, + 0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07, + 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0, + 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0, + 0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC, + 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9, + 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD, + 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66, + 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36, + 0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF, + 0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, + 0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, + 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF, + 0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11, + 0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, + 0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, + 0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01, + 0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74, + 0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF, + 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, + 0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, + 0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC, + 0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A, + 0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, + 0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, + 0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, + 0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD, + 0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08, + 0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, + 0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, + 0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32, + 0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1, + 0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70, + 0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5, + 0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47, + 0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF, + 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26, + 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00, + 0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, + 0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B, + 0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01, + 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E, + 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0, + 0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7, + 0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD, + 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F, + 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66, + 0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04, + 0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, + 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, + 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, + 0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48, + 0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC, + 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93, + 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01, + 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34, + 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA, + 0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF, + 0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF, + 0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10, + 0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, + 0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, + 0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01, + 0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30, + 0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, + 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, + 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC, + 0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A, + 0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, + 0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, + 0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03, + 0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04, + 0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, + 0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, + 0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1, + 0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE, + 0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48, + 0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71, + 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, + 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, + 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, + 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB, + 0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37, + 0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01, + 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2, + 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70, + 0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8, + 0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD, + 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9, + 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0, + 0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05, + 0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, + 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06, + 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40, + 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, + 0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53, + 0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC, + 0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, + 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC, + 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D, + 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6, + 0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00, + 0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, + 0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, + 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF, + 0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06, + 0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, + 0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, + 0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01, + 0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57, + 0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, + 0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, + 0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD, + 0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B, + 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, + 0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, + 0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03, + 0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24, + 0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, + 0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, + 0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1, + 0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8, + 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, + 0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04, + 0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C, + 0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02, + 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00, + 0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, + 0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33, + 0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00, + 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, + 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B, + 0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA, + 0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD, + 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58, + 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14, + 0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06, + 0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, + 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, + 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0, + 0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC, + 0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, + 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA, + 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4, + 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA, + 0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00, + 0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00, + 0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06, + 0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, + 0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, + 0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, + 0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE, + 0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF, + 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, + 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, + 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD, + 0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0, + 0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, + 0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, + 0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04, + 0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B, + 0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, + 0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, + 0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, + 0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2, + 0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE, + 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E, + 0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08, + 0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4, + 0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, + 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, + 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B, + 0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E, + 0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00, + 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E, + 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C, + 0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC, + 0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC, + 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6, + 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00, + 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B, + 0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06, + 0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, + 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05, + 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, + 0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC, + 0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD, + 0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8, + 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A, + 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2, + 0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01, + 0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, + 0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, + 0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, + 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00, + 0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20, + 0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF, + 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, + 0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, + 0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, + 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, + 0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7, + 0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, + 0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, + 0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE, + 0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F, + 0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, + 0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, + 0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05, + 0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D, + 0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, + 0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, + 0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2, + 0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, + 0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C, + 0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B, + 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0, + 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF, + 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66, + 0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29, + 0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32, + 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF, + 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E, + 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4, + 0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01, + 0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC, + 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB, + 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00, + 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12, + 0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06, + 0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, + 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, + 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, + 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12, + 0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD, + 0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7, + 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD, + 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0, + 0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01, + 0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00, + 0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64, + 0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, + 0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, + 0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, + 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73, + 0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, + 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, + 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF, + 0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC, + 0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, + 0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, + 0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE, + 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06, + 0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF, + 0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, + 0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, + 0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, + 0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9, + 0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4, + 0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, + 0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11, + 0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63, + 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, + 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, + 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, + 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22, + 0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25, + 0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF, + 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20, + 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87, + 0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE, + 0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15, + 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC, + 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3, + 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7, + 0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07, + 0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03, + 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97, + 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA, + 0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02, + 0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5, + 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A, + 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE, + 0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01, + 0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7, + 0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, + 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00, + 0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4, + 0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, + 0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, + 0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, + 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, + 0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60, + 0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, + 0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, + 0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF, + 0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D, + 0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, + 0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, + 0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06, + 0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15, + 0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, + 0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, + 0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7, + 0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6, + 0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0, + 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15, + 0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C, + 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF, + 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3, + 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF, + 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1, + 0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F, + 0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF, + 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8, + 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23, + 0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA, + 0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC, + 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB, + 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49, + 0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07, + 0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, + 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, + 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, + 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07, + 0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01, + 0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4, + 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E, + 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B, + 0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01, + 0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00, + 0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB, + 0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, + 0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, + 0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01, + 0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB, + 0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00, + 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, + 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, + 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00, + 0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48, + 0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, + 0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, + 0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06, + 0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81, + 0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, + 0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, + 0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, + 0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, + 0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8, + 0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4, + 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A, + 0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9, + 0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, + 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, + 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, + 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74, + 0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A, + 0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE, + 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B, + 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC, + 0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8, + 0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC, + 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F, + 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96, + 0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06, + 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, + 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00, + 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C, + 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56, + 0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00, + 0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3, + 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8, + 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, + 0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6, + 0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, + 0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, + 0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1, + 0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00, + 0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, + 0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, + 0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01, + 0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D, + 0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, + 0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, + 0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01, + 0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73, + 0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, + 0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, + 0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07, + 0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08, + 0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, + 0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, + 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD, + 0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA, + 0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F, + 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F, + 0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB, + 0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF, + 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89, + 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D, + 0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15, + 0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01, + 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6, + 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF, + 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86, + 0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6, + 0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC, + 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0, + 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, + 0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2, + 0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06, + 0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, + 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, + 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3, + 0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF, + 0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2, + 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35, + 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00, + 0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD, + 0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01, + 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, + 0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41, + 0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00, + 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, + 0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, + 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01, + 0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1, + 0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, + 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, + 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02, + 0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95, + 0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, + 0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, + 0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07, + 0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9, + 0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00, + 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, + 0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, + 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7, + 0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE, + 0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62, + 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, + 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25, + 0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2, + 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, + 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, + 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F, + 0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11, + 0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01, + 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26, + 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53, + 0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4, + 0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC, + 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D, + 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D, + 0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06, + 0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD, + 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12, + 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29, + 0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF, + 0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1, + 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75, + 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, + 0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0, + 0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, + 0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, + 0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, + 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, + 0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02, + 0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00, + 0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, + 0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01, + 0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C, + 0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, + 0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, + 0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD, + 0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64, + 0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, + 0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, + 0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06, + 0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65, + 0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, + 0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, + 0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, + 0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01, + 0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F, + 0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, + 0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29, + 0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80, + 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF, + 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E, + 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B, + 0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C, + 0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01, + 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB, + 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF, + 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, + 0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2, + 0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC, + 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29, + 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D, + 0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05, + 0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, + 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, + 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3, + 0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE, + 0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, + 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1, + 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7, + 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF, + 0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF, + 0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, + 0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E, + 0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00, + 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, + 0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, + 0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01, + 0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7, + 0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, + 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, + 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD, + 0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC, + 0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, + 0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, + 0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06, + 0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E, + 0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, + 0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, + 0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC, + 0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68, + 0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55, + 0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E, + 0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63, + 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, + 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, + 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4, + 0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08, + 0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01, + 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C, + 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF, + 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33, + 0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2, + 0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD, + 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06, + 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6, + 0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04, + 0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04, + 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10, + 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F, + 0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD, + 0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, + 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1, + 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA, + 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, + 0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0, + 0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01, + 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, + 0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, + 0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, + 0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7, + 0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00, + 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, + 0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, + 0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00, + 0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5, + 0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, + 0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, + 0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC, + 0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78, + 0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, + 0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, + 0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06, + 0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, + 0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, + 0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, + 0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA, + 0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE, + 0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D, + 0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33, + 0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E, + 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00, + 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96, + 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E, + 0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04, + 0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01, + 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD, + 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF, + 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49, + 0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1, + 0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, + 0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD, + 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8, + 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E, + 0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03, + 0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, + 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, + 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97, + 0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD, + 0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, + 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2, + 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF, + 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF, + 0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5, + 0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01, + 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7, + 0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF, + 0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, + 0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, + 0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, + 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00, + 0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C, + 0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, + 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, + 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC, + 0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3, + 0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00, + 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, + 0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, + 0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, + 0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05, + 0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, + 0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, + 0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, + 0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, + 0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8, + 0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54, + 0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B, + 0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37, + 0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, + 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, + 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, + 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, + 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42, + 0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48, + 0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01, + 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0, + 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF, + 0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A, + 0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1, + 0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, + 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE, + 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43, + 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF, + 0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03, + 0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05, + 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B, + 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, + 0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7, + 0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC, + 0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08, + 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3, + 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF, + 0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86, + 0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01, + 0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A, + 0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, + 0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, + 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E, + 0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF, + 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, + 0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, + 0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF, + 0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0, + 0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, + 0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, + 0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, + 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC, + 0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04, + 0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, + 0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, + 0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, + 0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04, + 0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63, + 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, + 0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, + 0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, + 0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, + 0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7, + 0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7, + 0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0, + 0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B, + 0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00, + 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC, + 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF, + 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72, + 0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47, + 0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF, + 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56, + 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7, + 0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1, + 0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD, + 0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF, + 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD, + 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4, + 0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD, + 0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, + 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, + 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A, + 0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC, + 0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, + 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4, + 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF, + 0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D, + 0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01, + 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF, + 0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97, + 0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF, + 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, + 0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, + 0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF, + 0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45, + 0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, + 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, + 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC, + 0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08, + 0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00, + 0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, + 0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, + 0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, + 0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03, + 0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D, + 0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, + 0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, + 0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, + 0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5, + 0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33, + 0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB, + 0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F, + 0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, + 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, + 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, + 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D, + 0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46, + 0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5, + 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, + 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D, + 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30, + 0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2, + 0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00, + 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC, + 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0, + 0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF, + 0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06, + 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07, + 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF, + 0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87, + 0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC, + 0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5, + 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6, + 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE, + 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, + 0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4, + 0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01, + 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, + 0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, + 0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, + 0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60, + 0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, + 0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, + 0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE, + 0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE, + 0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, + 0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, + 0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, + 0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB, + 0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, + 0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, + 0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, + 0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01, + 0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9, + 0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, + 0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, + 0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4, + 0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96, + 0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B, + 0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42, + 0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00, + 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50, + 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF, + 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF, + 0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44, + 0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA, + 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, + 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1, + 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3, + 0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3, + 0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00, + 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35, + 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F, + 0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00, + 0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, + 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, + 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, + 0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9, + 0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC, + 0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1, + 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8, + 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1, + 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F, + 0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE, + 0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF, + 0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC, + 0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF, + 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, + 0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, + 0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE, + 0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1, + 0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, + 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, + 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC, + 0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B, + 0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, + 0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, + 0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, + 0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00, + 0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45, + 0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, + 0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, + 0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3, + 0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF, + 0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F, + 0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44, + 0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43, + 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, + 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, + 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, + 0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41, + 0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01, + 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C, + 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00, + 0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E, + 0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4, + 0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01, + 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60, + 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D, + 0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02, + 0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07, + 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE, + 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4, + 0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB, + 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB, + 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B, + 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, + 0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73, + 0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF, + 0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, + 0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, + 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF, + 0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B, + 0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, + 0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, + 0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01, + 0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70, + 0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF, + 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, + 0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, + 0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, + 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC, + 0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7, + 0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, + 0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, + 0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, + 0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE, + 0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC, + 0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, + 0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, + 0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, + 0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2, + 0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B, + 0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76, + 0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46, + 0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF, + 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C, + 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00, + 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E, + 0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF, + 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01, + 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8, + 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00, + 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00, + 0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5, + 0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE, + 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0, + 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00, + 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3, + 0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03, + 0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD, + 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, + 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, + 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, + 0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF, + 0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5, + 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE, + 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E, + 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, + 0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A, + 0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF, + 0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF, + 0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30, + 0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, + 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, + 0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, + 0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01, + 0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9, + 0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF, + 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, + 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, + 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, + 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC, + 0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20, + 0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, + 0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, + 0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, + 0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD, + 0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C, + 0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, + 0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, + 0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, + 0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1, + 0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A, + 0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF, + 0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48, + 0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62, + 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, + 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, + 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, + 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A, + 0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01, + 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B, + 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4, + 0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7, + 0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD, + 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89, + 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00, + 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B, + 0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04, + 0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, + 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06, + 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61, + 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, + 0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B, + 0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC, + 0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F, + 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01, + 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B, + 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, + 0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6, + 0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF, + 0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, + 0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, + 0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, + 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF, + 0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C, + 0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, + 0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, + 0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01, + 0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7, + 0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF, + 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, + 0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, + 0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC, + 0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28, + 0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, + 0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, + 0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03, + 0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC, + 0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, + 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, + 0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, + 0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, + 0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1, + 0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB, + 0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8, + 0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48, + 0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, + 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD, + 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00, + 0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36, + 0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00, + 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC, + 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96, + 0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8, + 0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD, + 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F, + 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF, + 0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05, + 0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, + 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, + 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, + 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, + 0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB, + 0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC, + 0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, + 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB, + 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72, + 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7, + 0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00, + 0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF, + 0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23, + 0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, + 0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, + 0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01, + 0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2, + 0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF, + 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, + 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, + 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD, + 0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01, + 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, + 0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, + 0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04, + 0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD, + 0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, + 0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, + 0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, + 0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1, + 0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD, + 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, + 0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04, + 0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03, + 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, + 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, + 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, + 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD, + 0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32, + 0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00, + 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10, + 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73, + 0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA, + 0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC, + 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98, + 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00, + 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B, + 0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06, + 0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05, + 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31, + 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, + 0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C, + 0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD, + 0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, + 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA, + 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9, + 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD, + 0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00, + 0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, + 0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, + 0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, + 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00, + 0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25, + 0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, + 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, + 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, + 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, + 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, + 0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, + 0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, + 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, + 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, + 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26, + 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06, + 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, + 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, + 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84, + 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD, + 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, + 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, + 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, + 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78, + 0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00, + 0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12, + 0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, + 0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, + 0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, + 0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, + 0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, + 0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, + 0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, + 0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, + 0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, + 0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, + 0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, + 0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD, + 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70, + 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, + 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD, + 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7, + 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC, + 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC, + 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2, + 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC, + 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79, + 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81, + 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00, + 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47, + 0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, + 0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, + 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, + 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, + 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, + 0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, + 0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, + 0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, + 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, + 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, + 0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, + 0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD, + 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07, + 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00, + 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96, + 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07, + 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, + 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, + 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, + 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85, + 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC, + 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C, + 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, + 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, + 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80, + 0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01, + 0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, + 0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, + 0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, + 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, + 0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, + 0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, + 0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, + 0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, + 0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, + 0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, + 0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, + 0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, + 0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC, + 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83, + 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03, + 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45, + 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF, + 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C, + 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC, + 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86, + 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03, + 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14, + 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, + 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62, + 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01, + 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, + 0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, + 0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, + 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, + 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, + 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, + 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, + 0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, + 0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, + 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, + 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, + 0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, + 0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, + 0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC, + 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC, + 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38, + 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06, + 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, + 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, + 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7, + 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC, + 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, + 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, + 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, + 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, + 0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15, + 0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01, + 0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, + 0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, + 0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, + 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, + 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, + 0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, + 0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, + 0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, + 0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, + 0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, + 0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, + 0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, + 0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, + 0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC, + 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C, + 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05, + 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A, + 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC, + 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC, + 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, + 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9, + 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19, + 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00, + 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86, + 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01, + 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, + 0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, + 0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, + 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, + 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, + 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, + 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, + 0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, + 0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, + 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, + 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, + 0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, + 0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC, + 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F, + 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15, + 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05, + 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, + 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, + 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00, + 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10, + 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC, + 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, + 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, + 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, + 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00, + 0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5, + 0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, + 0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, + 0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, + 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, + 0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, + 0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, + 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, + 0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, + 0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, + 0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, + 0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, + 0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, + 0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, + 0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, + 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1, + 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06, + 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C, + 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00, + 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7, + 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC, + 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, + 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6, + 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE, + 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00, + 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61, + 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56, + 0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, + 0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, + 0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, + 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, + 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, + 0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, + 0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, + 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, + 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, + 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, + 0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, + 0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, + 0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC, + 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F, + 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, + 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D, + 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04, + 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, + 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, + 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0, + 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC, + 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, + 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, + 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, + 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF, + 0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB, + 0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01, + 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47, + 0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, + 0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, + 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, + 0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, + 0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, + 0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, + 0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, + 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, + 0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, + 0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, + 0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, + 0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, + 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC, + 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA, + 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07, + 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18, + 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, + 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B, + 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD, + 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3, + 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4, + 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF, + 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77, + 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01, + 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, + 0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, + 0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, + 0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, + 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, + 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, + 0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, + 0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, + 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, + 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, + 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, + 0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, + 0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC, + 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21, + 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, + 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13, + 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02, + 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, + 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, + 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00, + 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94, + 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD, + 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, + 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, + 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, + 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF, + 0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8, + 0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01, + 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, + 0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, + 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, + 0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, + 0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, + 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, + 0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, + 0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, + 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, + 0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, + 0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, + 0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, + 0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, + 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC, + 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27, + 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07, + 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E, + 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00, + 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05, + 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD, + 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, + 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1, + 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C, + 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF, + 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69, + 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, + 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, + 0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, + 0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, + 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, + 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, + 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, + 0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, + 0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, + 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, + 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, + 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, + 0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, + 0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, + 0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD, + 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF, + 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D, + 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF, + 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE, + 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, + 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, + 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47, + 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02, + 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, + 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, + 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, + 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF, + 0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C, + 0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01, + 0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, + 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, + 0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, + 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, + 0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, + 0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, + 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, + 0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, + 0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, + 0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, + 0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, + 0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, + 0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, + 0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD, + 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC, + 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF, + 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06, + 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31, + 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94, + 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01, + 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, + 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1, + 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE, + 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, + 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2, + 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01, + 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, + 0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, + 0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, + 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, + 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, + 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, + 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, + 0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, + 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, + 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, + 0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, + 0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, + 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE, + 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33, + 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, + 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A, + 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02, + 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01, + 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, + 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, + 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6, + 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00, + 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C, + 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, + 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, + 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, + 0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1, + 0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE, + 0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, + 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, + 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, + 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, + 0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, + 0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, + 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, + 0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, + 0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, + 0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, + 0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, + 0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, + 0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, + 0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, + 0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF, + 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A, + 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04, + 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04, + 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C, + 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, + 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48, + 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF, + 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E, + 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2, + 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5, + 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, + 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E, + 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF, + 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, + 0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, + 0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, + 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, + 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, + 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, + 0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, + 0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, + 0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, + 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, + 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, + 0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, + 0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, + 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00, + 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6, + 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, + 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC, + 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04, + 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08, + 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, + 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, + 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF, + 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6, + 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE, + 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF, + 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, + 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, + 0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0, + 0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF, + 0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, + 0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, + 0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, + 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, + 0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, + 0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, + 0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, + 0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, + 0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, + 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, + 0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, + 0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, + 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, + 0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, + 0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, + 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00, + 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D, + 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02, + 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD, + 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C, + 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE, + 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED, + 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5, + 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7, + 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, + 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68, + 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF, + 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD, + 0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, + 0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, + 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, + 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, + 0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, + 0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, + 0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, + 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, + 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, + 0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, + 0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, + 0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, + 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01, + 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46, + 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27, + 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06, + 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11, + 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, + 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, + 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47, + 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD, + 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9, + 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, + 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, + 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56, + 0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00, + 0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA, + 0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, + 0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, + 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, + 0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, + 0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, + 0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, + 0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, + 0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, + 0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, + 0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, + 0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, + 0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE, + 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13, + 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, + 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF, + 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D, + 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61, + 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD, + 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4, + 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA, + 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C, + 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B, + 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00, + 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B, + 0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, + 0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, + 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, + 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, + 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, + 0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, + 0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, + 0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, + 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, + 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, + 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, + 0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, + 0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD, + 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA, + 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00, + 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA, + 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07, + 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, + 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, + 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, + 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, + 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6, + 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC, + 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE, + 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, + 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, + 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64, + 0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01, + 0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, + 0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, + 0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, + 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, + 0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, + 0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, + 0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, + 0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, + 0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, + 0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, + 0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, + 0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, + 0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, + 0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD, + 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D, + 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03, + 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD, + 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, + 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80, + 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC, + 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98, + 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00, + 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40, + 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, + 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60, + 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01, + 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, + 0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, + 0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, + 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, + 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, + 0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, + 0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, + 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, + 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, + 0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, + 0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, + 0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC, + 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3, + 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00, + 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1, + 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07, + 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, + 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, + 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, + 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF, + 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97, + 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC, + 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, + 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, + 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, + 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, + 0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A, + 0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01, + 0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, + 0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, + 0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, + 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, + 0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, + 0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, + 0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, + 0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, + 0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, + 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, + 0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, + 0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, + 0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, + 0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, + 0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, + 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC, + 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15, + 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04, + 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44, + 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF, + 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14, + 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC, + 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, + 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA, + 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8, + 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, + 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3, + 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01, + 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, + 0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, + 0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, + 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, + 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, + 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, + 0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, + 0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, + 0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, + 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, + 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, + 0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, + 0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, + 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC, + 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E, + 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00, + 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C, + 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06, + 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, + 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, + 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, + 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00, + 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD, + 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC, + 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, + 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, + 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, + 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00, + 0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46, + 0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, + 0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, + 0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, + 0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, + 0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, + 0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, + 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, + 0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, + 0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, + 0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, + 0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, + 0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, + 0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, + 0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC, + 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38, + 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06, + 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9, + 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00, + 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55, + 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC, + 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, + 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7, + 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2, + 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, + 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54, + 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01, + 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, + 0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, + 0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, + 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, + 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, + 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, + 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, + 0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, + 0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, + 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, + 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, + 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, + 0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, + 0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, + 0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, + 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00, + 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83, + 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05, + 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, + 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, + 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, + 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00, + 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20, + 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC, + 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, + 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, + 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, + 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, + 0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE, + 0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01, + 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53, + 0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, + 0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, + 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, + 0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, + 0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, + 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, + 0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, + 0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, + 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, + 0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, + 0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, + 0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, + 0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, + 0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC, + 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96, + 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06, + 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E, + 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, + 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E, + 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC, + 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, + 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4, + 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77, + 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF, + 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32, + 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01, + 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45, + 0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, + 0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, + 0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, + 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, + 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, + 0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, + 0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, + 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, + 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, + 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, + 0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, + 0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, + 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC, + 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7, + 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, + 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70, + 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03, + 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, + 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, + 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00, + 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C, + 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD, + 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, + 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, + 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF, + 0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7, + 0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01, + 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B, + 0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, + 0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, + 0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, + 0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, + 0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, + 0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, + 0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, + 0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, + 0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, + 0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, + 0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, + 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC, + 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26, + 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD, + 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, + 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, + 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00, + 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, + 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, + 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05, + 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3, + 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, + 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, + 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84, + 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, + 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, + 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, + 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, + 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, + 0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B, + 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11, + 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F, + 0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00, + 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C, + 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, + 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, + 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, + 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, + 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, + 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7, + 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07, + 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, + 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, + 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, + 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, + 0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, + 0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, + 0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, + 0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, + 0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, + 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, + 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, + 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, + 0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, + 0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, + 0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, + 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, + 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, + 0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1, + 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07, + 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24, + 0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF, + 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A, + 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00, + 0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, + 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, + 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, + 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, + 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, + 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, + 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00, + 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07, + 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, + 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, + 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, + 0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, + 0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, + 0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, + 0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, + 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, + 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, + 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, + 0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, + 0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, + 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, + 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, + 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, + 0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43, + 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48, + 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99, + 0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE, + 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7, + 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00, + 0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, + 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, + 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, + 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, + 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, + 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E, + 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06, + 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, + 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, + 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, + 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, + 0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, + 0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, + 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D, + 0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, + 0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, + 0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, + 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, + 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, + 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, + 0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, + 0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, + 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, + 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, + 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, + 0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E, + 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46, + 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6, + 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01, + 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D, + 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF, + 0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, + 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, + 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, + 0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, + 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, + 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3, + 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05, + 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06, + 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, + 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, + 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00, + 0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, + 0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, + 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A, + 0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, + 0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, + 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00, + 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, + 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, + 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43, + 0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, + 0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, + 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, + 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, + 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, + 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, + 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41, + 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE, + 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01, + 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9, + 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF, + 0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, + 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, + 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, + 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, + 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, + 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA, + 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03, + 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F, + 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, + 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, + 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00, + 0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, + 0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, + 0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD, + 0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, + 0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, + 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00, + 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, + 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, + 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A, + 0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, + 0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, + 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, + 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, + 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, + 0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, + 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A, + 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, + 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85, + 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF, + 0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, + 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, + 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, + 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, + 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, + 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46, + 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE, + 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A, + 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, + 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, + 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00, + 0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, + 0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, + 0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7, + 0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, + 0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, + 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF, + 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, + 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, + 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC, + 0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, + 0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, + 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, + 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, + 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, + 0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC, + 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32, + 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01, + 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A, + 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, + 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, + 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, + 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, + 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, + 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, + 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6, + 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01, + 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00, + 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, + 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, + 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00, + 0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, + 0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, + 0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC, + 0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, + 0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, + 0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, + 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, + 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, + 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06, + 0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, + 0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, + 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, + 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, + 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, + 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59, + 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28, + 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33, + 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, + 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, + 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, + 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, + 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, + 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, + 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, + 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, + 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33, + 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03, + 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF, + 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, + 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, + 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, + 0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, + 0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, + 0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, + 0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF, + 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, + 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, + 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71, + 0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, + 0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, + 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, + 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, + 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, + 0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF, + 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E, + 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01, + 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B, + 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00, + 0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, + 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, + 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, + 0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, + 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, + 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, + 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF, + 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05, + 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, + 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, + 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF, + 0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, + 0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, + 0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B, + 0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, + 0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, + 0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, + 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, + 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3, + 0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, + 0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, + 0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, + 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, + 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, + 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7, + 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14, + 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32, + 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00, + 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47, + 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, + 0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, + 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, + 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, + 0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, + 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, + 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, + 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21, + 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06, + 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, + 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, + 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, + 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, + 0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, + 0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, + 0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, + 0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, + 0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00, + 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, + 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, + 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C, + 0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, + 0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, + 0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, + 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, + 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12, + 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B, + 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF, + 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19, + 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00, + 0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, + 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, + 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, + 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, + 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, + 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, + 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F, + 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07, + 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD, + 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, + 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, + 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, + 0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, + 0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, + 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, + 0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, + 0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00, + 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, + 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, + 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, + 0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, + 0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, + 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, + 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, + 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, + 0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18, + 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48, + 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87, + 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE, + 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61, + 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, + 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, + 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, + 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, + 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, + 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, + 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F, + 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06, + 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, + 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, + 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, + 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, + 0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, + 0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, + 0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32, + 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, + 0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, + 0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, + 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, + 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, + 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, + 0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, + 0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, + 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, + 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, + 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, + 0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C, + 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47, + 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3, + 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01, + 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73, + 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF, + 0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, + 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, + 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, + 0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, + 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, + 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, + 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC, + 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05, + 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04, + 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, + 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, + 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, + 0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, + 0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, + 0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23, + 0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, + 0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, + 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00, + 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, + 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, + 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B, + 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, + 0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, + 0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, + 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, + 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, + 0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5, + 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43, + 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE, + 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01, + 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1, + 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF, + 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, + 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, + 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, + 0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, + 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, + 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07, + 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04, + 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, + 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, + 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00, + 0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, + 0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, + 0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, + 0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, + 0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, + 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00, + 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, + 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, + 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57, + 0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, + 0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, + 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, + 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, + 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, + 0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, + 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D, + 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03, + 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, + 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02, + 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF, + 0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, + 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, + 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, + 0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, + 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, + 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, + 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, + 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, + 0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06, + 0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16, + 0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF, + 0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88, + 0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02, + 0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC, + 0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A, + 0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18, + 0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF, + 0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3, + 0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00, + 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75, + 0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10, + 0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72, + 0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF, + 0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6, + 0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF, + 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C, + 0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10, + 0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD, + 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00, + 0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35, + 0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1, + 0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E, + 0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9, + 0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02, + 0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F, + 0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF, + 0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71, + 0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05, + 0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06, + 0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6, + 0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF, + 0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B, + 0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02, + 0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89, + 0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E, + 0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1, + 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, + 0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD, + 0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00, + 0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F, + 0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10, + 0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E, + 0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF, + 0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA, + 0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF, + 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6, + 0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10, + 0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2, + 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00, + 0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3, + 0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50, + 0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E, + 0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2, + 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02, + 0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03, + 0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF, + 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB, + 0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06, + 0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00, + 0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05, + 0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73, + 0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, + 0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88, + 0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02, + 0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, + 0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E, + 0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9, + 0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF, + 0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E, + 0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00, + 0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C, + 0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10, + 0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C, + 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF, + 0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38, + 0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF, + 0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6, + 0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10, + 0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97, + 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00, + 0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D, + 0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF, + 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB, + 0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A, + 0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, + 0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02, + 0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99, + 0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF, + 0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45, + 0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06, + 0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04, + 0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4, + 0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF, + 0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00, + 0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03, + 0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, + 0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D, + 0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7, + 0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF, + 0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05, + 0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01, + 0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, + 0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10, + 0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D, + 0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF, + 0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C, + 0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF, + 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB, + 0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10, + 0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D, + 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00, + 0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3, + 0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, + 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C, + 0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B, + 0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01, + 0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F, + 0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF, + 0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF, + 0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07, + 0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6, + 0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09, + 0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D, + 0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73, + 0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03, + 0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8, + 0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D, + 0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA, + 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF, + 0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9, + 0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF, + 0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70, + 0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F, + 0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72, + 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF, + 0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6, + 0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF, + 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96, + 0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10, + 0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84, + 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF, + 0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52, + 0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, + 0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3, + 0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B, + 0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE, + 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01, + 0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5, + 0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, + 0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C, + 0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07, + 0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC, + 0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09, + 0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2, + 0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3, + 0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04, + 0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3, + 0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C, + 0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24, + 0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF, + 0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E, + 0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF, + 0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75, + 0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F, + 0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A, + 0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF, + 0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15, + 0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF, + 0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0, + 0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F, + 0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7, + 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF, + 0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC, + 0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF, + 0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F, + 0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C, + 0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC, + 0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04, + 0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E, + 0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, + 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B, + 0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08, + 0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2, + 0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08, + 0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E, + 0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50, + 0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04, + 0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE, + 0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C, + 0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53, + 0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF, + 0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88, + 0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF, + 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C, + 0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F, + 0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86, + 0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF, + 0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A, + 0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF, + 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF, + 0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F, + 0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF, + 0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E, + 0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF, + 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2, + 0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C, + 0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9, + 0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04, + 0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6, + 0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD, + 0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09, + 0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7, + 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07, + 0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91, + 0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, + 0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82, + 0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01, + 0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8, + 0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B, + 0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A, + 0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, + 0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65, + 0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF, + 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90, + 0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10, + 0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81, + 0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF, + 0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73, + 0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF, + 0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74, + 0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F, + 0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4, + 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF, + 0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8, + 0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF, + 0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B, + 0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D, + 0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5, + 0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03, + 0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32, + 0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, + 0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72, + 0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09, + 0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA, + 0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07, + 0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA, + 0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF, + 0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14, + 0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01, + 0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, + 0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B, + 0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6, + 0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, + 0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37, + 0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00, + 0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81, + 0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10, + 0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79, + 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF, + 0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91, + 0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF, + 0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41, + 0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10, + 0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA, + 0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01, + 0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88, + 0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF, + 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B, + 0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D, + 0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, + 0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03, + 0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1, + 0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF, + 0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22, + 0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04, + 0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD, + 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06, + 0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A, + 0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF, + 0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F, + 0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02, + 0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, + 0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A, + 0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A, + 0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF, + 0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF, + 0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00, + 0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77, + 0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10, + 0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73, + 0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF, + 0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4, + 0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF, + 0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14, + 0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10, + 0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF, + 0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00, + 0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A, + 0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1, + 0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E, + 0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA, + 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02, + 0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53, + 0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF, + 0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D, + 0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05, + 0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06, + 0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF, + 0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF, + 0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23, + 0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02, + 0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87, + 0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E, + 0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D, + 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, + 0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB, + 0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00, + 0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70, + 0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10, + 0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E, + 0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF, + 0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB, + 0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF, + 0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC, + 0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10, + 0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4, + 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00, + 0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9, + 0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF, + 0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F, + 0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E, + 0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3, + 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02, + 0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7, + 0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF, + 0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7, + 0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06, + 0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05, + 0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B, + 0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF, + 0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1, + 0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02, + 0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91, + 0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E, + 0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5, + 0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, + 0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D, + 0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00, + 0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D, + 0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10, + 0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C, + 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, + 0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27, + 0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF, + 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB, + 0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10, + 0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99, + 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00, + 0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34, + 0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF, + 0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3, + 0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A, + 0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00, + 0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02, + 0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D, + 0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61, + 0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06, + 0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00, + 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05, + 0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC, + 0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, + 0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A, + 0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03, + 0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, + 0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D, + 0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1, + 0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, + 0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16, + 0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00, + 0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C, + 0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10, + 0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D, + 0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, + 0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D, + 0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF, + 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0, + 0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10, + 0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F, + 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00, + 0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA, + 0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, + 0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43, + 0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A, + 0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01, + 0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13, + 0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, + 0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB, + 0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07, + 0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4, + 0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09, + 0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E, + 0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, + 0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E, + 0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03, + 0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6, + 0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D, + 0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3, + 0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF, + 0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA, + 0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF, + 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F, + 0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F, + 0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71, + 0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF, + 0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47, + 0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE, + 0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30, + 0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20, + 0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42, + 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF, + 0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC, + 0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00, + 0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F, + 0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09, + 0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB, + 0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2, + 0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73, + 0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03, + 0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE, + 0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC, + 0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49, + 0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, + 0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65, + 0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB, + 0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36, + 0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E, + 0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02, + 0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00, + 0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0, + 0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00, + 0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1, + 0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21, + 0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D, + 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00, + 0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62, + 0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00, + 0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52, + 0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20, + 0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D, + 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF, + 0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2, + 0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78, + 0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A, + 0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD, + 0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB, + 0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4, + 0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58, + 0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04, + 0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB, + 0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F, + 0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56, + 0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB, + 0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D, + 0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D, + 0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9, + 0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00, + 0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A, + 0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00, + 0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB, + 0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21, + 0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00, + 0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F, + 0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00, + 0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71, + 0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20, + 0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58, + 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00, + 0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9, + 0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00, + 0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96, + 0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B, + 0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC, + 0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB, + 0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10, + 0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF, + 0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37, + 0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB, + 0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB, + 0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D, + 0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, + 0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C, + 0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB, + 0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24, + 0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C, + 0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD, + 0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00, + 0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24, + 0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00, + 0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3, + 0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21, + 0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6, + 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00, + 0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A, + 0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00, + 0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C, + 0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21, + 0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64, + 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00, + 0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02, + 0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00, + 0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB, + 0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C, + 0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB, + 0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB, + 0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56, + 0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF, + 0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69, + 0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB, + 0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB, + 0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44, + 0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, + 0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49, + 0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB, + 0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, + 0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B, + 0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E, + 0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00, + 0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD, + 0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00, + 0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8, + 0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20, + 0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2, + 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00, + 0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF, + 0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00, + 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4, + 0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21, + 0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70, + 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00, + 0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A, + 0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00, + 0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4, + 0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D, + 0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA, + 0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB, + 0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8, + 0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF, + 0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4, + 0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB, + 0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB, + 0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53, + 0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B, + 0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB, + 0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16, + 0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A, + 0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D, + 0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00, + 0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26, + 0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF, + 0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB, + 0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20, + 0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD, + 0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00, + 0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5, + 0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9, + 0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21, + 0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D, + 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00, + 0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32, + 0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00, + 0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12, + 0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E, + 0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9, + 0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB, + 0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05, + 0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF, + 0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA, + 0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC, + 0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, + 0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03, + 0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2, + 0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, + 0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54, + 0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB, + 0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, + 0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09, + 0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A, + 0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00, + 0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90, + 0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF, + 0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC, + 0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20, + 0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7, + 0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00, + 0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4, + 0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00, + 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB, + 0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21, + 0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A, + 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00, + 0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47, + 0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00, + 0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43, + 0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F, + 0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8, + 0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB, + 0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E, + 0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF, + 0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B, + 0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC, + 0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8, + 0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02, + 0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65, + 0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66, + 0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB, + 0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A, + 0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08, + 0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4, + 0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00, + 0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB, + 0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF, + 0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA, + 0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F, + 0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1, + 0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00, + 0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8, + 0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00, + 0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5, + 0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17, + 0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB, + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00, + 0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59, + 0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00, + 0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77, + 0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10, + 0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7, + 0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB, + 0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2, + 0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF, + 0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98, + 0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC, + 0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7, + 0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01, + 0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29, + 0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80, + 0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB, + 0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05, + 0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07, + 0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC, + 0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00, + 0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38, + 0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF, + 0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6, + 0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F, + 0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9, + 0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00, + 0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91, + 0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00, + 0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30, + 0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18, + 0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00, + 0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66, + 0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00, + 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE, + 0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11, + 0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7, + 0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC, + 0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62, + 0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF, + 0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF, + 0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC, + 0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7, + 0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01, + 0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF, + 0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, + 0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F, + 0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD, + 0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, + 0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06, + 0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72, + 0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00, + 0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78, + 0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF, + 0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1, + 0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E, + 0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0, + 0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00, + 0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F, + 0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00, + 0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B, + 0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19, + 0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00, + 0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E, + 0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00, + 0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7, + 0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13, + 0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7, + 0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC, + 0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED, + 0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF, + 0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73, + 0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD, + 0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7, + 0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00, + 0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5, + 0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, + 0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87, + 0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD, + 0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05, + 0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38, + 0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, + 0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC, + 0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE, + 0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA, + 0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E, + 0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6, + 0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00, + 0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21, + 0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00, + 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5, + 0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A, + 0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05, + 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD, + 0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63, + 0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22, + 0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14, + 0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7, + 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC, + 0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84, + 0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF, + 0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1, + 0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD, + 0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7, + 0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF, + 0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E, + 0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, + 0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B, + 0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD, + 0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC, + 0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04, + 0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC, + 0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00, + 0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3, + 0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE, + 0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2, + 0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D, + 0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA, + 0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00, + 0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6, + 0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00, + 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD, + 0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B, + 0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09, + 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE, + 0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48, + 0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E, + 0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15, + 0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8, + 0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC, + 0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26, + 0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF, + 0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C, + 0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE, + 0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8, + 0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF, + 0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49, + 0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, + 0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69, + 0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC, + 0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B, + 0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16, + 0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD, + 0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, + 0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0, + 0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE, + 0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9, + 0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C, + 0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB, + 0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00, + 0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C, + 0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00, + 0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13, + 0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C, + 0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F, + 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE, + 0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36, + 0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A, + 0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16, + 0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9, + 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC, + 0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4, + 0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF, + 0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12, + 0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE, + 0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9, + 0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE, + 0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18, + 0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, + 0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54, + 0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC, + 0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E, + 0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15, + 0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC, + 0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04, + 0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE, + 0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE, + 0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B, + 0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB, + 0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00, + 0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14, + 0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00, + 0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47, + 0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C, + 0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15, + 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE, + 0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C, + 0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00, + 0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56, + 0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04, + 0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD, + 0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C, + 0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF, + 0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6, + 0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8, + 0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE, + 0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42, + 0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5, + 0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03, + 0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC, + 0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3, + 0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF, + 0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF, + 0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3, + 0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE, + 0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68, + 0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A, + 0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1, + 0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00, + 0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8, + 0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00, + 0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB, + 0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27, + 0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3, + 0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE, + 0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E, + 0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF, + 0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4, + 0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4, + 0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01, + 0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03, + 0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F, + 0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62, + 0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05, + 0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05, + 0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB, + 0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54, + 0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03, + 0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1, + 0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3, + 0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7, + 0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF, + 0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB, + 0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE, + 0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55, + 0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28, + 0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4, + 0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00, + 0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8, + 0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00, + 0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8, + 0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29, + 0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD, + 0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE, + 0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02, + 0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A, + 0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3, + 0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01, + 0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03, + 0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28, + 0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00, + 0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16, + 0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05, + 0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05, + 0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00, + 0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE, + 0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04, + 0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5, + 0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4, + 0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2, + 0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF, + 0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F, + 0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE, + 0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43, + 0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26, + 0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6, + 0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00, + 0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98, + 0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00, + 0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25, + 0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B, + 0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7, + 0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE, + 0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8, + 0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF, + 0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F, + 0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3, + 0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03, + 0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39, + 0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC, + 0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05, + 0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05, + 0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40, + 0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71, + 0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04, + 0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9, + 0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4, + 0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E, + 0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF, + 0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70, + 0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE, + 0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31, + 0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24, + 0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7, + 0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00, + 0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68, + 0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00, + 0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51, + 0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D, + 0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1, + 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE, + 0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0, + 0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF, + 0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4, + 0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3, + 0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02, + 0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02, + 0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41, + 0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00, + 0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84, + 0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06, + 0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9, + 0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8, + 0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D, + 0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04, + 0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD, + 0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5, + 0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D, + 0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF, + 0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42, + 0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD, + 0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20, + 0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22, + 0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07, + 0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00, + 0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24, + 0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00, + 0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C, + 0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F, + 0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB, + 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE, + 0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC, + 0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF, + 0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79, + 0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3, + 0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02, + 0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40, + 0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C, + 0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06, + 0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E, + 0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9, + 0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2, + 0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0, + 0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04, + 0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5, + 0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F, + 0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF, + 0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16, + 0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD, + 0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10, + 0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20, + 0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17, + 0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00, + 0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD, + 0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00, + 0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7, + 0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31, + 0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5, + 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF, + 0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C, + 0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF, + 0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F, + 0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2, + 0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03, + 0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02, + 0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35, + 0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00, + 0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3, + 0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06, + 0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F, + 0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA, + 0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2, + 0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A, + 0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05, + 0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, + 0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6, + 0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34, + 0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF, + 0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED, + 0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD, + 0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00, + 0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D, + 0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26, + 0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00, + 0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F, + 0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, + 0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1, + 0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32, + 0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0, + 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF, + 0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41, + 0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF, + 0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6, + 0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2, + 0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04, + 0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01, + 0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20, + 0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00, + 0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8, + 0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06, + 0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB, + 0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5, + 0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, + 0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1, + 0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC, + 0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6, + 0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D, + 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF, + 0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8, + 0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD, + 0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2, + 0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B, + 0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33, + 0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00, + 0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB, + 0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01, + 0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9, + 0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34, + 0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA, + 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF, + 0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B, + 0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF, + 0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F, + 0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2, + 0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05, + 0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01, + 0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02, + 0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00, + 0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A, + 0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06, + 0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10, + 0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC, + 0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE, + 0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, + 0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2, + 0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC, + 0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7, + 0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09, + 0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB, + 0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD, + 0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4, + 0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19, + 0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F, + 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00, + 0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C, + 0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01, + 0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20, + 0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36, + 0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5, + 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF, + 0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB, + 0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF, + 0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A, + 0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3, + 0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06, + 0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01, + 0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9, + 0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00, + 0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09, + 0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06, + 0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC, + 0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC, + 0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00, + 0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34, + 0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC, + 0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8, + 0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8, + 0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, + 0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95, + 0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD, + 0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7, + 0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17, + 0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00, + 0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87, + 0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01, + 0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45, + 0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37, + 0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0, + 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF, + 0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61, + 0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF, + 0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8, + 0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3, + 0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06, + 0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00, + 0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7, + 0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3, + 0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06, + 0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11, + 0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD, + 0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F, + 0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87, + 0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC, + 0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96, + 0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08, + 0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D, + 0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89, + 0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD, + 0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB, + 0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15, + 0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53, + 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF, + 0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5, + 0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01, + 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69, + 0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39, + 0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B, + 0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF, + 0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF, + 0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF, + 0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79, + 0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3, + 0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07, + 0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00, + 0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A, + 0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00, + 0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58, + 0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06, + 0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10, + 0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE, + 0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27, + 0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00, + 0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE, + 0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC, + 0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92, + 0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06, + 0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56, + 0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88, + 0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD, + 0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13, + 0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF, + 0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6, + 0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01, + 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A, + 0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A, + 0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97, + 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00, + 0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93, + 0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF, + 0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D, + 0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3, + 0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08, + 0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00, + 0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23, + 0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00, + 0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7, + 0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05, + 0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10, + 0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE, + 0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45, + 0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00, + 0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69, + 0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC, + 0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F, + 0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04, + 0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D, + 0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, + 0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94, + 0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC, + 0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6, + 0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11, + 0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F, + 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF, + 0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB, + 0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01, + 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9, + 0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C, + 0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93, + 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00, + 0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F, + 0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF, + 0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4, + 0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4, + 0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09, + 0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF, + 0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1, + 0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00, + 0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90, + 0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05, + 0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F, + 0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF, + 0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67, + 0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00, + 0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8, + 0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD, + 0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C, + 0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02, + 0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41, + 0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF, + 0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A, + 0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01, + 0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD, + 0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F, + 0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63, + 0x01, 0x8D, 0xFF, 0x0F, 0x00 +}; + +static u16 +coefficient_sizes[8 * 2] = { + /* Playback */ + 0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000, + /* capture */ + 0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000, +}; + diff -Nru linux/sound/pci/rme96.c linux-2.4.19-pre5-mjc/sound/pci/rme96.c --- linux/sound/pci/rme96.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/rme96.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2523 @@ +/* + * ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio + * interfaces + * + * Copyright (c) 2000, 2001 Anders Torger + * + * Thanks to Henk Hesselink for the analog volume control + * code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +/* note, two last pcis should be equal, it is not a bug */ +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Anders Torger "); +MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, " + "Digi96/8 PAD"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Digi96}," + "{RME,Digi96/8}," + "{RME,Digi96/8 PRO}," + "{RME,Digi96/8 PST}," + "{RME,Digi96/8 PAD}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable RME Digi96 soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); + +/* + * Defines for RME Digi96 series, from internal RME reference documents + * dated 12.01.00 + */ + +#define RME96_SPDIF_NCHANNELS 2 + +/* Playback and capture buffer size */ +#define RME96_BUFFER_SIZE 0x10000 + +/* IO area size */ +#define RME96_IO_SIZE 0x60000 + +/* IO area offsets */ +#define RME96_IO_PLAY_BUFFER 0x0 +#define RME96_IO_REC_BUFFER 0x10000 +#define RME96_IO_CONTROL_REGISTER 0x20000 +#define RME96_IO_ADDITIONAL_REG 0x20004 +#define RME96_IO_CONFIRM_PLAY_IRQ 0x20008 +#define RME96_IO_CONFIRM_REC_IRQ 0x2000C +#define RME96_IO_SET_PLAY_POS 0x40000 +#define RME96_IO_RESET_PLAY_POS 0x4FFFC +#define RME96_IO_SET_REC_POS 0x50000 +#define RME96_IO_RESET_REC_POS 0x5FFFC +#define RME96_IO_GET_PLAY_POS 0x20000 +#define RME96_IO_GET_REC_POS 0x30000 + +/* Write control register bits */ +#define RME96_WCR_START (1 << 0) +#define RME96_WCR_START_2 (1 << 1) +#define RME96_WCR_GAIN_0 (1 << 2) +#define RME96_WCR_GAIN_1 (1 << 3) +#define RME96_WCR_MODE24 (1 << 4) +#define RME96_WCR_MODE24_2 (1 << 5) +#define RME96_WCR_BM (1 << 6) +#define RME96_WCR_BM_2 (1 << 7) +#define RME96_WCR_ADAT (1 << 8) +#define RME96_WCR_FREQ_0 (1 << 9) +#define RME96_WCR_FREQ_1 (1 << 10) +#define RME96_WCR_DS (1 << 11) +#define RME96_WCR_PRO (1 << 12) +#define RME96_WCR_EMP (1 << 13) +#define RME96_WCR_SEL (1 << 14) +#define RME96_WCR_MASTER (1 << 15) +#define RME96_WCR_PD (1 << 16) +#define RME96_WCR_INP_0 (1 << 17) +#define RME96_WCR_INP_1 (1 << 18) +#define RME96_WCR_THRU_0 (1 << 19) +#define RME96_WCR_THRU_1 (1 << 20) +#define RME96_WCR_THRU_2 (1 << 21) +#define RME96_WCR_THRU_3 (1 << 22) +#define RME96_WCR_THRU_4 (1 << 23) +#define RME96_WCR_THRU_5 (1 << 24) +#define RME96_WCR_THRU_6 (1 << 25) +#define RME96_WCR_THRU_7 (1 << 26) +#define RME96_WCR_DOLBY (1 << 27) +#define RME96_WCR_MONITOR_0 (1 << 28) +#define RME96_WCR_MONITOR_1 (1 << 29) +#define RME96_WCR_ISEL (1 << 30) +#define RME96_WCR_IDIS (1 << 31) + +#define RME96_WCR_BITPOS_GAIN_0 2 +#define RME96_WCR_BITPOS_GAIN_1 3 +#define RME96_WCR_BITPOS_FREQ_0 9 +#define RME96_WCR_BITPOS_FREQ_1 10 +#define RME96_WCR_BITPOS_INP_0 17 +#define RME96_WCR_BITPOS_INP_1 18 +#define RME96_WCR_BITPOS_MONITOR_0 28 +#define RME96_WCR_BITPOS_MONITOR_1 29 + +/* Read control register bits */ +#define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF +#define RME96_RCR_IRQ_2 (1 << 16) +#define RME96_RCR_T_OUT (1 << 17) +#define RME96_RCR_DEV_ID_0 (1 << 21) +#define RME96_RCR_DEV_ID_1 (1 << 22) +#define RME96_RCR_LOCK (1 << 23) +#define RME96_RCR_VERF (1 << 26) +#define RME96_RCR_F0 (1 << 27) +#define RME96_RCR_F1 (1 << 28) +#define RME96_RCR_F2 (1 << 29) +#define RME96_RCR_AUTOSYNC (1 << 30) +#define RME96_RCR_IRQ (1 << 31) + +#define RME96_RCR_BITPOS_F0 27 +#define RME96_RCR_BITPOS_F1 28 +#define RME96_RCR_BITPOS_F2 29 + +/* Additonal register bits */ +#define RME96_AR_WSEL (1 << 0) +#define RME96_AR_ANALOG (1 << 1) +#define RME96_AR_FREQPAD_0 (1 << 2) +#define RME96_AR_FREQPAD_1 (1 << 3) +#define RME96_AR_FREQPAD_2 (1 << 4) +#define RME96_AR_PD2 (1 << 5) +#define RME96_AR_DAC_EN (1 << 6) +#define RME96_AR_CLATCH (1 << 7) +#define RME96_AR_CCLK (1 << 8) +#define RME96_AR_CDATA (1 << 9) + +#define RME96_AR_BITPOS_F0 2 +#define RME96_AR_BITPOS_F1 3 +#define RME96_AR_BITPOS_F2 4 + +/* Monitor tracks */ +#define RME96_MONITOR_TRACKS_1_2 0 +#define RME96_MONITOR_TRACKS_3_4 1 +#define RME96_MONITOR_TRACKS_5_6 2 +#define RME96_MONITOR_TRACKS_7_8 3 + +/* Attenuation */ +#define RME96_ATTENUATION_0 0 +#define RME96_ATTENUATION_6 1 +#define RME96_ATTENUATION_12 2 +#define RME96_ATTENUATION_18 3 + +/* Input types */ +#define RME96_INPUT_OPTICAL 0 +#define RME96_INPUT_COAXIAL 1 +#define RME96_INPUT_INTERNAL 2 +#define RME96_INPUT_XLR 3 +#define RME96_INPUT_ANALOG 4 + +/* Clock modes */ +#define RME96_CLOCKMODE_SLAVE 0 +#define RME96_CLOCKMODE_MASTER 1 +#define RME96_CLOCKMODE_WORDCLOCK 2 + +/* Block sizes in bytes */ +#define RME96_SMALL_BLOCK_SIZE 2048 +#define RME96_LARGE_BLOCK_SIZE 8192 + +/* Volume control */ +#define RME96_AD1852_VOL_BITS 14 +#define RME96_AD1855_VOL_BITS 10 + +/* + * PCI vendor/device ids, could in the future be defined in , + * therefore #ifndef is used. + */ +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_DIGI96 +#define PCI_DEVICE_ID_DIGI96 0x3fc0 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8 +#define PCI_DEVICE_ID_DIGI96_8 0x3fc1 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8_PRO +#define PCI_DEVICE_ID_DIGI96_8_PRO 0x3fc2 +#endif +#ifndef PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST +#define PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST 0x3fc3 +#endif + +typedef struct snd_rme96 { + spinlock_t lock; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + + u32 wcreg; /* cached write control register value */ + u32 wcreg_spdif; /* S/PDIF setup */ + u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */ + u32 rcreg; /* cached read control register value */ + u32 areg; /* cached additional register value */ + u16 vol[2]; /* cached volume of analog output */ + + u8 rev; /* card revision number */ + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + + int playback_frlog; /* log2 of framesize */ + int capture_frlog; + + size_t playback_periodsize; /* in bytes, zero if not used */ + size_t capture_periodsize; /* in bytes, zero if not used */ + + snd_pcm_uframes_t playback_last_appl_ptr; + size_t playback_ptr; + size_t capture_ptr; + + snd_card_t *card; + snd_pcm_t *spdif_pcm; + snd_pcm_t *adat_pcm; + struct pci_dev *pci; + snd_info_entry_t *proc_entry; + snd_kcontrol_t *spdif_ctl; +} rme96_t; + +static struct pci_device_id snd_rme96_ids[] __devinitdata = { + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PRO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_rme96_ids); + +#define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START) +#define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2) +#define RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) +#define RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO || \ + (rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) +#define RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4) +#define RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \ + ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO && (rme96)->rev == 2)) +#define RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1) + +static int +snd_rme96_playback_prepare(snd_pcm_substream_t *substream); + +static int +snd_rme96_capture_prepare(snd_pcm_substream_t *substream); + +static int +snd_rme96_playback_trigger(snd_pcm_substream_t *substream, + int cmd); + +static int +snd_rme96_capture_trigger(snd_pcm_substream_t *substream, + int cmd); + +static snd_pcm_uframes_t +snd_rme96_playback_pointer(snd_pcm_substream_t *substream); + +static snd_pcm_uframes_t +snd_rme96_capture_pointer(snd_pcm_substream_t *substream); + +static void __init +snd_rme96_proc_init(rme96_t *rme96); + +static void +snd_rme96_proc_done(rme96_t *rme96); + +static int +snd_rme96_create_switches(snd_card_t *card, + rme96_t *rme96); + +static inline unsigned int +snd_rme96_playback_ptr(rme96_t *rme96) +{ + return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS) + & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog; +} + +static inline unsigned int +snd_rme96_capture_ptr(rme96_t *rme96) +{ + return (readl(rme96->iobase + RME96_IO_GET_REC_POS) + & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog; +} + +static int +snd_rme96_playback_silence(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->playback_frlog; + pos <<= rme96->playback_frlog; + memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + 0, count); + return 0; +} + +static int +snd_rme96_playback_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *src, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->playback_frlog; + pos <<= rme96->playback_frlog; + copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, + count); + return 0; +} + +static int +snd_rme96_capture_copy(snd_pcm_substream_t *substream, + int channel, /* not used (interleaved data) */ + snd_pcm_uframes_t pos, + void *dst, + snd_pcm_uframes_t count) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + count <<= rme96->capture_frlog; + pos <<= rme96->capture_frlog; + copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, + count); + return 0; +} + +/* + * Digital output capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme96_playback_spdif_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + rates: (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 32000, + rate_max: 96000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * Digital input capabilites (S/PDIF) + */ +static snd_pcm_hardware_t snd_rme96_capture_spdif_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE), + rates: (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 32000, + rate_max: 96000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * Digital output capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme96_playback_adat_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: 8, + channels_max: 8, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * Digital input capabilites (ADAT) + */ +static snd_pcm_hardware_t snd_rme96_capture_adat_info = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 44100, + rate_max: 48000, + channels_min: 8, + channels_max: 8, + buffer_bytes_max: RME96_BUFFER_SIZE, + period_bytes_min: RME96_SMALL_BLOCK_SIZE, + period_bytes_max: RME96_LARGE_BLOCK_SIZE, + periods_min: RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, + periods_max: RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, + fifo_size: 0, +}; + +/* + * The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface + * of the AD1852 or AD1852 D/A converter on the board. CDATA must be set up + * on the falling edge of CCLK and be stable on the rising edge. The rising + * edge of CLATCH after the last data bit clocks in the whole data word. + * A fast processor could probably drive the SPI interface faster than the + * DAC can handle (3MHz for the 1855, unknown for the 1852). The udelay(1) + * limits the data rate to 500KHz and only causes a delay of 33 microsecs. + * + * NOTE: increased delay from 1 to 10, since there where problems setting + * the volume. + */ +static void +snd_rme96_write_SPI(rme96_t *rme96, u16 val) +{ + int i; + + for (i = 0; i < 16; i++) { + if (val & 0x8000) { + rme96->areg |= RME96_AR_CDATA; + } else { + rme96->areg &= ~RME96_AR_CDATA; + } + rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + rme96->areg |= RME96_AR_CCLK; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + val <<= 1; + } + rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA); + rme96->areg |= RME96_AR_CLATCH; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + udelay(10); + rme96->areg &= ~RME96_AR_CLATCH; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); +} + +static void +snd_rme96_apply_dac_volume(rme96_t *rme96) +{ + if (RME96_DAC_IS_1852(rme96)) { + snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0); + snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2); + } else if (RME96_DAC_IS_1855(rme96)) { + snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000); + snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400); + } +} + +static void +snd_rme96_reset_dac(rme96_t *rme96) +{ + writel(rme96->wcreg | RME96_WCR_PD, + rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static int +snd_rme96_getmontracks(rme96_t *rme96) +{ + return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1); +} + +static int +snd_rme96_setmontracks(rme96_t *rme96, + int montracks) +{ + if (montracks & 1) { + rme96->wcreg |= RME96_WCR_MONITOR_0; + } else { + rme96->wcreg &= ~RME96_WCR_MONITOR_0; + } + if (montracks & 2) { + rme96->wcreg |= RME96_WCR_MONITOR_1; + } else { + rme96->wcreg &= ~RME96_WCR_MONITOR_1; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_getattenuation(rme96_t *rme96) +{ + return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1); +} + +static int +snd_rme96_setattenuation(rme96_t *rme96, + int attenuation) +{ + switch (attenuation) { + case 0: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) & + ~RME96_WCR_GAIN_1; + break; + case 1: + rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) & + ~RME96_WCR_GAIN_1; + break; + case 2: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) | + RME96_WCR_GAIN_1; + break; + case 3: + rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) | + RME96_WCR_GAIN_1; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_capture_getrate(rme96_t *rme96, + int *is_adat) +{ + int n, rate; + + *is_adat = 0; + if (rme96->areg & RME96_AR_ANALOG) { + /* Analog input, overrides S/PDIF setting */ + n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) + + (((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1); + switch (n) { + case 1: + rate = 32000; + break; + case 2: + rate = 44100; + break; + case 3: + rate = 48000; + break; + default: + return -1; + } + return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate; + } + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_LOCK) { + /* ADAT rate */ + *is_adat = 1; + if (rme96->rcreg & RME96_RCR_T_OUT) { + return 48000; + } + return 44100; + } + + if (rme96->rcreg & RME96_RCR_VERF) { + return -1; + } + + /* S/PDIF rate */ + n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) + + (((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) + + (((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2); + + switch (n) { + case 0: + if (rme96->rcreg & RME96_RCR_T_OUT) { + return 64000; + } + return -1; + case 3: return 96000; + case 4: return 88200; + case 5: return 48000; + case 6: return 44100; + case 7: return 32000; + default: + break; + } + return -1; +} + +static int +snd_rme96_playback_getrate(rme96_t *rme96) +{ + int rate, dummy; + + if (!(rme96->wcreg & RME96_WCR_MASTER) && + (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) + { + /* slave clock */ + return rate; + } + rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1); + switch (rate) { + case 1: + rate = 32000; + break; + case 2: + rate = 44100; + break; + case 3: + rate = 48000; + break; + default: + return -1; + } + return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate; +} + +static int +snd_rme96_playback_setrate(rme96_t *rme96, + int rate) +{ + int ds; + + ds = rme96->wcreg & RME96_WCR_DS; + switch (rate) { + case 32000: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & + ~RME96_WCR_FREQ_1; + break; + case 44100: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & + ~RME96_WCR_FREQ_0; + break; + case 48000: + rme96->wcreg &= ~RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | + RME96_WCR_FREQ_1; + break; + case 64000: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & + ~RME96_WCR_FREQ_1; + break; + case 88200: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & + ~RME96_WCR_FREQ_0; + break; + case 96000: + rme96->wcreg |= RME96_WCR_DS; + rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | + RME96_WCR_FREQ_1; + break; + default: + return -EINVAL; + } + if ((!ds && rme96->wcreg & RME96_WCR_DS) || + (ds && !(rme96->wcreg & RME96_WCR_DS))) + { + /* change to/from double-speed: reset the DAC (if available) */ + snd_rme96_reset_dac(rme96); + } else { + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + } + return 0; +} + +static int +snd_rme96_capture_analog_setrate(rme96_t *rme96, + int rate) +{ + switch (rate) { + case 32000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & + ~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 44100: + rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 48000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; + break; + case 64000: + if (rme96->rev < 4) { + return -EINVAL; + } + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & + ~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + case 88200: + if (rme96->rev < 4) { + return -EINVAL; + } + rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + case 96000: + rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | + RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; + break; + default: + return -EINVAL; + } + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + return 0; +} + +static int +snd_rme96_setclockmode(rme96_t *rme96, + int mode) +{ + switch (mode) { + case RME96_CLOCKMODE_SLAVE: + rme96->wcreg &= ~RME96_WCR_MASTER; + rme96->areg &= ~RME96_AR_WSEL; + break; + case RME96_CLOCKMODE_MASTER: + rme96->wcreg |= RME96_WCR_MASTER; + rme96->areg &= ~RME96_AR_WSEL; + break; + case RME96_CLOCKMODE_WORDCLOCK: + /* Word clock is a master mode */ + rme96->wcreg |= RME96_WCR_MASTER; + rme96->areg |= RME96_AR_WSEL; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + return 0; +} + +static int +snd_rme96_getclockmode(rme96_t *rme96) +{ + if (rme96->areg & RME96_AR_WSEL) { + return RME96_CLOCKMODE_WORDCLOCK; + } + return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER : + RME96_CLOCKMODE_SLAVE; +} + +static int +snd_rme96_setinputtype(rme96_t *rme96, + int type) +{ + int n; + + switch (type) { + case RME96_INPUT_OPTICAL: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) & + ~RME96_WCR_INP_1; + break; + case RME96_INPUT_COAXIAL: + rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) & + ~RME96_WCR_INP_1; + break; + case RME96_INPUT_INTERNAL: + rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) | + RME96_WCR_INP_1; + break; + case RME96_INPUT_XLR: + if ((rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && + rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PRO) || + (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && + rme96->rev > 4)) + { + /* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */ + return -EINVAL; + } + rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) | + RME96_WCR_INP_1; + break; + case RME96_INPUT_ANALOG: + if (!RME96_HAS_ANALOG_IN(rme96)) { + return -EINVAL; + } + rme96->areg |= RME96_AR_ANALOG; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + if (rme96->rev < 4) { + /* + * Revision less than 004 does not support 64 and + * 88.2 kHz + */ + if (snd_rme96_capture_getrate(rme96, &n) == 88200) { + snd_rme96_capture_analog_setrate(rme96, 44100); + } + if (snd_rme96_capture_getrate(rme96, &n) == 64000) { + snd_rme96_capture_analog_setrate(rme96, 32000); + } + } + return 0; + default: + return -EINVAL; + } + if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) { + rme96->areg &= ~RME96_AR_ANALOG; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_getinputtype(rme96_t *rme96) +{ + if (rme96->areg & RME96_AR_ANALOG) { + return RME96_INPUT_ANALOG; + } + return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) + + (((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1); +} + +static void +snd_rme96_setframelog(rme96_t *rme96, + int n_channels, + int is_playback) +{ + int frlog; + + if (n_channels == 2) { + frlog = 1; + } else { + /* assume 8 channels */ + frlog = 3; + } + if (is_playback) { + frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1; + rme96->playback_frlog = frlog; + } else { + frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1; + rme96->capture_frlog = frlog; + } +} + +static int +snd_rme96_playback_setformat(rme96_t *rme96, + int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + rme96->wcreg &= ~RME96_WCR_MODE24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rme96->wcreg |= RME96_WCR_MODE24; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static int +snd_rme96_capture_setformat(rme96_t *rme96, + int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + rme96->wcreg &= ~RME96_WCR_MODE24_2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rme96->wcreg |= RME96_WCR_MODE24_2; + break; + default: + return -EINVAL; + } + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + return 0; +} + +static void +snd_rme96_set_period_properties(rme96_t *rme96, + size_t period_bytes) +{ + switch (period_bytes) { + case RME96_LARGE_BLOCK_SIZE: + rme96->wcreg &= ~RME96_WCR_ISEL; + break; + case RME96_SMALL_BLOCK_SIZE: + rme96->wcreg |= RME96_WCR_ISEL; + break; + default: + snd_BUG(); + break; + } + rme96->wcreg &= ~RME96_WCR_IDIS; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static int +snd_rme96_playback_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) + return err; + spin_lock_irqsave(&rme96->lock, flags); + if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + snd_rme96_setframelog(rme96, params_channels(params), 1); + if (rme96->capture_periodsize != 0) { + if (params_period_size(params) << rme96->playback_frlog != + rme96->capture_periodsize) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + } + rme96->playback_periodsize = + params_period_size(params) << rme96->playback_frlog; + snd_rme96_set_period_properties(rme96, rme96->playback_periodsize); + /* S/PDIF setup */ + if ((rme96->wcreg & RME96_WCR_ADAT) == 0) { + rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); + writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER); + } + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_playback_hw_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int +snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int err, isadat; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) + return err; + spin_lock_irqsave(&rme96->lock, flags); + if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { + if ((err = snd_rme96_capture_analog_setrate(rme96, + params_rate(params))) < 0) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return err; + } + } else if (params_rate(params) != snd_rme96_capture_getrate(rme96, &isadat)) { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + snd_rme96_setframelog(rme96, params_channels(params), 0); + if (rme96->playback_periodsize != 0) { + if (params_period_size(params) << rme96->capture_frlog != + rme96->playback_periodsize) + { + spin_unlock_irqrestore(&rme96->lock, flags); + return -EBUSY; + } + } + rme96->capture_periodsize = + params_period_size(params) << rme96->capture_frlog; + snd_rme96_set_period_properties(rme96, rme96->capture_periodsize); + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_capture_hw_free(snd_pcm_substream_t *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static void +snd_rme96_playback_start(rme96_t *rme96, + int from_pause) +{ + if (!from_pause) { + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + } + + rme96->wcreg |= RME96_WCR_START; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_capture_start(rme96_t *rme96, + int from_pause) +{ + if (!from_pause) { + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + rme96->capture_ptr = 0; + } + + rme96->wcreg |= RME96_WCR_START_2; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_playback_stop(rme96_t *rme96) +{ + /* + * Check if there is an unconfirmed IRQ, if so confirm it, or else + * the hardware will not stop generating interrupts + */ + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ) { + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + rme96->wcreg &= ~RME96_WCR_START; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_capture_stop(rme96_t *rme96) +{ + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ_2) { + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } + rme96->wcreg &= ~RME96_WCR_START_2; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); +} + +static void +snd_rme96_interrupt(int irq, + void *dev_id, + struct pt_regs *regs) +{ + rme96_t *rme96 = (rme96_t *)dev_id; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + /* fastpath out, to ease interrupt sharing */ + if (!((rme96->rcreg & RME96_RCR_IRQ) || + (rme96->rcreg & RME96_RCR_IRQ_2))) + { + return; + } + + if (rme96->rcreg & RME96_RCR_IRQ) { + /* playback */ + snd_pcm_period_elapsed(rme96->playback_substream); + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + if (rme96->rcreg & RME96_RCR_IRQ_2) { + /* capture */ + snd_pcm_period_elapsed(rme96->capture_substream); + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } +} + +static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE }; + +#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { + count: PERIOD_BYTES, + list: period_bytes, + mask: 0 +}; + +static int +snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->wcreg &= ~RME96_WCR_ADAT; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + rme96->playback_substream = substream; + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_playback_spdif_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + + rme96->wcreg_spdif_stream = rme96->wcreg_spdif; + rme96->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); + return 0; +} + +static int +snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int isadat; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { + /* no input */ + return -EIO; + } + if (isadat) { + /* ADAT input */ + return -EBUSY; + } + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->capture_substream = substream; + rme96->capture_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_capture_spdif_info; + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + + return 0; +} + +static int +snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->wcreg |= RME96_WCR_ADAT; + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + rme96->playback_substream = substream; + rme96->playback_last_appl_ptr = 0; + rme96->playback_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_playback_adat_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int +snd_rme96_capture_adat_open(snd_pcm_substream_t *substream) +{ + unsigned long flags; + int isadat; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (snd_rme96_capture_getrate(rme96, &isadat) < 0) { + /* no input */ + return -EIO; + } + if (!isadat) { + /* S/PDIF input */ + return -EBUSY; + } + snd_pcm_set_sync(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->capture_substream = substream; + rme96->capture_ptr = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + + runtime->hw = snd_rme96_capture_adat_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + return 0; +} + +static int +snd_rme96_playback_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + int spdif = 0; + + spin_lock_irqsave(&rme96->lock, flags); + rme96->playback_substream = NULL; + rme96->playback_periodsize = 0; + spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0; + spin_unlock_irqrestore(&rme96->lock, flags); + if (spdif) { + rme96->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); + } + return 0; +} + +static int +snd_rme96_capture_close(snd_pcm_substream_t *substream) +{ + unsigned long flags; + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&rme96->lock, flags); + rme96->capture_substream = NULL; + rme96->capture_periodsize = 0; + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_playback_prepare(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_capture_prepare(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} + +static int +snd_rme96_playback_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME96_ISPLAYING(rme96)) { + if (substream != rme96->playback_substream) { + return -EBUSY; + } + snd_rme96_playback_start(rme96, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME96_ISPLAYING(rme96)) { + if (substream != rme96->playback_substream) { + return -EBUSY; + } + snd_rme96_playback_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME96_ISPLAYING(rme96)) { + snd_rme96_playback_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME96_ISPLAYING(rme96)) { + snd_rme96_playback_start(rme96, 1); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int +snd_rme96_capture_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!RME96_ISRECORDING(rme96)) { + if (substream != rme96->capture_substream) { + return -EBUSY; + } + snd_rme96_capture_start(rme96, 0); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (RME96_ISRECORDING(rme96)) { + if (substream != rme96->capture_substream) { + return -EBUSY; + } + snd_rme96_capture_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (RME96_ISRECORDING(rme96)) { + snd_rme96_capture_stop(rme96); + } + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!RME96_ISRECORDING(rme96)) { + snd_rme96_capture_start(rme96, 1); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t +snd_rme96_playback_pointer(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_sframes_t diff; + size_t bytes; + + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + diff = runtime->control->appl_ptr - + rme96->playback_last_appl_ptr; + rme96->playback_last_appl_ptr = runtime->control->appl_ptr; + if (diff != 0 && + diff < -(snd_pcm_sframes_t)(runtime->boundary >> 1)) + { + diff += runtime->boundary; + } + bytes = diff << rme96->playback_frlog; + + if (bytes > RME96_BUFFER_SIZE - rme96->playback_ptr) { + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + + rme96->playback_ptr, + runtime->dma_area + rme96->playback_ptr, + RME96_BUFFER_SIZE - rme96->playback_ptr); + bytes -= RME96_BUFFER_SIZE - rme96->playback_ptr; + if (bytes > RME96_BUFFER_SIZE) { + bytes = RME96_BUFFER_SIZE; + } + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER, + runtime->dma_area, + bytes); + rme96->playback_ptr = bytes; + } else if (bytes != 0) { + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + + rme96->playback_ptr, + runtime->dma_area + rme96->playback_ptr, + bytes); + rme96->playback_ptr += bytes; + } + } + return snd_rme96_playback_ptr(rme96); +} + +static snd_pcm_uframes_t +snd_rme96_capture_pointer(snd_pcm_substream_t *substream) +{ + rme96_t *rme96 = _snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t frameptr; + size_t ptr; + + frameptr = snd_rme96_capture_ptr(rme96); + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + ptr = frameptr << rme96->capture_frlog; + if (ptr > rme96->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme96->capture_ptr, + rme96->iobase + RME96_IO_REC_BUFFER + + rme96->capture_ptr, + ptr - rme96->capture_ptr); + rme96->capture_ptr += ptr - rme96->capture_ptr; + } else if (ptr < rme96->capture_ptr) { + memcpy_fromio(runtime->dma_area + rme96->capture_ptr, + rme96->iobase + RME96_IO_REC_BUFFER + + rme96->capture_ptr, + RME96_BUFFER_SIZE - rme96->capture_ptr); + memcpy_fromio(runtime->dma_area, + rme96->iobase + RME96_IO_REC_BUFFER, + ptr); + rme96->capture_ptr = ptr; + } + } + return frameptr; +} + +static snd_pcm_ops_t snd_rme96_playback_spdif_ops = { + open: snd_rme96_playback_spdif_open, + close: snd_rme96_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_playback_hw_params, + hw_free: snd_rme96_playback_hw_free, + prepare: snd_rme96_playback_prepare, + trigger: snd_rme96_playback_trigger, + pointer: snd_rme96_playback_pointer, + copy: snd_rme96_playback_copy, + silence: snd_rme96_playback_silence, +}; + +static snd_pcm_ops_t snd_rme96_capture_spdif_ops = { + open: snd_rme96_capture_spdif_open, + close: snd_rme96_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_capture_hw_params, + hw_free: snd_rme96_capture_hw_free, + prepare: snd_rme96_capture_prepare, + trigger: snd_rme96_capture_trigger, + pointer: snd_rme96_capture_pointer, + copy: snd_rme96_capture_copy, +}; + +static snd_pcm_ops_t snd_rme96_playback_adat_ops = { + open: snd_rme96_playback_adat_open, + close: snd_rme96_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_playback_hw_params, + hw_free: snd_rme96_playback_hw_free, + prepare: snd_rme96_playback_prepare, + trigger: snd_rme96_playback_trigger, + pointer: snd_rme96_playback_pointer, + copy: snd_rme96_playback_copy, + silence: snd_rme96_playback_silence, +}; + +static snd_pcm_ops_t snd_rme96_capture_adat_ops = { + open: snd_rme96_capture_adat_open, + close: snd_rme96_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_rme96_capture_hw_params, + hw_free: snd_rme96_capture_hw_free, + prepare: snd_rme96_capture_prepare, + trigger: snd_rme96_capture_trigger, + pointer: snd_rme96_capture_pointer, + copy: snd_rme96_capture_copy, +}; + +static void +snd_rme96_free(void *private_data) +{ + rme96_t *rme96 = (rme96_t *)private_data; + + if (rme96 == NULL) { + return; + } + if (rme96->irq >= 0) { + snd_rme96_playback_stop(rme96); + snd_rme96_capture_stop(rme96); + rme96->areg &= ~RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + snd_rme96_proc_done(rme96); + free_irq(rme96->irq, (void *)rme96); + rme96->irq = -1; + } + if (rme96->iobase) { + iounmap((void *)rme96->iobase); + rme96->iobase = 0; + } + if (rme96->res_port != NULL) { + release_resource(rme96->res_port); + kfree_nocheck(rme96->res_port); + rme96->res_port = NULL; + } +} + +static void +snd_rme96_free_spdif_pcm(snd_pcm_t *pcm) +{ + rme96_t *rme96 = (rme96_t *) pcm->private_data; + rme96->spdif_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void +snd_rme96_free_adat_pcm(snd_pcm_t *pcm) +{ + rme96_t *rme96 = (rme96_t *) pcm->private_data; + rme96->adat_pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __init +snd_rme96_create(rme96_t *rme96) +{ + struct pci_dev *pci = rme96->pci; + int err; + + rme96->irq = -1; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + rme96->port = pci_resource_start(rme96->pci, 0); + + if ((rme96->res_port = request_mem_region(rme96->port, RME96_IO_SIZE, "RME96")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme96->irq = pci->irq; + + spin_lock_init(&rme96->lock); + if ((rme96->iobase = (unsigned long) ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { + snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); + return -ENOMEM; + } + + /* read the card's revision number */ + pci_read_config_byte(pci, 8, &rme96->rev); + + /* set up ALSA pcm device for S/PDIF */ + if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0, + 1, 1, &rme96->spdif_pcm)) < 0) + { + return err; + } + rme96->spdif_pcm->private_data = rme96; + rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm; + strcpy(rme96->spdif_pcm->name, "Digi96 IEC958"); + snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops); + snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops); + + rme96->spdif_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + + /* set up ALSA pcm device for ADAT */ + if (pci->device == PCI_DEVICE_ID_DIGI96) { + /* ADAT is not available on the base model */ + rme96->adat_pcm = NULL; + } else { + if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1, + 1, 1, &rme96->adat_pcm)) < 0) + { + return err; + } + rme96->adat_pcm->private_data = rme96; + rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm; + strcpy(rme96->adat_pcm->name, "Digi96 ADAT"); + snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops); + snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); + + rme96->adat_pcm->info_flags = 0; + + snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + } + + rme96->playback_periodsize = 0; + rme96->capture_periodsize = 0; + + /* make sure playback/capture is stopped, if by some reason active */ + snd_rme96_playback_stop(rme96); + snd_rme96_capture_stop(rme96); + + /* set default values in registers */ + rme96->wcreg = + RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */ + RME96_WCR_SEL | /* normal playback */ + RME96_WCR_MASTER | /* set to master clock mode */ + RME96_WCR_INP_0; /* set coaxial input */ + + rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */ + + writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset the ADC */ + writel(rme96->areg | RME96_AR_PD2, + rme96->iobase + RME96_IO_ADDITIONAL_REG); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset and enable the DAC (order is important). */ + snd_rme96_reset_dac(rme96); + rme96->areg |= RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset playback and record buffer pointers */ + writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); + writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); + + /* reset volume */ + rme96->vol[0] = rme96->vol[1] = 0; + if (RME96_HAS_ANALOG_OUT(rme96)) { + snd_rme96_apply_dac_volume(rme96); + } + + /* init switch interface */ + if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) { + return err; + } + + /* init proc interface */ + snd_rme96_proc_init(rme96); + + return 0; +} + +/* + * proc interface + */ + +static void +snd_rme96_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + int n; + rme96_t *rme96 = (rme96_t *)entry->private_data; + + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + + snd_iprintf(buffer, rme96->card->longname); + snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1); + + snd_iprintf(buffer, "\nGeneral settings\n"); + if (rme96->wcreg & RME96_WCR_IDIS) { + snd_iprintf(buffer, " period size: N/A (interrupts " + "disabled)\n"); + } else if (rme96->wcreg & RME96_WCR_ISEL) { + snd_iprintf(buffer, " period size: 2048 bytes\n"); + } else { + snd_iprintf(buffer, " period size: 8192 bytes\n"); + } + snd_iprintf(buffer, "\nInput settings\n"); + switch (snd_rme96_getinputtype(rme96)) { + case RME96_INPUT_OPTICAL: + snd_iprintf(buffer, " input: optical"); + break; + case RME96_INPUT_COAXIAL: + snd_iprintf(buffer, " input: coaxial"); + break; + case RME96_INPUT_INTERNAL: + snd_iprintf(buffer, " input: internal"); + break; + case RME96_INPUT_XLR: + snd_iprintf(buffer, " input: XLR"); + break; + case RME96_INPUT_ANALOG: + snd_iprintf(buffer, " input: analog"); + break; + } + if (snd_rme96_capture_getrate(rme96, &n) < 0) { + snd_iprintf(buffer, "\n sample rate: no valid signal\n"); + } else { + if (n) { + snd_iprintf(buffer, " (8 channels)\n"); + } else { + snd_iprintf(buffer, " (2 channels)\n"); + } + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme96_capture_getrate(rme96, &n)); + } + if (rme96->wcreg & RME96_WCR_MODE24_2) { + snd_iprintf(buffer, " sample format: 24 bit\n"); + } else { + snd_iprintf(buffer, " sample format: 16 bit\n"); + } + + snd_iprintf(buffer, "\nOutput settings\n"); + if (rme96->wcreg & RME96_WCR_SEL) { + snd_iprintf(buffer, " output signal: normal playback\n"); + } else { + snd_iprintf(buffer, " output signal: same as input\n"); + } + snd_iprintf(buffer, " sample rate: %d Hz\n", + snd_rme96_playback_getrate(rme96)); + if (rme96->wcreg & RME96_WCR_MODE24) { + snd_iprintf(buffer, " sample format: 24 bit\n"); + } else { + snd_iprintf(buffer, " sample format: 16 bit\n"); + } + if (rme96->areg & RME96_AR_WSEL) { + snd_iprintf(buffer, " clock mode: word clock\n"); + } else if (rme96->wcreg & RME96_WCR_MASTER) { + snd_iprintf(buffer, " clock mode: master\n"); + } else { + snd_iprintf(buffer, " clock mode: slave\n"); + } + if (rme96->wcreg & RME96_WCR_PRO) { + snd_iprintf(buffer, " format: AES/EBU (professional)\n"); + } else { + snd_iprintf(buffer, " format: IEC958 (consumer)\n"); + } + if (rme96->wcreg & RME96_WCR_EMP) { + snd_iprintf(buffer, " emphasis: on\n"); + } else { + snd_iprintf(buffer, " emphasis: off\n"); + } + if (rme96->wcreg & RME96_WCR_DOLBY) { + snd_iprintf(buffer, " non-audio (dolby): on\n"); + } else { + snd_iprintf(buffer, " non-audio (dolby): off\n"); + } + if (RME96_HAS_ANALOG_IN(rme96)) { + snd_iprintf(buffer, "\nAnalog output settings\n"); + switch (snd_rme96_getmontracks(rme96)) { + case RME96_MONITOR_TRACKS_1_2: + snd_iprintf(buffer, " monitored ADAT tracks: 1+2\n"); + break; + case RME96_MONITOR_TRACKS_3_4: + snd_iprintf(buffer, " monitored ADAT tracks: 3+4\n"); + break; + case RME96_MONITOR_TRACKS_5_6: + snd_iprintf(buffer, " monitored ADAT tracks: 5+6\n"); + break; + case RME96_MONITOR_TRACKS_7_8: + snd_iprintf(buffer, " monitored ADAT tracks: 7+8\n"); + break; + } + switch (snd_rme96_getattenuation(rme96)) { + case RME96_ATTENUATION_0: + snd_iprintf(buffer, " attenuation: 0 dB\n"); + break; + case RME96_ATTENUATION_6: + snd_iprintf(buffer, " attenuation: -6 dB\n"); + break; + case RME96_ATTENUATION_12: + snd_iprintf(buffer, " attenuation: -12 dB\n"); + break; + case RME96_ATTENUATION_18: + snd_iprintf(buffer, " attenuation: -18 dB\n"); + break; + } + snd_iprintf(buffer, " volume left: %u\n", rme96->vol[0]); + snd_iprintf(buffer, " volume right: %u\n", rme96->vol[1]); + } +} + +static void __init +snd_rme96_proc_init(rme96_t *rme96) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(rme96->card, "rme96", rme96->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = rme96; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_rme96_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + rme96->proc_entry = entry; +} + +static void +snd_rme96_proc_done(rme96_t * rme96) +{ + if (rme96->proc_entry) { + snd_info_unregister(rme96->proc_entry); + rme96->proc_entry = NULL; + } +} + +/* + * control interface + */ + +static int +snd_rme96_info_loopback_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} +static int +snd_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1; + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL; + spin_lock_irqsave(&rme96->lock, flags); + val = (rme96->wcreg & ~RME96_WCR_SEL) | val; + change = val != rme96->wcreg; + writel(rme96->wcreg = val, rme96->iobase + RME96_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" }; + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + uinfo->value.enumerated.items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + uinfo->value.enumerated.items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + /* PST */ + uinfo->value.enumerated.items = 4; + texts[3] = _texts[4]; /* Analog instead of XLR */ + } else { + /* PAD */ + uinfo->value.enumerated.items = 5; + } + break; + default: + snd_BUG(); + break; + } + if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) { + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int items = 3; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96); + + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + /* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */ + if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) { + ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR; + } + items = 4; + } else { + items = 5; + } + break; + default: + snd_BUG(); + break; + } + if (ucontrol->value.enumerated.item[0] >= items) { + ucontrol->value.enumerated.item[0] = items - 1; + } + + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change, items = 3; + + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + case PCI_DEVICE_ID_DIGI96_8: + items = 3; + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + items = 4; + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + if (rme96->rev > 4) { + items = 4; + } else { + items = 5; + } + break; + default: + snd_BUG(); + break; + } + val = ucontrol->value.enumerated.item[0] % items; + + /* special case for PST */ + if (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4) { + if (val == RME96_INPUT_XLR) { + val = RME96_INPUT_ANALOG; + } + } + + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getinputtype(rme96); + snd_rme96_setinputtype(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_clockmode_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = { "Slave", "Master", "Wordclock" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) { + uinfo->value.enumerated.item = 2; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getclockmode(rme96); + snd_rme96_setclockmode(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_attenuation_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 4; + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getattenuation(rme96); + snd_rme96_setattenuation(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int +snd_rme96_info_montracks_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; + } + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} +static int +snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96); + spin_unlock_irqrestore(&rme96->lock, flags); + return 0; +} +static int +snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ucontrol->value.enumerated.item[0] % 4; + spin_lock_irqsave(&rme96->lock, flags); + change = val != snd_rme96_getmontracks(rme96); + snd_rme96_setmontracks(rme96, val); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static u32 snd_rme96_convert_from_aes(snd_aes_iec958_t *aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0; + val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0; + if (val & RME96_WCR_PRO) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0; + return val; +} + +static void snd_rme96_convert_to_aes(snd_aes_iec958_t *aes, u32 val) +{ + aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) | + ((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0); + if (val & RME96_WCR_PRO) + aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_rme96_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif); + return 0; +} + +static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme96->lock, flags); + change = val != rme96->wcreg_spdif; + rme96->wcreg_spdif = val; + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int snd_rme96_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream); + return 0; +} + +static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme96->lock, flags); + change = val != rme96->wcreg_spdif_stream; + rme96->wcreg_spdif_stream = val; + rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); + writel(rme96->wcreg |= val, rme96->iobase + RME96_IO_CONTROL_REGISTER); + spin_unlock_irqrestore(&rme96->lock, flags); + return change; +} + +static int snd_rme96_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme96_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +static int +snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96); + return 0; +} + +static int +snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme96->lock, flags); + u->value.integer.value[0] = rme96->vol[0]; + u->value.integer.value[1] = rme96->vol[1]; + spin_unlock_irqrestore(&rme96->lock, flags); + + return 0; +} + +static int +snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) +{ + rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change = 0; + + if (!RME96_HAS_ANALOG_OUT(rme96)) { + return -EINVAL; + } + spin_lock_irqsave(&rme96->lock, flags); + if (u->value.integer.value[0] != rme96->vol[0]) { + rme96->vol[0] = u->value.integer.value[0]; + change = 1; + } + if (u->value.integer.value[1] != rme96->vol[1]) { + rme96->vol[1] = u->value.integer.value[1]; + change = 1; + } + if (change) { + snd_rme96_apply_dac_volume(rme96); + } + spin_unlock_irqrestore(&rme96->lock, flags); + + return change; +} + +static snd_kcontrol_new_t snd_rme96_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_rme96_control_spdif_info, + get: snd_rme96_control_spdif_get, + put: snd_rme96_control_spdif_put +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_rme96_control_spdif_stream_info, + get: snd_rme96_control_spdif_stream_get, + put: snd_rme96_control_spdif_stream_put +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_rme96_control_spdif_mask_info, + get: snd_rme96_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_EMPHASIS +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_rme96_control_spdif_mask_info, + get: snd_rme96_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_EMPHASIS +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Input Connector", + info: snd_rme96_info_inputtype_control, + get: snd_rme96_get_inputtype_control, + put: snd_rme96_put_inputtype_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Loopback Input", + info: snd_rme96_info_loopback_control, + get: snd_rme96_get_loopback_control, + put: snd_rme96_put_loopback_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Clock Mode", + info: snd_rme96_info_clockmode_control, + get: snd_rme96_get_clockmode_control, + put: snd_rme96_put_clockmode_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Monitor Tracks", + info: snd_rme96_info_montracks_control, + get: snd_rme96_get_montracks_control, + put: snd_rme96_put_montracks_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Attenuation", + info: snd_rme96_info_attenuation_control, + get: snd_rme96_get_attenuation_control, + put: snd_rme96_put_attenuation_control +}, +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "DAC Playback Volume", + info: snd_rme96_dac_volume_info, + get: snd_rme96_dac_volume_get, + put: snd_rme96_dac_volume_put +} +}; + +static int +snd_rme96_create_switches(snd_card_t *card, + rme96_t *rme96) +{ + int idx, err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < 7; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + rme96->spdif_ctl = kctl; + } + + if (RME96_HAS_ANALOG_OUT(rme96)) { + for (idx = 7; idx < 10; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) + return err; + } + + return 0; +} + +/* + * Card initialisation + */ + +static void snd_rme96_card_free(snd_card_t *card) +{ + snd_rme96_free(card->private_data); +} + +static int __devinit +snd_rme96_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + rme96_t *rme96; + snd_card_t *card; + int err; + u8 val; + + if (dev >= SNDRV_CARDS) { + return -ENODEV; + } + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(rme96_t))) == NULL) + return -ENOMEM; + card->private_free = snd_rme96_card_free; + rme96 = (rme96_t *)card->private_data; + rme96->card = card; + rme96->pci = pci; + if ((err = snd_rme96_create(rme96)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Digi96"); + switch (rme96->pci->device) { + case PCI_DEVICE_ID_DIGI96: + strcpy(card->shortname, "RME Digi96"); + break; + case PCI_DEVICE_ID_DIGI96_8: + strcpy(card->shortname, "RME Digi96/8"); + break; + case PCI_DEVICE_ID_DIGI96_8_PRO: + strcpy(card->shortname, "RME Digi96/8 PRO"); + break; + case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: + pci_read_config_byte(rme96->pci, 8, &val); + if (val < 5) { + strcpy(card->shortname, "RME Digi96/8 PAD"); + } else { + strcpy(card->shortname, "RME Digi96/8 PST"); + } + break; + } + sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, + rme96->port, rme96->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_rme96_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "RME Digi96", + id_table: snd_rme96_ids, + probe: snd_rme96_probe, + remove: __devexit_p(snd_rme96_remove), +}; + +static int __init alsa_card_rme96_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "No RME Digi96 cards found\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_rme96_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_rme96_init) +module_exit(alsa_card_rme96_exit) + +#ifndef MODULE + +/* format is: snd-rme96=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_rme96_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-rme96=", alsa_card_rme96_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/rme9652/Makefile linux-2.4.19-pre5-mjc/sound/pci/rme9652/Makefile --- linux/sound/pci/rme9652/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/rme9652/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,24 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _rme9652.o + +list-multi := snd-rme9652-mem.o snd-rme9652.o + +export-objs := rme9652_mem.o + +snd-rme9652-mem-objs := rme9652_mem.o +snd-rme9652-objs := rme9652.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_RME9652) += snd-rme9652.o snd-rme9652-mem.o + +include $(TOPDIR)/Rules.make + +snd-rme9652-mem.o: $(snd-rme9652-mem-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme9652-mem-objs) + +snd-rme9652.o: $(snd-rme9652-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-rme9652-objs) diff -Nru linux/sound/pci/rme9652/rme9652.c linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652.c --- linux/sound/pci/rme9652/rme9652.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2761 @@ +/* + * ALSA driver for RME Digi9652 audio interfaces + * + * Copyright (c) 1999 IEM - Winfried Ritsch + * Copyright (c) 1999-2001 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */ + +EXPORT_NO_SYMBOLS; +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for RME Digi9652 (Hammerfall) soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for RME Digi9652 (Hammerfall) soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable/disable specific RME96{52,36} soundcards."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_precise_ptr, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_precise_ptr, "Enable precise pointer (doesn't work reliably)."); +MODULE_PARM_SYNTAX(snd_precise_ptr, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); +MODULE_AUTHOR("Paul Davis , Winfried Ritsch"); +MODULE_DESCRIPTION("RME Digi9652/Digi9636"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{RME,Hammerfall}," + "{RME,Hammerfall-Light}}"); + +/* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for + capture, one for playback. Both the ADAT and S/PDIF channels appear + to the host CPU in the same block of memory. There is no functional + difference between them in terms of access. + + The Hammerfall Light is identical to the Hammerfall, except that it + has 2 sets 18 channels (16 ADAT + 2 S/PDIF) for capture and playback. +*/ + +#define RME9652_NCHANNELS 26 +#define RME9636_NCHANNELS 18 + +/* Preferred sync source choices - used by "sync_pref" control switch */ + +#define RME9652_SYNC_FROM_SPDIF 0 +#define RME9652_SYNC_FROM_ADAT1 1 +#define RME9652_SYNC_FROM_ADAT2 2 +#define RME9652_SYNC_FROM_ADAT3 3 + +/* Possible sources of S/PDIF input */ + +#define RME9652_SPDIFIN_OPTICAL 0 /* optical (ADAT1) */ +#define RME9652_SPDIFIN_COAXIAL 1 /* coaxial (RCA) */ +#define RME9652_SPDIFIN_INTERN 2 /* internal (CDROM) */ + +/* ------------- Status-Register bits --------------------- */ + +#define RME9652_IRQ (1<<0) /* IRQ is High if not reset by irq_clear */ +#define RME9652_lock_2 (1<<1) /* ADAT 3-PLL: 1=locked, 0=unlocked */ +#define RME9652_lock_1 (1<<2) /* ADAT 2-PLL: 1=locked, 0=unlocked */ +#define RME9652_lock_0 (1<<3) /* ADAT 1-PLL: 1=locked, 0=unlocked */ +#define RME9652_fs48 (1<<4) /* sample rate is 0=44.1/88.2,1=48/96 Khz */ +#define RME9652_wsel_rd (1<<5) /* if Word-Clock is used and valid then 1 */ + /* bits 6-15 encode h/w buffer pointer position */ +#define RME9652_sync_2 (1<<16) /* if ADAT-IN 3 in sync to system clock */ +#define RME9652_sync_1 (1<<17) /* if ADAT-IN 2 in sync to system clock */ +#define RME9652_sync_0 (1<<18) /* if ADAT-IN 1 in sync to system clock */ +#define RME9652_DS_rd (1<<19) /* 1=Double Speed Mode, 0=Normal Speed */ +#define RME9652_tc_busy (1<<20) /* 1=time-code copy in progress (960ms) */ +#define RME9652_tc_out (1<<21) /* time-code out bit */ +#define RME9652_F_0 (1<<22) /* 000=64kHz, 100=88.2kHz, 011=96kHz */ +#define RME9652_F_1 (1<<23) /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ +#define RME9652_F_2 (1<<24) /* external Crystal Chip if ERF=1 */ +#define RME9652_ERF (1<<25) /* Error-Flag of SDPIF Receiver (1=No Lock) */ +#define RME9652_buffer_id (1<<26) /* toggles by each interrupt on rec/play */ +#define RME9652_tc_valid (1<<27) /* 1 = a signal is detected on time-code input */ +#define RME9652_SPDIF_READ (1<<28) /* byte available from Rev 1.5+ S/PDIF interface */ + +#define RME9652_sync (RME9652_sync_0|RME9652_sync_1|RME9652_sync_2) +#define RME9652_lock (RME9652_lock_0|RME9652_lock_1|RME9652_lock_2) +#define RME9652_F (RME9652_F_0|RME9652_F_1|RME9652_F_2) +#define rme9652_decode_spdif_rate(x) ((x)>>22) + +/* Bit 6..15 : h/w buffer pointer */ + +#define RME9652_buf_pos 0x000FFC0 + +/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later + Rev G EEPROMS and Rev 1.5 cards or later. +*/ + +#define RME9652_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME9652_buf_pos)) + +#ifndef PCI_VENDOR_ID_XILINX +#define PCI_VENDOR_ID_XILINX 0x10ee +#endif +#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL +#define PCI_DEVICE_ID_XILINX_HAMMERFALL 0x3fc4 +#endif + +/* amount of io space we remap for register access. i'm not sure we + even need this much, but 1K is nice round number :) +*/ + +#define RME9652_IO_EXTENT 1024 + +#define RME9652_init_buffer 0 +#define RME9652_play_buffer 32 /* holds ptr to 26x64kBit host RAM */ +#define RME9652_rec_buffer 36 /* holds ptr to 26x64kBit host RAM */ +#define RME9652_control_register 64 +#define RME9652_irq_clear 96 +#define RME9652_time_code 100 /* useful if used with alesis adat */ +#define RME9652_thru_base 128 /* 132...228 Thru for 26 channels */ + +/* Read-only registers */ + +/* Writing to any of the register locations writes to the status + register. We'll use the first location as our point of access. +*/ + +#define RME9652_status_register 0 + +/* --------- Control-Register Bits ---------------- */ + + +#define RME9652_start_bit (1<<0) /* start record/play */ + /* bits 1-3 encode buffersize/latency */ +#define RME9652_Master (1<<4) /* Clock Mode Master=1,Slave/Auto=0 */ +#define RME9652_IE (1<<5) /* Interupt Enable */ +#define RME9652_freq (1<<6) /* samplerate 0=44.1/88.2, 1=48/96 kHz */ +#define RME9652_freq1 (1<<7) /* if 0, 32kHz, else always 1 */ +#define RME9652_DS (1<<8) /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ +#define RME9652_PRO (1<<9) /* S/PDIF out: 0=consumer, 1=professional */ +#define RME9652_EMP (1<<10) /* Emphasis 0=None, 1=ON */ +#define RME9652_Dolby (1<<11) /* Non-audio bit 1=set, 0=unset */ +#define RME9652_opt_out (1<<12) /* Use 1st optical OUT as SPDIF: 1=yes,0=no */ +#define RME9652_wsel (1<<13) /* use Wordclock as sync (overwrites master) */ +#define RME9652_inp_0 (1<<14) /* SPDIF-IN: 00=optical (ADAT1), */ +#define RME9652_inp_1 (1<<15) /* 01=koaxial (Cinch), 10=Internal CDROM */ +#define RME9652_SyncPref_ADAT2 (1<<16) +#define RME9652_SyncPref_ADAT3 (1<<17) +#define RME9652_SPDIF_RESET (1<<18) /* Rev 1.5+: h/w S/PDIF receiver */ +#define RME9652_SPDIF_SELECT (1<<19) +#define RME9652_SPDIF_CLOCK (1<<20) +#define RME9652_SPDIF_WRITE (1<<21) +#define RME9652_ADAT1_INTERNAL (1<<22) /* Rev 1.5+: if set, internal CD connector carries ADAT */ + +/* buffersize = 512Bytes * 2^n, where n is made from Bit2 ... Bit0 */ + +#define RME9652_latency 0x0e +#define rme9652_encode_latency(x) (((x)&0x7)<<1) +#define rme9652_decode_latency(x) (((x)>>1)&0x7) +#define rme9652_running_double_speed(s) ((s)->control_register & RME9652_DS) +#define RME9652_inp (RME9652_inp_0|RME9652_inp_1) +#define rme9652_encode_spdif_in(x) (((x)&0x3)<<14) +#define rme9652_decode_spdif_in(x) (((x)>>14)&0x3) + +#define RME9652_SyncPref_Mask (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3) +#define RME9652_SyncPref_ADAT1 0 +#define RME9652_SyncPref_SPDIF (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3) + +/* the size of a substream (1 mono data stream) */ + +#define RME9652_CHANNEL_BUFFER_SAMPLES (16*1024) +#define RME9652_CHANNEL_BUFFER_BYTES (4*RME9652_CHANNEL_BUFFER_SAMPLES) + +/* the size of the area we need to allocate for DMA transfers. the + size is the same regardless of the number of channels - the + 9636 still uses the same memory area. + + Note that we allocate 1 more channel than is apparently needed + because the h/w seems to write 1 byte beyond the end of the last + page. Sigh. +*/ + +#define RME9652_DMA_AREA_BYTES ((RME9652_NCHANNELS+1) * RME9652_CHANNEL_BUFFER_BYTES) +#define RME9652_DMA_AREA_KILOBYTES (RME9652_DMA_AREA_BYTES/1024) + +typedef struct snd_rme9652 { + int dev; + + spinlock_t lock; + int irq; + unsigned long port; + struct resource *res_port; + unsigned long iobase; + + int precise_ptr; + + u32 control_register; /* cached value */ + u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ + + u32 creg_spdif; + u32 creg_spdif_stream; + + char *card_name; /* hammerfall or hammerfall light names */ + + size_t hw_offsetmask; /* &-with status register to get real hw_offset */ + size_t prev_hw_offset; /* previous hw offset */ + size_t max_jitter; /* maximum jitter in frames for + hw pointer */ + size_t period_bytes; /* guess what this is */ + + unsigned char ds_channels; + unsigned char ss_channels; /* different for hammerfall/hammerfall-light */ + + void *capture_buffer_unaligned; /* original buffer addresses */ + void *playback_buffer_unaligned; /* original buffer addresses */ + unsigned char *capture_buffer; /* suitably aligned address */ + unsigned char *playback_buffer; /* suitably aligned address */ + dma_addr_t capture_buffer_addr; + dma_addr_t playback_buffer_addr; + + pid_t capture_pid; + pid_t playback_pid; + + snd_pcm_substream_t *capture_substream; + snd_pcm_substream_t *playback_substream; + int running; + + int passthru; /* non-zero if doing pass-thru */ + int hw_rev; /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */ + + int last_spdif_sample_rate; /* so that we can catch externally ... */ + int last_adat_sample_rate; /* ... induced rate changes */ + + char *channel_map; + + snd_card_t *card; + snd_pcm_t *pcm; + struct pci_dev *pci; + snd_info_entry_t *proc_entry; + snd_kcontrol_t *spdif_ctl; + +} rme9652_t; + +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refer to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_9652_ss[26] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25 +}; + +static char channel_map_9636_ss[26] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + /* channels 16 and 17 are S/PDIF */ + 24, 25, + /* channels 18-25 don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_9652_ds[26] = { + /* ADAT channels are remapped */ + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, + /* channels 12 and 13 are S/PDIF */ + 24, 25, + /* others don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_9636_ds[26] = { + /* ADAT channels are remapped */ + 1, 3, 5, 7, 9, 11, 13, 15, + /* channels 8 and 9 are S/PDIF */ + 24, 25 + /* others don't exist */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#define RME9652_PREALLOCATE_MEMORY /* via module snd-rme9652_mem */ + +#ifdef RME9652_PREALLOCATE_MEMORY +extern void *snd_rme9652_get_buffer(int card, dma_addr_t *dmaaddr); +extern void snd_rme9652_free_buffer(int card, void *ptr); +#endif + +static struct pci_device_id snd_rme9652_ids[] __devinitdata = { + {0x10ee, 0x3fc4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, /* RME Digi9652 */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, snd_rme9652_ids); + +static inline void rme9652_write(rme9652_t *rme9652, int reg, int val) +{ + writel(val, rme9652->iobase + reg); +} + +static inline unsigned int rme9652_read(rme9652_t *rme9652, int reg) +{ + return readl(rme9652->iobase + reg); +} + +static inline int snd_rme9652_use_is_exclusive(rme9652_t *rme9652) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&rme9652->lock, flags); + if ((rme9652->playback_pid != rme9652->capture_pid) && + (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0)) { + ret = 0; + } + spin_unlock_irqrestore(&rme9652->lock, flags); + return ret; +} + +static inline int rme9652_adat_sample_rate(rme9652_t *rme9652) +{ + if (rme9652_running_double_speed(rme9652)) { + return (rme9652_read(rme9652, RME9652_status_register) & + RME9652_fs48) ? 96000 : 88200; + } else { + return (rme9652_read(rme9652, RME9652_status_register) & + RME9652_fs48) ? 48000 : 44100; + } +} + +static inline void rme9652_compute_period_size(rme9652_t *rme9652) +{ + unsigned int i; + + i = rme9652->control_register & RME9652_latency; + rme9652->period_bytes = 1 << ((rme9652_decode_latency(i) + 8)); + rme9652->hw_offsetmask = + (rme9652->period_bytes * 2 - 1) & RME9652_buf_pos; + rme9652->max_jitter = 80; +} + +static snd_pcm_uframes_t rme9652_hw_pointer(rme9652_t *rme9652) +{ + int status; + int offset, frag; + snd_pcm_uframes_t period_size = rme9652->period_bytes / 4; + snd_pcm_sframes_t delta; + + status = rme9652_read(rme9652, RME9652_status_register); + if (!rme9652->precise_ptr) + return (status & RME9652_buffer_id) ? period_size : 0; + offset = status & RME9652_buf_pos; + + /* The hardware may give a backward movement for up to 80 frames + Martin Kirst knows the details. + */ + + delta = rme9652->prev_hw_offset - offset; + delta &= 0xffff; + if (delta <= rme9652->max_jitter * 4) + offset = rme9652->prev_hw_offset; + else + rme9652->prev_hw_offset = offset; + offset &= rme9652->hw_offsetmask; + offset /= 4; + frag = status & RME9652_buffer_id; + + if (offset < period_size) { + if (offset > rme9652->max_jitter) { + if (frag) + printk(KERN_ERR "Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset); + } else if (!frag) + return 0; + offset -= rme9652->max_jitter; + if (offset < 0) + offset += period_size * 2; + } else { + if (offset > period_size + rme9652->max_jitter) { + if (!frag) + printk(KERN_ERR "Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset); + } else if (frag) + return period_size; + offset -= rme9652->max_jitter; + } + + return offset; +} + +static inline void rme9652_reset_hw_pointer(rme9652_t *rme9652) +{ + int i; + + /* reset the FIFO pointer to zero. We do this by writing to 8 + registers, each of which is a 32bit wide register, and set + them all to zero. Note that s->iobase is a pointer to + int32, not pointer to char. + */ + + for (i = 0; i < 8; i++) { + rme9652_write(rme9652, i * 4, 0); + udelay(10); + } + rme9652->prev_hw_offset = 0; +} + +static inline void rme9652_start(rme9652_t *s) +{ + s->control_register |= (RME9652_IE | RME9652_start_bit); + rme9652_write(s, RME9652_control_register, s->control_register); +} + +static inline void rme9652_stop(rme9652_t *s) +{ + s->control_register &= ~(RME9652_start_bit | RME9652_IE); + rme9652_write(s, RME9652_control_register, s->control_register); +} + +static int rme9652_set_interrupt_interval(rme9652_t *s, + unsigned int frames) +{ + int restart = 0; + int n; + + spin_lock_irq(&s->lock); + + if ((restart = s->running)) { + rme9652_stop(s); + } + + frames >>= 7; + n = 0; + while (frames) { + n++; + frames >>= 1; + } + + s->control_register &= ~RME9652_latency; + s->control_register |= rme9652_encode_latency(n); + + rme9652_write(s, RME9652_control_register, s->control_register); + + rme9652_compute_period_size(s); + + if (restart) + rme9652_start(s); + + spin_unlock_irq(&s->lock); + + return 0; +} + +static int rme9652_set_rate(rme9652_t *rme9652, int rate) +{ + int restart; + int reject_if_open = 0; + int xrate; + + /* Changing from a "single speed" to a "double speed" rate is + not allowed if any substreams are open. This is because + such a change causes a shift in the location of + the DMA buffers and a reduction in the number of available + buffers. + + Note that a similar but essentially insoluble problem + exists for externally-driven rate changes. All we can do + is to flag rate changes in the read/write routines. + */ + + spin_lock_irq(&rme9652->lock); + xrate = rme9652_adat_sample_rate(rme9652); + + switch (rate) { + case 44100: + if (xrate > 48000) { + reject_if_open = 1; + } + rate = 0; + break; + case 48000: + if (xrate > 48000) { + reject_if_open = 1; + } + rate = RME9652_freq; + break; + case 88200: + if (xrate < 48000) { + reject_if_open = 1; + } + rate = RME9652_DS; + break; + case 96000: + if (xrate < 48000) { + reject_if_open = 1; + } + rate = RME9652_DS | RME9652_freq; + break; + default: + return -EINVAL; + } + + if (reject_if_open && + (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0)) { + spin_unlock_irq(&rme9652->lock); + return -EBUSY; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652->control_register &= ~(RME9652_freq | RME9652_DS); + rme9652->control_register |= rate; + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + if (rate & RME9652_DS) { + if (rme9652->ss_channels == RME9652_NCHANNELS) { + rme9652->channel_map = channel_map_9652_ds; + } else { + rme9652->channel_map = channel_map_9636_ds; + } + } else { + if (rme9652->ss_channels == RME9652_NCHANNELS) { + rme9652->channel_map = channel_map_9652_ss; + } else { + rme9652->channel_map = channel_map_9636_ss; + } + } + + spin_unlock_irq(&rme9652->lock); + return 0; +} + +static void rme9652_set_thru(rme9652_t *rme9652, int channel, int enable) +{ + int i; + + rme9652->passthru = 0; + + if (channel < 0) { + + /* set thru for all channels */ + + if (enable) { + for (i = 0; i < RME9652_NCHANNELS; i++) { + rme9652->thru_bits |= (1 << i); + rme9652_write(rme9652, RME9652_thru_base + i * 4, 1); + } + } else { + for (i = 0; i < RME9652_NCHANNELS; i++) { + rme9652->thru_bits &= ~(1 << i); + rme9652_write(rme9652, RME9652_thru_base + i * 4, 0); + } + } + + } else { + int mapped_channel; + + snd_assert(channel == RME9652_NCHANNELS, return); + + mapped_channel = rme9652->channel_map[channel]; + + if (enable) { + rme9652->thru_bits |= (1 << mapped_channel); + } else { + rme9652->thru_bits &= ~(1 << mapped_channel); + } + + rme9652_write(rme9652, + RME9652_thru_base + mapped_channel * 4, + enable ? 1 : 0); + } +} + +static int rme9652_set_passthru(rme9652_t *rme9652, int onoff) +{ + if (onoff) { + rme9652_set_thru(rme9652, -1, 1); + + /* we don't want interrupts, so do a + custom version of rme9652_start(). + */ + + rme9652->control_register = + RME9652_inp_0 | + rme9652_encode_latency(7) | + RME9652_start_bit; + + rme9652_reset_hw_pointer(rme9652); + + rme9652_write(rme9652, RME9652_control_register, + rme9652->control_register); + rme9652->passthru = 1; + } else { + rme9652_set_thru(rme9652, -1, 0); + rme9652_stop(rme9652); + rme9652->passthru = 0; + } + + return 0; +} + +static void rme9652_spdif_set_bit (rme9652_t *rme9652, int mask, int onoff) +{ + if (onoff) + rme9652->control_register |= mask; + else + rme9652->control_register &= ~mask; + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); +} + +static void rme9652_spdif_write_byte (rme9652_t *rme9652, const int val) +{ + long mask; + long i; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + if (val & mask) + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 1); + else + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 0); + + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0); + } +} + +static int rme9652_spdif_read_byte (rme9652_t *rme9652) +{ + long mask; + long val; + long i; + + val = 0; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1); + if (rme9652_read (rme9652, RME9652_status_register) & RME9652_SPDIF_READ) + val |= mask; + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0); + } + + return val; +} + +static void rme9652_write_spdif_codec (rme9652_t *rme9652, const int address, const int data) +{ + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + rme9652_spdif_write_byte (rme9652, 0x20); + rme9652_spdif_write_byte (rme9652, address); + rme9652_spdif_write_byte (rme9652, data); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); +} + + +static int rme9652_spdif_read_codec (rme9652_t *rme9652, const int address) +{ + int ret; + + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + rme9652_spdif_write_byte (rme9652, 0x20); + rme9652_spdif_write_byte (rme9652, address); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1); + + rme9652_spdif_write_byte (rme9652, 0x21); + ret = rme9652_spdif_read_byte (rme9652); + rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0); + + return ret; +} + +static void rme9652_initialize_spdif_receiver (rme9652_t *rme9652) +{ + /* XXX what unsets this ? */ + + rme9652->control_register |= RME9652_SPDIF_RESET; + + rme9652_write_spdif_codec (rme9652, 4, 0x40); + rme9652_write_spdif_codec (rme9652, 17, 0x13); + rme9652_write_spdif_codec (rme9652, 6, 0x02); +} + +static inline int rme9652_spdif_sample_rate(rme9652_t *s) +{ + unsigned int rate_bits; + + if (rme9652_read(s, RME9652_status_register) & RME9652_ERF) { + return -1; /* error condition */ + } + + if (s->hw_rev == 15) { + + int x, y, ret; + + x = rme9652_spdif_read_codec (s, 30); + + if (x != 0) + y = 48000 * 64 / x; + else + y = 0; + + if (y > 30400 && y < 33600) ret = 32000; + else if (y > 41900 && y < 46000) ret = 44100; + else if (y > 46000 && y < 50400) ret = 48000; + else if (y > 60800 && y < 67200) ret = 64000; + else if (y > 83700 && y < 92000) ret = 88200; + else if (y > 92000 && y < 100000) ret = 96000; + else ret = 0; + return ret; + } + + rate_bits = rme9652_read(s, RME9652_status_register) & RME9652_F; + + switch (rme9652_decode_spdif_rate(rate_bits)) { + case 0x7: + return 32000; + break; + + case 0x6: + return 44100; + break; + + case 0x5: + return 48000; + break; + + case 0x4: + return 88200; + break; + + case 0x3: + return 96000; + break; + + case 0x0: + return 64000; + break; + + default: + snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n", + s->card_name, rate_bits); + return 0; + break; + } +} + +/*----------------------------------------------------------------------------- + Control Interface + ----------------------------------------------------------------------------*/ + +static u32 snd_rme9652_convert_from_aes(snd_aes_iec958_t *aes) +{ + u32 val = 0; + val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME9652_PRO : 0; + val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME9652_Dolby : 0; + if (val & RME9652_PRO) + val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME9652_EMP : 0; + else + val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME9652_EMP : 0; + return val; +} + +static void snd_rme9652_convert_to_aes(snd_aes_iec958_t *aes, u32 val) +{ + aes->status[0] = ((val & RME9652_PRO) ? IEC958_AES0_PROFESSIONAL : 0) | + ((val & RME9652_Dolby) ? IEC958_AES0_NONAUDIO : 0); + if (val & RME9652_PRO) + aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; + else + aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; +} + +static int snd_rme9652_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif); + return 0; +} + +static int snd_rme9652_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652->creg_spdif; + rme9652->creg_spdif = val; + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif_stream); + return 0; +} + +static int snd_rme9652_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + u32 val; + + val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652->creg_spdif_stream; + rme9652->creg_spdif_stream = val; + rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP); + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_rme9652_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = kcontrol->private_value; + return 0; +} + +#define RME9652_ADAT1_IN(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_adat1_in, \ + get: snd_rme9652_get_adat1_in, \ + put: snd_rme9652_put_adat1_in } + +static unsigned int rme9652_adat1_in(rme9652_t *rme9652) +{ + if (rme9652->control_register & RME9652_ADAT1_INTERNAL) + return 1; + return 0; +} + +static int rme9652_set_adat1_input(rme9652_t *rme9652, int internal) +{ + int restart = 0; + + if (internal) { + rme9652->control_register |= RME9652_ADAT1_INTERNAL; + } else { + rme9652->control_register &= ~RME9652_ADAT1_INTERNAL; + } + + /* XXX do we actually need to stop the card when we do this ? */ + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_adat1_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[2] = {"ADAT1", "Internal"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0] % 2; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_adat1_in(rme9652); + if (change) + rme9652_set_adat1_input(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SPDIF_IN(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_spdif_in, \ + get: snd_rme9652_get_spdif_in, put: snd_rme9652_put_spdif_in } + +static unsigned int rme9652_spdif_in(rme9652_t *rme9652) +{ + return rme9652_decode_spdif_in(rme9652->control_register & + RME9652_inp); +} + +static int rme9652_set_spdif_input(rme9652_t *rme9652, int in) +{ + int restart = 0; + + rme9652->control_register &= ~RME9652_inp; + rme9652->control_register |= rme9652_encode_spdif_in(in); + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = {"ADAT1", "Coaxial", "Internal"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_spdif_in(rme9652); + if (change) + rme9652_set_spdif_input(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SPDIF_OUT(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_spdif_out, \ + get: snd_rme9652_get_spdif_out, put: snd_rme9652_put_spdif_out } + +static int rme9652_spdif_out(rme9652_t *rme9652) +{ + return (rme9652->control_register & RME9652_opt_out) ? 1 : 0; +} + +static int rme9652_set_spdif_output(rme9652_t *rme9652, int out) +{ + int restart = 0; + + if (out) { + rme9652->control_register |= RME9652_opt_out; + } else { + rme9652->control_register &= ~RME9652_opt_out; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_spdif_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_spdif_out(rme9652); + rme9652_set_spdif_output(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SYNC_MODE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_sync_mode, \ + get: snd_rme9652_get_sync_mode, put: snd_rme9652_put_sync_mode } + +static int rme9652_sync_mode(rme9652_t *rme9652) +{ + if (rme9652->control_register & RME9652_wsel) { + return 2; + } else if (rme9652->control_register & RME9652_Master) { + return 1; + } else { + return 0; + } +} + +static int rme9652_set_sync_mode(rme9652_t *rme9652, int mode) +{ + int restart = 0; + + switch (mode) { + case 0: + rme9652->control_register &= + ~(RME9652_Master | RME9652_wsel); + break; + case 1: + rme9652->control_register = + (rme9652->control_register & ~RME9652_wsel) | RME9652_Master; + break; + case 2: + rme9652->control_register |= + (RME9652_Master | RME9652_wsel); + break; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_sync_mode(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[3] = {"AutoSync", "Master", "Word Clock"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + + val = ucontrol->value.enumerated.item[0] % 3; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_sync_mode(rme9652); + rme9652_set_sync_mode(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +#define RME9652_SYNC_PREF(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_sync_pref, \ + get: snd_rme9652_get_sync_pref, put: snd_rme9652_put_sync_pref } + +static int rme9652_sync_pref(rme9652_t *rme9652) +{ + switch (rme9652->control_register & RME9652_SyncPref_Mask) { + case RME9652_SyncPref_ADAT1: + return RME9652_SYNC_FROM_ADAT1; + case RME9652_SyncPref_ADAT2: + return RME9652_SYNC_FROM_ADAT2; + case RME9652_SyncPref_ADAT3: + return RME9652_SYNC_FROM_ADAT3; + case RME9652_SyncPref_SPDIF: + return RME9652_SYNC_FROM_SPDIF; + } + /* Not reachable */ + return 0; +} + +static int rme9652_set_sync_pref(rme9652_t *rme9652, int pref) +{ + int restart; + + rme9652->control_register &= ~RME9652_SyncPref_Mask; + switch (pref) { + case RME9652_SYNC_FROM_ADAT1: + rme9652->control_register |= RME9652_SyncPref_ADAT1; + break; + case RME9652_SYNC_FROM_ADAT2: + rme9652->control_register |= RME9652_SyncPref_ADAT2; + break; + case RME9652_SYNC_FROM_ADAT3: + rme9652->control_register |= RME9652_SyncPref_ADAT3; + break; + case RME9652_SYNC_FROM_SPDIF: + rme9652->control_register |= RME9652_SyncPref_SPDIF; + break; + } + + if ((restart = rme9652->running)) { + rme9652_stop(rme9652); + } + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + if (restart) { + rme9652_start(rme9652); + } + + return 0; +} + +static int snd_rme9652_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"}; + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change, max; + unsigned int val; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; + val = ucontrol->value.enumerated.item[0] % max; + spin_lock_irqsave(&rme9652->lock, flags); + change = val != rme9652_sync_pref(rme9652); + rme9652_set_sync_pref(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return change; +} + +static int snd_rme9652_info_thru(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = rme9652->ss_channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned int k; + u32 thru_bits = rme9652->thru_bits; + + for (k = 0; k < rme9652->ss_channels; ++k) { + ucontrol->value.integer.value[k] = !!(thru_bits & (1 << k)); + } + return 0; +} + +static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int chn; + u32 thru_bits = 0; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + + for (chn = 0; chn < rme9652->ss_channels; ++chn) { + if (ucontrol->value.integer.value[chn]) + thru_bits |= 1 << chn; + } + + spin_lock_irqsave(&rme9652->lock, flags); + change = thru_bits ^ rme9652->thru_bits; + if (change) { + for (chn = 0; chn < rme9652->ss_channels; ++chn) { + if (!(change & (1 << chn))) + continue; + rme9652_set_thru(rme9652,chn,thru_bits&(1<lock, flags); + return !!change; +} + +#define RME9652_PASSTHRU(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + info: snd_rme9652_info_passthru, \ + put: snd_rme9652_put_passthru, \ + get: snd_rme9652_get_passthru } + +static int snd_rme9652_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652->passthru; + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + int change; + unsigned int val; + int err = 0; + + if (!snd_rme9652_use_is_exclusive(rme9652)) + return -EBUSY; + + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irqsave(&rme9652->lock, flags); + change = (ucontrol->value.integer.value[0] != rme9652->passthru); + if (change) + err = rme9652_set_passthru(rme9652, val); + spin_unlock_irqrestore(&rme9652->lock, flags); + return err ? err : change; +} + +/* Read-only switches */ + +#define RME9652_SPDIF_RATE(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + info: snd_rme9652_info_spdif_rate, \ + get: snd_rme9652_get_spdif_rate } + +static int snd_rme9652_info_spdif_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 96000; + return 0; +} + +static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652); + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +#define RME9652_ADAT_SYNC(xname, xindex, xidx) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + info: snd_rme9652_info_adat_sync, \ + get: snd_rme9652_get_adat_sync, private_value: xidx } + +static int snd_rme9652_info_adat_sync(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + unsigned int mask1, mask2, val; + + switch (kcontrol->private_value) { + case 0: mask1 = RME9652_lock_0; mask2 = RME9652_sync_0; break; + case 1: mask1 = RME9652_lock_1; mask2 = RME9652_sync_1; break; + case 2: mask1 = RME9652_lock_2; mask2 = RME9652_sync_2; break; + default: return -EINVAL; + } + val = rme9652_read(rme9652, RME9652_status_register); + ucontrol->value.enumerated.item[0] = (val & mask1) ? 1 : 0; + ucontrol->value.enumerated.item[0] |= (val & mask2) ? 2 : 0; + return 0; +} + +#define RME9652_TC_VALID(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \ + access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + info: snd_rme9652_info_tc_valid, \ + get: snd_rme9652_get_tc_valid } + +static int snd_rme9652_info_tc_valid(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + (rme9652_read(rme9652, RME9652_status_register) & RME9652_tc_valid) ? 1 : 0; + return 0; +} + +#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE + +/* FIXME: this routine needs a port to the new control API --jk */ + +static int snd_rme9652_get_tc_value(void *private_data, + snd_kswitch_t *kswitch, + snd_switch_t *uswitch) +{ + rme9652_t *s = (rme9652_t *) private_data; + u32 value; + int i; + + uswitch->type = SNDRV_SW_TYPE_DWORD; + + if ((rme9652_read(s, RME9652_status_register) & + RME9652_tc_valid) == 0) { + uswitch->value.data32[0] = 0; + return 0; + } + + /* timecode request */ + + rme9652_write(s, RME9652_time_code, 0); + + /* XXX bug alert: loop-based timing !!!! */ + + for (i = 0; i < 50; i++) { + if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) + break; + } + + if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) { + return -EIO; + } + + value = 0; + + for (i = 0; i < 32; i++) { + value >>= 1; + + if (rme9652_read(s, i * 4) & RME9652_tc_out) + value |= 0x80000000; + } + + if (value > 2 * 60 * 48000) { + value -= 2 * 60 * 48000; + } else { + value = 0; + } + + uswitch->value.data32[0] = value; + + return 0; +} + +#endif /* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */ + +#define RME9652_CONTROLS (sizeof(snd_rme9652_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_rme9652_controls[] = { +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_rme9652_control_spdif_info, + get: snd_rme9652_control_spdif_get, + put: snd_rme9652_control_spdif_put, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_rme9652_control_spdif_stream_info, + get: snd_rme9652_control_spdif_stream_get, + put: snd_rme9652_control_spdif_stream_put, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_rme9652_control_spdif_mask_info, + get: snd_rme9652_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_CON_EMPHASIS, +}, +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + info: snd_rme9652_control_spdif_mask_info, + get: snd_rme9652_control_spdif_mask_get, + private_value: IEC958_AES0_NONAUDIO | + IEC958_AES0_PROFESSIONAL | + IEC958_AES0_PRO_EMPHASIS, +}, +RME9652_SPDIF_IN("IEC958 Input Connector", 0), +RME9652_SPDIF_OUT("IEC958 Output also on ADAT1", 0), +RME9652_SYNC_MODE("Sync Mode", 0), +RME9652_SYNC_PREF("Preferred Sync Source", 0), +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: "Channels Thru", + index: 0, + info: snd_rme9652_info_thru, + get: snd_rme9652_get_thru, + put: snd_rme9652_put_thru, +}, +RME9652_SPDIF_RATE("IEC958 Sample Rate", 0), +RME9652_ADAT_SYNC("ADAT1 Sync Check", 0, 0), +RME9652_ADAT_SYNC("ADAT2 Sync Check", 0, 1), +RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2), +RME9652_TC_VALID("Timecode Valid", 0), +RME9652_PASSTHRU("Passthru", 0) +}; + +static snd_kcontrol_new_t snd_rme9652_adat3_check = +RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2); + +static snd_kcontrol_new_t snd_rme9652_adat1_input = +RME9652_ADAT1_IN("ADAT1 Input Source", 0); + +int snd_rme9652_create_controls(snd_card_t *card, rme9652_t *rme9652) +{ + int idx, err; + snd_kcontrol_t *kctl; + + for (idx = 0; idx < RME9652_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0) + return err; + if (idx == 1) /* IEC958 (S/PDIF) Stream */ + rme9652->spdif_ctl = kctl; + } + + if (rme9652->ss_channels == RME9652_NCHANNELS) + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652))) < 0) + return err; + + if (rme9652->hw_rev >= 15) + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652))) < 0) + return err; + + return 0; +} + +/*------------------------------------------------------------ + /proc interface + ------------------------------------------------------------*/ + +static void +snd_rme9652_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + rme9652_t *rme9652 = (rme9652_t *) entry->private_data; + u32 thru_bits = rme9652->thru_bits; + int show_auto_sync_source = 0; + int i; + unsigned int status; + int x; + + status = rme9652_read(rme9652, RME9652_status_register); + + snd_iprintf(buffer, "%s (Card #%d)\n", rme9652->card_name, rme9652->card->number + 1); + snd_iprintf(buffer, "Buffers: capture %p playback %p\n", + rme9652->capture_buffer, rme9652->playback_buffer); + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + rme9652->irq, rme9652->port, rme9652->iobase); + snd_iprintf(buffer, "Control register: %x\n", rme9652->control_register); + + snd_iprintf(buffer, "\n"); + + x = 1 << (6 + rme9652_decode_latency(rme9652->control_register & + RME9652_latency)); + + snd_iprintf(buffer, "Latency: %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) rme9652->period_bytes); + snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", + rme9652_hw_pointer(rme9652)); + snd_iprintf(buffer, "Passthru: %s\n", + rme9652->passthru ? "yes" : "no"); + + if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) { + snd_iprintf(buffer, "Clock mode: autosync\n"); + show_auto_sync_source = 1; + } else if (rme9652->control_register & RME9652_wsel) { + if (status & RME9652_wsel_rd) { + snd_iprintf(buffer, "Clock mode: word clock\n"); + } else { + snd_iprintf(buffer, "Clock mode: word clock (no signal)\n"); + } + } else { + snd_iprintf(buffer, "Clock mode: master\n"); + } + + if (show_auto_sync_source) { + switch (rme9652->control_register & RME9652_SyncPref_Mask) { + case RME9652_SyncPref_ADAT1: + snd_iprintf(buffer, "Pref. sync source: ADAT1\n"); + break; + case RME9652_SyncPref_ADAT2: + snd_iprintf(buffer, "Pref. sync source: ADAT2\n"); + break; + case RME9652_SyncPref_ADAT3: + snd_iprintf(buffer, "Pref. sync source: ADAT3\n"); + break; + case RME9652_SyncPref_SPDIF: + snd_iprintf(buffer, "Pref. sync source: IEC958\n"); + break; + default: + snd_iprintf(buffer, "Pref. sync source: ???\n"); + } + } + + if (rme9652->hw_rev >= 15) + snd_iprintf(buffer, "\nADAT1 Input source: %s\n", + (rme9652->control_register & RME9652_ADAT1_INTERNAL) ? + "Internal" : "ADAT1 optical"); + + snd_iprintf(buffer, "\n"); + + switch (rme9652_decode_spdif_in(rme9652->control_register & + RME9652_inp)) { + case RME9652_SPDIFIN_OPTICAL: + snd_iprintf(buffer, "IEC958 input: ADAT1\n"); + break; + case RME9652_SPDIFIN_COAXIAL: + snd_iprintf(buffer, "IEC958 input: Coaxial\n"); + break; + case RME9652_SPDIFIN_INTERN: + snd_iprintf(buffer, "IEC958 input: Internal\n"); + break; + default: + snd_iprintf(buffer, "IEC958 input: ???\n"); + break; + } + + if (rme9652->control_register & RME9652_opt_out) { + snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n"); + } else { + snd_iprintf(buffer, "IEC958 output: Coaxial only\n"); + } + + if (rme9652->control_register & RME9652_PRO) { + snd_iprintf(buffer, "IEC958 quality: Professional\n"); + } else { + snd_iprintf(buffer, "IEC958 quality: Consumer\n"); + } + + if (rme9652->control_register & RME9652_EMP) { + snd_iprintf(buffer, "IEC958 emphasis: on\n"); + } else { + snd_iprintf(buffer, "IEC958 emphasis: off\n"); + } + + if (rme9652->control_register & RME9652_Dolby) { + snd_iprintf(buffer, "IEC958 Dolby: on\n"); + } else { + snd_iprintf(buffer, "IEC958 Dolby: off\n"); + } + + i = rme9652_spdif_sample_rate(rme9652); + + if (i < 0) { + snd_iprintf(buffer, + "IEC958 sample rate: error flag set\n"); + } else if (i == 0) { + snd_iprintf(buffer, "IEC958 sample rate: undetermined\n"); + } else { + snd_iprintf(buffer, "IEC958 sample rate: %d\n", i); + } + + snd_iprintf(buffer, "\n"); + + snd_iprintf(buffer, "ADAT Sample rate: %dHz\n", + rme9652_adat_sample_rate(rme9652)); + + /* Sync Check */ + + x = status & RME9652_sync_0; + if (status & RME9652_lock_0) { + snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT1: No Lock\n"); + } + + x = status & RME9652_sync_1; + if (status & RME9652_lock_1) { + snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT2: No Lock\n"); + } + + x = status & RME9652_sync_2; + if (status & RME9652_lock_2) { + snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock"); + } else { + snd_iprintf(buffer, "ADAT3: No Lock\n"); + } + + snd_iprintf(buffer, "\n"); + + snd_iprintf(buffer, "Timecode signal: %s\n", + (status & RME9652_tc_valid) ? "yes" : "no"); + + /* thru modes */ + + snd_iprintf(buffer, "Punch Status:\n\n"); + + for (i = 0; i < rme9652->ss_channels; i++) { + if (thru_bits & (1 << i)) { + snd_iprintf(buffer, "%2d: on ", i + 1); + } else { + snd_iprintf(buffer, "%2d: off ", i + 1); + } + + if (((i + 1) % 8) == 0) { + snd_iprintf(buffer, "\n"); + } + } + + snd_iprintf(buffer, "\n"); +} + +static void __init snd_rme9652_proc_init(rme9652_t *rme9652) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(rme9652->card, "rme9652", rme9652->card->proc_root)) != + NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = rme9652; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_rme9652_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + rme9652->proc_entry = entry; +} + +static void snd_rme9652_proc_done(rme9652_t *rme9652) +{ + if (rme9652->proc_entry) { + snd_info_unregister(rme9652->proc_entry); + rme9652->proc_entry = NULL; + } +} + +static void snd_rme9652_free_buffers(rme9652_t *rme9652) +{ + if (rme9652->capture_buffer_unaligned) { +#ifndef RME9652_PREALLOCATE_MEMORY + snd_free_pci_pages(rme9652->pci, + RME9652_DMA_AREA_BYTES, + rme9652->capture_buffer_unaligned, + rme9652->capture_buffer_addr); +#else + snd_rme9652_free_buffer(rme9652->dev, rme9652->capture_buffer_unaligned); +#endif + } + + if (rme9652->playback_buffer_unaligned) { +#ifndef RME9652_PREALLOCATE_MEMORY + snd_free_pci_pages(rme9652->pci, + RME9652_DMA_AREA_BYTES, + rme9652->playback_buffer_unaligned, + rme9652->playback_buffer_addr); +#else + snd_rme9652_free_buffer(rme9652->dev, rme9652->playback_buffer_unaligned); +#endif + } +} + +static int snd_rme9652_free(rme9652_t *rme9652) +{ + if (rme9652->irq >= 0) + rme9652_stop(rme9652); + snd_rme9652_proc_done(rme9652); + snd_rme9652_free_buffers(rme9652); + + if (rme9652->iobase) + iounmap((void *) rme9652->iobase); + if (rme9652->res_port) { + release_resource(rme9652->res_port); + kfree_nocheck(rme9652->res_port); + } + if (rme9652->irq >= 0) + free_irq(rme9652->irq, (void *)rme9652); + return 0; +} + +static int __init snd_rme9652_initialize_memory(rme9652_t *rme9652) +{ + void *pb, *cb; + dma_addr_t pb_addr, cb_addr; + unsigned long pb_bus, cb_bus; + +#ifndef RME9652_PREALLOCATE_MEMORY + cb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &cb_addr); + pb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &pb_addr); +#else + cb = snd_rme9652_get_buffer(rme9652->dev, &cb_addr); + pb = snd_rme9652_get_buffer(rme9652->dev, &pb_addr); +#endif + + if (cb == 0 || pb == 0) { + if (cb) { +#ifdef RME9652_PREALLOCATE_MEMORY + snd_rme9652_free_buffer(rme9652->dev, cb); +#else + snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, cb, cb_addr); +#endif + } + if (pb) { +#ifdef RME9652_PREALLOCATE_MEMORY + snd_rme9652_free_buffer(rme9652->dev, pb); +#else + snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, pb, pb_addr); +#endif + } + + printk(KERN_ERR "%s: no buffers available\n", rme9652->card_name); + return -ENOMEM; + } + + /* save raw addresses for use when freeing memory later */ + + rme9652->capture_buffer_unaligned = cb; + rme9652->playback_buffer_unaligned = pb; + rme9652->capture_buffer_addr = cb_addr; + rme9652->playback_buffer_addr = pb_addr; + + /* Align to bus-space 64K boundary */ + + cb_bus = (cb_addr + 0xFFFF) & ~0xFFFFl; + pb_bus = (pb_addr + 0xFFFF) & ~0xFFFFl; + + /* Tell the card where it is */ + + rme9652_write(rme9652, RME9652_rec_buffer, cb_bus); + rme9652_write(rme9652, RME9652_play_buffer, pb_bus); + + rme9652->capture_buffer = cb + (cb_bus - cb_addr); + rme9652->playback_buffer = pb + (pb_bus - pb_addr); + + return 0; +} + +static void snd_rme9652_set_defaults(rme9652_t *rme9652) +{ + unsigned int k; + + /* ASSUMPTION: rme9652->lock is either held, or + there is no need to hold it (e.g. during module + initalization). + */ + + /* set defaults: + + SPDIF Input via Coax + autosync clock mode + maximum latency (7 = 8192 samples, 64Kbyte buffer, + which implies 2 4096 sample, 32Kbyte periods). + + if rev 1.5, initialize the S/PDIF receiver. + + */ + + rme9652->control_register = + RME9652_inp_0 | rme9652_encode_latency(7); + + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register); + + rme9652_reset_hw_pointer(rme9652); + rme9652_compute_period_size(rme9652); + + /* default: thru off for all channels */ + + for (k = 0; k < RME9652_NCHANNELS; ++k) + rme9652_write(rme9652, RME9652_thru_base + k * 4, 0); + + rme9652->thru_bits = 0; + rme9652->passthru = 0; + + /* set a default rate so that the channel map is set up */ + + rme9652_set_rate(rme9652, 48000); +} + +void snd_rme9652_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + rme9652_t *rme9652 = (rme9652_t *) dev_id; + + if (!(rme9652_read(rme9652, RME9652_status_register) & RME9652_IRQ)) { + return; + } + + rme9652_write(rme9652, RME9652_irq_clear, 0); + + if (rme9652->capture_substream) { + snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + } + + if (rme9652->playback_substream) { + snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); + } +} + +static snd_pcm_uframes_t snd_rme9652_hw_pointer(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + return rme9652_hw_pointer(rme9652); +} + +static char *rme9652_channel_buffer_location(rme9652_t *rme9652, + int stream, + int channel) + +{ + int mapped_channel; + + snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL); + + if ((mapped_channel = rme9652->channel_map[channel]) < 0) { + return NULL; + } + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return rme9652->capture_buffer + + (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES); + } else { + return rme9652->playback_buffer + + (mapped_channel * RME9652_CHANNEL_BUFFER_BYTES); + } +} + +static int snd_rme9652_playback_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + copy_from_user(channel_buf + pos * 4, src, count * 4); + return count; +} + +static int snd_rme9652_capture_copy(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + copy_to_user(dst, channel_buf + pos * 4, count * 4); + return count; +} + +static int snd_rme9652_hw_silence(snd_pcm_substream_t *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = rme9652_channel_buffer_location (rme9652, + substream->pstr->stream, + channel); + snd_assert(channel_buf != NULL, return -EIO); + memset(channel_buf + pos * 4, 0, count * 4); + return count; +} + +static int snd_rme9652_reset(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = rme9652->capture_substream; + else + other = rme9652->playback_substream; + if (rme9652->running) + runtime->status->hw_ptr = rme9652_hw_pointer(rme9652); + else + runtime->status->hw_ptr = 0; + if (other) { + snd_pcm_substream_t *s = substream; + snd_pcm_runtime_t *oruntime = other->runtime; + do { + s = s->link_next; + if (s == other) { + oruntime->status->hw_ptr = runtime->status->hw_ptr; + break; + } + } while (s != substream); + } + return 0; +} + +static int snd_rme9652_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int err; + pid_t this_pid; + pid_t other_pid; + + spin_lock_irq(&rme9652->lock); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP); + rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream); + this_pid = rme9652->playback_pid; + other_pid = rme9652->capture_pid; + } else { + this_pid = rme9652->capture_pid; + other_pid = rme9652->playback_pid; + } + + if ((other_pid > 0) && (this_pid != other_pid)) { + + /* The other stream is open, and not by the same + task as this one. Make sure that the parameters + that matter are the same. + */ + + if (params_rate(params) != + rme9652_adat_sample_rate(rme9652)) { + spin_unlock_irq(&rme9652->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return -EBUSY; + } + + if (params_period_size(params) != rme9652->period_bytes / 4) { + spin_unlock_irq(&rme9652->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return -EBUSY; + } + + /* We're fine. */ + + spin_unlock_irq(&rme9652->lock); + return 0; + + } else { + spin_unlock_irq(&rme9652->lock); + } + + /* how to make sure that the rate matches an externally-set one ? + */ + + if ((err = rme9652_set_rate(rme9652, params_rate(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return err; + } + + if ((err = rme9652_set_interrupt_interval(rme9652, params_period_size(params))) < 0) { + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + return err; + } + + return 0; +} + +static int snd_rme9652_channel_info(snd_pcm_substream_t *substream, + snd_pcm_channel_info_t *info) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int chn; + + snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL); + + if ((chn = rme9652->channel_map[info->channel]) < 0) { + return -EINVAL; + } + + info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES; + info->first = 0; + info->step = 32; + return 0; +} + +static int snd_rme9652_ioctl(snd_pcm_substream_t *substream, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case SNDRV_PCM_IOCTL1_RESET: + { + return snd_rme9652_reset(substream); + } + case SNDRV_PCM_IOCTL1_CHANNEL_INFO: + { + snd_pcm_channel_info_t *info = arg; + return snd_rme9652_channel_info(substream, info); + } + default: + break; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static void rme9652_silence_playback(rme9652_t *rme9652) +{ + memset(rme9652->playback_buffer, 0, RME9652_DMA_AREA_BYTES); +} + +static int snd_rme9652_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + snd_pcm_substream_t *other; + int running; + spin_lock(&rme9652->lock); + running = rme9652->running; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + running |= 1 << substream->stream; + break; + case SNDRV_PCM_TRIGGER_STOP: + running &= ~(1 << substream->stream); + break; + default: + snd_BUG(); + spin_unlock(&rme9652->lock); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + other = rme9652->capture_substream; + else + other = rme9652->playback_substream; + + if (other) { + snd_pcm_substream_t *s = substream; + do { + s = s->link_next; + if (s == other) { + snd_pcm_trigger_done(s, substream); + if (cmd == SNDRV_PCM_TRIGGER_START) + running |= 1 << s->stream; + else + running &= ~(1 << s->stream); + goto _ok; + } + } while (s != substream); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) && + substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rme9652_silence_playback(rme9652); + } else { + if (running && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rme9652_silence_playback(rme9652); + } + } + _ok: + snd_pcm_trigger_done(substream, substream); + if (!rme9652->running && running) + rme9652_start(rme9652); + else if (rme9652->running && !running) + rme9652_stop(rme9652); + rme9652->running = running; + spin_unlock(&rme9652->lock); + + return 0; +} + +static int snd_rme9652_prepare(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + int result = 0; + + spin_lock_irq(&rme9652->lock); + if (!rme9652->running) + rme9652_reset_hw_pointer(rme9652); + spin_unlock_irq(&rme9652->lock); + return result; +} + +static snd_pcm_hardware_t snd_rme9652_playback_subinfo = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_DOUBLE), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 44100, + rate_max: 96000, + channels_min: 10, + channels_max: 26, + buffer_bytes_max: 1024*1024, + period_bytes_min: 1, + period_bytes_max: 1024*1024, + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_rme9652_capture_subinfo = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_SYNC_START), + formats: SNDRV_PCM_FMTBIT_S32_LE, + rates: (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + rate_min: 44100, + rate_max: 96000, + channels_min: 10, + channels_max: 26, + buffer_bytes_max: 1024*1024, + period_bytes_min: 1, + period_bytes_max: 1024*1024, + periods_min: 2, + periods_max: 2, + fifo_size: 0, +}; + +static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; + +#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { + count: PERIOD_SIZES, + list: period_sizes, + mask: 0 +}; + +static int snd_rme9652_hw_rule_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + unsigned int list[2] = { rme9652->ds_channels, rme9652->ss_channels }; + return snd_interval_list(c, 2, list, 0); +} + +static int snd_rme9652_hw_rule_channels_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (r->min > 48000) { + snd_interval_t t = { + min: rme9652->ds_channels, + max: rme9652->ds_channels, + integer: 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 88200) { + snd_interval_t t = { + min: rme9652->ss_channels, + max: rme9652->ss_channels, + integer: 1, + }; + return snd_interval_refine(c, &t); + } + return 0; +} + +static int snd_rme9652_hw_rule_rate_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + rme9652_t *rme9652 = rule->private; + snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (c->min >= rme9652->ss_channels) { + snd_interval_t t = { + min: 44100, + max: 48000, + integer: 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= rme9652->ds_channels) { + snd_interval_t t = { + min: 88200, + max: 96000, + integer: 1, + }; + return snd_interval_refine(r, &t); + } + return 0; +} + +static int snd_rme9652_playback_open(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&rme9652->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme9652_playback_subinfo; + runtime->dma_area = rme9652->playback_buffer; + runtime->dma_bytes = RME9652_DMA_AREA_BYTES; + + if (rme9652->capture_substream == NULL) { + rme9652_stop(rme9652); + rme9652_set_thru(rme9652, -1, 0); + } + + rme9652->playback_pid = current->pid; + rme9652->playback_substream = substream; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels_rate, rme9652, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_rme9652_hw_rule_rate_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + rme9652->creg_spdif_stream = rme9652->creg_spdif; + rme9652->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id); + return 0; +} + +static int snd_rme9652_playback_release(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + + rme9652->playback_pid = -1; + rme9652->playback_substream = NULL; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + rme9652->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id); + return 0; +} + + +static int snd_rme9652_capture_open(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irqsave(&rme9652->lock, flags); + + snd_pcm_set_sync(substream); + + runtime->hw = snd_rme9652_capture_subinfo; + runtime->dma_area = rme9652->capture_buffer; + runtime->dma_bytes = RME9652_DMA_AREA_BYTES; + + if (rme9652->playback_substream == NULL) { + rme9652_stop(rme9652); + rme9652_set_thru(rme9652, -1, 0); + } + + rme9652->capture_pid = current->pid; + rme9652->capture_substream = substream; + + spin_unlock_irqrestore(&rme9652->lock, flags); + + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_rme9652_hw_rule_channels_rate, rme9652, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_rme9652_hw_rule_rate_channels, rme9652, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + return 0; +} + +static int snd_rme9652_capture_release(snd_pcm_substream_t *substream) +{ + rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&rme9652->lock, flags); + + rme9652->capture_pid = -1; + rme9652->capture_substream = NULL; + + spin_unlock_irqrestore(&rme9652->lock, flags); + return 0; +} + +static snd_pcm_ops_t snd_rme9652_playback_ops = { + open: snd_rme9652_playback_open, + close: snd_rme9652_playback_release, + ioctl: snd_rme9652_ioctl, + hw_params: snd_rme9652_hw_params, + prepare: snd_rme9652_prepare, + trigger: snd_rme9652_trigger, + pointer: snd_rme9652_hw_pointer, + copy: snd_rme9652_playback_copy, + silence: snd_rme9652_hw_silence, +}; + +static snd_pcm_ops_t snd_rme9652_capture_ops = { + open: snd_rme9652_capture_open, + close: snd_rme9652_capture_release, + ioctl: snd_rme9652_ioctl, + hw_params: snd_rme9652_hw_params, + prepare: snd_rme9652_prepare, + trigger: snd_rme9652_trigger, + pointer: snd_rme9652_hw_pointer, + copy: snd_rme9652_capture_copy, +}; + +static int __init snd_rme9652_create_pcm(snd_card_t *card, + rme9652_t *rme9652) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(card, + rme9652->card_name, + 0, 1, 1, &pcm)) < 0) { + return err; + } + + rme9652->pcm = pcm; + pcm->private_data = rme9652; + strcpy(pcm->name, rme9652->card_name); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + + return 0; +} + +static int __init snd_rme9652_create(snd_card_t *card, + rme9652_t *rme9652, + int precise_ptr) +{ + struct pci_dev *pci = rme9652->pci; + int err; + int status; + unsigned short rev; + + rme9652->irq = -1; + rme9652->card = card; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + spin_lock_init(&rme9652->lock); + + rme9652->port = pci_resource_start(pci, 0); + if ((rme9652->res_port = request_mem_region(rme9652->port, RME9652_IO_EXTENT, "rme9652")) == NULL) { + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) { + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme9652->irq = pci->irq; + + rme9652->iobase = (unsigned long) ioremap_nocache(rme9652->port, RME9652_IO_EXTENT); + if (rme9652->iobase == 0) { + snd_printk("unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); + return -EBUSY; + } + + rme9652->precise_ptr = precise_ptr; + + /* Determine the h/w rev level of the card. This seems like + a particularly kludgy way to encode it, but its what RME + chose to do, so we follow them ... + */ + + status = rme9652_read(rme9652, RME9652_status_register); + if (rme9652_decode_spdif_rate(status&RME9652_F) == 1) { + rme9652->hw_rev = 15; + } else { + rme9652->hw_rev = 11; + } + + /* Differentiate between the standard Hammerfall, and the + "Light", which does not have the expansion board. This + method comes from information received from Mathhias + Clausen at RME. Display the EEPROM and h/w revID where + relevant. + */ + + pci_read_config_word(rme9652->pci, PCI_CLASS_REVISION, &rev); + strcpy(card->driver, "RME9652"); + switch (rev & 0xff) { + case 8: /* original eprom */ + strcpy(card->driver, "RME9636"); + if (rme9652->hw_rev == 15) { + rme9652->card_name = "RME Digi9636 (Rev 1.5)"; + } else { + rme9652->card_name = "RME Digi9636"; + } + rme9652->ss_channels = RME9636_NCHANNELS; + break; + case 9: /* W36_G EPROM */ + strcpy(card->driver, "RME9636"); + rme9652->card_name = "RME Digi9636 (Rev G)"; + rme9652->ss_channels = RME9636_NCHANNELS; + break; + case 4: /* W52_G EPROM */ + rme9652->card_name = "RME Digi9652 (Rev G)"; + rme9652->ss_channels = RME9652_NCHANNELS; + break; + default: + case 3: /* original eprom */ + if (rme9652->hw_rev == 15) { + rme9652->card_name = "RME Digi9652 (Rev 1.5)"; + } else { + rme9652->card_name = "RME Digi9652"; + } + rme9652->ss_channels = RME9652_NCHANNELS; + break; + } + + rme9652->ds_channels = (rme9652->ss_channels - 2) / 2 + 2; + + pci_set_master(rme9652->pci); + + if ((err = snd_rme9652_initialize_memory(rme9652)) < 0) { + return err; + } + + if ((err = snd_rme9652_create_pcm(card, rme9652)) < 0) { + return err; + } + + if ((err = snd_rme9652_create_controls(card, rme9652)) < 0) { + return err; + } + + snd_rme9652_proc_init(rme9652); + + rme9652->last_spdif_sample_rate = -1; + rme9652->last_adat_sample_rate = -1; + rme9652->playback_pid = -1; + rme9652->capture_pid = -1; + rme9652->capture_substream = NULL; + rme9652->playback_substream = NULL; + + snd_rme9652_set_defaults(rme9652); + + if (rme9652->hw_rev == 15) { + rme9652_initialize_spdif_receiver (rme9652); + } + + return 0; +} + +static void snd_rme9652_card_free(snd_card_t *card) +{ + rme9652_t *rme9652 = (rme9652_t *) card->private_data; + + if (rme9652) + snd_rme9652_free(rme9652); +} + +static int __devinit snd_rme9652_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + rme9652_t *rme9652; + snd_card_t *card; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, + sizeof(rme9652_t)); + + if (!card) + return -ENOMEM; + + rme9652 = (rme9652_t *) card->private_data; + card->private_free = snd_rme9652_card_free; + rme9652->dev = dev; + rme9652->pci = pci; + + if ((err = snd_rme9652_create(card, rme9652, snd_precise_ptr[dev])) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->shortname, rme9652->card_name); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, rme9652->port, rme9652->irq); + + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_rme9652_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name:"RME Digi9652 (Hammerfall)", + id_table:snd_rme9652_ids, + probe:snd_rme9652_probe, + remove:__devexit_p(snd_rme9652_remove), +}; + +static int __init alsa_card_hammerfall_init(void) +{ + if (pci_module_init(&driver) < 0) { +#ifdef MODULE + printk(KERN_ERR "RME Digi9652/Digi9636: no cards found\n"); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit alsa_card_hammerfall_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_hammerfall_init) +module_exit(alsa_card_hammerfall_exit) + +#ifndef MODULE + +/* format is: snd-rme9652=snd_enable,snd_index,snd_id */ + +static int __init alsa_card_rme9652_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-rme9652=", alsa_card_rme9652_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/rme9652/rme9652_mem.c linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652_mem.c --- linux/sound/pci/rme9652/rme9652_mem.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/rme9652/rme9652_mem.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,278 @@ +/* + ALSA memory allocation module for the RME Digi9652 + + Copyright(c) 1999 IEM - Winfried Ritsch + Copyright (C) 1999 Paul Barton-Davis + + This module is only needed if you compiled the rme9652 driver with + the PREALLOCATE_MEMORY option. It allocates the memory need to + run the board and holds it until the module is unloaded. Because + we need 2 contiguous 1.6MB regions for the board, it can be + a problem getting them once the system memory has become fairly + fragmented. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + $Id: rme9652_mem.c,v 1.6 2002/02/04 10:21:33 tiwai Exp $ + + + Tue Oct 17 2000 Jaroslav Kysela + * space is allocated only for physical devices + * added support for 2.4 kernels (pci_alloc_consistent) + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define RME9652_CARDS 8 +#define RME9652_CHANNEL_BUFFER_SAMPLES (16*1024) +#define RME9652_CHANNEL_BUFFER_BYTES (4*RME9652_CHANNEL_BUFFER_SAMPLES) + +/* export */ + +static int snd_enable[8] = {1,1,1,1,1,1,1,1}; +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(RME9652_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable cards to allocate buffers for."); + +MODULE_AUTHOR("Winfried Ritsch, Paul Barton-Davis "); +MODULE_DESCRIPTION("Memory allocator for RME Hammerfall"); +MODULE_CLASSES("{sound}"); +MODULE_LICENSE("GPL"); + +/* Since we don't know at this point if we're allocating memory for a + Hammerfall or a Hammerfall/Light, assume the worst and allocate + space for the maximum number of channels. + + See note in rme9652.h about why we allocate for an extra channel. +*/ + +#define TOTAL_SIZE (26+1)*(RME9652_CHANNEL_BUFFER_BYTES) +#define NBUFS 2*RME9652_CARDS + +#define RME9652_BUF_ALLOCATED 0x1 +#define RME9652_BUF_USED 0x2 + +typedef struct rme9652_buf_stru rme9652_buf_t; + +struct rme9652_buf_stru { + struct pci_dev *pci; + void *buf; + dma_addr_t addr; + char flags; +}; + +static rme9652_buf_t rme9652_buffers[NBUFS]; + +/* These are here so that we have absolutely no dependencies on any + other modules. Dependencies can (1) cause us to lose in the rush + for 2x 1.6MB chunks of contiguous memory and (2) make driver + debugging difficult because unloading and reloading the snd module + causes us to have to do the same for this one. Since on 2.2 + kernels, and before, we can rarely if ever allocate memory after + starting things running, this would be bad. +*/ + +/* remove hack for pci_alloc_consistent to avoid dependecy on snd module */ +#ifdef HACK_PCI_ALLOC_CONSISTENT +#undef pci_alloc_consistent +#endif + +static void *rme9652_malloc_pages(struct pci_dev *pci, + unsigned long size, + dma_addr_t *dmaaddr) +{ + void *res; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) + res = (void *) pci_alloc_consistent(pci, size, dmaaddr); +#else + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = (void *)__get_free_pages(GFP_KERNEL, pg); + if (res != NULL) + *dmaaddr = virt_to_bus(res); +#endif + if (res != NULL) { + mem_map_t *page = virt_to_page(res); + mem_map_t *last_page = page + (size + PAGE_SIZE - 1) / PAGE_SIZE; + while (page < last_page) + set_bit(PG_reserved, &(page++)->flags); + } + return res; +} + +static void rme9652_free_pages(struct pci_dev *pci, unsigned long size, + void *ptr, dma_addr_t dmaaddr) +{ + mem_map_t *page, *last_page; + + if (ptr == NULL) + return; + page = virt_to_page(ptr); + last_page = virt_to_page(ptr) + (size + PAGE_SIZE - 1) / PAGE_SIZE; + while (page < last_page) + clear_bit(PG_reserved, &(page++)->flags); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) + pci_free_consistent(pci, size, ptr, dmaaddr); +#else + { + int pg; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if (bus_to_virt(dmaaddr) != ptr) { + printk(KERN_ERR "rme9652_free_pages: dmaaddr != ptr\n"); + return; + } + free_pages((unsigned long)ptr, pg); + } +#endif +} + +void *snd_rme9652_get_buffer (int card, dma_addr_t *dmaaddr) + +{ + int i; + rme9652_buf_t *rbuf; + + if (card < 0 || card >= RME9652_CARDS) { + printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card); + return NULL; + } + for (i = card * 2; i < card * 2 + 2; i++) { + rbuf = &rme9652_buffers[i]; + if (rbuf->flags == RME9652_BUF_ALLOCATED) { + rbuf->flags |= RME9652_BUF_USED; + MOD_INC_USE_COUNT; + *dmaaddr = rbuf->addr; + return rbuf->buf; + } + } + + return NULL; +} + +void snd_rme9652_free_buffer (int card, void *addr) + +{ + int i; + rme9652_buf_t *rbuf; + + if (card < 0 || card >= RME9652_CARDS) { + printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card); + return; + } + for (i = card * 2; i < card * 2 + 2; i++) { + rbuf = &rme9652_buffers[i]; + if (rbuf->buf == addr) { + MOD_DEC_USE_COUNT; + rbuf->flags &= ~RME9652_BUF_USED; + return; + } + } + + printk ("RME9652 memory allocator: unknown buffer address passed to free buffer"); +} + +static void __exit rme9652_free_buffers (void) + +{ + int i; + rme9652_buf_t *rbuf; + + for (i = 0; i < NBUFS; i++) { + + /* We rely on general module code to prevent + us from being unloaded with buffers in use. + + However, not quite. Do not release memory + if it is still marked as in use. This might + be unnecessary. + */ + + rbuf = &rme9652_buffers[i]; + + if (rbuf->flags == RME9652_BUF_ALLOCATED) { + rme9652_free_pages (rbuf->pci, TOTAL_SIZE, rbuf->buf, rbuf->addr); + rbuf->buf = NULL; + rbuf->flags = 0; + } + } +} + +static int __init alsa_rme9652_mem_init(void) +{ + int i; + struct pci_dev *pci; + rme9652_buf_t *rbuf; + + /* make sure our buffer records are clean */ + + for (i = 0; i < NBUFS; i++) { + rbuf = &rme9652_buffers[i]; + rbuf->pci = NULL; + rbuf->buf = NULL; + rbuf->flags = 0; + } + + /* ensure sane values for the number of buffers */ + + /* Remember: 2 buffers per card, one for capture, one for + playback. + */ + + i = 0; /* card number */ + rbuf = rme9652_buffers; + pci_for_each_dev(pci) { + int k; + if (pci->vendor != 0x10ee || pci->device != 0x3fc4) + continue; + + if (!snd_enable[i]) + continue; + + for (k = 0; k < 2; ++k) { + rbuf->buf = rme9652_malloc_pages(pci, TOTAL_SIZE, &rbuf->addr); + if (rbuf->buf == NULL) { + rme9652_free_buffers(); + printk(KERN_ERR "RME9652 memory allocator: no memory available for card %d buffer %d\n", i, k + 1); + return -ENOMEM; + } + rbuf->flags = RME9652_BUF_ALLOCATED; + rbuf++; + } + i++; + } + + if (i == 0) + printk(KERN_ERR "RME9652 memory allocator: no RME9652 card found...\n"); + + return 0; +} + +static void __exit alsa_rme9652_mem_exit(void) +{ + rme9652_free_buffers(); +} + +module_init(alsa_rme9652_mem_init) +module_exit(alsa_rme9652_mem_exit) + +EXPORT_SYMBOL(snd_rme9652_get_buffer); +EXPORT_SYMBOL(snd_rme9652_free_buffer); diff -Nru linux/sound/pci/sonicvibes.c linux-2.4.19-pre5-mjc/sound/pci/sonicvibes.c --- linux/sound/pci/sonicvibes.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/sonicvibes.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1592 @@ +/* + * Driver for S3 SonicVibes soundcard + * Copyright (c) by Jaroslav Kysela + * + * BUGS: + * It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB? + * Driver sometimes hangs... Nobody knows why at this moment... + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#ifndef LINUX_2_2 +#include +#endif + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("S3 SonicVibes PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{S3,SonicVibes PCI}}"); + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int snd_mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static unsigned int snd_dmaio = 0x7a00; /* DDMA i/o address */ + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_reverb, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_reverb, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mge, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_mge, "MIC Gain Enable for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_mge, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); +MODULE_PARM(snd_dmaio, "i"); +MODULE_PARM_DESC(snd_dmaio, "DDMA i/o base address for S3 SonicVibes soundcard."); +MODULE_PARM_SYNTAX(snd_dmaio, "global," SNDRV_PORT_DESC); + +/* + * Enhanced port direct registers + */ + +#define SV_REG(sonic, x) ((sonic)->enh_port + SV_REG_##x) + +#define SV_REG_CONTROL 0x00 /* R/W: CODEC/Mixer control register */ +#define SV_ENHANCED 0x01 /* audio mode select - enhanced mode */ +#define SV_TEST 0x02 /* test bit */ +#define SV_REVERB 0x04 /* reverb enable */ +#define SV_WAVETABLE 0x08 /* wavetable active / FM active if not set */ +#define SV_INTA 0x20 /* INTA driving - should be always 1 */ +#define SV_RESET 0x80 /* reset chip */ +#define SV_REG_IRQMASK 0x01 /* R/W: CODEC/Mixer interrupt mask register */ +#define SV_DMAA_MASK 0x01 /* mask DMA-A interrupt */ +#define SV_DMAC_MASK 0x04 /* mask DMA-C interrupt */ +#define SV_SPEC_MASK 0x08 /* special interrupt mask - should be always masked */ +#define SV_UD_MASK 0x40 /* Up/Down button interrupt mask */ +#define SV_MIDI_MASK 0x80 /* mask MIDI interrupt */ +#define SV_REG_STATUS 0x02 /* R/O: CODEC/Mixer status register */ +#define SV_DMAA_IRQ 0x01 /* DMA-A interrupt */ +#define SV_DMAC_IRQ 0x04 /* DMA-C interrupt */ +#define SV_SPEC_IRQ 0x08 /* special interrupt */ +#define SV_UD_IRQ 0x40 /* Up/Down interrupt */ +#define SV_MIDI_IRQ 0x80 /* MIDI interrupt */ +#define SV_REG_INDEX 0x04 /* R/W: CODEC/Mixer index address register */ +#define SV_MCE 0x40 /* mode change enable */ +#define SV_TRD 0x80 /* DMA transfer request disabled */ +#define SV_REG_DATA 0x05 /* R/W: CODEC/Mixer index data register */ + +/* + * Enhanced port indirect registers + */ + +#define SV_IREG_LEFT_ADC 0x00 /* Left ADC Input Control */ +#define SV_IREG_RIGHT_ADC 0x01 /* Right ADC Input Control */ +#define SV_IREG_LEFT_AUX1 0x02 /* Left AUX1 Input Control */ +#define SV_IREG_RIGHT_AUX1 0x03 /* Right AUX1 Input Control */ +#define SV_IREG_LEFT_CD 0x04 /* Left CD Input Control */ +#define SV_IREG_RIGHT_CD 0x05 /* Right CD Input Control */ +#define SV_IREG_LEFT_LINE 0x06 /* Left Line Input Control */ +#define SV_IREG_RIGHT_LINE 0x07 /* Right Line Input Control */ +#define SV_IREG_MIC 0x08 /* MIC Input Control */ +#define SV_IREG_GAME_PORT 0x09 /* Game Port Control */ +#define SV_IREG_LEFT_SYNTH 0x0a /* Left Synth Input Control */ +#define SV_IREG_RIGHT_SYNTH 0x0b /* Right Synth Input Control */ +#define SV_IREG_LEFT_AUX2 0x0c /* Left AUX2 Input Control */ +#define SV_IREG_RIGHT_AUX2 0x0d /* Right AUX2 Input Control */ +#define SV_IREG_LEFT_ANALOG 0x0e /* Left Analog Mixer Output Control */ +#define SV_IREG_RIGHT_ANALOG 0x0f /* Right Analog Mixer Output Control */ +#define SV_IREG_LEFT_PCM 0x10 /* Left PCM Input Control */ +#define SV_IREG_RIGHT_PCM 0x11 /* Right PCM Input Control */ +#define SV_IREG_DMA_DATA_FMT 0x12 /* DMA Data Format */ +#define SV_IREG_PC_ENABLE 0x13 /* Playback/Capture Enable Register */ +#define SV_IREG_UD_BUTTON 0x14 /* Up/Down Button Register */ +#define SV_IREG_REVISION 0x15 /* Revision */ +#define SV_IREG_ADC_OUTPUT_CTRL 0x16 /* ADC Output Control */ +#define SV_IREG_DMA_A_UPPER 0x18 /* DMA A Upper Base Count */ +#define SV_IREG_DMA_A_LOWER 0x19 /* DMA A Lower Base Count */ +#define SV_IREG_DMA_C_UPPER 0x1c /* DMA C Upper Base Count */ +#define SV_IREG_DMA_C_LOWER 0x1d /* DMA C Lower Base Count */ +#define SV_IREG_PCM_RATE_LOW 0x1e /* PCM Sampling Rate Low Byte */ +#define SV_IREG_PCM_RATE_HIGH 0x1f /* PCM Sampling Rate High Byte */ +#define SV_IREG_SYNTH_RATE_LOW 0x20 /* Synthesizer Sampling Rate Low Byte */ +#define SV_IREG_SYNTH_RATE_HIGH 0x21 /* Synthesizer Sampling Rate High Byte */ +#define SV_IREG_ADC_CLOCK 0x22 /* ADC Clock Source Selection */ +#define SV_IREG_ADC_ALT_RATE 0x23 /* ADC Alternative Sampling Rate Selection */ +#define SV_IREG_ADC_PLL_M 0x24 /* ADC PLL M Register */ +#define SV_IREG_ADC_PLL_N 0x25 /* ADC PLL N Register */ +#define SV_IREG_SYNTH_PLL_M 0x26 /* Synthesizer PLL M Register */ +#define SV_IREG_SYNTH_PLL_N 0x27 /* Synthesizer PLL N Register */ +#define SV_IREG_MPU401 0x2a /* MPU-401 UART Operation */ +#define SV_IREG_DRIVE_CTRL 0x2b /* Drive Control */ +#define SV_IREG_SRS_SPACE 0x2c /* SRS Space Control */ +#define SV_IREG_SRS_CENTER 0x2d /* SRS Center Control */ +#define SV_IREG_WAVE_SOURCE 0x2e /* Wavetable Sample Source Select */ +#define SV_IREG_ANALOG_POWER 0x30 /* Analog Power Down Control */ +#define SV_IREG_DIGITAL_POWER 0x31 /* Digital Power Down Control */ + +#define SV_IREG_ADC_PLL SV_IREG_ADC_PLL_M +#define SV_IREG_SYNTH_PLL SV_IREG_SYNTH_PLL_M + +/* + * DMA registers + */ + +#define SV_DMA_ADDR0 0x00 +#define SV_DMA_ADDR1 0x01 +#define SV_DMA_ADDR2 0x02 +#define SV_DMA_ADDR3 0x03 +#define SV_DMA_COUNT0 0x04 +#define SV_DMA_COUNT1 0x05 +#define SV_DMA_COUNT2 0x06 +#define SV_DMA_MODE 0x0b +#define SV_DMA_RESET 0x0d +#define SV_DMA_MASK 0x0f + +/* + * Record sources + */ + +#define SV_RECSRC_RESERVED (0x00<<5) +#define SV_RECSRC_CD (0x01<<5) +#define SV_RECSRC_DAC (0x02<<5) +#define SV_RECSRC_AUX2 (0x03<<5) +#define SV_RECSRC_LINE (0x04<<5) +#define SV_RECSRC_AUX1 (0x05<<5) +#define SV_RECSRC_MIC (0x06<<5) +#define SV_RECSRC_OUT (0x07<<5) + +/* + * constants + */ + +#define SV_FULLRATE 48000 +#define SV_REFFREQUENCY 24576000 +#define SV_ADCMULT 512 + +#define SV_MODE_PLAY 1 +#define SV_MODE_CAPTURE 2 + +/* + + */ + +typedef struct _snd_sonicvibes sonicvibes_t; +#define chip_t sonicvibes_t + +struct _snd_sonicvibes { + unsigned long dma1size; + unsigned long dma2size; + int irq; + + unsigned long sb_port; + struct resource *res_sb_port; + unsigned long enh_port; + struct resource *res_enh_port; + unsigned long synth_port; + struct resource *res_synth_port; + unsigned long midi_port; + struct resource *res_midi_port; + unsigned long game_port; + unsigned int dmaa_port; + struct resource *res_dmaa; + unsigned int dmac_port; + struct resource *res_dmac; + + unsigned char enable; + unsigned char irqmask; + unsigned char revision; + unsigned char format; + unsigned char srs_space; + unsigned char srs_center; + unsigned char mpu_switch; + unsigned char wave_source; + + unsigned int mode; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_rawmidi_t *rmidi; + snd_hwdep_t *fmsynth; /* S3FM */ + + spinlock_t reg_lock; + snd_info_entry_t *proc_entry; + + unsigned int p_dma_size; + unsigned int c_dma_size; + + snd_kcontrol_t *master_mute; + snd_kcontrol_t *master_volume; + +#ifndef LINUX_2_2 + struct gameport gameport; +#endif +}; + +static struct pci_device_id snd_sonic_ids[] __devinitdata = { + { 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_sonic_ids); + +static ratden_t sonicvibes_adc_clock = { + num_min: 4000 * 65536, + num_max: 48000UL * 65536, + num_step: 1, + den: 65536, +}; +static snd_pcm_hw_constraint_ratdens_t snd_sonicvibes_hw_constraints_adc_clock = { + nrats: 1, + rats: &sonicvibes_adc_clock, +}; + +/* + * common I/O routines + */ + +static inline void snd_sonicvibes_setdmaa(sonicvibes_t * sonic, + unsigned int addr, + unsigned int count) +{ + count--; + outl(addr, sonic->dmaa_port + SV_DMA_ADDR0); + outl(count, sonic->dmaa_port + SV_DMA_COUNT0); + outb(0x18, sonic->dmaa_port + SV_DMA_MODE); +#if 0 + printk("program dmaa: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmaa_port + SV_DMA_ADDR0)); +#endif +} + +static inline void snd_sonicvibes_setdmac(sonicvibes_t * sonic, + unsigned int addr, + unsigned int count) +{ + /* note: dmac is working in word mode!!! */ + count >>= 1; + count--; + outl(addr, sonic->dmac_port + SV_DMA_ADDR0); + outl(count, sonic->dmac_port + SV_DMA_COUNT0); + outb(0x14, sonic->dmac_port + SV_DMA_MODE); +#if 0 + printk("program dmac: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmac_port + SV_DMA_ADDR0)); +#endif +} + +static inline unsigned int snd_sonicvibes_getdmaa(sonicvibes_t * sonic) +{ + return (inl(sonic->dmaa_port + SV_DMA_COUNT0) & 0xffffff) + 1; +} + +static inline unsigned int snd_sonicvibes_getdmac(sonicvibes_t * sonic) +{ + /* note: dmac is working in word mode!!! */ + return ((inl(sonic->dmac_port + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +} + +static void snd_sonicvibes_out1(sonicvibes_t * sonic, + unsigned char reg, + unsigned char value) +{ + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + outb(value, SV_REG(sonic, DATA)); + udelay(10); +} + +static void snd_sonicvibes_out(sonicvibes_t * sonic, + unsigned char reg, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + outb(value, SV_REG(sonic, DATA)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static unsigned char snd_sonicvibes_in1(sonicvibes_t * sonic, unsigned char reg) +{ + unsigned char value; + + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + value = inb(SV_REG(sonic, DATA)); + udelay(10); + return value; +} + +static unsigned char snd_sonicvibes_in(sonicvibes_t * sonic, unsigned char reg) +{ + unsigned long flags; + unsigned char value; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(reg, SV_REG(sonic, INDEX)); + udelay(10); + value = inb(SV_REG(sonic, DATA)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return value; +} + +#ifdef CONFIG_SND_DEBUG +void snd_sonicvibes_debug(sonicvibes_t * sonic) +{ + printk("SV REGS: INDEX = 0x%02x ", inb(SV_REG(sonic, INDEX))); + printk(" STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS))); + printk(" 0x00: left input = 0x%02x ", snd_sonicvibes_in(sonic, 0x00)); + printk(" 0x20: synth rate low = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20)); + printk(" 0x01: right input = 0x%02x ", snd_sonicvibes_in(sonic, 0x01)); + printk(" 0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21)); + printk(" 0x02: left AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x02)); + printk(" 0x22: ADC clock = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22)); + printk(" 0x03: right AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x03)); + printk(" 0x23: ADC alt rate = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23)); + printk(" 0x04: left CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x04)); + printk(" 0x24: ADC pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24)); + printk(" 0x05: right CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x05)); + printk(" 0x25: ADC pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25)); + printk(" 0x06: left line = 0x%02x ", snd_sonicvibes_in(sonic, 0x06)); + printk(" 0x26: Synth pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26)); + printk(" 0x07: right line = 0x%02x ", snd_sonicvibes_in(sonic, 0x07)); + printk(" 0x27: Synth pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27)); + printk(" 0x08: MIC = 0x%02x ", snd_sonicvibes_in(sonic, 0x08)); + printk(" 0x28: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28)); + printk(" 0x09: Game port = 0x%02x ", snd_sonicvibes_in(sonic, 0x09)); + printk(" 0x29: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29)); + printk(" 0x0a: left synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0a)); + printk(" 0x2a: MPU401 = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a)); + printk(" 0x0b: right synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0b)); + printk(" 0x2b: drive ctrl = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b)); + printk(" 0x0c: left AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0c)); + printk(" 0x2c: SRS space = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c)); + printk(" 0x0d: right AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0d)); + printk(" 0x2d: SRS center = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d)); + printk(" 0x0e: left analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0e)); + printk(" 0x2e: wave source = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e)); + printk(" 0x0f: right analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0f)); + printk(" 0x2f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f)); + printk(" 0x10: left PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x10)); + printk(" 0x30: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30)); + printk(" 0x11: right PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x11)); + printk(" 0x31: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31)); + printk(" 0x12: DMA data format = 0x%02x ", snd_sonicvibes_in(sonic, 0x12)); + printk(" 0x32: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32)); + printk(" 0x13: P/C enable = 0x%02x ", snd_sonicvibes_in(sonic, 0x13)); + printk(" 0x33: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33)); + printk(" 0x14: U/D button = 0x%02x ", snd_sonicvibes_in(sonic, 0x14)); + printk(" 0x34: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34)); + printk(" 0x15: revision = 0x%02x ", snd_sonicvibes_in(sonic, 0x15)); + printk(" 0x35: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35)); + printk(" 0x16: ADC output ctrl = 0x%02x ", snd_sonicvibes_in(sonic, 0x16)); + printk(" 0x36: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36)); + printk(" 0x17: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x17)); + printk(" 0x37: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37)); + printk(" 0x18: DMA A upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x18)); + printk(" 0x38: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38)); + printk(" 0x19: DMA A lower cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x19)); + printk(" 0x39: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39)); + printk(" 0x1a: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1a)); + printk(" 0x3a: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a)); + printk(" 0x1b: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1b)); + printk(" 0x3b: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b)); + printk(" 0x1c: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1c)); + printk(" 0x3c: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c)); + printk(" 0x1d: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1d)); + printk(" 0x3d: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d)); + printk(" 0x1e: PCM rate low = 0x%02x ", snd_sonicvibes_in(sonic, 0x1e)); + printk(" 0x3e: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e)); + printk(" 0x1f: PCM rate high = 0x%02x ", snd_sonicvibes_in(sonic, 0x1f)); + printk(" 0x3f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f)); +} + +#endif + +static void snd_sonicvibes_setfmt(sonicvibes_t * sonic, + unsigned char mask, + unsigned char value) +{ + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX)); + if (mask) { + sonic->format = inb(SV_REG(sonic, DATA)); + udelay(10); + } + sonic->format = (sonic->format & mask) | value; + outb(sonic->format, SV_REG(sonic, DATA)); + udelay(10); + outb(0, SV_REG(sonic, INDEX)); + udelay(10); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static void snd_sonicvibes_pll(unsigned int rate, + unsigned int *res_r, + unsigned int *res_m, + unsigned int *res_n) +{ + unsigned int r, m = 0, n = 0; + unsigned int xm, xn, xr, xd, metric = ~0U; + + if (rate < 625000 / SV_ADCMULT) + rate = 625000 / SV_ADCMULT; + if (rate > 150000000 / SV_ADCMULT) + rate = 150000000 / SV_ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000 / SV_ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 33; xn++) /* 35 */ + for (xm = 3; xm < 257; xm++) { + xr = ((SV_REFFREQUENCY / SV_ADCMULT) * xm) / xn; + if (xr >= rate) + xd = xr - rate; + else + xd = rate - xr; + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + *res_r = r; + *res_m = m; + *res_n = n; +#if 0 + printk("metric = %i, xm = %i, xn = %i\n", metric, xm, xn); + printk("pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n); +#endif +} + +static void snd_sonicvibes_setpll(sonicvibes_t * sonic, + unsigned char reg, + unsigned int rate) +{ + unsigned long flags; + unsigned int r, m, n; + + snd_sonicvibes_pll(rate, &r, &m, &n); + if (sonic != NULL) { + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, reg, m); + snd_sonicvibes_out1(sonic, reg + 1, r | n); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + } +} + +static void snd_sonicvibes_set_adc_rate(sonicvibes_t * sonic, unsigned int rate) +{ + unsigned long flags; + unsigned int div; + unsigned char clock; + + div = 48000 / rate; + if (div > 8) + div = 8; + if ((48000 / div) == rate) { /* use the alternate clock */ + clock = 0x10; + } else { /* use the PLL source */ + clock = 0x00; + snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate); + } + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4); + snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static int snd_sonicvibes_hw_constraint_dac_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int rate, div, r, m, n; + + if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min == + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max) { + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min; + div = 48000 / rate; + if (div > 8) + div = 8; + if ((48000 / div) == rate) { + params->rate_num = rate; + params->rate_den = 1; + } else { + snd_sonicvibes_pll(rate, &r, &m, &n); + snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL); + snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL); + params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r; + params->rate_den = (SV_ADCMULT/512) * (m+2); + } + } + return 0; +} + +static void snd_sonicvibes_set_dac_rate(sonicvibes_t * sonic, unsigned int rate) +{ + unsigned int div; + unsigned long flags; + + div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div); + spin_unlock_irqrestore(&sonic->reg_lock, flags); +} + +static int snd_sonicvibes_trigger(sonicvibes_t * sonic, int what, int cmd) +{ + int result = 0; + + spin_lock(&sonic->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!(sonic->enable & what)) { + sonic->enable |= what; + snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); + } + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + if (sonic->enable & what) { + sonic->enable &= ~what; + snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); + } + } else { + result = -EINVAL; + } + spin_unlock(&sonic->reg_lock); + return result; +} + +static void snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id, return); + unsigned char status; + + status = inb(SV_REG(sonic, STATUS)); + if (!(status & (SV_DMAA_IRQ | SV_DMAC_IRQ | SV_MIDI_IRQ))) + return; + if (status == 0xff) { /* failure */ + outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK)); + snd_printk("IRQ failure - interrupts disabled!!\n"); + return; + } + if (sonic->pcm) { + if (status & SV_DMAA_IRQ) + snd_pcm_period_elapsed(sonic->playback_substream); + if (status & SV_DMAC_IRQ) + snd_pcm_period_elapsed(sonic->capture_substream); + } + if (sonic->rmidi) { + if (status & SV_MIDI_IRQ) + snd_mpu401_uart_interrupt(irq, sonic->rmidi->private_data, regs); + } + if (status & SV_UD_IRQ) { + unsigned char udreg; + int vol, oleft, oright, mleft, mright; + + spin_lock(&sonic->reg_lock); + udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON); + vol = udreg & 0x3f; + if (!(udreg & 0x40)) + vol = -vol; + oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG); + oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG); + oleft &= 0x1f; + oright &= 0x1f; + oleft += vol; + if (oleft < 0) + oleft = 0; + if (oleft > 0x1f) + oleft = 0x1f; + oright += vol; + if (oright < 0) + oright = 0; + if (oright > 0x1f) + oright = 0x1f; + if (udreg & 0x80) { + mleft ^= 0x80; + mright ^= 0x80; + } + oleft |= mleft & 0x80; + oright |= mright & 0x80; + snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft); + snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright); + spin_unlock(&sonic->reg_lock); + snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id); + snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id); + } +} + +/* + * PCM part + */ + +static int snd_sonicvibes_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + return snd_sonicvibes_trigger(sonic, 1, cmd); +} + +static int snd_sonicvibes_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + return snd_sonicvibes_trigger(sonic, 2, cmd); +} + +static int snd_sonicvibes_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_sonicvibes_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_sonicvibes_playback_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char fmt = 0; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + sonic->p_dma_size = size; + count--; + if (runtime->channels > 1) + fmt |= 1; + if (snd_pcm_format_width(runtime->format) == 16) + fmt |= 2; + snd_sonicvibes_setfmt(sonic, ~3, fmt); + snd_sonicvibes_set_dac_rate(sonic, runtime->rate); + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static int snd_sonicvibes_capture_prepare(snd_pcm_substream_t * substream) +{ + unsigned long flags; + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned char fmt = 0; + unsigned int size = snd_pcm_lib_buffer_bytes(substream); + unsigned int count = snd_pcm_lib_period_bytes(substream); + + sonic->c_dma_size = size; + count >>= 1; + count--; + if (runtime->channels > 1) + fmt |= 0x10; + if (snd_pcm_format_width(runtime->format) == 16) + fmt |= 0x20; + snd_sonicvibes_setfmt(sonic, ~0x30, fmt); + snd_sonicvibes_set_adc_rate(sonic, runtime->rate); + spin_lock_irqsave(&sonic->reg_lock, flags); + snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8); + snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static snd_pcm_uframes_t snd_sonicvibes_playback_pointer(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + size_t ptr; + + if (!(sonic->enable & 1)) + return 0; + ptr = sonic->p_dma_size - snd_sonicvibes_getdmaa(sonic); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_sonicvibes_capture_pointer(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + size_t ptr; + if (!(sonic->enable & 2)) + return 0; + ptr = sonic->c_dma_size - snd_sonicvibes_getdmac(sonic); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_hardware_t snd_sonicvibes_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 32, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_sonicvibes_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 32, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static int snd_sonicvibes_playback_open(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + sonic->mode |= SV_MODE_PLAY; + sonic->playback_substream = substream; + runtime->hw = snd_sonicvibes_playback; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_sonicvibes_hw_constraint_dac_rate, 0, SNDRV_PCM_HW_PARAM_RATE, -1); + return 0; +} + +static int snd_sonicvibes_capture_open(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + sonic->mode |= SV_MODE_CAPTURE; + sonic->capture_substream = substream; + runtime->hw = snd_sonicvibes_capture; + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_sonicvibes_hw_constraints_adc_clock); + return 0; +} + +static int snd_sonicvibes_playback_close(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + + sonic->playback_substream = NULL; + sonic->mode &= ~SV_MODE_PLAY; + return 0; +} + +static int snd_sonicvibes_capture_close(snd_pcm_substream_t * substream) +{ + sonicvibes_t *sonic = snd_pcm_substream_chip(substream); + + sonic->capture_substream = NULL; + sonic->mode &= ~SV_MODE_CAPTURE; + return 0; +} + +static snd_pcm_ops_t snd_sonicvibes_playback_ops = { + open: snd_sonicvibes_playback_open, + close: snd_sonicvibes_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sonicvibes_hw_params, + hw_free: snd_sonicvibes_hw_free, + prepare: snd_sonicvibes_playback_prepare, + trigger: snd_sonicvibes_playback_trigger, + pointer: snd_sonicvibes_playback_pointer, +}; + +static snd_pcm_ops_t snd_sonicvibes_capture_ops = { + open: snd_sonicvibes_capture_open, + close: snd_sonicvibes_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_sonicvibes_hw_params, + hw_free: snd_sonicvibes_hw_free, + prepare: snd_sonicvibes_capture_prepare, + trigger: snd_sonicvibes_capture_trigger, + pointer: snd_sonicvibes_capture_pointer, +}; + +static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, pcm->private_data, return); + sonic->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_sonicvibes_pcm(sonicvibes_t * sonic, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0) + return err; + snd_assert(pcm != NULL, return -EINVAL); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops); + + pcm->private_data = sonic; + pcm->private_free = snd_sonicvibes_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "S3 SonicVibes"); + sonic->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer part + */ + +#define SONICVIBES_MUX(xname, xindex) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_sonicvibes_info_mux, \ + get: snd_sonicvibes_get_mux, put: snd_sonicvibes_put_mux } + +static int snd_sonicvibes_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + static char *texts[7] = { + "CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 2; + uinfo->value.enumerated.items = 7; + if (uinfo->value.enumerated.item >= 7) + uinfo->value.enumerated.item = 6; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_sonicvibes_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1; + ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return 0; +} + +static int snd_sonicvibes_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned short left, right, oval1, oval2; + int change; + + if (ucontrol->value.enumerated.item[0] >= 7 || + ucontrol->value.enumerated.item[1] >= 7) + return -EINVAL; + left = (ucontrol->value.enumerated.item[0] + 1) << 5; + right = (ucontrol->value.enumerated.item[1] + 1) << 5; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC); + oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC); + left = (oval1 & ~SV_RECSRC_OUT) | left; + right = (oval2 & ~SV_RECSRC_OUT) | right; + change = left != oval1 || right != oval2; + snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left); + snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_sonicvibes_info_single, \ + get: snd_sonicvibes_get_single, put: snd_sonicvibes_put_single, \ + private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) } + +static int snd_sonicvibes_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sonicvibes_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_sonicvibes_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change; + unsigned short val, oval; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval = snd_sonicvibes_in1(sonic, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + snd_sonicvibes_out1(sonic, reg, val); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_sonicvibes_info_double, \ + get: snd_sonicvibes_get_double, put: snd_sonicvibes_put_double, \ + private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static int snd_sonicvibes_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_sonicvibes_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&sonic->reg_lock, flags); + ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask; + ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask; + spin_unlock_irqrestore(&sonic->reg_lock, flags); + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_sonicvibes_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change; + unsigned short val1, val2, oval1, oval2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&sonic->reg_lock, flags); + oval1 = snd_sonicvibes_in1(sonic, left_reg); + oval2 = snd_sonicvibes_in1(sonic, right_reg); + val1 = (oval1 & ~(mask << shift_left)) | val1; + val2 = (oval2 & ~(mask << shift_right)) | val2; + change = val1 != oval1 || val2 != oval2; + snd_sonicvibes_out1(sonic, left_reg, val1); + snd_sonicvibes_out1(sonic, right_reg, val2); + spin_unlock_irqrestore(&sonic->reg_lock, flags); + return change; +} + +#define SONICVIBES_CONTROLS (sizeof(snd_sonicvibes_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sonicvibes_controls[] __devinitdata = { +SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0), +SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1), +SONICVIBES_DOUBLE("CD Playback Switch", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 7, 7, 1, 1), +SONICVIBES_DOUBLE("CD Playback Volume", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Line Playback Switch", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Line Playback Volume", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 0, 0, 31, 1), +SONICVIBES_SINGLE("Mic Playback Switch", 0, SV_IREG_MIC, 7, 1, 1), +SONICVIBES_SINGLE("Mic Playback Volume", 0, SV_IREG_MIC, 0, 15, 1), +SONICVIBES_SINGLE("Mic Boost", 0, SV_IREG_LEFT_ADC, 4, 1, 0), +SONICVIBES_DOUBLE("Synth Playback Switch", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Synth Playback Volume", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Aux Playback Switch", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Aux Playback Volume", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 0, 0, 31, 1), +SONICVIBES_DOUBLE("Master Playback Switch", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 7, 7, 1, 1), +SONICVIBES_DOUBLE("Master Playback Volume", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 0, 0, 31, 1), +SONICVIBES_DOUBLE("PCM Playback Switch", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 7, 7, 1, 1), +SONICVIBES_DOUBLE("PCM Playback Volume", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 0, 0, 63, 1), +SONICVIBES_SINGLE("Loopback Capture Switch", 0, SV_IREG_ADC_OUTPUT_CTRL, 0, 1, 0), +SONICVIBES_SINGLE("Loopback Capture Volume", 0, SV_IREG_ADC_OUTPUT_CTRL, 2, 63, 1), +SONICVIBES_MUX("Capture Source", 0) +}; + +static void snd_sonicvibes_master_free(snd_kcontrol_t *kcontrol) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, _snd_kcontrol_chip(kcontrol), return); + sonic->master_mute = NULL; + sonic->master_volume = NULL; +} + +static int __devinit snd_sonicvibes_mixer(sonicvibes_t * sonic) +{ + snd_card_t *card; + snd_kcontrol_t *kctl; + int idx, err; + + snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL); + card = sonic->card; + strcpy(card->mixername, "S3 SonicVibes"); + + for (idx = 0; idx < SONICVIBES_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0) + return err; + switch (idx) { + case 0: + case 1: kctl->private_free = snd_sonicvibes_master_free; break; + } + } + return 0; +} + +/* + + */ + +static void snd_sonicvibes_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, entry->private_data, return); + unsigned char tmp; + + tmp = sonic->srs_space & 0x0f; + snd_iprintf(buffer, "SRS 3D : %s\n", + sonic->srs_space & 0x80 ? "off" : "on"); + snd_iprintf(buffer, "SRS Space : %s\n", + tmp == 0x00 ? "100%" : + tmp == 0x01 ? "75%" : + tmp == 0x02 ? "50%" : + tmp == 0x03 ? "25%" : "0%"); + tmp = sonic->srs_center & 0x0f; + snd_iprintf(buffer, "SRS Center : %s\n", + tmp == 0x00 ? "100%" : + tmp == 0x01 ? "75%" : + tmp == 0x02 ? "50%" : + tmp == 0x03 ? "25%" : "0%"); + tmp = sonic->wave_source & 0x03; + snd_iprintf(buffer, "WaveTable Source : %s\n", + tmp == 0x00 ? "on-board ROM" : + tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus"); + tmp = sonic->mpu_switch; + snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off"); + snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off"); + snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off"); +} + +static void __devinit snd_sonicvibes_proc_init(sonicvibes_t * sonic) +{ + snd_info_entry_t *entry; + + if ((entry = snd_info_create_card_entry(sonic->card, "sonicvibes", sonic->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = sonic; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_sonicvibes_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + sonic->proc_entry = entry; +} + +static void snd_sonicvibes_proc_done(sonicvibes_t * sonic) +{ + if (sonic->proc_entry) { + snd_info_unregister(sonic->proc_entry); + sonic->proc_entry = NULL; + } +} + +/* + + */ + +static snd_kcontrol_new_t snd_sonicvibes_game_control __devinitdata = +SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0); + +static int snd_sonicvibes_free(sonicvibes_t *sonic) +{ +#ifndef LINUX_2_2 + if (sonic->gameport.io) + gameport_unregister_port(&sonic->gameport); +#endif + snd_sonicvibes_proc_done(sonic); + pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port); + pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port); + if (sonic->res_sb_port) { + release_resource(sonic->res_sb_port); + kfree_nocheck(sonic->res_sb_port); + } + if (sonic->res_enh_port) { + release_resource(sonic->res_enh_port); + kfree_nocheck(sonic->res_enh_port); + } + if (sonic->res_synth_port) { + release_resource(sonic->res_synth_port); + kfree_nocheck(sonic->res_synth_port); + } + if (sonic->res_midi_port) { + release_resource(sonic->res_midi_port); + kfree_nocheck(sonic->res_midi_port); + } + if (sonic->res_dmaa) { + release_resource(sonic->res_dmaa); + kfree_nocheck(sonic->res_dmaa); + } + if (sonic->res_dmac) { + release_resource(sonic->res_dmac); + kfree_nocheck(sonic->res_dmac); + } + if (sonic->irq >= 0) + free_irq(sonic->irq, (void *)sonic); + snd_magic_kfree(sonic); + return 0; +} + +static int snd_sonicvibes_dev_free(snd_device_t *device) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, device->device_data, return -ENXIO); + return snd_sonicvibes_free(sonic); +} + +static int __devinit snd_sonicvibes_create(snd_card_t * card, + struct pci_dev *pci, + int reverb, + int mge, + sonicvibes_t ** rsonic) +{ + sonicvibes_t *sonic; + unsigned int dmaa, dmac; + int err; + static snd_device_ops_t ops = { + dev_free: snd_sonicvibes_dev_free, + }; + + *rsonic = NULL; + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 24 bits */ + if (!pci_dma_supported(pci, 0x00ffffff)) { + snd_printk("architecture does not support 24bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x00ffffff); + + sonic = snd_magic_kcalloc(sonicvibes_t, 0, GFP_KERNEL); + if (sonic == NULL) + return -ENOMEM; + spin_lock_init(&sonic->reg_lock); + sonic->card = card; + sonic->pci = pci; + sonic->irq = -1; + sonic->sb_port = pci_resource_start(pci, 0); + if ((sonic->res_sb_port = request_region(sonic->sb_port, 0x10, "S3 SonicVibes SB")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab SB port at 0x%lx-0x%lx\n", sonic->sb_port, sonic->sb_port + 0x10 - 1); + return -EBUSY; + } + sonic->enh_port = pci_resource_start(pci, 1); + if ((sonic->res_enh_port = request_region(sonic->enh_port, 0x10, "S3 SonicVibes Enhanced")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab PCM port at 0x%lx-0x%lx\n", sonic->enh_port, sonic->enh_port + 0x10 - 1); + return -EBUSY; + } + sonic->synth_port = pci_resource_start(pci, 2); + if ((sonic->res_synth_port = request_region(sonic->synth_port, 4, "S3 SonicVibes Synth")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab synth port at 0x%lx-0x%lx\n", sonic->synth_port, sonic->synth_port + 4 - 1); + return -EBUSY; + } + sonic->midi_port = pci_resource_start(pci, 3); + if ((sonic->res_midi_port = request_region(sonic->midi_port, 4, "S3 SonicVibes Midi")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab MIDI port at 0x%lx-0x%lx\n", sonic->midi_port, sonic->midi_port + 4 - 1); + return -EBUSY; + } + sonic->game_port = pci_resource_start(pci, 4); + if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) { + snd_magic_kfree(sonic); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + sonic->irq = pci->irq; + + pci_read_config_dword(pci, 0x40, &dmaa); + pci_read_config_dword(pci, 0x48, &dmac); + snd_dmaio &= ~0x0f; + dmaa &= ~0x0f; + dmac &= ~0x0f; + if (!dmaa) { + dmaa = snd_dmaio; + snd_dmaio += 0x10; + snd_printk("BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", dmaa); + } + if (!dmac) { + dmac = snd_dmaio; + snd_dmaio += 0x10; + snd_printk("BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", dmac); + } + pci_write_config_dword(pci, 0x40, dmaa); + pci_write_config_dword(pci, 0x48, dmac); + + if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab DDMA-A port at 0x%x-0x%x\n", dmaa, dmaa + 0x10 - 1); + return -EBUSY; + } + if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) { + snd_sonicvibes_free(sonic); + snd_printk("unable to grab DDMA-C port at 0x%x-0x%x\n", dmac, dmac + 0x10 - 1); + return -EBUSY; + } + + pci_read_config_dword(pci, 0x40, &sonic->dmaa_port); + pci_read_config_dword(pci, 0x48, &sonic->dmac_port); + sonic->dmaa_port &= ~0x0f; + sonic->dmac_port &= ~0x0f; + pci_write_config_dword(pci, 0x40, sonic->dmaa_port | 9); /* enable + enhanced */ + pci_write_config_dword(pci, 0x48, sonic->dmac_port | 9); /* enable */ + /* ok.. initialize S3 SonicVibes chip */ + outb(SV_RESET, SV_REG(sonic, CONTROL)); /* reset chip */ + udelay(100); + outb(0, SV_REG(sonic, CONTROL)); /* release reset */ + udelay(100); + outb(SV_ENHANCED | SV_INTA | (reverb ? SV_REVERB : 0), SV_REG(sonic, CONTROL)); + inb(SV_REG(sonic, STATUS)); /* clear IRQs */ +#if 1 + snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0); /* drive current 16mA */ +#else + snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0x40); /* drive current 8mA */ +#endif + snd_sonicvibes_out(sonic, SV_IREG_PC_ENABLE, sonic->enable = 0); /* disable playback & capture */ + outb(sonic->irqmask = ~(SV_DMAA_MASK | SV_DMAC_MASK | SV_UD_MASK), SV_REG(sonic, IRQMASK)); + inb(SV_REG(sonic, STATUS)); /* clear IRQs */ + snd_sonicvibes_out(sonic, SV_IREG_ADC_CLOCK, 0); /* use PLL as clock source */ + snd_sonicvibes_out(sonic, SV_IREG_ANALOG_POWER, 0); /* power up analog parts */ + snd_sonicvibes_out(sonic, SV_IREG_DIGITAL_POWER, 0); /* power up digital parts */ + snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, 8000); + snd_sonicvibes_out(sonic, SV_IREG_SRS_SPACE, sonic->srs_space = 0x80); /* SRS space off */ + snd_sonicvibes_out(sonic, SV_IREG_SRS_CENTER, sonic->srs_center = 0x00);/* SRS center off */ + snd_sonicvibes_out(sonic, SV_IREG_MPU401, sonic->mpu_switch = 0x05); /* MPU-401 switch */ + snd_sonicvibes_out(sonic, SV_IREG_WAVE_SOURCE, sonic->wave_source = 0x00); /* onboard ROM */ + snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_LOW, (8000 * 65536 / SV_FULLRATE) & 0xff); + snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_HIGH, ((8000 * 65536 / SV_FULLRATE) >> 8) & 0xff); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_ADC, mge ? 0xd0 : 0xc0); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ADC, 0xc0); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX1, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX1, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_CD, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_CD, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_LINE, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_LINE, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_MIC, 0x8f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_SYNTH, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_SYNTH, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX2, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX2, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_ANALOG, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ANALOG, 0x9f); + snd_sonicvibes_out(sonic, SV_IREG_LEFT_PCM, 0xbf); + snd_sonicvibes_out(sonic, SV_IREG_RIGHT_PCM, 0xbf); + snd_sonicvibes_out(sonic, SV_IREG_ADC_OUTPUT_CTRL, 0xfc); +#if 0 + snd_sonicvibes_debug(sonic); +#endif + sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION); + snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic)); + snd_sonicvibes_proc_init(sonic); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) { + snd_sonicvibes_free(sonic); + return err; + } + + *rsonic = sonic; + return 0; +} + +/* + * MIDI section + */ + +#define SONICVIBES_MIDI_CONTROLS (sizeof(snd_sonicvibes_midi_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_sonicvibes_midi_controls[] __devinitdata = { +SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0), +SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0), +SONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0), +SONICVIBES_SINGLE("SonicVibes External Rx to Synth", 0, SV_IREG_MPU401, 1, 1, 0), +SONICVIBES_SINGLE("SonicVibes External Tx", 0, SV_IREG_MPU401, 2, 1, 0) +}; + +static int snd_sonicvibes_midi_input_open(mpu401_t * mpu) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return -EIO); + outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); + return 0; +} + +static void snd_sonicvibes_midi_input_close(mpu401_t * mpu) +{ + sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return); + outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); +} + +static int __devinit snd_sonicvibes_midi(sonicvibes_t * sonic, snd_rawmidi_t * rmidi) +{ + mpu401_t * mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENXIO); + snd_card_t *card = sonic->card; + snd_rawmidi_str_t *dir; + int idx, err; + + mpu->private_data = sonic; + mpu->open_input = snd_sonicvibes_midi_input_open; + mpu->close_input = snd_sonicvibes_midi_input_close; + dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + for (idx = 0; idx < SONICVIBES_MIDI_CONTROLS; idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0) + return err; + return 0; +} + +static int __devinit snd_sonic_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + sonicvibes_t *sonic; + snd_rawmidi_t *midi_uart; + opl3_t *opl3; + int idx, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + for (idx = 0; idx < 5; idx++) { + if (pci_resource_start(pci, idx) == 0 || + !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) { + snd_card_free(card); + return -ENODEV; + } + } + if ((err = snd_sonicvibes_create(card, pci, + snd_reverb[dev] ? 1 : 0, + snd_mge[dev] ? 1 : 0, + &sonic)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_sonicvibes_mixer(sonic)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES, + sonic->midi_port, 1, + sonic->irq, 0, + &midi_uart)) < 0) { + snd_card_free(card); + return err; + } + snd_sonicvibes_midi(sonic, midi_uart); + if ((err = snd_opl3_create(card, sonic->synth_port, + sonic->synth_port + 2, + OPL3_HW_OPL3_SV, 1, &opl3)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } +#ifndef LINUX_2_2 + sonic->gameport.io = sonic->game_port; + gameport_register_port(&sonic->gameport); +#endif + strcpy(card->driver, "SonicVibes"); + strcpy(card->shortname, "S3 SonicVibes"); + sprintf(card->longname, "%s rev %i at 0x%lx, irq %i", + card->shortname, + sonic->revision, + pci_resource_start(pci, 1), + sonic->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_sonic_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "S3 SonicVibes", + id_table: snd_sonic_ids, + probe: snd_sonic_probe, + remove: __devexit_p(snd_sonic_remove), +}; + +static int __init alsa_card_sonicvibes_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "S3 SonicVibes soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_sonicvibes_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_sonicvibes_init) +module_exit(alsa_card_sonicvibes_exit) + +#ifndef MODULE + +/* format is: snd-sonicvibes=snd_enable,snd_index,snd_id, + snd_reverb,snd_mge,snd_dmaio */ + +static int __init alsa_card_sonicvibes_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_reverb[nr_dev]) == 2 && + get_option(&str,&snd_mge[nr_dev]) == 2 && + get_option(&str,(int *)&snd_dmaio) == 2); + nr_dev++; + return 1; +} + +__setup("snd-sonicvibes=", alsa_card_sonicvibes_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/trident/Makefile linux-2.4.19-pre5-mjc/sound/pci/trident/Makefile --- linux/sound/pci/trident/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/trident/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _trident.o + +list-multi := snd-trident.o snd-trident-synth.o + +export-objs := trident_main.o + +snd-trident-objs := trident.o trident_main.o trident_memory.o +snd-trident-synth-objs := trident_synth.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_TRIDENT) += snd-trident.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_TRIDENT) += snd-trident-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-trident.o: $(snd-trident-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-objs) + +snd-trident-synth.o: $(snd-trident-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-trident-synth-objs) diff -Nru linux/sound/pci/trident/trident.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident.c --- linux/sound/pci/trident/trident.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,263 @@ +/* + * Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard + * + * Driver was originated by Trident + * Fri Feb 19 15:55:28 MST 1999 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +MODULE_AUTHOR("Jaroslav Kysela , "); +MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Trident,4DWave DX}," + "{Trident,4DWave NX}," + "{SiS,SI7018 PCI Audio}," + "{Best Union,Miss Melody 4DWave PCI}," + "{HIS,4DWave PCI}," + "{Warpspeed,ONSpeed 4DWave PCI}," + "{Aztech Systems,PCI 64-Q3D}," + "{Addonics,SV 750}," + "{CHIC,True Sound 4Dwave}," + "{Shark,Predator4D-PCI}," + "{Jaton,SonicWave 4D}," + "{Hoontech,SoundTrack Digital 4DWave NX}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32}; +static int snd_wavetable_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8192}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Trident 4DWave PCI soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pcm_channels, "Number of hardware channels assigned for PCM."); +MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); +MODULE_PARM(snd_wavetable_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_wavetable_size, "Maximum memory size in kB for wavetable synth."); +MODULE_PARM_SYNTAX(snd_wavetable_size, SNDRV_ENABLED ",default:8192,skill:advanced"); + +static struct pci_device_id snd_trident_ids[] __devinitdata = { + { 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave DX PCI Audio */ + { 0x1023, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave NX PCI Audio */ + { 0x1039, 0x7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* SiS SI7018 PCI Audio */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_trident_ids); + +static int __devinit snd_trident_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + trident_t *trident; + const char *str; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_trident_create(card, pci, + snd_pcm_channels[dev], + 2, + snd_wavetable_size[dev], + &trident)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_trident_pcm(trident, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + if ((err = snd_trident_foldback_pcm(trident, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + break; + } + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if ((err = snd_trident_spdif_pcm(trident, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, + trident->midi_port, 1, + trident->irq, 0, &trident->rmidi)) < 0) { + snd_card_free(card); + return err; + } + +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if ((err = snd_trident_attach_synthesizer(trident)) < 0) { + snd_card_free(card); + return err; + } +#endif + + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + str = "TRID4DWAVEDX"; + break; + case TRIDENT_DEVICE_ID_NX: + str = "TRID4DWAVENX"; + break; + case TRIDENT_DEVICE_ID_SI7018: + str = "SI7018"; + break; + default: + str = "Unknown"; + } + strcpy(card->driver, str); + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + strcpy(card->shortname, "SiS "); + } else { + strcpy(card->shortname, "Trident "); + } + strcat(card->shortname, card->driver); + sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", + card->shortname, trident->port, trident->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, trident); + dev++; + return 0; +} + +static void __devexit snd_trident_remove(struct pci_dev *pci) +{ + trident_t *trident = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + if (trident) + snd_card_free(trident->card); + pci_set_drvdata(pci, NULL); +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_trident_suspend(struct pci_dev *pci, u32 state) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO); + snd_trident_suspend(chip); + return 0; +} +static int snd_card_trident_resume(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return -ENXIO); + snd_trident_resume(chip); + return 0; +} +#else +static void snd_card_trident_suspend(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + snd_trident_suspend(chip); +} +static void snd_card_trident_resume(struct pci_dev *pci) +{ + trident_t *chip = snd_magic_cast(trident_t, pci_get_drvdata(pci), return); + snd_trident_resume(chip); +} +#endif +#endif + +static struct pci_driver driver = { + name: "Trident4DWaveAudio", + id_table: snd_trident_ids, + probe: snd_trident_probe, + remove: __devexit_p(snd_trident_remove), +#ifdef CONFIG_PM + suspend: snd_card_trident_suspend, + resume: snd_card_trident_resume, +#endif +}; + +static int __init alsa_card_trident_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Trident 4DWave PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_trident_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_trident_init) +module_exit(alsa_card_trident_exit) + +#ifndef MODULE + +/* format is: snd-trident=snd_enable,snd_index,snd_id, + snd_pcm_channels,snd_wavetable_size */ + +static int __init alsa_card_trident_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_pcm_channels[nr_dev]) == 2 && + get_option(&str,&snd_wavetable_size[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-trident=", alsa_card_trident_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/trident/trident_main.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident_main.c --- linux/sound/pci/trident/trident_main.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident_main.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,3636 @@ +/* + * Maintained by Jaroslav Kysela + * Originated by audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Routines for control of Trident 4DWave (DX and NX) chip + * + * BUGS: + * + * TODO: + * --- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t trident_t + +static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); +static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); +static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs); +#ifdef CONFIG_PM +static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state); +#endif + +/* + * common I/O routines + */ + + +#if 0 +static void snd_trident_print_voice_regs(trident_t *trident, int voice) +{ + unsigned int val, tmp; + + printk("Trident voice %i:\n", voice); + outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR)); + val = inl(TRID_REG(trident, CH_LBA)); + printk("LBA: 0x%x\n", val); + val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + printk("GVSel: %i\n", val >> 31); + printk("Pan: 0x%x\n", (val >> 24) & 0x7f); + printk("Vol: 0x%x\n", (val >> 16) & 0xff); + printk("CTRL: 0x%x\n", (val >> 12) & 0x0f); + printk("EC: 0x%x\n", val & 0x0fff); + if (trident->device != TRIDENT_DEVICE_ID_NX) { + val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS)); + printk("CSO: 0x%x\n", val >> 16); + printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff); + printk("FMS: 0x%x\n", val & 0x0f); + val = inl(TRID_REG(trident, CH_DX_ESO_DELTA)); + printk("ESO: 0x%x\n", val >> 16); + printk("Delta: 0x%x\n", val & 0xffff); + val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); + } else { // TRIDENT_DEVICE_ID_NX + val = inl(TRID_REG(trident, CH_NX_DELTA_CSO)); + tmp = (val >> 24) & 0xff; + printk("CSO: 0x%x\n", val & 0x00ffffff); + val = inl(TRID_REG(trident, CH_NX_DELTA_ESO)); + tmp |= (val >> 16) & 0xff00; + printk("Delta: 0x%x\n", tmp); + printk("ESO: 0x%x\n", val & 0x00ffffff); + val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL)); + printk("Alpha: 0x%x\n", val >> 20); + printk("FMS: 0x%x\n", (val >> 16) & 0x0f); + } + printk("FMC: 0x%x\n", (val >> 14) & 3); + printk("RVol: 0x%x\n", (val >> 7) & 0x7f); + printk("CVol: 0x%x\n", val & 0x7f); +} +#endif + +/*--------------------------------------------------------------------------- + unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg) + + Description: This routine will do all of the reading from the external + CODEC (AC97). + + Parameters: ac97 - ac97 codec structure + reg - CODEC register index, from AC97 Hal. + + returns: 16 bit value read from the AC97. + + ---------------------------------------------------------------------------*/ +static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg) +{ + unsigned int data = 0, treg; + unsigned short count = 0xffff; + unsigned long flags; + trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return -ENXIO); + + spin_lock_irqsave(&trident->reg_lock, flags); + if (trident->device == TRIDENT_DEVICE_ID_DX) { + data = (DX_AC97_BUSY_READ | (reg & 0x000000ff)); + outl(data, TRID_REG(trident, DX_ACR1_AC97_R)); + do { + data = inl(TRID_REG(trident, DX_ACR1_AC97_R)); + if ((data & DX_AC97_BUSY_READ) == 0) + break; + } while (--count); + } else if (trident->device == TRIDENT_DEVICE_ID_NX) { + data = (NX_AC97_BUSY_READ | (reg & 0x000000ff)); + treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY; + outl(data, TRID_REG(trident, treg)); + do { + data = inl(TRID_REG(trident, treg)); + if ((data & 0x00000C00) == 0) + break; + } while (--count); + } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); + if (ac97->num == 1) + data |= SI_AC97_SECONDARY; + outl(data, TRID_REG(trident, SI_AC97_READ)); + do { + data = inl(TRID_REG(trident, SI_AC97_READ)); + if ((data & (SI_AC97_BUSY_READ)) == 0) + break; + } while (--count); + } + + if (count == 0) { + snd_printk("ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data); + data = 0; + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return ((unsigned short) (data >> 16)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata) + + Description: This routine will do all of the writing to the external + CODEC (AC97). + + Parameters: ac97 - ac97 codec structure + reg - CODEC register index, from AC97 Hal. + data - Lower 16 bits are the data to write to CODEC. + + returns: TRUE if everything went ok, else FALSE. + + ---------------------------------------------------------------------------*/ +static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata) +{ + unsigned int address, data; + unsigned short count = 0xffff; + unsigned long flags; + trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return); + + data = ((unsigned long) wdata) << 16; + + spin_lock_irqsave(&trident->reg_lock, flags); + if (trident->device == TRIDENT_DEVICE_ID_DX) { + address = DX_ACR0_AC97_W; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0) + break; + } while (--count); + + data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff)); + } else if (trident->device == TRIDENT_DEVICE_ID_NX) { + address = NX_ACR1_AC97_W; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0) + break; + } while (--count); + + data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff)); + } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + address = SI_AC97_WRITE; + + /* read AC-97 write register status */ + do { + if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0) + break; + } while (--count); + + data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); + if (ac97->num == 1) + data |= SI_AC97_SECONDARY; + } else { + address = 0; /* keep GCC happy */ + count = 0; /* return */ + } + + if (count == 0) { + spin_unlock_irqrestore(&trident->reg_lock, flags); + return; + } + outl(data, TRID_REG(trident, address)); + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +/*--------------------------------------------------------------------------- + void snd_trident_enable_eso(trident_t *trident) + + Description: This routine will enable end of loop interrupts. + End of loop interrupts will occur when a running + channel reaches ESO. + Also enables middle of loop interrupts. + + Parameters: trident - pointer to target device class for 4DWave. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_enable_eso(trident_t * trident) +{ + unsigned int val; + + val = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + val |= ENDLP_IE; + val |= MIDLP_IE; + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + val |= BANK_B_EN; + outl(val, TRID_REG(trident, T4D_LFO_GC_CIR)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_disable_eso(trident_t *trident) + + Description: This routine will disable end of loop interrupts. + End of loop interrupts will occur when a running + channel reaches ESO. + Also disables middle of loop interrupts. + + Parameters: + trident - pointer to target device class for 4DWave. + + returns: TRUE if everything went ok, else FALSE. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_disable_eso(trident_t * trident) +{ + unsigned int tmp; + + tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + tmp &= ~ENDLP_IE; + tmp &= ~MIDLP_IE; + outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_start_voice(trident_t * trident, unsigned int voice) + + Description: Start a voice, any channel 0 thru 63. + This routine automatically handles the fact that there are + more than 32 channels available. + + Parameters : voice - Voice number 0 thru n. + trident - pointer to target device class for 4DWave. + + Return Value: None. + + ---------------------------------------------------------------------------*/ + +void snd_trident_start_voice(trident_t * trident, unsigned int voice) +{ + unsigned int mask = 1 << (voice & 0x1f); + unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A; + + outl(mask, TRID_REG(trident, reg)); +} + +/*--------------------------------------------------------------------------- + void snd_trident_stop_voice(trident_t * trident, unsigned int voice) + + Description: Stop a voice, any channel 0 thru 63. + This routine automatically handles the fact that there are + more than 32 channels available. + + Parameters : voice - Voice number 0 thru n. + trident - pointer to target device class for 4DWave. + + Return Value: None. + + ---------------------------------------------------------------------------*/ + +void snd_trident_stop_voice(trident_t * trident, unsigned int voice) +{ + unsigned int mask = 1 << (voice & 0x1f); + unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A; + + outl(mask, TRID_REG(trident, reg)); +} + +/*--------------------------------------------------------------------------- + int snd_trident_allocate_pcm_channel(trident_t *trident) + + Description: Allocate hardware channel in Bank B (32-63). + + Parameters : trident - pointer to target device class for 4DWave. + + Return Value: hardware channel - 32-63 or -1 when no channel is available + + ---------------------------------------------------------------------------*/ + +static int snd_trident_allocate_pcm_channel(trident_t * trident) +{ + int idx; + + if (trident->ChanPCMcnt >= trident->ChanPCM) + return -1; + for (idx = 31; idx >= 0; idx--) { + if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) { + trident->ChanMap[T4D_BANK_B] |= 1 << idx; + trident->ChanPCMcnt++; + return idx + 32; + } + } + return -1; +} + +/*--------------------------------------------------------------------------- + void snd_trident_free_pcm_channel(int channel) + + Description: Free hardware channel in Bank B (32-63) + + Parameters : trident - pointer to target device class for 4DWave. + channel - hardware channel number 0-63 + + Return Value: none + + ---------------------------------------------------------------------------*/ + +static void snd_trident_free_pcm_channel(trident_t *trident, int channel) +{ + if (channel < 32 || channel > 63) + return; + channel &= 0x1f; + if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) { + trident->ChanMap[T4D_BANK_B] &= ~(1 << channel); + trident->ChanPCMcnt--; + } +} + +/*--------------------------------------------------------------------------- + unsigned int snd_trident_allocate_synth_channel(void) + + Description: Allocate hardware channel in Bank A (0-31). + + Parameters : trident - pointer to target device class for 4DWave. + + Return Value: hardware channel - 0-31 or -1 when no channel is available + + ---------------------------------------------------------------------------*/ + +static int snd_trident_allocate_synth_channel(trident_t * trident) +{ + int idx; + + for (idx = 31; idx >= 0; idx--) { + if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) { + trident->ChanMap[T4D_BANK_A] |= 1 << idx; + trident->synth.ChanSynthCount++; + return idx; + } + } + return -1; +} + +/*--------------------------------------------------------------------------- + void snd_trident_free_synth_channel( int channel ) + + Description: Free hardware channel in Bank B (0-31). + + Parameters : trident - pointer to target device class for 4DWave. + channel - hardware channel number 0-63 + + Return Value: none + + ---------------------------------------------------------------------------*/ + +static void snd_trident_free_synth_channel(trident_t *trident, int channel) +{ + if (channel < 0 || channel > 31) + return; + channel &= 0x1f; + if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) { + trident->ChanMap[T4D_BANK_A] &= ~(1 << channel); + trident->synth.ChanSynthCount--; + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_voice_regs + + Description: This routine will complete and write the 5 hardware channel + registers to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Each register field. + + ---------------------------------------------------------------------------*/ + +void snd_trident_write_voice_regs(trident_t * trident, + snd_trident_voice_t * voice) +{ + unsigned int FmcRvolCvol; + unsigned int regs[5]; + + regs[1] = voice->LBA; + regs[4] = (voice->GVSel << 31) | + ((voice->Pan & 0x0000007f) << 24) | + ((voice->CTRL & 0x0000000f) << 12); + FmcRvolCvol = ((voice->FMC & 3) << 14) | + ((voice->RVol & 0x7f) << 7) | + (voice->CVol & 0x7f); + + switch (trident->device) { + case TRIDENT_DEVICE_ID_SI7018: + regs[4] |= voice->number > 31 ? + (voice->Vol & 0x000003ff) : + ((voice->Vol & 0x00003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f); + regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); + regs[3] = (voice->Attribute << 16) | FmcRvolCvol; + break; + case TRIDENT_DEVICE_ID_DX: + regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f); + regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); + regs[3] = FmcRvolCvol; + break; + case TRIDENT_DEVICE_ID_NX: + regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | + (voice->EC & 0x00000fff); + regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff); + regs[2] = ((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff); + regs[3] = (voice->Alpha << 20) | ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol; + break; + default: + snd_BUG(); + } + + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl(regs[0], TRID_REG(trident, CH_START + 0)); + outl(regs[1], TRID_REG(trident, CH_START + 4)); + outl(regs[2], TRID_REG(trident, CH_START + 8)); + outl(regs[3], TRID_REG(trident, CH_START + 12)); + outl(regs[4], TRID_REG(trident, CH_START + 16)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_cso_reg + + Description: This routine will write the new CSO offset + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + CSO - new CSO value + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_cso_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CSO) +{ + voice->CSO = CSO; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device != TRIDENT_DEVICE_ID_NX) { + outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); + } else { + outl((voice->Delta << 24) | (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_vol_reg + + Description: This routine will write the new voice volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Vol - new voice volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_vol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Vol) +{ + voice->Vol = Vol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2)); + break; + case TRIDENT_DEVICE_ID_SI7018: + // printk("voice->Vol = 0x%x\n", voice->Vol); + outw((voice->CTRL << 12) | voice->Vol, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + break; + } +} + +/*--------------------------------------------------------------------------- + snd_trident_write_pan_reg + + Description: This routine will write the new voice pan + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + Pan - new pan value + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_pan_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int Pan) +{ + voice->Pan = Pan; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_rvol_reg + + Description: This routine will write the new reverb volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + RVol - new reverb volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_rvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int RVol) +{ + voice->RVol = RVol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f), + TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); +} + +/*--------------------------------------------------------------------------- + snd_trident_write_cvol_reg + + Description: This routine will write the new chorus volume + register to hardware. + + Paramters: trident - pointer to target device class for 4DWave. + voice - synthesizer voice structure + CVol - new chorus volume + + ---------------------------------------------------------------------------*/ + +static void snd_trident_write_cvol_reg(trident_t * trident, snd_trident_voice_t * voice, unsigned int CVol) +{ + voice->CVol = CVol; + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f), + TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); +} + +/*--------------------------------------------------------------------------- + snd_trident_convert_rate + + Description: This routine converts rate in HZ to hardware delta value. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_convert_rate(unsigned int rate) +{ + unsigned int delta; + + // We special case 44100 and 8000 since rounding with the equation + // does not give us an accurate enough value. For 11025 and 22050 + // the equation gives us the best answer. All other frequencies will + // also use the equation. JDW + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff; + return delta; +} + +/*--------------------------------------------------------------------------- + snd_trident_convert_adc_rate + + Description: This routine converts rate in HZ to hardware delta value. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +static unsigned int snd_trident_convert_adc_rate(unsigned int rate) +{ + unsigned int delta; + + // We special case 44100 and 8000 since rounding with the equation + // does not give us an accurate enough value. For 11025 and 22050 + // the equation gives us the best answer. All other frequencies will + // also use the equation. JDW + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + return delta; +} + +/*--------------------------------------------------------------------------- + snd_trident_spurious_threshold + + Description: This routine converts rate in HZ to spurious threshold. + + Paramters: trident - pointer to target device class for 4DWave. + rate - Real or Virtual channel number. + + Returns: Delta value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_spurious_threshold(unsigned int rate, unsigned int period_size) +{ + unsigned int res = (rate * period_size) / 48000; + if (res < 64) + res = res / 2; + else + res -= 32; + return res; +} + +/*--------------------------------------------------------------------------- + snd_trident_control_mode + + Description: This routine returns a control mode for a PCM channel. + + Paramters: trident - pointer to target device class for 4DWave. + substream - PCM substream + + Returns: Control value. + + ---------------------------------------------------------------------------*/ +unsigned int snd_trident_control_mode(snd_pcm_substream_t *substream) +{ + unsigned int CTRL; + snd_pcm_runtime_t *runtime = substream->runtime; + + /* set ctrl mode + CTRL default: 8-bit (unsigned) mono, loop mode enabled + */ + CTRL = 0x00000001; + if (snd_pcm_format_width(runtime->format) == 16) + CTRL |= 0x00000008; // 16-bit data + if (snd_pcm_format_signed(runtime->format)) + CTRL |= 0x00000002; // signed data + if (runtime->channels > 1) + CTRL |= 0x00000004; // stereo data + return CTRL; +} + +/* + * PCM part + */ + +/*--------------------------------------------------------------------------- + snd_trident_ioctl + + Description: Device I/O control handler for playback/capture parameters. + + Paramters: substream - PCM substream class + cmd - what ioctl message to process + arg - additional message infoarg + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + /* FIXME: it seems that with small periods the behaviour of + trident hardware is unpredictable and interrupt generator + is broken */ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_hw_params + + Description: Set the hardware parameters for the playback device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (evoice == NULL) + return -ENOMEM; + voice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = evoice = NULL; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_hw_free + + Description: Release the hardware resources for the playback device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_playback_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_prepare + + Description: Prepare playback device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_playback_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number]; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + /* set delta (rate) value */ + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + /* set Loop Begin Address */ + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + + voice->CSO = 0; + voice->ESO = runtime->buffer_size - 1; /* in samples */ + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->GVSel = 1; + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Vol = mix->vol; + voice->RVol = mix->rvol; + voice->CVol = mix->cvol; + voice->Pan = mix->pan; + voice->Attribute = 0; + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = voice->Delta; + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) - 1; /* in samples */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ +#if 0 + evoice->Attribute = (1<<(30-16))|(2<<(26-16))| + (1<<(24-16))|(0x1f<<(19-16)); +#else + evoice->Attribute = 0; +#endif + snd_trident_write_voice_regs(trident, evoice); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_hw_params + + Description: Set the hardware parameters for the capture device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_hw_free + + Description: Release the hardware resources for the capture device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_prepare + + Description: Prepare capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int val, ESO_bytes; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + // Initilize the channel and set channel Mode + outb(0, TRID_REG(trident, LEGACY_DMAR15)); + + // Set DMA channel operation mode register + outb(0x54, TRID_REG(trident, LEGACY_DMAR11)); + + // Set channel buffer Address + voice->LBA = runtime->dma_addr; + outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); + if (voice->memblk) + voice->LBA = voice->memblk->offset; + + // set ESO + ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1; + outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6)); + outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4)); + ESO_bytes++; + + // Set channel sample rate, 4.12 format + val = ((unsigned int) 48000L << 12) / runtime->rate; + outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R)); + + // Set channel interrupt blk length + if (snd_pcm_format_width(runtime->format) == 16) { + val = (unsigned short) ((ESO_bytes >> 1) - 1); + } else { + val = (unsigned short) (ESO_bytes - 1); + } + + outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL)); + + // Right now, set format and start to run captureing, + // continuous run loop enable. + trident->bDMAStart = 0x19; // 0001 1001b + + if (snd_pcm_format_width(runtime->format) == 16) + trident->bDMAStart |= 0x80; + if (snd_pcm_format_signed(runtime->format)) + trident->bDMAStart |= 0x20; + if (runtime->channels > 1) + trident->bDMAStart |= 0x40; + + // Prepare capture intr channel + + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + // Set voice parameters + voice->CSO = 0; + /* the +2 is a correction for a h/w problem. if not + used, the ESO interrupt is received before the capture pointer + has actually reached the ESO point. this causes errors in + the mid-level code. + */ + voice->ESO = (runtime->period_size * 2) + 2 - 1; + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; /* mute */ + voice->Vol = 0x3ff; /* mute */ + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + snd_trident_write_voice_regs(trident, voice); + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_hw_params + + Description: Set the hardware parameters for the capture device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (evoice == NULL) + return -ENOMEM; + voice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = evoice = NULL; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_hw_free + + Description: Release the hardware resources for the capture device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_si7018_capture_prepare + + Description: Prepare capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_si7018_capture_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + voice->LBA = runtime->dma_addr; + voice->Delta = snd_trident_convert_adc_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + // Set voice parameters + voice->CSO = 0; + voice->ESO = runtime->buffer_size - 1; /* in samples */ + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 0; + voice->RVol = 0; + voice->CVol = 0; + voice->GVSel = 1; + voice->Pan = T4D_DEFAULT_PCM_PAN; + voice->Vol = 0; + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + + voice->Attribute = (2 << (30-16)) | + (2 << (26-16)) | + (2 << (24-16)) | + (1 << (23-16)); + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = snd_trident_convert_rate(runtime->rate); + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) + 8 - 1; /* in samples, 8 means correction */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = 0; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ + evoice->Attribute = 0; + snd_trident_write_voice_regs(trident, evoice); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_hw_params + + Description: Set the hardware parameters for the foldback device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != params_buffer_size(hw_params)) { + if (evoice == NULL) { + evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (evoice == NULL) + return -ENOMEM; + voice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = evoice = NULL; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_hw_free + + Description: Release the hardware resources for the foldback device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice ? voice->extra : NULL; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + if (evoice != NULL) { + snd_trident_free_voice(trident, evoice); + voice->extra = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_prepare + + Description: Prepare foldback capture device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + snd_trident_voice_t *evoice = voice->extra; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + /* Set channel buffer Address */ + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = runtime->dma_addr; + + /* set target ESO for channel */ + voice->ESO = runtime->buffer_size - 1; /* in samples */ + + /* set sample rate */ + voice->Delta = 0x1000; + voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); + + voice->CSO = 0; + voice->CTRL = snd_trident_control_mode(substream); + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; /* mute */ + voice->Vol = 0x3ff; /* mute */ + voice->EC = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + /* set up capture channel */ + outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan)); + + snd_trident_write_voice_regs(trident, voice); + + if (evoice != NULL) { + evoice->Delta = voice->Delta; + evoice->spurious_threshold = voice->spurious_threshold; + evoice->LBA = voice->LBA; + evoice->CSO = 0; + evoice->ESO = (runtime->period_size * 2) - 1; /* in samples */ + evoice->CTRL = voice->CTRL; + evoice->FMC = 3; + evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; + evoice->EC = 0; + evoice->Alpha = 0; + evoice->FMS = 0; + evoice->Vol = 0x3ff; /* mute */ + evoice->RVol = evoice->CVol = 0x7f; /* mute */ + evoice->Pan = 0x7f; /* mute */ + evoice->Attribute = 0; + snd_trident_write_voice_regs(trident, evoice); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_hw_params + + Description: Set the hardware parameters for the spdif device. + + Parameters: substream - PCM substream class + hw_params - hardware parameters + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned long flags; + unsigned int old_bits = 0, change = 0; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if (err > 0 && trident->tlb.entries) { + if (voice->memblk) + snd_trident_free_pages(trident, voice->memblk); + spin_lock_irqsave(&trident->reg_lock, flags); + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + spin_unlock_irqrestore(&trident->reg_lock, flags); + if (voice->memblk == NULL) + return -ENOMEM; + } + + /* prepare SPDIF channel */ + spin_lock_irqsave(&trident->reg_lock, flags); + old_bits = trident->spdif_pcm_bits; + if (old_bits & IEC958_AES0_PROFESSIONAL) + trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS; + else + trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24); + if (params_rate(hw_params) >= 48000) { + trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_48000 : + (IEC958_AES3_CON_FS_48000 << 24); + } + else if (params_rate(hw_params) >= 44100) { + trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_44100 : + (IEC958_AES3_CON_FS_44100 << 24); + } + else { + trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz + trident->spdif_pcm_bits |= + trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? + IEC958_AES0_PRO_FS_32000 : + (IEC958_AES3_CON_FS_32000 << 24); + } + change = old_bits != trident->spdif_pcm_bits; + spin_unlock_irqrestore(&trident->reg_lock, flags); + + if (change) + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_hw_free + + Description: Release the hardware resources for the spdif device. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_hw_free(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + + if (trident->tlb.entries && voice && voice->memblk) { + snd_trident_free_pages(trident, voice->memblk); + voice->memblk = NULL; + } + snd_pcm_lib_free_pages(substream); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_prepare + + Description: Prepare SPDIF device for playback. + + Parameters: substream - PCM substream class + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int RESO, LBAO; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + + /* set delta (rate) value */ + voice->Delta = snd_trident_convert_rate(runtime->rate); + voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); + + /* set Loop Back Address */ + LBAO = runtime->dma_addr; + if (voice->memblk) + voice->LBA = voice->memblk->offset; + else + voice->LBA = LBAO; + + /* set target ESO for channel */ + RESO = runtime->buffer_size - 1; + voice->ESO = (runtime->period_size * 2) - 1; + + /* set ctrl mode */ + voice->CTRL = snd_trident_control_mode(substream); + + voice->FMC = 3; + voice->RVol = 0x7f; + voice->CVol = 0x7f; + voice->GVSel = 1; + voice->Pan = 0x7f; + voice->Vol = 0x3ff; + voice->EC = 0; + voice->CSO = 0; + voice->Alpha = 0; + voice->FMS = 0; + voice->Attribute = 0; + + /* prepare surrogate IRQ channel */ + snd_trident_write_voice_regs(trident, voice); + + outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO)); + outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2)); + outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA)); + outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO)); + outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2)); + + /* set SPDIF setting */ + outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + + spin_unlock_irqrestore(&trident->reg_lock, flags); + + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_trigger + + Description: Start/stop devices + + Parameters: substream - PCM substream class + cmd - trigger command (STOP, GO) + + Returns: Error status + + ---------------------------------------------------------------------------*/ + +static int snd_trident_trigger(snd_pcm_substream_t *substream, + int cmd) + +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_substream_t *s; + unsigned int what, whati, capture_flag, spdif_flag; + snd_trident_voice_t *voice, *evoice; + unsigned int val, go; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + go = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + go = 0; + break; + default: + return -EINVAL; + } + what = whati = capture_flag = spdif_flag = 0; + s = substream; + spin_lock(&trident->reg_lock); + val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; + do { + if ((trident_t *) _snd_pcm_chip(s->pcm) == trident) { + voice = (snd_trident_voice_t *) s->runtime->private_data; + evoice = voice->extra; + what |= 1 << (voice->number & 0x1f); + if (evoice == NULL) { + whati |= 1 << (voice->number & 0x1f); + } else { + what |= 1 << (evoice->number & 0x1f); + whati |= 1 << (evoice->number & 0x1f); + } + if (go) { + voice->running = 1; + voice->stimer = val; + } else { + voice->running = 0; + } + snd_pcm_trigger_done(s, substream); + if (voice->capture) + capture_flag = 1; + if (voice->spdif) + spdif_flag = 1; + } + s = s->link_next; + } while (s != substream); + if (spdif_flag) { + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } + if (!go) + outl(what, TRID_REG(trident, T4D_STOP_B)); + val = inl(TRID_REG(trident, T4D_AINTEN_B)); + if (go) { + val |= whati; + } else { + val &= ~whati; + } + outl(val, TRID_REG(trident, T4D_AINTEN_B)); + if (go) { + outl(what, TRID_REG(trident, T4D_START_B)); + + if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) + outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); + } else { + if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) + outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); + } + spin_unlock(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_pointer + + Description: This routine return the playback position + + Parameters: substream - PCM substream class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_playback_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int cso; + + if (!voice->running) + return 0; + + spin_lock(&trident->reg_lock); + + outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + + if (trident->device != TRIDENT_DEVICE_ID_NX) { + cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); + } else { // ID_4DWAVE_NX + cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff; + } + + if (++cso > runtime->buffer_size) + cso = runtime->buffer_size; + + spin_unlock(&trident->reg_lock); + + return cso; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_pointer + + Description: This routine return the capture position + + Paramters: pcm1 - PCM device class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_capture_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int result; + + if (!voice->running) + return 0; + + result = inw(TRID_REG(trident, T4D_SBBL_SBCL)); + if (runtime->channels > 1) + result >>= 1; + result = runtime->buffer_size - result; + + // printk("capture result = 0x%x, cso = 0x%x\n", result, cso); + + return result; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_pointer + + Description: This routine return the SPDIF playback position + + Parameters: substream - PCM substream class + + Returns: position of buffer + + ---------------------------------------------------------------------------*/ + +static snd_pcm_uframes_t snd_trident_spdif_pointer(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + unsigned int result; + + if (!voice->running) + return 0; + + result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; + + return result; +} + +/* + * Playback support device description + */ + +static snd_pcm_hardware_t snd_trident_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (256*1024), + period_bytes_min: 64, + period_bytes_max: (256*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Capture support device description + */ + +static snd_pcm_hardware_t snd_trident_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 4000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * Foldback capture support device description + */ + +static snd_pcm_hardware_t snd_trident_foldback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_48000, + rate_min: 48000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +/* + * SPDIF playback support device description + */ + +static snd_pcm_hardware_t snd_trident_spdif = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + rate_min: 32000, + rate_max: 48000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: (128*1024), + period_bytes_min: 64, + period_bytes_max: (128*1024), + periods_min: 1, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + unsigned long flags; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + trident_t *trident; + + if (voice) { + trident = voice->trident; + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_free_voice(trident, voice); + spin_unlock_irqrestore(&trident->reg_lock, flags); + } +} + +static int snd_trident_playback_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + spin_unlock_irq(&trident->reg_lock); + snd_trident_pcm_mixer_build(trident, voice, substream); + voice->substream = substream; + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_playback; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_playback_close + + Description: This routine will close the 4DWave playback device. For now + we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_playback_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; + + snd_trident_pcm_mixer_free(trident, voice, substream); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif_open + + Description: This routine will open the 4DWave SPDIF device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + voice->spdif = 1; + voice->substream = substream; + trident->spdif_pcm_bits = trident->spdif_bits; + spin_unlock_irq(&trident->reg_lock); + + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_spdif; + + trident->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + + +/*--------------------------------------------------------------------------- + snd_trident_spdif_close + + Description: This routine will close the 4DWave SPDIF device. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + + spin_lock_irq(&trident->reg_lock); + // restore default SPDIF setting + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + spin_unlock_irq(&trident->reg_lock); + trident->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_open + + Description: This routine will open the 4DWave capture device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_capture_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + voice->capture = 1; + spin_unlock_irq(&trident->reg_lock); + voice->substream = substream; + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_capture; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_capture_close + + Description: This routine will close the 4DWave capture device. For now + we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_capture_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_open + + Description: This routine will open the 4DWave foldback capture device. + + Parameters: substream - PCM substream class + + Returns: status - success or failure flag + + ---------------------------------------------------------------------------*/ + +static int snd_trident_foldback_open(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + + spin_lock_irq(&trident->reg_lock); + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); + if (voice == NULL) { + spin_unlock_irq(&trident->reg_lock); + return -EAGAIN; + } + if (trident->tlb.entries) { + voice->memblk = snd_trident_alloc_pages(trident, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + if (voice->memblk == NULL) { + snd_trident_free_voice(trident, voice); + spin_unlock_irq(&trident->reg_lock); + return -ENOMEM; + } + } + voice->substream = substream; + voice->foldback_chan = substream->number; + spin_unlock_irq(&trident->reg_lock); + runtime->private_data = voice; + runtime->private_free = snd_trident_pcm_free_substream; + runtime->hw = snd_trident_foldback; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_close + + Description: This routine will close the 4DWave foldback capture device. + For now we will simply free the dma transfer buffer. + + Parameters: substream - PCM substream class + + ---------------------------------------------------------------------------*/ +static int snd_trident_foldback_close(snd_pcm_substream_t * substream) +{ + trident_t *trident = snd_pcm_substream_chip(substream); + snd_trident_voice_t *voice; + snd_pcm_runtime_t *runtime = substream->runtime; + voice = (snd_trident_voice_t *) runtime->private_data; + + /* stop capture channel */ + spin_lock_irq(&trident->reg_lock); + outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); + spin_unlock_irq(&trident->reg_lock); + return 0; +} + +/*--------------------------------------------------------------------------- + PCM operations + ---------------------------------------------------------------------------*/ + +static snd_pcm_ops_t snd_trident_playback_ops = { + open: snd_trident_playback_open, + close: snd_trident_playback_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_playback_hw_params, + hw_free: snd_trident_playback_hw_free, + prepare: snd_trident_playback_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_capture_ops = { + open: snd_trident_capture_open, + close: snd_trident_capture_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_capture_hw_params, + hw_free: snd_trident_capture_hw_free, + prepare: snd_trident_capture_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_capture_pointer, +}; + +static snd_pcm_ops_t snd_trident_si7018_capture_ops = { + open: snd_trident_capture_open, + close: snd_trident_capture_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_si7018_capture_hw_params, + hw_free: snd_trident_si7018_capture_hw_free, + prepare: snd_trident_si7018_capture_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_foldback_ops = { + open: snd_trident_foldback_open, + close: snd_trident_foldback_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_foldback_hw_params, + hw_free: snd_trident_foldback_hw_free, + prepare: snd_trident_foldback_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_playback_pointer, +}; + +static snd_pcm_ops_t snd_trident_spdif_ops = { + open: snd_trident_spdif_open, + close: snd_trident_spdif_close, + ioctl: snd_trident_ioctl, + hw_params: snd_trident_spdif_hw_params, + hw_free: snd_trident_spdif_hw_free, + prepare: snd_trident_spdif_prepare, + trigger: snd_trident_trigger, + pointer: snd_trident_spdif_pointer, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_free + + Description: This routine release the 4DWave private data. + + Paramters: private_data - pointer to 4DWave device info. + + Returns: None + + ---------------------------------------------------------------------------*/ +static void snd_trident_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->foldback = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm) +{ + trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident->spdif = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +/*--------------------------------------------------------------------------- + snd_trident_pcm + + Description: This routine registers the 4DWave device for PCM support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0) + return err; + + pcm->private_data = trident; + pcm->private_free = snd_trident_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + trident->device != TRIDENT_DEVICE_ID_SI7018 ? + &snd_trident_capture_ops : + &snd_trident_si7018_capture_ops); + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "Trident 4DWave"); + trident->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_foldback_pcm + + Description: This routine registers the 4DWave device for foldback PCM support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *foldback; + int err; + int num_chan = 3; + snd_pcm_substream_t *substream; + + if (rpcm) + *rpcm = NULL; + if (trident->device == TRIDENT_DEVICE_ID_NX) + num_chan = 4; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0) + return err; + + foldback->private_data = trident; + foldback->private_free = snd_trident_foldback_pcm_free; + snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); + foldback->info_flags = 0; + strcpy(foldback->name, "Trident 4DWave"); + substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + strcpy(substream->name, "Front Mixer"); + substream = substream->next; + strcpy(substream->name, "Reverb Mixer"); + substream = substream->next; + strcpy(substream->name, "Chorus Mixer"); + if (num_chan == 4) { + substream = substream->next; + strcpy(substream->name, "Second AC'97 ADC"); + } + trident->foldback = foldback; + + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); + + if (rpcm) + *rpcm = foldback; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_spdif + + Description: This routine registers the 4DWave-NX device for SPDIF support. + + Paramters: trident - pointer to target device class for 4DWave-NX. + + Returns: None + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *spdif; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0) + return err; + + spdif->private_data = trident; + spdif->private_free = snd_trident_spdif_pcm_free; + snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops); + spdif->info_flags = 0; + strcpy(spdif->name, "Trident 4DWave IEC958"); + trident->spdif = spdif; + + snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); + + if (rpcm) + *rpcm = spdif; + return 0; +} + +/* + * Mixer part + */ + + +/*--------------------------------------------------------------------------- + snd_trident_spdif_control + + Description: enable/disable S/PDIF out from ac97 mixer + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_trident_spdif_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->spdif_ctrl; + ucontrol->value.integer.value[0] = val == kcontrol->private_value; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + int change; + + val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00; + spin_lock_irqsave(&trident->reg_lock, flags); + /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */ + change = trident->spdif_ctrl != val; + trident->spdif_ctrl = val; + if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) { + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), + info: snd_trident_spdif_control_info, + get: snd_trident_spdif_control_get, + put: snd_trident_spdif_control_put, + private_value: 0x28, +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_default + + Description: put/get the S/PDIF default settings + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&trident->reg_lock, flags); + change = trident->spdif_bits != val; + trident->spdif_bits = val; + if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_trident_spdif_default_info, + get: snd_trident_spdif_default_get, + put: snd_trident_spdif_default_put +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_mask + + Description: put/get the S/PDIF mask + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static snd_kcontrol_new_t snd_trident_spdif_mask __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + info: snd_trident_spdif_mask_info, + get: snd_trident_spdif_mask_get, +}; + +/*--------------------------------------------------------------------------- + snd_trident_spdif_stream + + Description: put/get the S/PDIF stream settings + ---------------------------------------------------------------------------*/ + +static int snd_trident_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_trident_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + spin_lock_irqsave(&trident->reg_lock, flags); + change = trident->spdif_pcm_bits != val; + trident->spdif_pcm_bits = val; + if (trident->spdif != NULL) + outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_trident_spdif_stream_info, + get: snd_trident_spdif_stream_get, + put: snd_trident_spdif_stream_put +}; + +/*--------------------------------------------------------------------------- + snd_trident_ac97_control + + Description: enable/disable rear path for ac97 + ---------------------------------------------------------------------------*/ + +static int snd_trident_ac97_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_trident_ac97_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0; + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_ac97_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned char val; + int change = 0; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + val &= ~(1 << kcontrol->private_value); + if (ucontrol->value.integer.value[0]) + val |= 1 << kcontrol->private_value; + change = val != trident->ac97_ctrl; + trident->ac97_ctrl = val; + outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_ac97_rear_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Rear Path", + info: snd_trident_ac97_control_info, + get: snd_trident_ac97_control_get, + put: snd_trident_ac97_control_put, + private_value: 4, +}; + +/*--------------------------------------------------------------------------- + snd_trident_vol_control + + Description: wave & music volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_trident_vol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned int val; + + val = trident->musicvol_wavevol; + ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff); + ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff); + return 0; +} + +static int snd_trident_vol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + spin_lock_irqsave(&trident->reg_lock, flags); + val = trident->musicvol_wavevol; + val &= ~(0xffff << kcontrol->private_value); + val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) | + ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value; + change = val != trident->musicvol_wavevol; + outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_vol_music_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Music Playback Volume", + info: snd_trident_vol_control_info, + get: snd_trident_vol_control_get, + put: snd_trident_vol_control_put, + private_value: 16, +}; + +static snd_kcontrol_new_t snd_trident_vol_wave_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Wave Playback Volume", + info: snd_trident_vol_control_info, + get: snd_trident_vol_control_get, + put: snd_trident_vol_control_put, + private_value: 0, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_vol_control + + Description: PCM front volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + uinfo->value.integer.max = 1023; + return 0; +} + +static int snd_trident_pcm_vol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + ucontrol->value.integer.value[0] = 1023 - mix->vol; + } else { + ucontrol->value.integer.value[0] = 255 - (mix->vol>>2); + } + return 0; +} + +static int snd_trident_pcm_vol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned int val; + int change = 0; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + val = 1023 - (ucontrol->value.integer.value[0] & 1023); + } else { + val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2; + } + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->vol; + mix->vol = val; + if (mix->voice != NULL) + snd_trident_write_vol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_vol_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Front Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_vol_control_info, + get: snd_trident_pcm_vol_control_get, + put: snd_trident_pcm_vol_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_pan_control + + Description: PCM front pan control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_pan_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_pan_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = mix->pan; + if (ucontrol->value.integer.value[0] & 0x40) { + ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)); + } else { + ucontrol->value.integer.value[0] |= 0x40; + } + return 0; +} + +static int snd_trident_pcm_pan_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned char val; + int change = 0; + + if (ucontrol->value.integer.value[0] & 0x40) + val = ucontrol->value.integer.value[0] & 0x3f; + else + val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40; + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->pan; + mix->pan = val; + if (mix->voice != NULL) + snd_trident_write_pan_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_pan_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Pan Playback Control", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_pan_control_info, + get: snd_trident_pcm_pan_control_get, + put: snd_trident_pcm_pan_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_rvol_control + + Description: PCM reverb volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_rvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_rvol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = 127 - mix->rvol; + return 0; +} + +static int snd_trident_pcm_rvol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned short val; + int change = 0; + + val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->rvol; + mix->rvol = val; + if (mix->voice != NULL) + snd_trident_write_rvol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_rvol_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Reverb Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_rvol_control_info, + get: snd_trident_pcm_rvol_control_get, + put: snd_trident_pcm_rvol_control_put, +}; + +/*--------------------------------------------------------------------------- + snd_trident_pcm_cvol_control + + Description: PCM chorus volume control + ---------------------------------------------------------------------------*/ + +static int snd_trident_pcm_cvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 127; + return 0; +} + +static int snd_trident_pcm_cvol_control_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = 127 - mix->cvol; + return 0; +} + +static int snd_trident_pcm_cvol_control_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + unsigned long flags; + trident_t *trident = snd_kcontrol_chip(kcontrol); + snd_trident_pcm_mixer_t *mix = (snd_trident_pcm_mixer_t *)kcontrol->private_value; + unsigned short val; + int change = 0; + + val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); + spin_lock_irqsave(&trident->reg_lock, flags); + change = val != mix->cvol; + mix->cvol = val; + if (mix->voice != NULL) + snd_trident_write_cvol_reg(trident, mix->voice, val); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_trident_pcm_cvol_control __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PCM Chorus Playback Volume", + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + info: snd_trident_pcm_cvol_control_info, + get: snd_trident_pcm_cvol_control_get, + put: snd_trident_pcm_cvol_control_put, +}; + +static void snd_trident_notify_pcm_change1(snd_card_t * card, snd_kcontrol_t *kctl, int activate) +{ + snd_runtime_check(kctl != NULL, return); + if (activate) + kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + else + kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); +} + +static void snd_trident_notify_pcm_change(snd_card_t * card, snd_trident_pcm_mixer_t * tmix, int activate) +{ + snd_trident_notify_pcm_change1(card, tmix->ctl_vol, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_pan, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_rvol, activate); + snd_trident_notify_pcm_change1(card, tmix->ctl_cvol, activate); +} + +static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream) +{ + snd_trident_pcm_mixer_t *tmix; + + snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL); + tmix = &trident->pcm_mixer[substream->number]; + tmix->voice = voice; + tmix->vol = T4D_DEFAULT_PCM_VOL; + tmix->pan = T4D_DEFAULT_PCM_PAN; + tmix->rvol = T4D_DEFAULT_PCM_RVOL; + tmix->cvol = T4D_DEFAULT_PCM_CVOL; + snd_trident_notify_pcm_change(trident->card, tmix, 1); + return 0; +} + +static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t *voice, snd_pcm_substream_t *substream) +{ + snd_trident_pcm_mixer_t *tmix; + + snd_assert(trident != NULL && substream != NULL, return -EINVAL); + tmix = &trident->pcm_mixer[substream->number]; + tmix->voice = NULL; + snd_trident_notify_pcm_change(trident->card, tmix, 0); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_mixer + + Description: This routine registers the 4DWave device for mixer support. + + Paramters: trident - pointer to target device class for 4DWave. + + Returns: None + + ---------------------------------------------------------------------------*/ + +static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device) +{ + ac97_t _ac97, *ac97; + snd_card_t * card = trident->card; + snd_kcontrol_t *kctl; + snd_ctl_elem_value_t uctl; + int idx, err; + + memset(&uctl, 0, sizeof(uctl)); + + memset(&_ac97, 0, sizeof(_ac97)); + _ac97.write = snd_trident_codec_write; + _ac97.read = snd_trident_codec_read; + _ac97.private_data = trident; + if ((err = snd_ac97_mixer(trident->card, &_ac97, &ac97)) < 0) + return err; + + if (trident->device != TRIDENT_DEVICE_ID_SI7018) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + } else { + outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); + } + + for (idx = 0; idx < 32; idx++) { + snd_trident_pcm_mixer_t *tmix; + + tmix = &trident->pcm_mixer[idx]; + tmix->voice = NULL; + if ((kctl = tmix->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + + if ((kctl = tmix->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL) + return -ENOMEM; + kctl->private_value = (long)tmix; + kctl->id.index = idx; + if ((err = snd_ctl_add(card, kctl))) + return err; + } + + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_control, trident))) < 0) + return err; + kctl->put(kctl, &uctl); + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_default, trident))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident))) < 0) + return err; + kctl->id.device = pcm_spdif_device; + trident->spdif_pcm_ctl = kctl; + } + + return 0; +} + +/* + * /proc interface + */ + +static void snd_trident_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + trident_t *trident = snd_magic_cast(trident_t, entry->private_data, return); + char *s; + + switch (trident->device) { + case TRIDENT_DEVICE_ID_SI7018: + s = "SiS 7018 Audio"; + break; + case TRIDENT_DEVICE_ID_DX: + s = "Trident 4DWave PCI DX"; + break; + case TRIDENT_DEVICE_ID_NX: + s = "Trident 4DWave PCI NX"; + break; + default: + s = "???"; + } + snd_iprintf(buffer, "%s\n\n", s); + snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count); + snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off"); + snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off"); + if (trident->tlb.entries) { + snd_iprintf(buffer,"\nVirtual Memory\n"); + snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size); + snd_iprintf(buffer, "Memory Used : %d\n", trident->tlb.memhdr->used); + snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); + } + } +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + snd_iprintf(buffer,"\nWavetable Synth\n"); + snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size); + snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size); + snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size)); +#endif +} + +static void __devinit snd_trident_proc_init(trident_t * trident) +{ + snd_info_entry_t *entry; + char *s = "trident"; + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + s = "sis7018"; + if ((entry = snd_info_create_card_entry(trident->card, s, trident->card->proc_root)) != NULL) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = trident; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 256; + entry->c.text.read = snd_trident_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + trident->proc_entry = entry; +} + +static void snd_trident_proc_done(trident_t * trident) +{ + if (trident->proc_entry) { + snd_info_unregister(trident->proc_entry); + trident->proc_entry = NULL; + } +} + +static int snd_trident_dev_free(snd_device_t *device) +{ + trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); + return snd_trident_free(trident); +} + +/*--------------------------------------------------------------------------- + snd_trident_create + + Description: This routine will create the device specific class for + the 4DWave card. It will also perform basic initialization. + + Paramters: card - which card to create + pci - interface to PCI bus resource info + dma1ptr - playback dma buffer + dma2ptr - capture dma buffer + irqptr - interrupt resource info + + Returns: 4DWave device class private data + + ---------------------------------------------------------------------------*/ + +int __devinit snd_trident_create(snd_card_t * card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + trident_t ** rtrident) +{ + trident_t *trident; + unsigned int i; + int err; + signed long end_time; + snd_trident_voice_t *voice; + snd_trident_pcm_mixer_t *tmix; + static snd_device_ops_t ops = { + dev_free: snd_trident_dev_free, + }; + + *rtrident = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 30 bits */ + if (!pci_dma_supported(pci, 0x3fffffff)) { + snd_printk("architecture does not support 30bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0x3fffffff); + + trident = snd_magic_kcalloc(trident_t, 0, GFP_KERNEL); + if (trident == NULL) + return -ENOMEM; + trident->device = (pci->vendor << 16) | pci->device; + trident->card = card; + trident->pci = pci; + spin_lock_init(&trident->reg_lock); + spin_lock_init(&trident->event_lock); + spin_lock_init(&trident->voice_alloc); + if (pcm_streams < 1) + pcm_streams = 1; + if (pcm_streams > 32) + pcm_streams = 32; + trident->ChanPCM = pcm_streams; + if (max_wavetable_size < 0 ) + max_wavetable_size = 0; + trident->synth.max_size = max_wavetable_size * 1024; + trident->port = pci_resource_start(pci, 0); + trident->irq = -1; + + trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE); + pci_set_master(pci); + trident->port = pci_resource_start(pci, 0); + + if ((trident->res_port = request_region(trident->port, 0x100, "Trident Audio")) == NULL) { + snd_trident_free(trident); + snd_printk("unable to grab I/O region 0x%lx-0x%lx\n", trident->port, trident->port + 0x100 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, "Trident Audio", (void *) trident)) { + snd_trident_free(trident); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + trident->irq = pci->irq; + + /* allocate 16k-aligned TLB for NX cards */ + trident->tlb.entries = NULL; + trident->tlb.buffer = NULL; + if (trident->device == TRIDENT_DEVICE_ID_NX) { + /* allocate and setup TLB page table */ + /* each entry has 4 bytes (physical PCI address) */ + /* TLB array must be aligned to 16kB !!! so we allocate + 32kB region and correct offset when necessary */ + trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); + if (trident->tlb.buffer == NULL) { + snd_trident_free(trident); + snd_printk("unable to allocate TLB buffer\n"); + return -ENOMEM; + } + trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); + trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); + /* allocate shadow TLB page table (virtual addresses) */ + trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); + if (trident->tlb.shadow_entries == NULL) { + snd_trident_free(trident); + snd_printk("unable to allocate shadow TLB entries\n"); + return -ENOMEM; + } + /* allocate and setup silent page and initialise TLB entries */ + trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); + if (trident->tlb.silent_page == 0UL) { + snd_trident_free(trident); + snd_printk("unable to allocate silent page\n"); + return -ENOMEM; + } + memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); + for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { + trident->tlb.entries[i] = trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1); + trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; + } + + /* use emu memory block manager code to manage tlb page allocation */ + trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); + if (trident->tlb.memhdr == NULL) { + snd_trident_free(trident); + return -ENOMEM; + } + trident->tlb.memhdr->block_extra_size = sizeof(snd_trident_memblk_arg_t); + } + + /* reset the legacy configuration and whole audio/wavetable block */ + if (trident->device == TRIDENT_DEVICE_ID_DX || + trident->device == TRIDENT_DEVICE_ID_NX) { + pci_write_config_dword(pci, 0x40, 0); /* DDMA */ + pci_write_config_byte(pci, 0x44, 0); /* ports */ + pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ + if (trident->device == TRIDENT_DEVICE_ID_DX) { + pci_write_config_byte(pci, 0x46, 4); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + } else /* NX */ { + pci_write_config_byte(pci, 0x46, 1); /* reset */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0); /* release reset */ + udelay(100); + } + } + + /* initialize chip */ + + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* DAC on, disable SB IRQ and try to force ADC valid signal */ + trident->ac97_ctrl = 0x0000004a; + outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) + goto __dx_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 codec ready error\n"); + snd_trident_free(trident); + return -EIO; + __dx_ok: + break; + case TRIDENT_DEVICE_ID_NX: + /* warm reset of the AC'97 codec */ + outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + udelay(100); + outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) + goto __nx_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); + snd_trident_free(trident); + return -EIO; + __nx_ok: + /* DAC on */ + trident->ac97_ctrl = 0x00000002; + outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); + /* disable SB IRQ */ + outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); + break; + case TRIDENT_DEVICE_ID_SI7018: + pci_write_config_byte(pci, 0x46, 0x04); /* SOFTWARE RESET */ + udelay(100); + pci_write_config_byte(pci, 0x46, 0x00); + udelay(100); + /* disable AC97 GPIO interrupt */ + outb(0x00, TRID_REG(trident, SI_AC97_GPIO)); + /* initialize serial interface, force cold reset */ + i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET; + outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + udelay(1000); + /* remove cold reset */ + i &= ~COLD_RESET; + outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); + udelay(2000); + /* wait, until the codec is ready */ + end_time = (jiffies + (HZ * 3) / 4) + 1; + do { + if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) + goto __si7018_ok; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (end_time - (signed long)jiffies >= 0); + snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL))); + snd_trident_free(trident); + return -EIO; + __si7018_ok: + /* enable 64 channel mode */ + outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR)); + break; + } + + outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); + outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); + outl(0, TRID_REG(trident, T4D_AINTEN_A)); + outl(0, TRID_REG(trident, T4D_AINTEN_B)); + + if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) { + snd_trident_free(trident); + return err; + } + + if (trident->device == TRIDENT_DEVICE_ID_NX) { + if (trident->tlb.entries != NULL) { + /* enable virtual addressing via TLB */ + i = trident->tlb.entries_dmaaddr; + i |= 0x00000001; + outl(i, TRID_REG(trident, NX_TLBC)); + } else { + outl(0, TRID_REG(trident, NX_TLBC)); + } + /* initialize S/PDIF */ + trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); + outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + } + + /* initialise synth voices */ + for (i = 0; i < 64; i++) { + voice = &trident->synth.voices[i]; + voice->number = i; + voice->trident = trident; + } + /* initialize pcm mixer entries */ + for (i = 0; i < 32; i++) { + tmix = &trident->pcm_mixer[i]; + tmix->vol = T4D_DEFAULT_PCM_VOL; + tmix->pan = T4D_DEFAULT_PCM_PAN; + tmix->rvol = T4D_DEFAULT_PCM_RVOL; + tmix->cvol = T4D_DEFAULT_PCM_CVOL; + } + + snd_trident_enable_eso(trident); + +#ifdef CONFIG_PM + card->set_power_state = snd_trident_set_power_state; + card->power_state_private_data = trident; +#endif + + snd_trident_proc_init(trident); + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) { + snd_trident_free(trident); + return err; + } + *rtrident = trident; + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_free + + Description: This routine will free the device specific class for + q the 4DWave card. + + Paramters: trident - device specific private data for 4DWave card + + Returns: None. + + ---------------------------------------------------------------------------*/ + +int snd_trident_free(trident_t *trident) +{ + snd_trident_disable_eso(trident); + // Disable S/PDIF out + if (trident->device == TRIDENT_DEVICE_ID_NX) + outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); + snd_trident_proc_done(trident); + if (trident->tlb.buffer) { + outl(0, TRID_REG(trident, NX_TLBC)); + if (trident->tlb.memhdr) + snd_util_memhdr_free(trident->tlb.memhdr); + if (trident->tlb.silent_page) + snd_free_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + if (trident->tlb.shadow_entries) + vfree(trident->tlb.shadow_entries); + snd_free_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, trident->tlb.buffer, trident->tlb.buffer_dmaaddr); + } + if (trident->irq >= 0) + free_irq(trident->irq, (void *)trident); + if (trident->res_port) { + release_resource(trident->res_port); + kfree_nocheck(trident->res_port); + } + snd_magic_kfree(trident); + return 0; +} + +/*--------------------------------------------------------------------------- + snd_trident_interrupt + + Description: ISR for Trident 4DWave device + + Paramters: trident - device specific private data for 4DWave card + + Problems: It seems that Trident chips generates interrupts more than + one time in special cases. The spurious interrupts are + detected via sample timer (T4D_STIMER) and computing + corresponding delta value. The limits are detected with + the method try & fail so it is possible that it won't + work on all computers. [jaroslav] + + Returns: None. + + ---------------------------------------------------------------------------*/ + +static void snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + trident_t *trident = snd_magic_cast(trident_t, dev_id, return); + unsigned int audio_int, chn_int, stimer, channel, mask; + int delta; + snd_trident_voice_t *voice; + + audio_int = inl(TRID_REG(trident, T4D_MISCINT)); + if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0) + return; + if (audio_int & ADDRESS_IRQ) { + // get interrupt status for all channels + spin_lock(&trident->reg_lock); + stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; + chn_int = inl(TRID_REG(trident, T4D_AINT_A)); + if (chn_int == 0) + goto __skip1; + outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */ + __skip1: + chn_int = inl(TRID_REG(trident, T4D_AINT_B)); + if (chn_int == 0) + goto __skip2; + for (channel = 63; channel >= 32; channel--) { + mask = 1 << (channel&0x1f); + if ((chn_int & mask) == 0) + continue; + voice = &trident->synth.voices[channel]; + if (!voice->pcm || voice->substream == NULL) { + outl(mask, TRID_REG(trident, T4D_STOP_B)); + continue; + } + delta = (int)stimer - (int)voice->stimer; + if (delta < 0) + delta = -delta; + if (delta < voice->spurious_threshold) { + /* do some statistics here */ + trident->spurious_irq_count++; + if (trident->spurious_irq_max_delta < delta) + trident->spurious_irq_max_delta = delta; + continue; + } + voice->stimer = stimer; + if (voice->extra) { + /* update CSO for extra voice to preserve sync */ + snd_trident_stop_voice(trident, voice->extra->number); + snd_trident_write_cso_reg(trident, voice->extra, 0); + snd_trident_start_voice(trident, voice->extra->number); + } else if (voice->spdif) { + snd_trident_stop_voice(trident, voice->number); + snd_trident_write_cso_reg(trident, voice, 0); + snd_trident_start_voice(trident, voice->number); + } + spin_unlock(&trident->reg_lock); + snd_pcm_period_elapsed(voice->substream); + spin_lock(&trident->reg_lock); + } + outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */ + __skip2: + spin_unlock(&trident->reg_lock); + } + if (audio_int & MPU401_IRQ) { + if (trident->rmidi) { + snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data, regs); + } else { + inb(TRID_REG(trident, T4D_MPUR0)); + } + } + // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT)); +} + +/*--------------------------------------------------------------------------- + snd_trident_attach_synthesizer, snd_trident_detach_synthesizer + + Description: Attach/detach synthesizer hooks + + Paramters: trident - device specific private data for 4DWave card + + Returns: None. + + ---------------------------------------------------------------------------*/ +int snd_trident_attach_synthesizer(trident_t *trident) +{ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT, + sizeof(trident_t*), &trident->seq_dev) >= 0) { + strcpy(trident->seq_dev->name, "4DWave"); + *(trident_t**)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident; + } +#endif + return 0; +} + +int snd_trident_detach_synthesizer(trident_t *trident) +{ +#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) + if (trident->seq_dev) { + snd_device_free(trident->card, trident->seq_dev); + trident->seq_dev = NULL; + } +#endif + return 0; +} + +snd_trident_voice_t *snd_trident_alloc_voice(trident_t * trident, int type, int client, int port) +{ + snd_trident_voice_t *pvoice; + unsigned long flags; + int idx; + + spin_lock_irqsave(&trident->voice_alloc, flags); + if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) { + idx = snd_trident_allocate_pcm_channel(trident); + if(idx < 0) { + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; + } + pvoice = &trident->synth.voices[idx]; + pvoice->use = 1; + pvoice->pcm = 1; + pvoice->capture = 0; + pvoice->spdif = 0; + pvoice->memblk = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return pvoice; + } + if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) { + idx = snd_trident_allocate_synth_channel(trident); + if(idx < 0) { + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; + } + pvoice = &trident->synth.voices[idx]; + pvoice->use = 1; + pvoice->synth = 1; + pvoice->client = client; + pvoice->port = port; + pvoice->memblk = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return pvoice; + } + if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) { + } + spin_unlock_irqrestore(&trident->voice_alloc, flags); + return NULL; +} + +void snd_trident_free_voice(trident_t * trident, snd_trident_voice_t *voice) +{ + unsigned long flags; + void (*private_free)(snd_trident_voice_t *); + void *private_data; + + if (voice == NULL || !voice->use) + return; + snd_trident_clear_voices(trident, voice->number, voice->number); + spin_lock_irqsave(&trident->voice_alloc, flags); + private_free = voice->private_free; + private_data = voice->private_data; + voice->private_free = NULL; + voice->private_data = NULL; + if (voice->pcm) + snd_trident_free_pcm_channel(trident, voice->number); + if (voice->synth) + snd_trident_free_synth_channel(trident, voice->number); + voice->use = voice->pcm = voice->synth = voice->midi = 0; + voice->capture = voice->spdif = 0; + voice->sample_ops = NULL; + voice->substream = NULL; + voice->extra = NULL; + spin_unlock_irqrestore(&trident->voice_alloc, flags); + if (private_free) + private_free(voice); +} + +void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max) +{ + unsigned int i, val, mask[2] = { 0, 0 }; + + snd_assert(v_min <= 63, return); + snd_assert(v_max <= 63, return); + for (i = v_min; i <= v_max; i++) + mask[i >> 5] |= 1 << (i & 0x1f); + if (mask[0]) { + outl(mask[0], TRID_REG(trident, T4D_STOP_A)); + val = inl(TRID_REG(trident, T4D_AINTEN_A)); + outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A)); + } + if (mask[1]) { + outl(mask[1], TRID_REG(trident, T4D_STOP_B)); + val = inl(TRID_REG(trident, T4D_AINTEN_B)); + outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B)); + } +} + +#ifdef CONFIG_PM + +void snd_trident_suspend(trident_t *trident) +{ + snd_card_t *card = trident->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + snd_pcm_suspend_all(trident->pcm); + if (trident->foldback) + snd_pcm_suspend_all(trident->foldback); + if (trident->spdif) + snd_pcm_suspend_all(trident->spdif); + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + break; /* TODO */ + case TRIDENT_DEVICE_ID_SI7018: + break; + } + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +void snd_trident_resume(trident_t *trident) +{ + snd_card_t *card = trident->card; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + break; /* TODO */ + case TRIDENT_DEVICE_ID_SI7018: + break; + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +static int snd_trident_set_power_state(snd_card_t *card, unsigned int power_state) +{ + trident_t *chip = snd_magic_cast(trident_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_trident_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_trident_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM */ + +EXPORT_SYMBOL(snd_trident_alloc_voice); +EXPORT_SYMBOL(snd_trident_free_voice); +EXPORT_SYMBOL(snd_trident_start_voice); +EXPORT_SYMBOL(snd_trident_stop_voice); +EXPORT_SYMBOL(snd_trident_write_voice_regs); +EXPORT_SYMBOL(snd_trident_clear_voices); +/* trident_memory.c symbols */ +EXPORT_SYMBOL(snd_trident_synth_alloc); +EXPORT_SYMBOL(snd_trident_synth_free); +EXPORT_SYMBOL(snd_trident_synth_bzero); +EXPORT_SYMBOL(snd_trident_synth_copy_from_user); diff -Nru linux/sound/pci/trident/trident_memory.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident_memory.c --- linux/sound/pci/trident/trident_memory.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident_memory.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,428 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Takashi Iwai + * Copyright (c) by Scott McNab + * + * Trident 4DWave-NX memory page allocation (TLB area) + * Trident chip can handle only 16MByte of the memory at the same time. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +/* page arguments of these two macros are Trident page (4096 bytes), not like + * aligned pages in others + */ +#define __set_tlb_bus(trident,page,ptr,addr) \ + do { (trident)->tlb.entries[page] = (addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1); \ + (trident)->tlb.shadow_entries[page] = (ptr); } while (0) +#define __tlb_to_ptr(trident,page) \ + (void*)((trident)->tlb.shadow_entries[page]) +#define __tlb_to_addr(trident,page) \ + (dma_addr_t)((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1)) + +#if PAGE_SIZE == 4096 +/* page size == SNDRV_TRIDENT_PAGE_SIZE */ +#define ALIGN_PAGE_SIZE PAGE_SIZE /* minimum page size for allocation */ +#define MAX_ALIGN_PAGES SNDRV_TRIDENT_MAX_PAGES /* maxmium aligned pages */ +/* fill TLB entrie(s) corresponding to page with ptr */ +#define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr) +/* fill TLB entrie(s) corresponding to page with silence pointer */ +#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr) +/* get aligned page from offset address */ +#define get_aligned_page(offset) ((offset) >> 12) +/* get offset address from aligned page */ +#define aligned_page_offset(page) ((page) << 12) +/* get buffer address from aligned page */ +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, page) +/* get PCI physical address from aligned page */ +#define page_to_addr(trident,page) __tlb_to_addr(trident, page) + +#elif PAGE_SIZE == 8192 +/* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/ +#define ALIGN_PAGE_SIZE PAGE_SIZE +#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / 2) +#define get_aligned_page(offset) ((offset) >> 13) +#define aligned_page_offset(page) ((page) << 13) +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) << 1) +#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) << 1) + +/* fill TLB entries -- we need to fill two entries */ +static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr) +{ + page <<= 1; + __set_tlb_bus(trident, page, ptr, addr); + __set_tlb_bus(trident, page+1, ptr + SNDRV_TRIDENT_PAGE_SIZE, addr + SNDRV_TRIDENT_PAGE_SIZE); +} +static inline void set_silent_tlb(trident_t *trident, int page) +{ + page <<= 1; + __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + __set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); +} + +#else +/* arbitrary size */ +#define UNIT_PAGES (PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE) +#define ALIGN_PAGE_SIZE (SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES) +#define MAX_ALIGN_PAGES (SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES) +/* Note: if alignment doesn't match to the maximum size, the last few blocks + * become unusable. To use such blocks, you'll need to check the validity + * of accessing page in set_tlb_bus and set_silent_tlb. search_empty() + * should also check it, too. + */ +#define get_aligned_page(offset) ((offset) / ALIGN_PAGE_SIZE) +#define aligned_page_offset(page) ((page) * ALIGN_PAGE_SIZE) +#define page_to_ptr(trident,page) __tlb_to_ptr(trident, (page) * UNIT_PAGES) +#define page_to_addr(trident,page) __tlb_to_addr(trident, (page) * UNIT_PAGES) + +/* fill TLB entries -- UNIT_PAGES entries must be filled */ +static inline void set_tlb_bus(trident_t *trident, int page, unsigned long ptr, dma_addr_t addr) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) { + __set_tlb_bus(trident, page, ptr, addr); + ptr += SNDRV_TRIDENT_PAGE_SIZE; + addr += SNDRV_TRIDENT_PAGE_SIZE; + } +} +static inline void set_silent_tlb(trident_t *trident, int page) +{ + int i; + page *= UNIT_PAGES; + for (i = 0; i < UNIT_PAGES; i++, page++) + __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); +} + +#endif /* PAGE_SIZE */ + +/* calculate buffer pointer from offset address */ +inline static void *offset_ptr(trident_t *trident, int offset) +{ + char *ptr; + ptr = page_to_ptr(trident, get_aligned_page(offset)); + ptr += offset % ALIGN_PAGE_SIZE; + return (void*)ptr; +} + +/* first and last (aligned) pages of memory block */ +#define firstpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->first_page) +#define lastpg(blk) (((snd_trident_memblk_arg_t*)snd_util_memblk_argptr(blk))->last_page) + +/* + * search empty pages which may contain given size + */ +static snd_util_memblk_t * +search_empty(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk, *prev; + int page, psize; + struct list_head *p; + + psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1); + prev = NULL; + page = 0; + list_for_each(p, &hdr->block) { + blk = list_entry(p, snd_util_memblk_t, list); + if (page + psize <= firstpg(blk)) + goto __found_pages; + page = lastpg(blk) + 1; + } + if (page + psize > MAX_ALIGN_PAGES) + return NULL; + +__found_pages: + /* create a new memory block */ + blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev); + if (blk == NULL) + return NULL; + blk->offset = aligned_page_offset(page); /* set aligned offset */ + firstpg(blk) = page; + lastpg(blk) = page + psize - 1; + return blk; +} + + +/* + * check if the given pointer is valid for pages + */ +static int is_valid_page(void *pages) +{ + unsigned long ptr = (unsigned long)virt_to_phys(pages); + if (ptr & ~0x3fffffffUL) { + snd_printk("max memory size is 1GB!!\n"); + return 0; + } + if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) { + snd_printk("page is not aligned\n"); + return 0; + } + return 1; +} + +/* + * page allocation for DMA + */ +snd_util_memblk_t * +snd_trident_alloc_pages(trident_t *trident, void *pages, dma_addr_t addr, unsigned long size) +{ + unsigned long ptr; + snd_util_memhdr_t *hdr; + snd_util_memblk_t *blk; + int page; + + snd_assert(trident != NULL, return NULL); + snd_assert(size > 0 && size < SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); + hdr = trident->tlb.memhdr; + snd_assert(hdr != NULL, return NULL); + + if (! is_valid_page(pages)) + return NULL; + + down(&hdr->block_mutex); + blk = search_empty(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + /* set TLB entries */ + ptr = (unsigned long)pages; + for (page = firstpg(blk); page <= lastpg(blk); page++) { + set_tlb_bus(trident, page, ptr, addr); + ptr += ALIGN_PAGE_SIZE; + addr += ALIGN_PAGE_SIZE; + } + up(&hdr->block_mutex); + return blk; +} + + +/* + * release DMA buffer from page table + */ +int snd_trident_free_pages(trident_t *trident, snd_util_memblk_t *blk) +{ + snd_util_memhdr_t *hdr; + int page; + + snd_assert(trident != NULL, return -EINVAL); + snd_assert(blk != NULL, return -EINVAL); + + hdr = trident->tlb.memhdr; + down(&hdr->block_mutex); + /* reset TLB entries */ + for (page = firstpg(blk); page <= lastpg(blk); page++) + set_silent_tlb(trident, page); + /* free memory block */ + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + + +/*---------------------------------------------------------------- + * memory allocation using multiple pages (for synth) + *---------------------------------------------------------------- + * Unlike the DMA allocation above, non-contiguous pages are + * assigned to TLB. + *----------------------------------------------------------------*/ + +/* + */ +static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk); +static int synth_free_pages(trident_t *hw, snd_util_memblk_t *blk); + +/* + * allocate a synth sample area + */ +snd_util_memblk_t * +snd_trident_synth_alloc(trident_t *hw, unsigned int size) +{ + snd_util_memblk_t *blk; + snd_util_memhdr_t *hdr = hw->tlb.memhdr; + + down(&hdr->block_mutex); + blk = __snd_util_mem_alloc(hdr, size); + if (blk == NULL) { + up(&hdr->block_mutex); + return NULL; + } + if (synth_alloc_pages(hw, blk)) { + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return NULL; + } + up(&hdr->block_mutex); + return blk; +} + + +/* + * free a synth sample area + */ +int +snd_trident_synth_free(trident_t *hw, snd_util_memblk_t *blk) +{ + snd_util_memhdr_t *hdr = hw->tlb.memhdr; + + down(&hdr->block_mutex); + synth_free_pages(hw, blk); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + + +/* + * reset TLB entry and free kernel page + */ +static void clear_tlb(trident_t *trident, int page) +{ + void *ptr = page_to_ptr(trident, page); + dma_addr_t addr = page_to_addr(trident, page); + set_silent_tlb(trident, page); + snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); +} + +/* check new allocation range */ +static void get_single_page_range(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk, int *first_page_ret, int *last_page_ret) +{ + struct list_head *p; + snd_util_memblk_t *q; + int first_page, last_page; + first_page = firstpg(blk); + if ((p = blk->list.prev) != &hdr->block) { + q = list_entry(p, snd_util_memblk_t, list); + if (lastpg(q) == first_page) + first_page++; /* first page was already allocated */ + } + last_page = lastpg(blk); + if ((p = blk->list.next) != &hdr->block) { + q = list_entry(p, snd_util_memblk_t, list); + if (firstpg(q) == last_page) + last_page--; /* last page was already allocated */ + } + *first_page_ret = first_page; + *last_page_ret = last_page; +} + +/* + * allocate kernel pages and assign them to TLB + */ +static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk) +{ + int page, first_page, last_page; + void *ptr; + dma_addr_t addr; + + firstpg(blk) = get_aligned_page(blk->offset); + lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1); + get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page); + + /* allocate a kernel page for each Trident page - + * fortunately Trident page size and kernel PAGE_SIZE is identical! + */ + for (page = first_page; page <= last_page; page++) { + ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr); + if (ptr == NULL) + goto __fail; + if (! is_valid_page(ptr)) { + snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr); + goto __fail; + } + set_tlb_bus(hw, page, (unsigned long)ptr, addr); + } + return 0; + +__fail: + /* release allocated pages */ + last_page = page - 1; + for (page = first_page; page <= last_page; page++) + clear_tlb(hw, page); + + return -ENOMEM; +} + +/* + * free pages + */ +static int synth_free_pages(trident_t *trident, snd_util_memblk_t *blk) +{ + int page, first_page, last_page; + + get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page); + for (page = first_page; page <= last_page; page++) + clear_tlb(trident, page); + + return 0; +} + +/* + * bzero(blk + offset, size) + */ +int snd_trident_synth_bzero(trident_t *trident, snd_util_memblk_t *blk, int offset, int size) +{ + int page, nextofs, end_offset, temp, temp1; + + offset += blk->offset; + end_offset = offset + size; + page = get_aligned_page(offset) + 1; + do { + nextofs = aligned_page_offset(page); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + memset(offset_ptr(trident, offset), 0, temp); + offset = nextofs; + page++; + } while (offset < end_offset); + return 0; +} + +/* + * copy_from_user(blk + offset, data, size) + */ +int snd_trident_synth_copy_from_user(trident_t *trident, snd_util_memblk_t *blk, int offset, const char *data, int size) +{ + int page, nextofs, end_offset, temp, temp1; + + offset += blk->offset; + end_offset = offset + size; + page = get_aligned_page(offset) + 1; + do { + nextofs = aligned_page_offset(page); + temp = nextofs - offset; + temp1 = end_offset - offset; + if (temp1 < temp) + temp = temp1; + if (copy_from_user(offset_ptr(trident, offset), data, temp)) + return -EFAULT; + offset = nextofs; + data += temp; + page++; + } while (offset < end_offset); + return 0; +} + diff -Nru linux/sound/pci/trident/trident_synth.c linux-2.4.19-pre5-mjc/sound/pci/trident/trident_synth.c --- linux/sound/pci/trident/trident_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/trident/trident_synth.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1029 @@ +/* + * Routines for Trident 4DWave NX/DX soundcards - Synthesizer + * Copyright (c) by Scott McNab + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Scott McNab "); +MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer"); +MODULE_LICENSE("GPL"); + +/* linear to log pan conversion table (4.2 channel attenuation format) */ +static unsigned int pan_table[63] = { + 7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, + 6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, + 5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, + 3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, + 3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, + 2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, + 1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, + 1588, 1543, 1499, 1456, 1415, 1375, 1336 +}; + +#define LOG_TABLE_SIZE 386 + +/* Linear half-attenuation to log conversion table in the format: + * {linear volume, logarithmic attenuation equivalent}, ... + * + * Provides conversion from a linear half-volume value in the range + * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB. + * Halving the linear volume is equivalent to an additional 6dB of + * logarithmic attenuation. The algorithm used in log_from_linear() + * therefore uses this table as follows: + * + * - loop and for every time the volume is less than half the maximum + * volume (16384), add another 6dB and halve the maximum value used + * for this comparison. + * - when the volume is greater than half the maximum volume, take + * the difference of the volume to half volume (in the range [0,8192]) + * and look up the log_table[] to find the nearest entry. + * - take the logarithic component of this entry and add it to the + * resulting attenuation. + * + * Thus this routine provides a linear->log conversion for a range of + * [0,16384] using only 386 table entries + * + * Note: although this table stores log attenuation in 8.8 format, values + * were only calculated for 6 bits fractional precision, since that is + * the most precision offered by the trident hardware. + */ + +static unsigned short log_table[LOG_TABLE_SIZE*2] = +{ + 4, 0x0604, 19, 0x0600, 34, 0x05fc, + 49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, + 123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, + 198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, + 274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, + 350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, + 428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, + 506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, + 584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, + 663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, + 743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, + 824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, + 906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, + 988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, + 1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, + 1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, + 1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, + 1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, + 1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, + 1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, + 1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, + 1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, + 1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, + 1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, + 1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, + 2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, + 2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, + 2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, + 2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, + 2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, + 2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, + 2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, + 2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, + 2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, + 2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, + 2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, + 3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, + 3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, + 3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, + 3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, + 3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, + 3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, + 3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, + 3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, + 3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, + 4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, + 4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, + 4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, + 4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, + 4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, + 4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, + 4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, + 4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, + 4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, + 5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, + 5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, + 5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, + 5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, + 5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, + 5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, + 5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, + 5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, + 6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, + 6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, + 6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, + 6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, + 6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, + 6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, + 6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, + 6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, + 7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, + 7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, + 7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, + 7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, + 7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, + 7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, + 7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, + 8133, 0x0008, 8162, 0x0004, 8192, 0x0000 +}; + +static unsigned short lookup_volume_table( unsigned short value ) +{ + /* This code is an optimised version of: + * int i = 0; + * while( volume_table[i*2] < value ) + * i++; + * return volume_table[i*2+1]; + */ + unsigned short *ptr = log_table; + while( *ptr < value ) + ptr += 2; + return *(ptr+1); +} + +/* this function calculates a 8.8 fixed point logarithmic attenuation + * value from a linear volume value in the range 0 to 16384 */ +static unsigned short log_from_linear( unsigned short value ) +{ + if (value >= 16384) + return 0x0000; + if (value) { + unsigned short result = 0; + int v, c; + for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) { + if( value >= v ) { + result += lookup_volume_table( (value - v) << c ); + return result; + } + result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */ + } + } + return 0xffff; +} + +/* + * Sample handling operations + */ + +static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position); +static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode); +static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq); +static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume); +static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop); +static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position); +static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data); + +static snd_trident_sample_ops_t sample_ops = +{ + sample_start, + sample_stop, + sample_freq, + sample_volume, + sample_loop, + sample_pos, + sample_private1 +}; + +static void snd_trident_simple_init(snd_trident_voice_t * voice) +{ + //voice->handler_wave = interrupt_wave; + //voice->handler_volume = interrupt_volume; + //voice->handler_effect = interrupt_effect; + //voice->volume_change = NULL; + voice->sample_ops = &sample_ops; +} + +static void sample_start(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position) +{ + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned long flags; + unsigned int loop_start, loop_end, sample_start, sample_end, start_offset; + unsigned int value; + unsigned int shift = 0; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + spin_lock_irqsave(&trident->reg_lock, flags); + + if (trident->device == TRIDENT_DEVICE_ID_SI7018) + voice->GVSel = 1; /* route to Wave volume */ + + voice->CTRL = 0; + voice->Alpha = 0; + voice->FMS = 0; + + loop_start = simple->loop_start >> 4; + loop_end = simple->loop_end >> 4; + sample_start = (simple->start + position) >> 4; + if( sample_start >= simple->size ) + sample_start = simple->start >> 4; + sample_end = simple->size; + start_offset = position >> 4; + + if (simple->format & SIMPLE_WAVE_16BIT) { + voice->CTRL |= 8; + shift++; + } + if (simple->format & SIMPLE_WAVE_STEREO) { + voice->CTRL |= 4; + shift++; + } + if (!(simple->format & SIMPLE_WAVE_UNSIGNED)) + voice->CTRL |= 2; + + voice->LBA = simple->address.memory; + + if (simple->format & SIMPLE_WAVE_LOOP) { + voice->CTRL |= 1; + voice->LBA += loop_start << shift; + if( start_offset >= loop_start ) { + voice->CSO = start_offset - loop_start; + voice->negCSO = 0; + } else { + voice->CSO = loop_start - start_offset; + voice->negCSO = 1; + } + voice->ESO = loop_end - loop_start - 1; + } else { + voice->LBA += start_offset << shift; + voice->CSO = sample_start; + voice->ESO = sample_end - 1; + voice->negCSO = 0; + } + + if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) { + snd_trident_stop_voice(trident, voice->number); + voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + } + + /* set CSO sign */ + value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); + if( voice->negCSO ) { + value |= 1 << (voice->number&31); + } else { + value &= ~(1 << (voice->number&31)); + } + outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); + + voice->Attribute = 0; + snd_trident_write_voice_regs(trident, voice); + snd_trident_start_voice(trident, voice->number); + voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING; + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_stop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_stop_mode_t mode) +{ + unsigned long flags; + + if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING)) + return; + + switch (mode) { + default: + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_stop_voice(trident, voice->number); + voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + spin_unlock_irqrestore(&trident->reg_lock, flags); + break; + case SAMPLE_STOP_LOOP: /* disable loop only */ + voice->CTRL &= ~1; + spin_lock_irqsave(&trident->reg_lock, flags); + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC); + spin_unlock_irqrestore(&trident->reg_lock, flags); + break; + } +} + +static void sample_freq(trident_t * trident, snd_trident_voice_t * voice, snd_seq_frequency_t freq) +{ + unsigned long flags; + freq >>= 4; + + spin_lock_irqsave(&trident->reg_lock, flags); + if (freq == 44100) + voice->Delta = 0xeb3; + else if (freq == 8000) + voice->Delta = 0x2ab; + else if (freq == 48000) + voice->Delta = 0x1000; + else + voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff; + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3)); + outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3)); + } else { + outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA)); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +static void sample_volume(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_volume_t * volume) +{ + unsigned long flags; + unsigned short value; + + spin_lock_irqsave(&trident->reg_lock, flags); + voice->GVSel = 0; /* use global music volume */ + voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */ + if (volume->volume >= 0) { + volume->volume &= 0x3fff; + /* linear volume -> logarithmic attenuation conversion + * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits) + * Vol register used when additional attenuation is required */ + voice->RVol = 0; + voice->CVol = 0; + value = log_from_linear( volume->volume ); + voice->Vol = 0; + voice->EC = (value & 0x3fff) >> 2; + if (value > 0x3fff) { + voice->EC |= 0xfc0; + if (value < 0x5f00 ) + voice->Vol = ((value >> 8) - 0x3f) << 5; + else { + voice->Vol = 0x3ff; + voice->EC = 0xfff; + } + } + } + if (volume->lr >= 0) { + volume->lr &= 0x3fff; + /* approximate linear pan by attenuating channels */ + if (volume->lr >= 0x2000) { /* attenuate left (pan right) */ + value = 0x3fff - volume->lr; + for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) + if (value >= pan_table[voice->Pan] ) + break; + } else { /* attenuate right (pan left) */ + for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) + if (volume->lr >= pan_table[voice->Pan] ) + break; + voice->Pan |= 0x40; + } + } + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) | + ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) | + (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); + value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f); + outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); + spin_unlock_irqrestore(&trident->reg_lock, flags); +} + +static void sample_loop(trident_t * trident, snd_trident_voice_t * voice, snd_seq_ev_loop_t * loop) +{ + unsigned long flags; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned int loop_start, loop_end; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + loop_start = loop->start >> 4; + loop_end = loop->end >> 4; + + spin_lock_irqsave(&trident->reg_lock, flags); + + voice->LBA = simple->address.memory + loop_start; + voice->CSO = 0; + voice->ESO = loop_end - loop_start - 1; + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2)); + outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2)); + outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO)); + outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + } else { + outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2)); + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_pos(trident_t * trident, snd_trident_voice_t * voice, snd_seq_position_t position) +{ + unsigned long flags; + simple_instrument_t *simple; + snd_seq_kinstr_t *instr; + unsigned int value; + + instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); + if (instr == NULL) + return; + voice->instr = instr->instr; /* copy ID to speedup aliases */ + simple = KINSTR_DATA(instr); + + spin_lock_irqsave(&trident->reg_lock, flags); + + if (simple->format & SIMPLE_WAVE_LOOP) { + if( position >= simple->loop_start ) { + voice->CSO = (position - simple->loop_start) >> 4; + voice->negCSO = 0; + } else { + voice->CSO = (simple->loop_start - position) >> 4; + voice->negCSO = 1; + } + } else { + voice->CSO = position >> 4; + voice->negCSO = 0; + } + + /* set CSO sign */ + value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); + if( voice->negCSO ) { + value |= 1 << (voice->number&31); + } else { + value &= ~(1 << (voice->number&31)); + } + outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); + + + outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); + if (trident->device == TRIDENT_DEVICE_ID_NX) { + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); + outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); + } else { + outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); + } + + spin_unlock_irqrestore(&trident->reg_lock, flags); + snd_seq_instr_free_use(trident->synth.ilist, instr); +} + +static void sample_private1(trident_t * trident, snd_trident_voice_t * voice, unsigned char *data) +{ +} + +/* + * Memory management / sample loading + */ + +static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr, + char *data, long len, int atomic) +{ + trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + unsigned char *block = NULL; + int size = instr->size; + int shift = 0; + + if (instr->format & SIMPLE_WAVE_BACKWARD || + instr->format & SIMPLE_WAVE_BIDIR || + instr->format & SIMPLE_WAVE_ULAW) + return -EINVAL; /* not supported */ + + if (instr->format & SIMPLE_WAVE_16BIT) + shift++; + if (instr->format & SIMPLE_WAVE_STEREO) + shift++; + size <<= shift; + + if (trident->synth.current_size + size > trident->synth.max_size) + return -ENOMEM; + + if (verify_area(VERIFY_READ, data, size)) + return -EFAULT; + + if (trident->tlb.entries) { + snd_util_memblk_t *memblk; + memblk = snd_trident_synth_alloc(trident,size); + if (memblk == NULL) + return -ENOMEM; + if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) { + snd_trident_synth_free(trident, memblk); + return -EFAULT; + } + instr->address.ptr = (unsigned char*)memblk; + instr->address.memory = memblk->offset; + } else { + dma_addr_t addr; + block = (unsigned char *) snd_malloc_pci_pages(trident->pci, size, &addr); + if (block == NULL) + return -ENOMEM; + + if (copy_from_user(block, data, size)) { + snd_free_pci_pages(trident->pci, size, block, addr); + return -EFAULT; + } + instr->address.ptr = block; + instr->address.memory = addr; + } + + trident->synth.current_size += size; + return 0; +} + +static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr, + char *data, long len, int atomic) +{ + //trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + int size = instr->size; + int shift = 0; + + if (instr->format & SIMPLE_WAVE_16BIT) + shift++; + if (instr->format & SIMPLE_WAVE_STEREO) + shift++; + size <<= shift; + + if (verify_area(VERIFY_WRITE, data, size)) + return -EFAULT; + + /* FIXME: not implemented yet */ + + return -EBUSY; +} + +static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr, + int atomic) +{ + trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + int size = instr->size; + + if (trident->tlb.entries) { + snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr; + if (memblk) + snd_trident_synth_free(trident, memblk); + else + return -EFAULT; + } else { + kfree(instr->address.ptr); + } + + if (instr->format & SIMPLE_WAVE_16BIT) + size <<= 1; + if (instr->format & SIMPLE_WAVE_STEREO) + size <<= 1; + + trident->synth.current_size -= size; + if (trident->synth.current_size < 0) /* shouldnt need this check... */ + trident->synth.current_size = 0; + + return 0; +} + +static void select_instrument(trident_t * trident, snd_trident_voice_t * v) +{ + snd_seq_kinstr_t *instr; + instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1); + if (instr != NULL) { + if (instr->ops) { + if (instr->ops->instr_type == snd_seq_simple_id) + snd_trident_simple_init(v); + } + snd_seq_instr_free_use(trident->synth.ilist, instr); + } +} + +/* + + */ + +static void event_sample(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.std = ev->data.sample.param.sample.std; + if (v->instr.std & 0xff000000) { /* private instrument */ + v->instr.std &= 0x00ffffff; + v->instr.std |= (unsigned int)ev->source.client << 24; + } + v->instr.bank = ev->data.sample.param.sample.bank; + v->instr.prg = ev->data.sample.param.sample.prg; + select_instrument(p->trident, v); +} + +static void event_cluster(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); + v->instr.cluster = ev->data.sample.param.cluster.cluster; + select_instrument(p->trident, v); +} + +static void event_start(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_start) + v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position); +} + +static void event_stop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_stop) + v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode); +} + +static void event_freq(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_freq) + v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency); +} + +static void event_volume(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_volume) + v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume); +} + +static void event_loop(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_loop) + v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop); +} + +static void event_position(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_pos) + v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position); +} + +static void event_private1(snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v) +{ + if (v->sample_ops && v->sample_ops->sample_private1) + v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8); +} + +typedef void (trident_sample_event_handler_t) (snd_seq_event_t * ev, snd_trident_port_t * p, snd_trident_voice_t * v); + +static trident_sample_event_handler_t *trident_sample_event_handlers[9] = +{ + event_sample, + event_cluster, + event_start, + event_stop, + event_freq, + event_volume, + event_loop, + event_position, + event_private1 +}; + +static void snd_trident_sample_event(snd_seq_event_t * ev, snd_trident_port_t * p) +{ + int idx, voice; + trident_t *trident = p->trident; + snd_trident_voice_t *v; + unsigned long flags; + + idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; + if (idx < 0 || idx > 8) + return; + for (voice = 0; voice < 64; voice++) { + v = &trident->synth.voices[voice]; + if (v->use && v->client == ev->source.client && + v->port == ev->source.port && + v->index == ev->data.sample.channel) { + spin_lock_irqsave(&trident->event_lock, flags); + trident_sample_event_handlers[idx] (ev, p, v); + spin_unlock_irqrestore(&trident->event_lock, flags); + return; + } + } +} + +/* + + */ + +static void snd_trident_synth_free_voices(trident_t * trident, int client, int port) +{ + int idx; + snd_trident_voice_t *voice; + + for (idx = 0; idx < 32; idx++) { + voice = &trident->synth.voices[idx]; + if (voice->use && voice->client == client && voice->port == port) + snd_trident_free_voice(trident, voice); + } +} + +static int snd_trident_synth_use(void *private_data, snd_seq_port_subscribe_t * info) +{ + snd_trident_port_t *port = (snd_trident_port_t *) private_data; + trident_t *trident = port->trident; + snd_trident_voice_t *voice; + int idx; + unsigned long flags; + + if (info->voices > 32) + return -EINVAL; + spin_lock_irqsave(&trident->reg_lock, flags); + for (idx = 0; idx < info->voices; idx++) { + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return -EBUSY; + } + voice->index = idx; + voice->Vol = 0x3ff; + voice->EC = 0x0fff; + } +#if 0 + for (idx = 0; idx < info->midi_voices; idx++) { + port->midi_has_voices = 1; + voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port); + if (voice == NULL) { + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return -EBUSY; + } + voice->Vol = 0x3ff; + voice->EC = 0x0fff; + } +#endif + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +static int snd_trident_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) +{ + snd_trident_port_t *port = (snd_trident_port_t *) private_data; + trident_t *trident = port->trident; + unsigned long flags; + + spin_lock_irqsave(&trident->reg_lock, flags); + snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); + spin_unlock_irqrestore(&trident->reg_lock, flags); + return 0; +} + +/* + + */ + +static void snd_trident_synth_free_private_instruments(snd_trident_port_t * p, int client) +{ + snd_seq_instr_header_t ifree; + + memset(&ifree, 0, sizeof(ifree)); + ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; + snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0); +} + +int snd_trident_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) +{ + snd_trident_port_t *p = (snd_trident_port_t *) private_data; + + if (p == NULL) + return -EINVAL; + if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && + ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { + snd_trident_sample_event(ev, p); + return 0; + } + if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && + ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { + if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { + snd_trident_synth_free_private_instruments(p, ev->data.addr.client); + return 0; + } + } + if (direct) { + if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { + snd_seq_instr_event(&p->trident->synth.simple_ops.kops, + p->trident->synth.ilist, ev, + p->trident->synth.seq_client, atomic, hop); + return 0; + } + } + return 0; +} + +static void snd_trident_synth_instr_notify(void *private_data, + snd_seq_kinstr_t * instr, + int what) +{ + int idx; + trident_t *trident = snd_magic_cast(trident_t, private_data, return); + snd_trident_voice_t *pvoice; + unsigned long flags; + + spin_lock_irqsave(&trident->event_lock, flags); + for (idx = 0; idx < 64; idx++) { + pvoice = &trident->synth.voices[idx]; + if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { + if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { + pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY); + } else { + snd_trident_stop_voice(trident, pvoice->number); + pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; + } + } + } + spin_unlock_irqrestore(&trident->event_lock, flags); +} + +/* + + */ + +static void snd_trident_synth_free_port(void *private_data) +{ + snd_trident_port_t *p = (snd_trident_port_t *) private_data; + + if (p) + snd_midi_channel_free_set(p->chset); +} + +static int snd_trident_synth_create_port(trident_t * trident, int idx) +{ + snd_trident_port_t *p; + snd_seq_port_callback_t callbacks; + char name[32]; + char *str; + int result; + + p = &trident->synth.seq_ports[idx]; + p->chset = snd_midi_channel_alloc_set(16); + if (p->chset == NULL) + return -ENOMEM; + p->chset->private_data = p; + p->trident = trident; + p->client = trident->synth.seq_client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.owner = THIS_MODULE; + callbacks.use = snd_trident_synth_use; + callbacks.unuse = snd_trident_synth_unuse; + callbacks.event_input = snd_trident_synth_event_input; + callbacks.private_free = snd_trident_synth_free_port; + callbacks.private_data = p; + + str = "???"; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; + case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; + case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; + } + sprintf(name, "%s port %i", str, idx); + p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client, + &callbacks, + SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_MIDI_GS | + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | + SNDRV_SEQ_PORT_TYPE_SYNTH, + name); + if (p->chset->port < 0) { + result = p->chset->port; + snd_trident_synth_free_port(p); + return result; + } + p->port = p->chset->port; + return 0; +} + +/* + + */ + +static int snd_trident_synth_new_device(snd_seq_device_t *dev) +{ + trident_t *trident; + int client, i; + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + snd_seq_port_subscribe_t sub; + snd_simple_ops_t *simpleops; + char *str; + + trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (trident == NULL) + return -EINVAL; + + trident->synth.seq_client = -1; + + /* allocate new client */ + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = trident; + callbacks.allow_output = callbacks.allow_input = 1; + client = trident->synth.seq_client = + snd_seq_create_kernel_client(trident->card, 1, &callbacks); + if (client < 0) + return client; + + /* change name of client */ + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + str = "???"; + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; + case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; + case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; + } + sprintf(cinfo.name, str); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + for (i = 0; i < 4; i++) + snd_trident_synth_create_port(trident, i); + + trident->synth.ilist = snd_seq_instr_list_new(); + if (trident->synth.ilist == NULL) { + snd_seq_delete_kernel_client(client); + trident->synth.seq_client = -1; + return -ENOMEM; + } + trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; + + simpleops = &trident->synth.simple_ops; + snd_seq_simple_init(simpleops, trident, NULL); + simpleops->put_sample = snd_trident_simple_put_sample; + simpleops->get_sample = snd_trident_simple_get_sample; + simpleops->remove_sample = snd_trident_simple_remove_sample; + simpleops->notify = snd_trident_synth_instr_notify; + + memset(&sub, 0, sizeof(sub)); + sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; + sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; + sub.dest.client = client; + sub.dest.port = 0; + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); + + return 0; +} + +static int snd_trident_synth_delete_device(snd_seq_device_t *dev) +{ + trident_t *trident; + + trident = *(trident_t **)SNDRV_SEQ_DEVICE_ARGPTR(dev); + if (trident == NULL) + return -EINVAL; + + if (trident->synth.seq_client >= 0) { + snd_seq_delete_kernel_client(trident->synth.seq_client); + trident->synth.seq_client = -1; + } + if (trident->synth.ilist) + snd_seq_instr_list_free(&trident->synth.ilist); + return 0; +} + +static int __init alsa_trident_synth_init(void) +{ + static snd_seq_dev_ops_t ops = + { + snd_trident_synth_new_device, + snd_trident_synth_delete_device + }; + + return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops, + sizeof(trident_t*)); +} + +static void __exit alsa_trident_synth_exit(void) +{ + snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT); +} + +module_init(alsa_trident_synth_init) +module_exit(alsa_trident_synth_exit) diff -Nru linux/sound/pci/via686.c linux-2.4.19-pre5-mjc/sound/pci/via686.c --- linux/sound/pci/via686.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/via686.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1290 @@ +/* + * ALSA driver for VIA VT82C686A (South Bridge) + * + * Copyright (c) 2000 Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("VIA VT82C686A"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{VIA,VT82C686A,pci},{VIA,VT82C686B}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for VIA 82C686A bridge."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for VIA 82C686A bridge."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82C686A bridge."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz)."); +MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000"); + +/* + * Direct registers + */ + +#ifndef PCI_DEVICE_ID_VIA_82C686_5 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#endif + +#define VIAREG(via, x) ((via)->port + VIA_REG_##x) + +/* offsets */ +#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_STAT_ACTIVE 0x80 /* RO */ +#define VIA_REG_STAT_PAUSED 0x40 /* RO */ +#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */ +#define VIA_REG_STAT_STOPPED 0x04 /* RWC */ +#define VIA_REG_STAT_EOL 0x02 /* RWC */ +#define VIA_REG_STAT_FLAG 0x01 /* RWC */ +#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_CTRL_START 0x80 /* WO */ +#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */ +#define VIA_REG_CTRL_PAUSE 0x08 /* RW */ +#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */ +#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type */ +#define VIA_REG_TYPE_AUTOSTART 0x80 /* RW - autostart at EOL */ +#define VIA_REG_TYPE_16BIT 0x20 /* RW */ +#define VIA_REG_TYPE_STEREO 0x10 /* RW */ +#define VIA_REG_TYPE_INT_LLINE 0x00 +#define VIA_REG_TYPE_INT_LSAMPLE 0x04 +#define VIA_REG_TYPE_INT_LESSONE 0x08 +#define VIA_REG_TYPE_INT_MASK 0x0c +#define VIA_REG_TYPE_INT_EOL 0x02 +#define VIA_REG_TYPE_INT_FLAG 0x01 +#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count */ +/* playback block */ +#define VIA_REG_PLAYBACK_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_PLAYBACK_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_PLAYBACK_TYPE 0x02 /* byte - channel type */ +#define VIA_REG_PLAYBACK_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_PLAYBACK_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_PLAYBACK_CURR_COUNT 0x0c /* dword - channel current count */ +/* capture block */ +#define VIA_REG_CAPTURE_STATUS 0x10 /* byte - channel status */ +#define VIA_REG_CAPTURE_CONTROL 0x11 /* byte - channel control */ +#define VIA_REG_CAPTURE_TYPE 0x12 /* byte - channel type */ +#define VIA_REG_CAPTURE_TABLE_PTR 0x14 /* dword - channel table pointer */ +#define VIA_REG_CAPTURE_CURR_PTR 0x14 /* dword - channel current pointer */ +#define VIA_REG_CAPTURE_CURR_COUNT 0x1c /* dword - channel current count */ +/* FM block */ +#define VIA_REG_FM_STATUS 0x20 /* byte - channel status */ +#define VIA_REG_FM_CONTROL 0x21 /* byte - channel control */ +#define VIA_REG_FM_TYPE 0x22 /* byte - channel type */ +#define VIA_REG_FM_TABLE_PTR 0x24 /* dword - channel table pointer */ +#define VIA_REG_FM_CURR_PTR 0x24 /* dword - channel current pointer */ +#define VIA_REG_FM_CURR_COUNT 0x2c /* dword - channel current count */ +/* AC'97 */ +#define VIA_REG_AC97 0x80 /* dword */ +#define VIA_REG_AC97_CODEC_ID_MASK (3<<30) +#define VIA_REG_AC97_CODEC_ID_SHIFT 30 +#define VIA_REG_AC97_CODEC_ID_PRIMARY 0x00 +#define VIA_REG_AC97_CODEC_ID_SECONDARY 0x01 +#define VIA_REG_AC97_SECONDARY_VALID (1<<27) +#define VIA_REG_AC97_PRIMARY_VALID (1<<25) +#define VIA_REG_AC97_BUSY (1<<24) +#define VIA_REG_AC97_READ (1<<23) +#define VIA_REG_AC97_CMD_SHIFT 16 +#define VIA_REG_AC97_CMD_MASK 0x7e +#define VIA_REG_AC97_DATA_SHIFT 0 +#define VIA_REG_AC97_DATA_MASK 0xffff +#define VIA_REG_SGD_SHADOW 0x84 /* dword */ + +/* + * + */ + +#define VIA_MAX_FRAGS 32 + +/* + * + */ + +typedef struct { + unsigned long reg_offset; + unsigned int *table; + dma_addr_t table_addr; + snd_pcm_substream_t *substream; + dma_addr_t physbuf; + unsigned int size; + unsigned int fragsize; + unsigned int frags; + unsigned int lastptr; + unsigned int lastcount; +} viadev_t; + +typedef struct _snd_via686a via686a_t; +#define chip_t via686a_t + +struct _snd_via686a { + int irq; + + unsigned long port; + struct resource *res_port; + unsigned char revision; + + unsigned char old_legacy; + unsigned char old_legacy_cfg; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + snd_pcm_t *pcm_fm; + viadev_t playback; + viadev_t capture; + viadev_t playback_fm; + + snd_rawmidi_t *rmidi; + + ac97_t *ac97; + unsigned int ac97_clock; + unsigned int ac97_secondary; /* secondary AC'97 codec is present */ + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; + + void *tables; + dma_addr_t tables_addr; +}; + +static struct pci_device_id snd_via686a_ids[] __devinitdata = { + { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 686A */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via686a_ids); + +/* + * Basic I/O + */ + +static inline unsigned int snd_via686a_codec_xread(via686a_t *chip) +{ + return inl(VIAREG(chip, AC97)); +} + +static inline void snd_via686a_codec_xwrite(via686a_t *chip, unsigned int val) +{ + outl(val, VIAREG(chip, AC97)); +} + +static int snd_via686a_codec_ready(via686a_t *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + + while (timeout-- > 0) { + udelay(1); + if (!((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY)) + return val & 0xffff; + } + snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via686a_codec_xread(chip)); + return -EIO; +} + +static int snd_via686a_codec_valid(via686a_t *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID : + VIA_REG_AC97_SECONDARY_VALID; + + while (timeout-- > 0) { + udelay(1); + if ((val = snd_via686a_codec_xread(chip)) & stat) + return val & 0xffff; + } + snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via686a_codec_xread(chip)); + return -EIO; +} + +static void snd_via686a_codec_wait(ac97_t *ac97) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + int err; + err = snd_via686a_codec_ready(chip, ac97->num); + /* here we need to wait fairly for long time.. */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/2); +} + +static void snd_via686a_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + unsigned int xval; + + xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; + xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= reg << VIA_REG_AC97_CMD_SHIFT; + xval |= val << VIA_REG_AC97_DATA_SHIFT; + spin_lock(&chip->ac97_lock); + snd_via686a_codec_xwrite(chip, xval); + snd_via686a_codec_ready(chip, ac97->num); + spin_unlock(&chip->ac97_lock); +} + +static unsigned short snd_via686a_codec_read(ac97_t *ac97, unsigned short reg) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return ~0); + unsigned int xval, val = 0xffff; + int again = 0; + + xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; + xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; + xval = (!ac97->num ? VIA_REG_AC97_PRIMARY_VALID : VIA_REG_AC97_SECONDARY_VALID); + xval |= VIA_REG_AC97_READ; + xval |= reg << VIA_REG_AC97_CMD_SHIFT; + spin_lock(&chip->ac97_lock); + while (1) { + if (again++ > 3) { + spin_unlock(&chip->ac97_lock); + return 0xffff; + } + snd_via686a_codec_xwrite(chip, xval); + if (snd_via686a_codec_ready(chip, ac97->num) < 0) + continue; + if (snd_via686a_codec_valid(chip, ac97->num) >= 0) { + udelay(25); + val = snd_via686a_codec_xread(chip); + break; + } + } + spin_unlock(&chip->ac97_lock); + return val & 0xffff; +} + +#if 0 +static void snd_via686a_channel_print(via686a_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n", + port, + inb(port + VIA_REG_OFFSET_STATUS), + inb(port + VIA_REG_OFFSET_CONTROL), + inb(port + VIA_REG_OFFSET_TYPE), + inl(port + VIA_REG_OFFSET_CURR_PTR), + inl(port + VIA_REG_OFFSET_CURR_COUNT)); +} +#endif + +static void snd_via686a_channel_reset(via686a_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL); + udelay(50); + outb(0x00, port + VIA_REG_OFFSET_CONTROL); + outb(0xff, port + VIA_REG_OFFSET_STATUS); + outb(0x00, port + VIA_REG_OFFSET_TYPE); + outl(0, port + VIA_REG_OFFSET_CURR_PTR); +} + +static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd) +{ + unsigned char val = 0; + unsigned long port = chip->port + viadev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = VIA_REG_CTRL_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = VIA_REG_CTRL_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = 0; + break; + default: + return -EINVAL; + } + outb(val, port + VIA_REG_OFFSET_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_STOP) + snd_via686a_channel_reset(chip, viadev); + return 0; +} + +static void snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int idx, frags; + unsigned int *table = viadev->table; + unsigned long port = chip->port + viadev->reg_offset; + + viadev->physbuf = runtime->dma_addr; + viadev->size = snd_pcm_lib_buffer_bytes(substream); + viadev->fragsize = snd_pcm_lib_period_bytes(substream); + viadev->frags = runtime->periods; + viadev->lastptr = ~0; + viadev->lastcount = ~0; + + snd_via686a_channel_reset(chip, viadev); + outl(viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + outb(VIA_REG_TYPE_AUTOSTART | + (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | + ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) | + VIA_REG_TYPE_INT_EOL | + VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE); + if (viadev->size == viadev->fragsize) { + table[0] = cpu_to_le32(viadev->physbuf); + table[1] = cpu_to_le32(0xc0000000 | /* EOL + flag */ + viadev->fragsize); + } else { + frags = viadev->size / viadev->fragsize; + for (idx = 0; idx < frags - 1; idx++) { + table[(idx << 1) + 0] = cpu_to_le32(viadev->physbuf + (idx * viadev->fragsize)); + table[(idx << 1) + 1] = cpu_to_le32(0x40000000 | /* flag */ + viadev->fragsize); + } + table[((frags-1) << 1) + 0] = cpu_to_le32(viadev->physbuf + ((frags-1) * viadev->fragsize)); + table[((frags-1) << 1) + 1] = cpu_to_le32(0x80000000 | /* EOL */ + viadev->fragsize); + } +} + +/* + * Interrupt handler + */ + +static inline void snd_via686a_update(via686a_t *chip, viadev_t *viadev) +{ + snd_pcm_period_elapsed(viadev->substream); + outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); +} + +static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + via686a_t *chip = snd_magic_cast(via686a_t, dev_id, return); + unsigned int status; + + status = inl(VIAREG(chip, SGD_SHADOW)); + if ((status & 0x00000077) == 0) { + if (chip->rmidi != NULL) { + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + } + return; + } + if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via686a_update(chip, &chip->playback); + if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via686a_update(chip, &chip->playback_fm); + if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via686a_update(chip, &chip->capture); +} + +/* + * PCM part + */ + +static int snd_via686a_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + return snd_via686a_trigger(chip, &chip->playback, cmd); +} + +static int snd_via686a_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + return snd_via686a_trigger(chip, &chip->capture, cmd); +} + +static int snd_via686a_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_via686a_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_via686a_setup_periods(chip, &chip->playback, substream); + return 0; +} + +static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_via686a_setup_periods(chip, &chip->capture, substream); + return 0; +} + +static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev) +{ + unsigned int val, ptr, count; + // unsigned int tmp; + + ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset); + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset); + if (ptr == viadev->lastptr && count > viadev->lastcount) + ptr += 8; + if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) + return 0; + // tmp = + val = (((unsigned int)(ptr - viadev->table_addr) / 8) - 1) % viadev->frags; + val *= viadev->fragsize; + val += viadev->fragsize - count; + viadev->lastptr = ptr; + viadev->lastcount = count; + // printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, tmp, count, val); + return val; +} + +static snd_pcm_uframes_t snd_via686a_playback_pointer(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback)); +} + +static snd_pcm_uframes_t snd_via686a_capture_pointer(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->capture)); +} + +static snd_pcm_hardware_t snd_via686a_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_via686a_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static int snd_via686a_playback_open(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback.substream = substream; + runtime->hw = snd_via686a_playback; + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via686a_capture_open(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture.substream = substream; + runtime->hw = snd_via686a_capture; + runtime->hw.rates = chip->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via686a_playback_close(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + chip->playback.substream = NULL; + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static int snd_via686a_capture_close(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + chip->capture.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + return 0; +} + +static snd_pcm_ops_t snd_via686a_playback_ops = { + open: snd_via686a_playback_open, + close: snd_via686a_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via686a_hw_params, + hw_free: snd_via686a_hw_free, + prepare: snd_via686a_playback_prepare, + trigger: snd_via686a_playback_trigger, + pointer: snd_via686a_playback_pointer, +}; + +static snd_pcm_ops_t snd_via686a_capture_ops = { + open: snd_via686a_capture_open, + close: snd_via686a_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via686a_hw_params, + hw_free: snd_via686a_hw_free, + prepare: snd_via686a_capture_prepare, + trigger: snd_via686a_capture_trigger, + pointer: snd_via686a_capture_pointer, +}; + +static void snd_via686a_pcm_free(snd_pcm_t *pcm) +{ + via686a_t *chip = snd_magic_cast(via686a_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "VIA 82C686A", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686a_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_via686a_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "VIA 82C686A"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = NULL; + return 0; +} + +#if 0 + +/* + * PCM code - FM channel + */ + +static int snd_via686a_playback_fm_ioctl(snd_pcm_substream_t * substream, + unsigned int cmd, + void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_via686a_playback_fm_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + + return snd_via686a_trigger(chip, &chip->playback_fm, cmd); +} + +static int snd_via686a_playback_fm_prepare(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_via686a_setup_periods(chip, &chip->playback_fm, substream); + return 0; +} + +static snd_pcm_uframes_t snd_via686a_playback_fm_pointer(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback_fm)); +} + +static snd_pcm_hardware_t snd_via686a_playback_fm = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_KNOT, + rate_min: 24000, + rate_max: 24000, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static int snd_via686a_playback_fm_open(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma_fm_size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL) + return -ENOMEM; + chip->playback_fm.substream = substream; + runtime->hw = snd_via686a_playback_fm; +#if 0 + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.min_rate = 48000; +#endif + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via686a_playback_fm_close(snd_pcm_substream_t * substream) +{ + via686a_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + chip->playback_fm.substream = NULL; + snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static snd_pcm_ops_t snd_via686a_playback_fm_ops = { + open: snd_via686a_playback_fm_open, + close: snd_via686a_playback_fm_close, + ioctl: snd_pcm_lib_ioctl, + prepare: snd_via686a_playback_fm_prepare, + trigger: snd_via686a_playback_fm_trigger, + pointer: snd_via686a_playback_fm_pointer, +}; + +static void snd_via686a_pcm_fm_free(void *private_data) +{ + via686a_t *chip = snd_magic_cast(via686a_t, private_data, return); + chip->pcm_fm = NULL; + snd_pcm_lib_preallocate_pci_free_for_all(ensoniq->pci, pcm); +} + +static int __devinit snd_via686a_pcm_fm(via686a_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "VIA 82C686A - FM DAC", device, 1, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_fm_ops); + + pcm->private_data = chip; + pcm->private_free = snd_via686a_pcm_fm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "VIA 82C686A - FM DAC"); + + snd_pcm_add_buffer_bytes_controls(pcm); + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024); + + chip->pcm_fm = pcm; + if (rpcm) + *rpcm = NULL; + return 0; +} + +#endif + +/* + * Mixer part + */ + +static void snd_via686a_codec_init(ac97_t *ac97) +{ + // via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + + /* disable DAC & ADC power */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300); + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_via686a_mixer_free_ac97(ac97_t *ac97) +{ + via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static int __devinit snd_via686a_mixer(via686a_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_via686a_codec_write; + ac97.read = snd_via686a_codec_read; + ac97.init = snd_via686a_codec_init; + ac97.wait = snd_via686a_codec_wait; + ac97.private_data = chip; + ac97.private_free = snd_via686a_mixer_free_ac97; + ac97.clock = chip->ac97_clock; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + return 0; +} + +/* + * joystick + */ + +static int snd_via686a_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_via686a_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via686a_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, 0x42, &val); + ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0; + return 0; +} + +static int snd_via686a_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via686a_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, 0x42, &oval); + val = oval & ~0x08; + if (ucontrol->value.integer.value[0]) + val |= 0x08; + if (val != oval) { + pci_write_config_word(chip->pci, 0x42, val); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_via686a_joystick_control __devinitdata = { + name: "Joystick", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_via686a_joystick_info, + get: snd_via686a_joystick_get, + put: snd_via686a_joystick_put, +}; + +/* + * + */ + +static int __devinit snd_via686a_chip_init(via686a_t *chip) +{ + ac97_t ac97; + unsigned int val; + int max_count; + unsigned char pval; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + +#if 0 /* broken on K7M? */ + /* disable all legacy ports */ + pci_write_config_byte(chip->pci, 0x42, 0); +#endif + + /* deassert ACLink reset, force SYNC */ + pci_write_config_byte(chip->pci, 0x41, 0xe0); + udelay(100); + pci_write_config_byte(chip->pci, 0x41, 0x00); + udelay(100); + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + /* note - FM data out has trouble with non VRA codecs !! */ + pci_write_config_byte(chip->pci, 0x41, 0xcc); + udelay(100); + + /* wait until codec ready */ + max_count = ((3 * HZ) / 4) + 1; + do { + pci_read_config_byte(chip->pci, 0x40, &pval); + if (pval & 0x01) /* primary codec ready */ + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + + if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY) + snd_printk("AC'97 codec is not ready [0x%x]\n", val); + + /* and then reset codec.. */ + snd_via686a_codec_write(&ac97, AC97_RESET, 0x0000); + + /* check the primary codec */ + snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_PRIMARY_VALID | + (VIA_REG_AC97_CODEC_ID_PRIMARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + max_count = ((3 * HZ) / 4) + 1; + do { + if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_PRIMARY_VALID) + goto __ac97_ok1; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + snd_printk("Primary AC'97 codec is not valid [0x%x]\n", val); + + __ac97_ok1: +#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */ + snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + max_count = ((3 * HZ) / 4) + 1; + snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + do { + if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) { + chip->ac97_secondary = 1; + goto __ac97_ok2; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + /* This is ok, the most of motherboards have only one codec */ + + __ac97_ok2: +#endif + +#if 0 + { + unsigned char cmdb; + + pci_read_config_byte(chip->pci, 0x40, &cmdb); + printk("PCI[0x40] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x42, &cmdb); + printk("PCI[0x42] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x43, &cmdb); + printk("PCI[0x43] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x44, &cmdb); + printk("PCI[0x44] = 0x%x\n", cmdb); + pci_read_config_byte(chip->pci, 0x48, &cmdb); + printk("PCI[0x48] = 0x%x\n", cmdb); + } +#endif + + /* route FM trap to IRQ, disable FM trap */ + pci_write_config_byte(chip->pci, 0x48, 0); + + /* disable all GPI interrupts */ + outl(0, chip->port + 0x8c); + + /* disable interrupts */ + snd_via686a_channel_reset(chip, &chip->playback); + snd_via686a_channel_reset(chip, &chip->capture); + snd_via686a_channel_reset(chip, &chip->playback_fm); + return 0; +} + +static int snd_via686a_free(via686a_t *chip) +{ + if (chip->irq < 0) + goto __end_hw; + /* disable interrupts */ + snd_via686a_channel_reset(chip, &chip->playback); + snd_via686a_channel_reset(chip, &chip->capture); + snd_via686a_channel_reset(chip, &chip->playback_fm); + /* --- */ + __end_hw: + synchronize_irq(); + if (chip->tables) + snd_free_pci_pages(chip->pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, chip->tables, chip->tables_addr); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + pci_write_config_byte(chip->pci, 0x42, chip->old_legacy); + pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg); + snd_magic_kfree(chip); + return 0; +} + +static int snd_via686a_dev_free(snd_device_t *device) +{ + via686a_t *chip = snd_magic_cast(via686a_t, device->device_data, return -ENXIO); + return snd_via686a_free(chip); +} + +static int __devinit snd_via686a_create(snd_card_t * card, + struct pci_dev *pci, + unsigned int ac97_clock, + unsigned char old_legacy, + unsigned char old_legacy_cfg, + via686a_t ** r_via) +{ + via686a_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_via686a_dev_free, + }; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if ((chip = snd_magic_kcalloc(via686a_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + chip->old_legacy = old_legacy; + chip->old_legacy_cfg = old_legacy_cfg; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 256, "VIA686A")) == NULL) { + snd_via686a_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_via686a_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA686A", (void *)chip)) { + snd_via686a_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + chip->ac97_clock = ac97_clock; + pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); + synchronize_irq(); + + /* initialize offsets */ + chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; + chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; + chip->playback_fm.reg_offset = VIA_REG_FM_STATUS; + + /* allocate buffer descriptor lists */ + /* the start of each lists must be aligned to 8 bytes */ + chip->tables = (unsigned int *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, &chip->tables_addr); + if (chip->tables == NULL) { + snd_via686a_free(chip); + return -ENOMEM; + } + /* tables must be aligned to 8 bytes, but the kernel pages + are much bigger, so we don't care */ + chip->playback.table = chip->tables; + chip->playback.table_addr = chip->tables_addr; + chip->capture.table = chip->playback.table + VIA_MAX_FRAGS * 2; + chip->capture.table_addr = chip->playback.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2; + chip->playback_fm.table = chip->capture.table + VIA_MAX_FRAGS * 2; + chip->playback_fm.table_addr = chip->capture.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2; + + if ((err = snd_via686a_chip_init(chip)) < 0) { + snd_via686a_free(chip); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_via686a_free(chip); + return err; + } + + *r_via = chip; + return 0; +} + +static int __devinit snd_via686a_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + via686a_t *chip; + int pcm_dev = 0; + unsigned char legacy; + unsigned char legacy_cfg; + int rev_h = 0, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + pci_read_config_byte(pci, 0x42, &legacy); + pci_read_config_byte(pci, 0x43, &legacy_cfg); + + if ((err = snd_via686a_create(card, + pci, + snd_ac97_clock[dev], + legacy, + legacy_cfg, + &chip)) < 0) { + snd_card_free(card); + return err; + } + + + if (snd_via686a_mixer(chip) < 0) { + snd_card_free(card); + return err; + } + if (snd_via686a_pcm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } +#if 0 + if (snd_via686a_pcm_fm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } +#endif + + legacy |= 0x40; /* disable MIDI */ + legacy &= ~0x08; /* disable joystick */ + if (chip->revision >= 0x20) { + if (check_region(pci_resource_start(pci, 2), 4)) { + rev_h = 0; + legacy &= ~0x80; /* disable PCI I/O 2 */ + } else { + rev_h = 1; + legacy |= 0x80; /* enable PCI I/O 2 */ + } + } + pci_write_config_byte(pci, 0x42, legacy); + pci_write_config_byte(pci, 0x43, legacy_cfg); + if (rev_h && snd_mpu_port[dev] >= 0x200) { /* force MIDI */ + legacy |= 0x02; /* enable MPU */ + pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01); + } else { + if (rev_h && (legacy & 0x02)) { + snd_mpu_port[dev] = pci_resource_start(pci, 2); + if (snd_mpu_port[dev] < 0x200) /* bad value */ + legacy &= ~0x02; /* disable MIDI */ + } else { + switch (snd_mpu_port[dev]) { /* force MIDI */ + case 0x300: + case 0x310: + case 0x320: + case 0x330: + legacy_cfg &= ~(3 << 2); + legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2; + legacy |= 0x02; + break; + default: /* no, use BIOS settings */ + if (legacy & 0x02) + snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2); + } + } + } + pci_write_config_byte(pci, 0x42, legacy); + pci_write_config_byte(pci, 0x43, legacy_cfg); + if (legacy & 0x02) { + if (check_region(snd_mpu_port[dev], 2)) { + printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(pci, 0x42, legacy); + goto __skip_mpu; + } + if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A, + snd_mpu_port[dev], 0, + pci->irq, 0, + &chip->rmidi) < 0) { + printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(pci, 0x42, legacy); + goto __skip_mpu; + } + legacy &= ~0x40; /* enable MIDI interrupt */ + pci_write_config_byte(pci, 0x42, legacy); + __skip_mpu: + ; + } + + /* card switches */ + err = snd_ctl_add(card, snd_ctl_new1(&snd_via686a_joystick_control, chip)); + if (err < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "VIA686A"); + strcpy(card->shortname, "VIA 82C686A/B"); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_via686a_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "VIA 82C686A/B", + id_table: snd_via686a_ids, + probe: snd_via686a_probe, + remove: __devexit_p(snd_via686a_remove), +}; + +static int __init alsa_card_via686a_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "VIA 82C686A soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_via686a_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_via686a_init) +module_exit(alsa_card_via686a_exit) + +#ifndef MODULE + +/* format is: snd-via686a=snd_enable,snd_index,snd_id, + snd_mpu_port,snd_ac97_clock */ + +static int __init alsa_card_via686a_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-via686a=", alsa_card_via686a_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/via8233.c linux-2.4.19-pre5-mjc/sound/pci/via8233.c --- linux/sound/pci/via8233.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/via8233.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,915 @@ +/* + * ALSA driver for VIA VT8233 (South Bridge) + * + * Copyright (c) 2000 Jaroslav Kysela , + * Tjeerd.Mulder@fujitsu-siemens.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Tjeerd.Mulder@fujitsu-siemens.com"); +MODULE_DESCRIPTION("VIA VT8233"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{VIA,VT8233,pci}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for VIA 8233 bridge."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for VIA 8233 bridge."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 8233 bridge."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz)."); +MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000"); + +/* + * Direct registers + */ + +#ifndef PCI_DEVICE_ID_VIA_8233_5 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#endif + +#define VIAREG(via, x) ((via)->port + VIA_REG_##x) + +/* offsets */ +#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_STAT_ACTIVE 0x80 /* RO */ +#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */ +#define VIA_REG_STAT_STOPPED 0x04 /* RWC */ +#define VIA_REG_STAT_EOL 0x02 /* RWC */ +#define VIA_REG_STAT_FLAG 0x01 /* RWC */ +#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_CTRL_START 0x80 /* WO */ +#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */ +#define VIA_REG_CTRL_AUTOSTART 0x20 +#define VIA_REG_CTRL_PAUSE 0x08 /* RW */ +#define VIA_REG_CTRL_INT_STOP 0x04 +#define VIA_REG_CTRL_INT_EOL 0x02 +#define VIA_REG_CTRL_INT_FLAG 0x01 +#define VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART) +#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_OFFSET_TYPE 0x08 /* long - stop index, channel type, sample rate */ +#define VIA_REG_TYPE_16BIT 0x00200000 /* RW */ +#define VIA_REG_TYPE_STEREO 0x00100000 /* RW */ +#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */ +#define VIA_REG_OFFSET_CURR_INDEX 0x0f /* byte - channel current index */ + +#define VIA_NUM_OF_DMA_CHANNELS 2 +/* playback block */ +#define VIA_REG_PLAYBACK_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_PLAYBACK_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_PLAYBACK_VOLUME_L 0x02 /* byte */ +#define VIA_REG_PLAYBACK_VOLUME_R 0x03 /* byte */ +#define VIA_REG_PLAYBACK_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_PLAYBACK_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_PLAYBACK_TYPE 0x08 /* long - stop index, channel type, sample rate */ /* byte - channel type */ +#define VIA_REG_PLAYBACK_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */ +#define VIA_REG_PLAYBACK_CURR_INDEX 0x0f /* byte - channel current index */ +/* capture block */ +#define VIA_REG_CAPTURE_STATUS 0x60 /* byte - channel status */ +#define VIA_REG_CAPTURE_CONTROL 0x61 /* byte - channel control */ +#define VIA_REG_CAPTURE_FIFO 0x62 /* byte - bit 6 = fifo enable */ +#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40 +#define VIA_REG_CAPTURE_CHANNEL 0x63 /* byte - input select */ +#define VIA_REG_CAPTURE_CHANNEL_MIC 0x4 +#define VIA_REG_CAPTURE_CHANNEL_LINE 0 +#define VIA_REG_CAPTURE_TABLE_PTR 0x64 /* dword - channel table pointer */ +#define VIA_REG_CAPTURE_CURR_PTR 0x64 /* dword - channel current pointer */ +#define VIA_REG_CAPTURE_TYPE 0x68 /* byte - channel type */ +#define VIA_REG_CAPTURE_CURR_COUNT 0x6c /* dword - channel current count (24 bit) */ +#define VIA_REG_CAPTURE_CURR_INDEX 0x6f /* byte - channel current index */ +/* AC'97 */ +#define VIA_REG_AC97 0x80 /* dword */ +#define VIA_REG_AC97_CODEC_ID_MASK (3<<30) +#define VIA_REG_AC97_CODEC_ID_SHIFT 30 +#define VIA_REG_AC97_SECONDARY_VALID (1<<27) +#define VIA_REG_AC97_PRIMARY_VALID (1<<25) +#define VIA_REG_AC97_ANY_VALID (VIA_REG_AC97_PRIMARY_VALID | VIA_REG_AC97_SECONDARY_VALID | (1<<28)| (1<<29)) +#define VIA_REG_AC97_BUSY (1<<24) +#define VIA_REG_AC97_READ (1<<23) +#define VIA_REG_AC97_CMD_SHIFT 16 +#define VIA_REG_AC97_CMD_MASK 0x7e +#define VIA_REG_AC97_DATA_SHIFT 0 +#define VIA_REG_AC97_DATA_MASK 0xffff +#define VIA_REG_SGD_SHADOW 0x84 /* dword */ + +/* + * + */ + +#define VIA_MAX_FRAGS 32 + +/* + * + */ + +typedef struct { + unsigned long reg_offset; + unsigned int *table; + dma_addr_t table_addr; + snd_pcm_substream_t *substream; + dma_addr_t physbuf; + unsigned int size; + unsigned int fragsize; + unsigned int frags; +} viadev_t; + +typedef struct _snd_via8233 via8233_t; +#define chip_t via8233_t + +struct _snd_via8233 { + int irq; + + unsigned long port; + struct resource *res_port; + unsigned char revision; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + viadev_t playback; + viadev_t capture; + + ac97_t *ac97; + unsigned int ac97_clock; + + spinlock_t reg_lock; + spinlock_t update_lock; + snd_info_entry_t *proc_entry; + + void *tables; + dma_addr_t tables_addr; +}; + +static struct pci_device_id snd_via8233_ids[] __devinitdata = { + { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* VT8233 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via8233_ids); + +/* + * Basic I/O + */ + +static inline unsigned int snd_via8233_codec_xread(via8233_t *chip) +{ + /* this acces should be atomic */ + return inl(VIAREG(chip, AC97)); +} + +static inline void snd_via8233_codec_xwrite(via8233_t *chip, unsigned int val) +{ + /* this acces should be atomic */ + outl(val, VIAREG(chip, AC97)); +} + +static int snd_via8233_codec_ready(via8233_t *chip, int secondary) +{ + int time; + + time = 1000; + do { + udelay(1); + if ((snd_via8233_codec_xread(chip) & VIA_REG_AC97_BUSY) == 0) + return 0; + } while (time--); + snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via8233_codec_xread(chip)); + return -EIO; +} + +static void snd_via8233_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); + unsigned int xval; + + xval = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; + xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; + xval |= val << VIA_REG_AC97_DATA_SHIFT; + spin_lock(&chip->reg_lock); + snd_via8233_codec_ready(chip, ac97->num); + snd_via8233_codec_xwrite(chip, xval); + snd_via8233_codec_ready(chip, ac97->num); + spin_unlock(&chip->reg_lock); +} + +static unsigned short snd_via8233_codec_read(ac97_t *ac97, unsigned short reg) +{ + via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return ~0); + unsigned int val; + int valid = ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; + int i; + + val = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT; + val |= valid; + val |= VIA_REG_AC97_READ; + val |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; + spin_lock(&chip->reg_lock); + snd_via8233_codec_ready(chip, ac97->num); + snd_via8233_codec_xwrite(chip, val); + snd_via8233_codec_ready(chip, ac97->num); + for (i=1000; i--;) { + val = snd_via8233_codec_xread(chip); + if (val & valid) { + spin_unlock(&chip->reg_lock); + return (unsigned short)val; + } + } + spin_unlock(&chip->reg_lock); + snd_printk("codec_read: codec %i is not valid [0x%x]\n", ac97->num, val); + /* have to return some value, this is better then 0 */ + return ~0; +} + +#if 0 +static void snd_via8233_channel_print(via8233_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n", + port, + inb(port + VIA_REG_OFFSET_STATUS), + inb(port + VIA_REG_OFFSET_CONTROL), + inl(port + VIA_REG_OFFSET_TYPE), + inl(port + VIA_REG_OFFSET_CURR_PTR), + inl(port + VIA_REG_OFFSET_CURR_COUNT)); +} +#endif + +static void snd_via8233_channel_reset(via8233_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + outb(VIA_REG_CTRL_TERMINATE, port + VIA_REG_OFFSET_CONTROL); + udelay(50); + /* disable interrupts */ + outb(0, port + VIA_REG_OFFSET_CONTROL); + /* clear interrupts */ + outb(0x3, port + VIA_REG_OFFSET_STATUS); +} + +static int snd_via8233_trigger(via8233_t *chip, viadev_t *viadev, int cmd) +{ + unsigned char val; + unsigned long port = chip->port + viadev->reg_offset; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = VIA_REG_CTRL_INT | VIA_REG_CTRL_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + val = VIA_REG_CTRL_TERMINATE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = VIA_REG_CTRL_INT | VIA_REG_CTRL_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = VIA_REG_CTRL_INT; + break; + default: + return -EINVAL; + } + outb(val, port + VIA_REG_OFFSET_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_STOP) + snd_via8233_channel_reset(chip, viadev); + return 0; +} + +static void snd_via8233_setup_periods(via8233_t *chip, viadev_t *viadev, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int idx, frags; + unsigned int *table = viadev->table; + unsigned long port = chip->port + viadev->reg_offset; + + viadev->physbuf = runtime->dma_addr; + viadev->size = snd_pcm_lib_buffer_bytes(substream); + viadev->fragsize = snd_pcm_lib_period_bytes(substream); + viadev->frags = runtime->periods; + + snd_via8233_channel_reset(chip, viadev); + outl(viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | + 0xff000000, /* STOP index is never reached */ + port + VIA_REG_OFFSET_TYPE); + + if (viadev->size == viadev->fragsize) { + table[0] = cpu_to_le32(viadev->physbuf); + table[1] = cpu_to_le32(0xc0000000 | /* EOL + flag */ + viadev->fragsize); + } else { + frags = viadev->size / viadev->fragsize; + for (idx = 0; idx < frags - 1; idx++) { + table[(idx << 1) + 0] = cpu_to_le32(viadev->physbuf + (idx * viadev->fragsize)); + table[(idx << 1) + 1] = cpu_to_le32(0x40000000 | /* flag */ + viadev->fragsize); + } + table[((frags-1) << 1) + 0] = cpu_to_le32(viadev->physbuf + ((frags-1) * viadev->fragsize)); + table[((frags-1) << 1) + 1] = cpu_to_le32(0x80000000 | /* EOL */ + viadev->fragsize); + } +#if 0 + printk("%s: size = %d frags = %d fragsize = %d\n", __FUNCTION__, viadev->size, frags, viadev->fragsize); + for (idx=0; idx < frags; idx++) + printk(" address %x, count %x\n", table[idx*2], table[idx*2+1]); +#endif +} + +/* + * Interrupt handler + */ + +static inline void snd_via8233_update(via8233_t *chip, viadev_t *viadev) +{ + snd_pcm_period_elapsed(viadev->substream); + outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); +} + +static void snd_via8233_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + via8233_t *chip = snd_magic_cast(via8233_t, dev_id, return); + + if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via8233_update(chip, &chip->playback); + if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via8233_update(chip, &chip->capture); +} + +/* + * PCM part + */ + +static int snd_via8233_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + + return snd_via8233_trigger(chip, &chip->playback, cmd); +} + +static int snd_via8233_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + return snd_via8233_trigger(chip, &chip->capture, cmd); +} + +static int snd_via8233_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_via8233_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_via8233_playback_prepare(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long tmp; + + if (inb(VIAREG(chip, PLAYBACK_STATUS)) & VIA_REG_STAT_ACTIVE) + return 0; + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_via8233_setup_periods(chip, &chip->playback, substream); + /* I don't understand this stuff but its from the documentation and this way it works */ + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L)); + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R)); + tmp = inl(VIAREG(chip, PLAYBACK_TYPE)) & ~0xfffff; + outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_TYPE)); + return 0; +} + +static int snd_via8233_capture_prepare(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_via8233_setup_periods(chip, &chip->capture, substream); + outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL)); + outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO)); + return 0; +} + +static inline unsigned int snd_via8233_cur_ptr(via8233_t *chip, viadev_t *viadev) +{ + unsigned int val, count; + + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; + /* The via686a does not have this current index register, + * this register makes life easier for us here. */ + val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->frags; + val *= viadev->fragsize; + val += viadev->fragsize - count; + // printk("pointer: ptr = 0x%x, count = 0x%x, val = 0x%x\n", ptr, count, val); + return val; +} + +static snd_pcm_uframes_t snd_via8233_playback_pointer(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->playback)); +} + +static snd_pcm_uframes_t snd_via8233_capture_pointer(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->capture)); +} + +static snd_pcm_hardware_t snd_via8233_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_via8233_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: 0, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 128 * 1024, + period_bytes_min: 32, + period_bytes_max: 128 * 1024, + periods_min: 1, + periods_max: VIA_MAX_FRAGS, + fifo_size: 0, +}; + +static int snd_via8233_playback_open(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback.substream = substream; + runtime->hw = snd_via8233_playback; + runtime->hw.rates = chip->ac97->rates_front_dac; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via8233_capture_open(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture.substream = substream; + runtime->hw = snd_via8233_capture; + runtime->hw.rates = chip->ac97->rates_adc; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + return 0; +} + +static int snd_via8233_playback_close(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + + snd_via8233_channel_reset(chip, &chip->playback); + chip->playback.substream = NULL; + /* disable DAC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200); + return 0; +} + +static int snd_via8233_capture_close(snd_pcm_substream_t * substream) +{ + via8233_t *chip = snd_pcm_substream_chip(substream); + + snd_via8233_channel_reset(chip, &chip->capture); + chip->capture.substream = NULL; + /* disable ADC power */ + snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100); + return 0; +} + +static snd_pcm_ops_t snd_via8233_playback_ops = { + open: snd_via8233_playback_open, + close: snd_via8233_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via8233_hw_params, + hw_free: snd_via8233_hw_free, + prepare: snd_via8233_playback_prepare, + trigger: snd_via8233_playback_trigger, + pointer: snd_via8233_playback_pointer, +}; + +static snd_pcm_ops_t snd_via8233_capture_ops = { + open: snd_via8233_capture_open, + close: snd_via8233_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_via8233_hw_params, + hw_free: snd_via8233_hw_free, + prepare: snd_via8233_capture_prepare, + trigger: snd_via8233_capture_trigger, + pointer: snd_via8233_capture_pointer, +}; + +static void snd_via8233_pcm_free(snd_pcm_t *pcm) +{ + via8233_t *chip = snd_magic_cast(via8233_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, "VIA 8233", device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_via8233_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, "VIA 8233"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024); + + if (rpcm) + *rpcm = NULL; + return 0; +} + +/* + * Mixer part + */ + +static void snd_via8233_codec_init(ac97_t *ac97) +{ + // via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); + + /* disable DAC & ADC power */ + snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300); + /* disable center DAC/surround DAC/LFE DAC/MIC ADC */ + snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800); +} + +static void snd_via8233_mixer_free_ac97(ac97_t *ac97) +{ + via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static int __devinit snd_via8233_mixer(via8233_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_via8233_codec_write; + ac97.read = snd_via8233_codec_read; + ac97.init = snd_via8233_codec_init; + ac97.private_data = chip; + ac97.private_free = snd_via8233_mixer_free_ac97; + ac97.clock = chip->ac97_clock; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + return 0; +} + +/* + * + */ + +static int __devinit snd_via8233_chip_init(via8233_t *chip) +{ + ac97_t ac97; + unsigned char stat; + int i; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + + /* deassert ACLink reset */ + pci_write_config_byte(chip->pci, 0x41, 0x40); + udelay(100); + /* deassert ACLink reset, force SYNC (warm AC'97 reset) */ + pci_write_config_byte(chip->pci, 0x41, 0x60); + udelay(2); + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + pci_write_config_byte(chip->pci, 0x41, 0xcc); + + /* Wait for codec ready to be accessed. */ + for (i=HZ; i--; ) { + pci_read_config_byte(chip->pci, 0x40, &stat); + if (stat & 1) + break; + if (!i) { + snd_printk("chip_init: failed to access primary codec.\n"); + return ~0; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + snd_via8233_codec_ready(chip, 0); + snd_via8233_codec_write(&ac97, AC97_RESET, 0x0000); + snd_via8233_codec_read(&ac97, 0); + + /* disable interrupts */ + snd_via8233_channel_reset(chip, &chip->playback); + snd_via8233_channel_reset(chip, &chip->capture); + return 0; +} + +static int snd_via8233_free(via8233_t *chip) +{ + if (chip->irq < 0) + goto __end_hw; + /* disable interrupts */ + snd_via8233_channel_reset(chip, &chip->playback); + snd_via8233_channel_reset(chip, &chip->capture); + /* --- */ + __end_hw: + synchronize_irq(); + if (chip->tables) + snd_free_pci_pages(chip->pci, + VIA_NUM_OF_DMA_CHANNELS * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, + chip->tables, + chip->tables_addr); + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + snd_magic_kfree(chip); + return 0; +} + +static int snd_via8233_dev_free(snd_device_t *device) +{ + via8233_t *chip = snd_magic_cast(via8233_t, device->device_data, return -ENXIO); + return snd_via8233_free(chip); +} + +static int __devinit snd_via8233_create(snd_card_t * card, + struct pci_dev *pci, + unsigned int ac97_clock, + via8233_t ** r_via) +{ + via8233_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_via8233_dev_free, + }; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if ((chip = snd_magic_kcalloc(via8233_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->update_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 256, "VIA8233")) == NULL) { + snd_via8233_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_via8233_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA8233", (void *)chip)) { + snd_via8233_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + chip->ac97_clock = ac97_clock; + pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); + synchronize_irq(); + + /* initialize offsets */ + chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; + chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; + + /* allocate buffer descriptor lists */ + /* the start of each lists must be aligned to 8 bytes */ + chip->tables = (unsigned int *)snd_malloc_pci_pages(pci, + VIA_NUM_OF_DMA_CHANNELS * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, + &chip->tables_addr); + if (chip->tables == NULL) { + snd_via8233_free(chip); + return -ENOMEM; + } + /* tables must be aligned to 8 bytes, but the kernel pages + are much bigger, so we don't care */ + chip->playback.table = chip->tables; + chip->playback.table_addr = chip->tables_addr; + chip->capture.table = chip->playback.table + VIA_MAX_FRAGS * 2; + chip->capture.table_addr = chip->playback.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2; + if ((err = snd_via8233_chip_init(chip)) < 0) { + snd_via8233_free(chip); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_via8233_free(chip); + return err; + } + + /* The 8233 ac97 controller does not implement the master bit + * in the pci command register. IMHO this is a violation of the PCI spec. + * We call pci_set_master here because it does not hurt. */ + pci_set_master(pci); + + *r_via = chip; + return 0; +} + +static int __devinit snd_via8233_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + via8233_t *chip; + int pcm_dev = 0; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_via8233_create(card, + pci, + snd_ac97_clock[dev], + &chip)) < 0) { + snd_card_free(card); + return err; + } + + + if (snd_via8233_mixer(chip) < 0) { + snd_card_free(card); + return err; + } + if (snd_via8233_pcm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "VIA8233"); + strcpy(card->shortname, "VIA 8233"); + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->port, chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_via8233_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "VIA 8233", + id_table: snd_via8233_ids, + probe: snd_via8233_probe, + remove: __devexit_p(snd_via8233_remove), +}; + +static int __init alsa_card_via8233_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "VIA 8233 soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_via8233_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_via8233_init) +module_exit(alsa_card_via8233_exit) + +#ifndef MODULE + +/* format is: snd-via8233=snd_enable,snd_index,snd_id,snd_ac97_clock */ + +static int __init alsa_card_via8233_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,&snd_ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-via8233=", alsa_card_via8233_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/ymfpci/Makefile linux-2.4.19-pre5-mjc/sound/pci/ymfpci/Makefile --- linux/sound/pci/ymfpci/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _ymfpci.o + +list-multi := snd-ymfpci.o + +snd-ymfpci-objs := ymfpci.o ymfpci_main.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_YMFPCI) += snd-ymfpci.o + +include $(TOPDIR)/Rules.make + +snd-ymfpci.o: $(snd-ymfpci-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-ymfpci-objs) diff -Nru linux/sound/pci/ymfpci/ymfpci.c linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci.c --- linux/sound/pci/ymfpci/ymfpci.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,327 @@ +/* + * The driver for the Yamaha's DS1/DS1E cards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Yamaha DS-XG PCI"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Yamaha,YMF724}," + "{Yamaha,YMF724F}," + "{Yamaha,YMF740}," + "{Yamaha,YMF740C}," + "{Yamaha,YMF744}," + "{Yamaha,YMF754}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static long snd_fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for the Yamaha DS-XG PCI soundcard."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for the Yamaha DS-XG PCI soundcard."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable Yamaha DS-XG soundcard."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 Port."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED); +MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_fm_port, "FM OPL-3 Port."); +MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED); + +static struct pci_device_id snd_ymfpci_ids[] __devinitdata = { + { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724 */ + { 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724F */ + { 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740 */ + { 0x1073, 0x000c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740C */ + { 0x1073, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF744 */ + { 0x1073, 0x0012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF754 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids); + +static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev = 0; + snd_card_t *card; + ymfpci_t *chip; + opl3_t *opl3; + char *str; + int err; + u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + switch (id->device) { + case 0x0004: str = "YMF724"; break; + case 0x000d: str = "YMF724F"; break; + case 0x000a: str = "YMF740"; break; + case 0x000c: str = "YMF740C"; break; + case 0x0010: str = "YMF744"; break; + case 0x0012: str = "YMF754"; break; + default: str = "???"; break; + } + + legacy_ctrl = 0; + legacy_ctrl2 = 0x0800; /* SMOD = 01 */ + + if (id->device >= 0x0010) { /* YMF 744/754 */ + if (snd_fm_port[dev] < 0) + snd_fm_port[dev] = pci_resource_start(pci, 1); + else if (check_region(snd_fm_port[dev], 4)) + snd_fm_port[dev] = -1; + if (snd_fm_port[dev] >= 0) { + legacy_ctrl |= 2; + pci_write_config_word(pci, PCIR_DSXG_FMBASE, snd_fm_port[dev]); + } + if (snd_mpu_port[dev] < 0) + snd_mpu_port[dev] = pci_resource_start(pci, 1) + 0x20; + else if (check_region(snd_mpu_port[dev], 2)) + snd_mpu_port[dev] = -1; + if (snd_mpu_port[dev] >= 0) { + legacy_ctrl |= 8; + pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, snd_mpu_port[dev]); + snd_printd("MPU401 supported on 0x%lx\n", snd_mpu_port[dev]); + } + } else { + switch (snd_fm_port[dev]) { + case 0x388: legacy_ctrl2 |= 0; break; + case 0x398: legacy_ctrl2 |= 1; break; + case 0x3a0: legacy_ctrl2 |= 2; break; + case 0x3a8: legacy_ctrl2 |= 3; break; + default: snd_fm_port[dev] = -1; break; + } + if (snd_fm_port[dev] > 0 && check_region(snd_fm_port[dev], 4) == 0) + legacy_ctrl |= 2; + else { + legacy_ctrl2 &= ~3; + snd_fm_port[dev] = -1; + } + switch (snd_mpu_port[dev]) { + case 0x330: legacy_ctrl2 |= 0 << 4; break; + case 0x300: legacy_ctrl2 |= 1 << 4; break; + case 0x332: legacy_ctrl2 |= 2 << 4; break; + case 0x334: legacy_ctrl2 |= 3 << 4; break; + default: snd_mpu_port[dev] = -1; break; + } + if (snd_mpu_port[dev] > 0 && check_region(snd_mpu_port[dev], 2) == 0) { + snd_printd("MPU401 supported on 0x%lx\n", snd_mpu_port[dev]); + legacy_ctrl |= 8; + } else { + legacy_ctrl2 &= ~(3 << 4); + snd_mpu_port[dev] = -1; + } + } + if (snd_mpu_port[dev] > 0) { + legacy_ctrl |= 0x10; /* MPU401 irq enable */ + legacy_ctrl2 |= 1 << 15; /* IMOD */ + } + pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl); + //snd_printdd("legacy_ctrl = 0x%x\n", legacy_ctrl); + pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); + //snd_printdd("legacy_ctrl2 = 0x%x\n", legacy_ctrl2); + pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); + if ((err = snd_ymfpci_create(card, pci, + old_legacy_ctrl, + &chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ymfpci_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if (snd_mpu_port[dev] > 0) { + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, + snd_mpu_port[dev], 0, + pci->irq, 0, &chip->rawmidi)) < 0) { + printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", snd_mpu_port[dev]); + } else { + legacy_ctrl &= ~0x10; /* disable MPU401 irq */ + pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); + } + } + if (snd_fm_port[dev] > 0) { + if ((err = snd_opl3_create(card, + snd_fm_port[dev], + snd_fm_port[dev] + 2, + OPL3_HW_OPL3, 0, &opl3)) < 0) { + printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", snd_fm_port[dev]); + } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + snd_printk("cannot create opl3 hwdep\n"); + return err; + } + } + if ((err = snd_ymfpci_joystick(chip)) < 0) { + printk(KERN_WARNING "ymfpci: cannot initialize joystick, skipping...\n"); + } + strcpy(card->driver, str); + sprintf(card->shortname, "Yamaha DS-XG PCI (%s)", str); + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, + chip->reg_area_virt, + chip->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, chip); + dev++; + return 0; +} + +#ifdef CONFIG_PM +#ifndef PCI_OLD_SUSPEND +static int snd_card_ymfpci_suspend(struct pci_dev *pci, u32 state) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO); + snd_ymfpci_suspend(chip); + return 0; +} +static int snd_card_ymfpci_resume(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return -ENXIO); + snd_ymfpci_resume(chip); + return 0; +} +#else +static void snd_card_ymfpci_suspend(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + snd_ymfpci_suspend(chip); +} +static void snd_card_ymfpci_resume(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + snd_ymfpci_resume(chip); +} +#endif +#endif + +static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pci_get_drvdata(pci), return); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + name: "Yamaha DS-XG PCI", + id_table: snd_ymfpci_ids, + probe: snd_card_ymfpci_probe, + remove: __devexit_p(snd_card_ymfpci_remove), +#ifdef CONFIG_PM + suspend: snd_card_ymfpci_suspend, + resume: snd_card_ymfpci_resume, +#endif +}; + +static int __init alsa_card_ymfpci_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "Yamaha DS-XG PCI soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_ymfpci_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ymfpci_init) +module_exit(alsa_card_ymfpci_exit) + +#ifndef MODULE + +/* format is: snd-ymfpci=snd_enable,snd_index,snd_id, + snd_fm_port,snd_mpu_port */ + +static int __init alsa_card_ymfpci_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-ymfpci=", alsa_card_ymfpci_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/pci/ymfpci/ymfpci_image.h linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_image.h --- linux/sound/pci/ymfpci/ymfpci_image.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_image.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,1565 @@ +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09 creat +// 04/12 stop nise fix +// 06/21 WorkingOff timming +static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ diff -Nru linux/sound/pci/ymfpci/ymfpci_main.c linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_main.c --- linux/sound/pci/ymfpci/ymfpci_main.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/pci/ymfpci/ymfpci_main.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,2088 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of YMF724/740/744/754 chips + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chip_t ymfpci_t + +/* + * constants + */ + +/* + * common I/O routines + */ + +static void snd_ymfpci_irq_wait(ymfpci_t *chip); + +static inline u8 snd_ymfpci_readb(ymfpci_t *chip, u32 offset) +{ + return readb(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writeb(ymfpci_t *chip, u32 offset, u8 val) +{ + writeb(val, chip->reg_area_virt + offset); +} + +static inline u16 snd_ymfpci_readw(ymfpci_t *chip, u32 offset) +{ + return readw(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writew(ymfpci_t *chip, u32 offset, u16 val) +{ + writew(val, chip->reg_area_virt + offset); +} + +static inline u32 snd_ymfpci_readl(ymfpci_t *chip, u32 offset) +{ + return readl(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val) +{ + writel(val, chip->reg_area_virt + offset); +} + +static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary, int sched) +{ + signed long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = (jiffies + ((3 * HZ) / 4)) + 1; + do { + if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg)); + return -EBUSY; +} + +static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + u32 cmd; + + snd_ymfpci_codec_ready(chip, 0, 0); + cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; + snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd); +} + +static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return -ENXIO); + + if (snd_ymfpci_codec_ready(chip, 0, 0)) + return ~0; + snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (snd_ymfpci_codec_ready(chip, 0, 0)) + return ~0; + if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) { + int i; + for (i = 0; i < 600; i++) + snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); + } + return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); +} + +/* + * Misc routines + */ + +static u32 snd_ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 375) << 5; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 snd_ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 snd_ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +/* + * Hardware start management + */ + +static void snd_ymfpci_hw_start(ymfpci_t *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->start_count++ > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | 3); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_ymfpci_hw_stop(ymfpci_t *chip) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (--chip->start_count > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0) + break; + } + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * Playback voice management + */ + +static int voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice) +{ + ymfpci_voice_t *voice, *voice2; + int idx; + + *rvoice = NULL; + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &chip->voices[idx]; + voice2 = pair ? &chip->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + snd_ymfpci_hw_start(chip); + if (voice2) + snd_ymfpci_hw_start(chip); + *rvoice = voice; + return 0; + } + return -ENOMEM; +} + +int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice) +{ + unsigned long flags; + int result; + + snd_assert(rvoice != NULL, return -EINVAL); + snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL); + + spin_lock_irqsave(&chip->voice_lock, flags); + for (;;) { + result = voice_alloc(chip, type, pair, rvoice); + if (result == 0 || type != YMFPCI_PCM) + break; + /* TODO: synth/midi voice deallocation */ + break; + } + spin_unlock_irqrestore(&chip->voice_lock, flags); + return result; +} + +int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice) +{ + unsigned long flags; + + snd_assert(pvoice != NULL, return -EINVAL); + snd_ymfpci_hw_stop(chip); + spin_lock_irqsave(&chip->voice_lock, flags); + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; + pvoice->interrupt = NULL; + spin_unlock_irqrestore(&chip->voice_lock, flags); + return 0; +} + +/* + * PCM part + */ + +static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice) +{ + ymfpci_pcm_t *ypcm; + u32 pos, delta; + + if ((ypcm = voice->ypcm) == NULL) + return; + if (ypcm->substream == NULL) + return; + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(voice->bank[chip->active_bank].start); + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + while (ypcm->period_pos >= ypcm->period_size) { + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(ypcm->substream); + spin_lock(&chip->reg_lock); + ypcm->period_pos -= ypcm->period_size; + } + } + spin_unlock(&chip->reg_lock); +} + +static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + ymfpci_t *chip = ypcm->chip; + u32 pos, delta; + + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + while (ypcm->period_pos >= ypcm->period_size) { + ypcm->period_pos = 0; + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + +static int snd_ymfpci_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + int result = 0; + + spin_lock(&chip->reg_lock); + if (ypcm->voices[0] == NULL) { + result = -EINVAL; + goto __unlock; + } + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr); + if (ypcm->voices[1] != NULL) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + __unlock: + spin_unlock(&chip->reg_lock); + return result; +} +static int snd_ymfpci_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + int result = 0; + u32 tmp; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices) +{ + int err; + + if (ypcm->voices[1] != NULL && voices < 2) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + } + err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]); + if (err < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt; + if (voices > 1) { + ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1]; + ypcm->voices[1]->ypcm = ypcm; + } + return 0; +} + +static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo, + int rate, int w_16, unsigned long addr, + unsigned int end, int eff2) +{ + u32 format; + u32 delta = snd_ymfpci_calc_delta(rate); + u32 lpfQ = snd_ymfpci_calc_lpfQ(rate); + u32 lpfK = snd_ymfpci_calc_lpfK(rate); + snd_ymfpci_playback_bank_t *bank; + unsigned int nbank; + + snd_assert(voice != NULL, return); + format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + bank->format = cpu_to_le32(format); + bank->loop_default = 0; + bank->base = cpu_to_le32(addr); + bank->loop_start = 0; + bank->loop_end = cpu_to_le32(end); + bank->loop_frac = 0; + bank->eg_gain_end = cpu_to_le32(0x40000000); + bank->lpfQ = cpu_to_le32(lpfQ); + bank->status = 0; + bank->num_of_frames = 0; + bank->loop_count = 0; + bank->start = 0; + bank->start_frac = 0; + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = cpu_to_le32(0x40000000); + bank->lpfD1 = + bank->lpfD2 = 0; + + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = + bank->eff1_gain = + bank->eff2_gain = + bank->eff3_gain = + bank->eff1_gain_end = + bank->eff2_gain_end = + bank->eff3_gain_end = 0; + + if (!stereo) { + if (!eff2) { + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = cpu_to_le32(0x40000000); + } else { + bank->eff2_gain = + bank->eff2_gain_end = + bank->eff3_gain = + bank->eff3_gain_end = cpu_to_le32(0x40000000); + } + } else { + if (!eff2) { + if ((voice->number & 1) == 0) { + bank->left_gain = + bank->left_gain_end = cpu_to_le32(0x40000000); + } else { + bank->format |= cpu_to_le32(1); + bank->right_gain = + bank->right_gain_end = cpu_to_le32(0x40000000); + } + } else { + if ((voice->number & 1) == 0) { + bank->eff3_gain = + bank->eff3_gain_end = cpu_to_le32(0x40000000); + } else { + bank->format |= cpu_to_le32(1); + bank->eff2_gain = + bank->eff2_gain_end = cpu_to_le32(0x40000000); + } + } + } + } +} + +static int snd_ymfpci_ac3_init(ymfpci_t *chip) +{ + unsigned char *ptr; + dma_addr_t ptr_addr; + + if (chip->ac3_tmp_base != NULL) + return -EBUSY; + if ((ptr = snd_malloc_pci_pages(chip->pci, 4096, &ptr_addr)) == NULL) + return -ENOMEM; + + chip->ac3_tmp_base = ptr; + chip->ac3_tmp_base_addr = ptr_addr; + chip->bank_effect[3][0]->base = + chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr); + chip->bank_effect[3][0]->loop_end = + chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024); + chip->bank_effect[4][0]->base = + chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr + 2048); + chip->bank_effect[4][0]->loop_end = + chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024); + + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_ac3_done(ymfpci_t *chip) +{ + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3)); + spin_unlock_irq(&chip->reg_lock); + snd_ymfpci_irq_wait(chip); + if (chip->ac3_tmp_base) { + snd_free_pci_pages(chip->pci, 4096, chip->ac3_tmp_base, chip->ac3_tmp_base_addr); + chip->ac3_tmp_base = NULL; + } + return 0; +} + +static int snd_ymfpci_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0) + return err; + if (ypcm->spdif || ypcm->mode4ch) + if ((err = snd_ymfpci_ac3_init(chip)) < 0) + return err; + return 0; +} + +static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + + if (runtime->private_data == NULL) + return 0; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + snd_pcm_lib_free_pages(substream); + if (ypcm->voices[1]) { + snd_ymfpci_voice_free(chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (ypcm->voices[0]) { + snd_ymfpci_voice_free(chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + if (ypcm->spdif || ypcm->mode4ch) + snd_ymfpci_ac3_done(chip); + return 0; +} + +static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream) +{ + // ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + int nvoice; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + for (nvoice = 0; nvoice < runtime->channels; nvoice++) + snd_ymfpci_pcm_init_voice(ypcm->voices[nvoice], + runtime->channels == 2, + runtime->rate, + snd_pcm_format_width(runtime->format) == 16, + runtime->dma_addr, + ypcm->buffer_size, + ypcm->spdif || ypcm->mode4ch); + return 0; +} + +static int snd_ymfpci_capture_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_ymfpci_capture_hw_free(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ymfpci_capture_prepare(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + snd_ymfpci_capture_bank_t * bank; + int nbank; + u32 rate, format; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + ypcm->shift = 0; + rate = ((48000 * 4096) / runtime->rate) - 1; + format = 0; + if (runtime->channels == 2) { + format |= 2; + ypcm->shift++; + } + if (snd_pcm_format_width(runtime->format) == 8) + format |= 1; + else + ypcm->shift++; + switch (ypcm->capture_bank_number) { + case 0: + snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate); + break; + case 1: + snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = chip->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(runtime->dma_addr); + bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift); + bank->start = 0; + bank->num_of_loops = 0; + } + return 0; +} + +static snd_pcm_uframes_t snd_ymfpci_playback_pointer(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_voice_t *voice = ypcm->voices[0]; + + if (!(ypcm->running && voice)) + return 0; + return le32_to_cpu(voice->bank[chip->active_bank].start); +} + +static snd_pcm_uframes_t snd_ymfpci_capture_pointer(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + if (!ypcm->running) + return 0; + return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; +} + +static void snd_ymfpci_irq_wait(ymfpci_t *chip) +{ + wait_queue_t wait; + int loops = 4; + + while (loops-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0) + continue; + init_waitqueue_entry(&wait, current); + add_wait_queue(&chip->interrupt_sleep, &wait); + atomic_inc(&chip->interrupt_sleep_count); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + remove_wait_queue(&chip->interrupt_sleep, &wait); + } +} + +static void snd_ymfpci_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, dev_id, return); + u32 status, nvoice, mode; + ymfpci_voice_t *voice; + + status = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if (status & 0x80000000) { + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + spin_lock(&chip->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &chip->voices[nvoice]; + if (voice->interrupt) + voice->interrupt(chip, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + if (chip->capture_substream[nvoice]) + snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]); + } +#if 0 + for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) { + if (chip->effect_substream[nvoice]) + snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]); + } +#endif + spin_unlock(&chip->voice_lock); + spin_lock(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000); + mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2; + snd_ymfpci_writel(chip, YDSXGR_MODE, mode); + spin_unlock(&chip->reg_lock); + + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + } + + status = snd_ymfpci_readl(chip, YDSXGR_INTFLAG); + if (status & 1) { + /* timer handler */ + snd_ymfpci_writel(chip, YDSXGR_INTFLAG, ~0); + } + + if (chip->rawmidi) + snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data, regs); +} + +static snd_pcm_hardware_t snd_ymfpci_playback = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 256 * 1024, /* FIXME: enough? */ + period_bytes_min: 64, + period_bytes_max: 256 * 1024, /* FIXME: enough? */ + periods_min: 3, + periods_max: 1024, + fifo_size: 0, +}; + +static snd_pcm_hardware_t snd_ymfpci_capture = +{ + info: (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + rate_min: 8000, + rate_max: 48000, + channels_min: 1, + channels_max: 2, + buffer_bytes_max: 256 * 1024, /* FIXME: enough? */ + period_bytes_min: 64, + period_bytes_max: 256 * 1024, /* FIXME: enough? */ + periods_min: 3, + periods_max: 1024, + fifo_size: 0, +}; + +static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime) +{ + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + + if (ypcm) + snd_magic_kfree(ypcm); +} + +static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = PLAYBACK_VOICE; + ypcm->substream = substream; + runtime->hw = snd_ymfpci_playback; + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +static int snd_ymfpci_playback_spdif_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + unsigned long flags; + int err; + + if ((err = snd_ymfpci_playback_open(substream)) < 0) + return err; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm->spdif = 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30)); + chip->spdif_pcm_bits = chip->spdif_bits; + snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + chip->spdif_pcm_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +static int snd_ymfpci_playback_4ch_open(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + unsigned long flags; + int err; + + if ((err = snd_ymfpci_playback_open(substream)) < 0) + return err; + ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm->mode4ch = 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0030) | 0x0010); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30)); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +static int snd_ymfpci_capture_open(snd_pcm_substream_t * substream, + u32 capture_bank_number) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm; + + ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = capture_bank_number + CAPTURE_REC; + ypcm->substream = substream; + ypcm->capture_bank_number = capture_bank_number; + chip->capture_substream[capture_bank_number] = substream; + runtime->hw = snd_ymfpci_capture; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + snd_ymfpci_hw_start(chip); + return 0; +} + +static int snd_ymfpci_capture_rec_open(snd_pcm_substream_t * substream) +{ + return snd_ymfpci_capture_open(substream, 0); +} + +static int snd_ymfpci_capture_ac97_open(snd_pcm_substream_t * substream) +{ + return snd_ymfpci_capture_open(substream, 1); +} + +static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream) +{ + return 0; +} + +static int snd_ymfpci_playback_spdif_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30)); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + chip->spdif_pcm_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + return snd_ymfpci_playback_close(substream); +} + +static int snd_ymfpci_playback_4ch_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30)); + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return snd_ymfpci_playback_close(substream); +} + +static int snd_ymfpci_capture_close(snd_pcm_substream_t * substream) +{ + ymfpci_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + + if (ypcm != NULL) { + chip->capture_substream[ypcm->capture_bank_number] = NULL; + snd_ymfpci_hw_stop(chip); + } + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_ops = { + open: snd_ymfpci_playback_open, + close: snd_ymfpci_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_playback_hw_params, + hw_free: snd_ymfpci_playback_hw_free, + prepare: snd_ymfpci_playback_prepare, + trigger: snd_ymfpci_playback_trigger, + pointer: snd_ymfpci_playback_pointer, +}; + +static snd_pcm_ops_t snd_ymfpci_capture_rec_ops = { + open: snd_ymfpci_capture_rec_open, + close: snd_ymfpci_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_capture_hw_params, + hw_free: snd_ymfpci_capture_hw_free, + prepare: snd_ymfpci_capture_prepare, + trigger: snd_ymfpci_capture_trigger, + pointer: snd_ymfpci_capture_pointer, +}; + +static void snd_ymfpci_pcm_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_capture_ac97_ops = { + open: snd_ymfpci_capture_ac97_open, + close: snd_ymfpci_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_capture_hw_params, + hw_free: snd_ymfpci_capture_hw_free, + prepare: snd_ymfpci_capture_prepare, + trigger: snd_ymfpci_capture_trigger, + pointer: snd_ymfpci_capture_pointer, +}; + +static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm2 = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - AC'97", device, 0, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm2_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - AC'97"); + chip->pcm2 = pcm; + + snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_spdif_ops = { + open: snd_ymfpci_playback_spdif_open, + close: snd_ymfpci_playback_spdif_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_playback_hw_params, + hw_free: snd_ymfpci_playback_hw_free, + prepare: snd_ymfpci_playback_prepare, + trigger: snd_ymfpci_playback_trigger, + pointer: snd_ymfpci_playback_pointer, +}; + +static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm_spdif = NULL; +} + +int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_spdif_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - IEC958"); + chip->pcm_spdif = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static snd_pcm_ops_t snd_ymfpci_playback_4ch_ops = { + open: snd_ymfpci_playback_4ch_open, + close: snd_ymfpci_playback_4ch_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_ymfpci_playback_hw_params, + hw_free: snd_ymfpci_playback_hw_free, + prepare: snd_ymfpci_playback_prepare, + trigger: snd_ymfpci_playback_trigger, + pointer: snd_ymfpci_playback_pointer, +}; + +static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + chip->pcm_4ch = NULL; +} + +int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + pcm->private_free = snd_ymfpci_pcm_4ch_free; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - Rear PCM"); + chip->pcm_4ch = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int snd_ymfpci_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_default_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_spdif_default_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irqsave(&chip->reg_lock, flags); + change = chip->spdif_bits != val; + chip->spdif_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_default __devinitdata = +{ + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + info: snd_ymfpci_spdif_default_info, + get: snd_ymfpci_spdif_default_get, + put: snd_ymfpci_spdif_default_put +}; + +static int snd_ymfpci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = 0x3e; + ucontrol->value.iec958.status[1] = 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_mask __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READ, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + info: snd_ymfpci_spdif_mask_info, + get: snd_ymfpci_spdif_mask_get, +}; + +static int snd_ymfpci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_stream_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_spdif_stream_put(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irqsave(&chip->reg_lock, flags); + change = chip->spdif_pcm_bits != val; + chip->spdif_pcm_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2)) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +static snd_kcontrol_new_t snd_ymfpci_spdif_stream __devinitdata = +{ + access: SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + iface: SNDRV_CTL_ELEM_IFACE_PCM, + name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + info: snd_ymfpci_spdif_stream_info, + get: snd_ymfpci_spdif_stream_get, + put: snd_ymfpci_spdif_stream_put +}; + +/* + * Mixer controls + */ + +#define YMFPCI_SINGLE(xname, xindex, reg) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ymfpci_info_single, \ + get: snd_ymfpci_get_single, put: snd_ymfpci_put_single, \ + private_value: reg } + +static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + unsigned int mask = 1; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value; + unsigned int shift = 0, mask = 1, invert = 0; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value; + unsigned int shift = 0, mask = 1, invert = 0; + int change; + unsigned int val, oval; + + switch (kcontrol->private_value) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_ymfpci_readl(chip, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + snd_ymfpci_writel(chip, reg, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define YMFPCI_DOUBLE(xname, xindex, reg) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \ + info: snd_ymfpci_info_double, \ + get: snd_ymfpci_get_double, put: snd_ymfpci_put_double, \ + private_value: reg } + +static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + unsigned int reg = kcontrol->private_value; + unsigned int mask = 16383; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + unsigned int val; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + spin_lock_irqsave(&chip->reg_lock, flags); + val = snd_ymfpci_readl(chip, reg); + spin_unlock_irqrestore(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (val >> shift_left) & mask; + ucontrol->value.integer.value[1] = (val >> shift_right) & mask; + if (invert) { + ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; + } + return 0; +} + +static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; + int change; + unsigned int val1, val2, oval; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irqsave(&chip->reg_lock, flags); + oval = snd_ymfpci_readl(chip, reg); + val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval; + snd_ymfpci_writel(chip, reg, val1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return change; +} + +#define YMFPCI_CONTROLS (sizeof(snd_ymfpci_controls)/sizeof(snd_kcontrol_new_t)) + +static snd_kcontrol_new_t snd_ymfpci_controls[] __devinitdata = { +YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL), +YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL) +}; + +/* + * Mixer routines + */ + +static void snd_ymfpci_mixer_free_ac97(ac97_t *ac97) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +int __devinit snd_ymfpci_mixer(ymfpci_t *chip) +{ + ac97_t ac97; + snd_kcontrol_t *kctl; + int err, idx; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_ymfpci_codec_write; + ac97.read = snd_ymfpci_codec_read; + ac97.private_data = chip; + ac97.private_free = snd_ymfpci_mixer_free_ac97; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + + for (idx = 0; idx < YMFPCI_CONTROLS; idx++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0) + return err; + } + + /* add S/PDIF control */ + snd_assert(chip->pcm_spdif != NULL, return -EIO); + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + chip->spdif_pcm_ctl = kctl; + + return 0; +} + + +/* + * joystick support + */ + +static int ymfpci_joystick_ports[4] = { + 0x201, 0x202, 0x204, 0x205 +}; + +static void setup_joystick_base(ymfpci_t *chip) +{ + if (chip->pci->device >= 0x0010) /* YMF 744/754 */ + pci_write_config_word(chip->pci, PCIR_DSXG_JOYBASE, + ymfpci_joystick_ports[chip->joystick_port]); + else { + u16 legacy_ctrl2; + pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY, &legacy_ctrl2); + legacy_ctrl2 &= ~(3 << 6); + legacy_ctrl2 |= chip->joystick_port << 6; + pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY, legacy_ctrl2); + } +} + +static int snd_ymfpci_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ymfpci_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &val); + ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0; + return 0; +} + +static int snd_ymfpci_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY, &oval); + val = oval & ~0x04; + if (ucontrol->value.integer.value[0]) + val |= 0x04; + if (val != oval) { + setup_joystick_base(chip); + pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY, val); + return 1; + } + return 0; +} + +static int snd_ymfpci_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 3; + sprintf(uinfo->value.enumerated.name, "port 0x%x", ymfpci_joystick_ports[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ymfpci_joystick_addr_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->joystick_port; + return 0; +} + +static int snd_ymfpci_joystick_addr_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] != chip->joystick_port) { + snd_assert(ucontrol->value.integer.value[0] >= 0 && ucontrol->value.integer.value[0] < 4, return -EINVAL); + chip->joystick_port = ucontrol->value.integer.value[0]; + setup_joystick_base(chip); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_control_joystick __devinitdata = { + name: "Joystick", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_ymfpci_joystick_info, + get: snd_ymfpci_joystick_get, + put: snd_ymfpci_joystick_put, +}; + +static snd_kcontrol_new_t snd_ymfpci_control_joystick_addr __devinitdata = { + name: "Joystick Address", + iface: SNDRV_CTL_ELEM_IFACE_CARD, + info: snd_ymfpci_joystick_addr_info, + get: snd_ymfpci_joystick_addr_get, + put: snd_ymfpci_joystick_addr_put, +}; + +int __devinit snd_ymfpci_joystick(ymfpci_t *chip) +{ + int err; + + chip->joystick_port = 0; /* default */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick, chip))) < 0) + return err; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_control_joystick_addr, chip))) < 0) + return err; + return 0; +} + + +/* + * proc interface + */ + +static void snd_ymfpci_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + // ymfpci_t *chip = snd_magic_cast(ymfpci_t, private_data, return); + + snd_iprintf(buffer, "YMFPCI\n\n"); +} + +static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip) +{ + snd_info_entry_t *entry; + + entry = snd_info_create_card_entry(card, "ymfpci", card->proc_root); + if (entry) { + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = chip; + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->c.text.read_size = 4096; + entry->c.text.read = snd_ymfpci_proc_read; + if (snd_info_register(entry) < 0) { + snd_info_unregister(entry); + entry = NULL; + } + } + chip->proc_entry = entry; + return 0; +} + +static int snd_ymfpci_proc_done(ymfpci_t *chip) +{ + if (chip->proc_entry) + snd_info_unregister((snd_info_entry_t *) chip->proc_entry); + return 0; +} + +/* + * initialization routines + */ + +static void snd_ymfpci_aclink_reset(struct pci_dev * pci) +{ + u8 cmd; + + pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd); +#if 0 // force to reset + if (cmd & 0x03) { +#endif + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0); +#if 0 + } +#endif +} + +static void snd_ymfpci_enable_dsp(ymfpci_t *chip) +{ + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001); +} + +static void snd_ymfpci_disable_dsp(ymfpci_t *chip) +{ + u32 val; + int timeout = 1000; + + val = snd_ymfpci_readl(chip, YDSXGR_CONFIG); + if (val) + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000); + while (timeout-- > 0) { + val = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if ((val & 0x00000002) == 0) + break; + } +} + +#include "ymfpci_image.h" + +static void snd_ymfpci_download_image(ymfpci_t *chip) +{ + int i; + u16 ctrl; + unsigned long *inst; + + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + + /* setup DSP instruction code */ + for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); + + /* setup control instruction code */ + switch (chip->device_id) { + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + inst = CntrlInst1E; + break; + default: + inst = CntrlInst; + break; + } + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]); + + snd_ymfpci_enable_dsp(chip); +} + +static int __devinit snd_ymfpci_memalloc(ymfpci_t *chip) +{ + long size, playback_ctrl_size; + int voice, bank, reg; + u8 *ptr; + dma_addr_t ptr_addr; + + playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; + chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2; + chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2; + chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2; + chip->work_size = YDSXG_DEFAULT_WORK_SIZE; + + size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + + ((chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0x00ff) & ~0x00ff) + + ((chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0x00ff) & ~0x00ff) + + ((chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0x00ff) & ~0x00ff) + + chip->work_size; + /* work_ptr must be aligned to 256 bytes, but it's already + covered with the kernel page allocation mechanism */ + if ((ptr = snd_malloc_pci_pages(chip->pci, size, &ptr_addr)) == NULL) + return -ENOMEM; + memset(ptr, 0, size); /* for sure */ + chip->work_ptr = ptr; + chip->work_ptr_addr = ptr_addr; + chip->work_ptr_size = size; + + chip->bank_base_playback = ptr; + chip->bank_base_playback_addr = ptr_addr; + chip->ctrl_playback = (u32 *)ptr; + chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); + ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + ptr_addr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { + chip->voices[voice].number = voice; + chip->voices[voice].bank = (snd_ymfpci_playback_bank_t *)ptr; + chip->voices[voice].bank_addr = ptr_addr; + for (bank = 0; bank < 2; bank++) { + chip->bank_playback[voice][bank] = (snd_ymfpci_playback_bank_t *)ptr; + ptr += chip->bank_size_playback; + ptr_addr += chip->bank_size_playback; + } + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->bank_base_capture = ptr; + chip->bank_base_capture_addr = ptr_addr; + for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_capture[voice][bank] = (snd_ymfpci_capture_bank_t *)ptr; + ptr += chip->bank_size_capture; + ptr_addr += chip->bank_size_capture; + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->bank_base_effect = ptr; + chip->bank_base_effect_addr = ptr_addr; + for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_effect[voice][bank] = (snd_ymfpci_effect_bank_t *)ptr; + ptr += chip->bank_size_effect; + ptr_addr += chip->bank_size_effect; + } + ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff); + ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff; + chip->work_base = ptr; + chip->work_base_addr = ptr_addr; + + snd_assert(ptr + chip->work_size == chip->work_ptr + chip->work_ptr_size, ); + + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2); + + /* S/PDIF output initialization */ + chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff; + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + + /* S/PDIF input initialization */ + snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0); + + /* digital mixer setup */ + for (reg = 0x80; reg < 0xc0; reg += 4) + snd_ymfpci_writel(chip, reg, 0); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff); + + return 0; +} + +static int snd_ymfpci_free(ymfpci_t *chip) +{ + u16 ctrl; + + snd_assert(chip != NULL, return -EINVAL); + snd_ymfpci_proc_done(chip); + + if (chip->res_reg_area) { /* don't touch busy hardware */ + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + } + + /* Set PCI device to D3 state */ + // pci_set_power_state(chip->pci, 3); + +#ifdef CONFIG_PM + if (chip->saved_regs) + kfree(chip->saved_regs); +#endif + if (chip->reg_area_virt) + iounmap((void *)chip->reg_area_virt); + if (chip->work_ptr) + snd_free_pci_pages(chip->pci, chip->work_ptr_size, chip->work_ptr, chip->work_ptr_addr); + + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->res_reg_area) { + release_resource(chip->res_reg_area); + kfree_nocheck(chip->res_reg_area); + } + + pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); + + snd_magic_kfree(chip); + return 0; +} + +static int snd_ymfpci_dev_free(snd_device_t *device) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, device->device_data, return -ENXIO); + return snd_ymfpci_free(chip); +} + +#ifdef CONFIG_PM +static int saved_regs_index[] = { + /* spdif */ + YDSXGR_SPDIFOUTCTRL, + YDSXGR_SPDIFOUTSTATUS, + YDSXGR_SPDIFINCTRL, + /* volumes */ + YDSXGR_PRIADCLOOPVOL, + YDSXGR_NATIVEDACINVOL, + YDSXGR_NATIVEDACOUTVOL, + // YDSXGR_BUF441OUTVOL, + YDSXGR_NATIVEADCINVOL, + YDSXGR_SPDIFLOOPVOL, + YDSXGR_SPDIFOUTVOL, + YDSXGR_ZVOUTVOL, + /* address bases */ + YDSXGR_PLAYCTRLBASE, + YDSXGR_RECCTRLBASE, + YDSXGR_EFFCTRLBASE, + YDSXGR_WORKBASE, + /* capture set up */ + YDSXGR_MAPOFREC, + YDSXGR_RECFORMAT, + YDSXGR_RECSLOTSR, + YDSXGR_ADCFORMAT, + YDSXGR_ADCSLOTSR, +}; +#define YDSXGR_NUM_SAVED_REGS (sizeof(saved_regs_index)/sizeof(saved_regs_index[0])) + +void snd_ymfpci_suspend(ymfpci_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + snd_power_lock(card); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + snd_pcm_suspend_all(chip->pcm); + snd_pcm_suspend_all(chip->pcm2); + snd_pcm_suspend_all(chip->pcm_spdif); + snd_pcm_suspend_all(chip->pcm_4ch); + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]); + chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_disable_dsp(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +void snd_ymfpci_resume(ymfpci_t *chip) +{ + snd_card_t *card = chip->card; + int i; + + snd_power_lock(card); + + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + pci_enable_device(chip->pci); + pci_set_master(chip->pci); + snd_ymfpci_aclink_reset(chip->pci); + snd_ymfpci_codec_ready(chip, 0, 0); + snd_ymfpci_download_image(chip); + udelay(100); + + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]); + + snd_ac97_resume(chip->ac97); + + /* start hw again */ + if (chip->start_count > 0) { + spin_lock(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT); + spin_unlock(&chip->reg_lock); + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +static int snd_ymfpci_set_power_state(snd_card_t *card, unsigned int power_state) +{ + ymfpci_t *chip = snd_magic_cast(ymfpci_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_ymfpci_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_ymfpci_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} +#endif /* CONFIG_PM */ + +int __devinit snd_ymfpci_create(snd_card_t * card, + struct pci_dev * pci, + unsigned short old_legacy_ctrl, + ymfpci_t ** rchip) +{ + ymfpci_t *chip; + int err; + static snd_device_ops_t ops = { + dev_free: snd_ymfpci_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = snd_magic_kcalloc(ymfpci_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->old_legacy_ctrl = old_legacy_ctrl; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->voice_lock); + init_waitqueue_head(&chip->interrupt_sleep); + atomic_set(&chip->interrupt_sleep_count, 0); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->device_id = pci->device; + pci_read_config_byte(pci, PCI_REVISION_ID, (u8 *)&chip->rev); + chip->reg_area_phys = pci_resource_start(pci, 0); + chip->reg_area_virt = (unsigned long)ioremap_nocache(chip->reg_area_phys, 0x8000); + pci_set_master(pci); + + if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) { + snd_ymfpci_free(chip); + snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) { + snd_ymfpci_free(chip); + snd_printk("unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + chip->irq = pci->irq; + + snd_ymfpci_aclink_reset(pci); + if (snd_ymfpci_codec_ready(chip, 0, 1) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + + snd_ymfpci_download_image(chip); + + udelay(100); /* seems we need a delay after downloading image.. */ + + if (snd_ymfpci_memalloc(chip) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + +#ifdef CONFIG_PM + chip->saved_regs = kmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32), GFP_KERNEL); + if (chip->saved_regs == NULL) { + snd_ymfpci_free(chip); + return -ENOMEM; + } + card->set_power_state = snd_ymfpci_set_power_state; + card->power_state_private_data = chip; +#endif + + snd_ymfpci_proc_init(card, chip); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ymfpci_free(chip); + return err; + } + + *rchip = chip; + return 0; +} diff -Nru linux/sound/ppc/Config.in linux-2.4.19-pre5-mjc/sound/ppc/Config.in --- linux/sound/ppc/Config.in Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/Config.in Mon Apr 8 22:31:24 2002 @@ -0,0 +1,8 @@ +# ALSA PowerMac drivers + +mainmenu_option next_comment +comment 'ALSA PowerMac devices' + +dep_tristate 'PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)' CONFIG_SND_POWERMAC $CONFIG_SND + +endmenu diff -Nru linux/sound/ppc/Makefile linux-2.4.19-pre5-mjc/sound/ppc/Makefile --- linux/sound/ppc/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,18 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := ppc.o + +list-multi := snd-powermac.o + +snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o + +include $(TOPDIR)/Rules.make + +snd-powermac.o: $(snd-powermac-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-powermac-objs) diff -Nru linux/sound/ppc/awacs.c linux-2.4.19-pre5-mjc/sound/ppc/awacs.c --- linux/sound/ppc/awacs.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/awacs.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,420 @@ +/* + * PMac AWACS lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pmac.h" + +#define chip_t pmac_t + + +/* + * write AWACS register + */ +static void +snd_pmac_awacs_write(pmac_t *chip, int val) +{ + long timeout = 5000000; + + if (chip->model <= PMAC_SCREAMER) + return; + + while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) { + if (! --timeout) { + snd_printd("snd_pmac_awacs_write timeout\n"); + break; + } + } + out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22)); +} + +static void +snd_pmac_awacs_write_reg(pmac_t *chip, int reg, int val) +{ + snd_pmac_awacs_write(chip, val | (reg << 12)); + chip->awacs_reg[reg] = val; +} + +static void +snd_pmac_awacs_write_noreg(pmac_t *chip, int reg, int val) +{ + snd_pmac_awacs_write(chip, val | (reg << 12)); +} + +#ifdef CONFIG_PMAC_PBOOK +static void +screamer_recalibrate(pmac_t *chip) +{ + /* Sorry for the horrible delays... I hope to get that improved + * by making the whole PM process asynchronous in a future version + */ + mdelay(750); + snd_pmac_awacs_write_noreg(chip, 1, + chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE); + mdelay(1000); + snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); +} +#endif + + +/* + * additional callback to set the pcm format + */ +static void snd_pmac_awacs_set_format(pmac_t *chip) +{ + chip->awacs_reg[1] &= ~MASK_SAMPLERATE; + chip->awacs_reg[1] |= chip->rate_index << 3; + snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); +} + + +#ifdef PMAC_AMP_AVAIL +/* Turn on sound output, needed on G3 desktop powermacs */ +/* vol = 0 - 31, stereo */ +static void +snd_pmac_awacs_enable_amp(pmac_t *chip, int lvol, int rvol) +{ + struct adb_request req; + + if (! chip->amp_only) + return; + + /* turn on headphones */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 4, 0); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 6, 0); + while (!req.complete) cuda_poll(); + + /* turn on speaker */ + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 3, lvol & 0xff); + while (!req.complete) cuda_poll(); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, + 0x8a, 5, rvol & 0xff); + while (!req.complete) cuda_poll(); + + cuda_request(&req, NULL, 5, CUDA_PACKET, + CUDA_GET_SET_IIC, 0x8a, 1, 0x29); + while (!req.complete) cuda_poll(); + + /* update */ + chip->amp_vol[0] = lvol; + chip->amp_vol[1] = rvol; +} +#endif + +/* + * AWACS volume callbacks + */ +/* + * volumes: 0-15 stereo + */ +static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int n = kcontrol->private_value & 0xff; + int lshift = (kcontrol->private_value >> 8) & 0xff; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = 0x0f - ((chip->awacs_reg[n] >> lshift) & 0xf); + ucontrol->value.integer.value[1] = 0x0f - (chip->awacs_reg[n] & 0xf); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int n = kcontrol->private_value & 0xff; + int lshift = (kcontrol->private_value >> 8) & 0xff; + int rn, oldval; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + oldval = chip->awacs_reg[n]; + rn = oldval & ~(0xf | (0xf << lshift)); + rn |= ((0x0f - (ucontrol->value.integer.value[0] & 0xf)) << lshift); + rn |= 0x0f - (ucontrol->value.integer.value[1] & 0xf); + snd_pmac_awacs_write_reg(chip, n, rn); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return oldval != rn; +} + + +#define AWACS_VOLUME(xname, xreg, xshift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \ + info: snd_pmac_awacs_info_volume, \ + get: snd_pmac_awacs_get_volume, \ + put: snd_pmac_awacs_put_volume, \ + private_value: (xreg) | (xshift << 8) } + +/* + * mute master/ogain for AWACS: mono + */ +static int snd_pmac_awacs_info_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int mask = kcontrol->private_value & 0xff; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + ucontrol->value.integer.value[0] = (chip->awacs_reg[1] & mask) ? 0 : 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int mask = kcontrol->private_value & 0xff; + int val, changed; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + val = chip->awacs_reg[1] & ~mask; + if (! ucontrol->value.integer.value[0]) + val |= mask; + snd_pmac_awacs_write_reg(chip, 1, val); + changed = chip->awacs_reg[1] != val; + spin_unlock_irqrestore(&chip->reg_lock, flags); + return changed; +} + +#define AWACS_SWITCH(xname, xreg, xmask) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \ + info: snd_pmac_awacs_info_switch, \ + get: snd_pmac_awacs_get_switch, \ + put: snd_pmac_awacs_put_switch, \ + private_value: (xreg) | ((xmask) << 8) } + + +#ifdef PMAC_AMP_AVAIL +/* + * Master volume for awacs revision 3 + */ +static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 31; + return 0; +} + +static int snd_pmac_awacs_get_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->amp_vol[0]; + ucontrol->value.integer.value[1] = chip->amp_vol[1]; + return 0; +} + +static int snd_pmac_awacs_put_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int changed = ucontrol->value.integer.value[0] != chip->amp_vol[0] || + ucontrol->value.integer.value[1] != chip->amp_vol[1]; + snd_pmac_awacs_enable_amp(chip, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + return changed; +} +#endif /* PMAC_AMP_AVAIL */ + + +/* + * lists of mixer elements + */ +static snd_kcontrol_new_t snd_pmac_awacs_mixers1[] = { + AWACS_VOLUME("Master Playback Volume", 2, 6), + AWACS_SWITCH("Master Playback Switch", 1, MASK_AMUTE), + AWACS_SWITCH("Master Capture Switch", 1, MASK_LOOPTHRU), +}; + +static snd_kcontrol_new_t snd_pmac_awacs_mixers2[] = { + AWACS_VOLUME("Capture Volume", 0, 4), + AWACS_SWITCH("Line Capture Switch", 0, MASK_MUX_AUDIN), + AWACS_SWITCH("CD Capture Switch", 0, MASK_MUX_CD), + AWACS_SWITCH("Mic Capture Switch", 0, MASK_MUX_MIC), + AWACS_SWITCH("Mic Boost", 0, MASK_GAINLINE), +}; + +static snd_kcontrol_new_t snd_pmac_awacs_speaker_mixers[] = { + AWACS_VOLUME("PC Speaker Playback Volume", 4, 6), + AWACS_SWITCH("PC Speaker Playback Switch", 1, MASK_CMUTE), +}; + +#ifdef PMAC_AMP_AVAIL +static snd_kcontrol_new_t snd_pmac_awacs_amp_mixers[] = { + { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "PC Speaker Playback Volume", index: 0, + info: snd_pmac_awacs_info_volume_amp, \ + get: snd_pmac_awacs_get_volume_amp, + put: snd_pmac_awacs_put_volume_amp, + }, +}; +#endif + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +/* + * add new mixer elements to the card + */ +static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers) +{ + int i, err; + + for (i = 0; i < nums; i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) + return err; + } + return 0; +} + +#ifdef CONFIG_PMAC_PBOOK +static void snd_pmac_awacs_resume(pmac_t *chip) +{ + snd_pmac_awacs_write_reg(chip, 0, chip->awacs_reg[0]); + snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); + snd_pmac_awacs_write_reg(chip, 2, chip->awacs_reg[2]); + snd_pmac_awacs_write_reg(chip, 4, chip->awacs_reg[4]); + if (chip->model == PMAC_SCREAMER) { + snd_pmac_awacs_write_reg(chip, 5, chip->awacs_reg[5]); + snd_pmac_awacs_write_reg(chip, 6, chip->awacs_reg[6]); + snd_pmac_awacs_write_reg(chip, 7, chip->awacs_reg[7]); + screamer_recalibrate(chip); + } +} +#endif /* CONFIG_PMAC_PBOOK */ + +/* + * initialize chip + */ +int __init +snd_pmac_awacs_init(pmac_t *chip) +{ + int err, vol; + + snd_pmac_awacs_write_reg(chip, 0, MASK_MUX_CD); + /* FIXME: Only machines with external SRS module need MASK_PAROUT */ + chip->awacs_reg[1] = MASK_LOOPTHRU; + if (chip->has_iic || chip->device_id == 0x5 || + /*chip->_device_id == 0x8 || */ + chip->device_id == 0xb) + chip->awacs_reg[1] |= MASK_PAROUT; + snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); + /* get default volume from nvram */ + vol = (~nvram_read_byte(0x1308) & 7) << 1; + snd_pmac_awacs_write_reg(chip, 2, vol + (vol << 6)); + snd_pmac_awacs_write_reg(chip, 4, vol + (vol << 6)); + if (chip->model == PMAC_SCREAMER) { + snd_pmac_awacs_write_reg(chip, 5, 0); + snd_pmac_awacs_write_reg(chip, 6, 0); + snd_pmac_awacs_write_reg(chip, 7, 0); + } + +#ifdef CONFIG_PMAC_PBOOK + /* Recalibrate chip */ + if (chip->model == PMAC_SCREAMER) + screamer_recalibrate(chip); +#endif + + if (chip->model <= PMAC_SCREAMER && chip->revision == 0) { + chip->revision = + (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf; + if (chip->revision == 3) { +#ifdef PMAC_AMP_AVAIL +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + if (adb_hardware == ADB_VIACUDA) + chip->amp_only = 1; +#elif defined(CONFIG_ADB_CUDA) + if (sys_ctrler == SYS_CTRLER_CUDA) + chip->amp_only = 1; +#endif + if (chip->amp_only) { + /*chip->amp_vol[0] = chip->amp_vol[1] = 31;*/ + snd_pmac_awacs_enable_amp(chip, chip->amp_vol[0], chip->amp_vol[1]); + } +#endif /* PMAC_AMP_AVAIL */ + } + } + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac AWACS"); + + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers1), + snd_pmac_awacs_mixers1)) < 0) + return err; +#ifdef PMAC_AMP_AVAIL + if (chip->amp_only) { + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_amp_mixers), + snd_pmac_awacs_amp_mixers)) < 0) + return err; + } else { +#endif /* PMAC_AMP_AVAIL */ + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_speaker_mixers), + snd_pmac_awacs_speaker_mixers)) < 0) + return err; +#ifdef PMAC_AMP_AVAIL + } +#endif /* PMAC_AMP_AVAIL */ + if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers2), + snd_pmac_awacs_mixers2)) < 0) + return err; + + /* + * set lowlevel callbacks + */ + chip->set_format = snd_pmac_awacs_set_format; +#ifdef CONFIG_PMAC_PBOOK + chip->resume = snd_pmac_awacs_resume; +#endif + + return 0; +} diff -Nru linux/sound/ppc/awacs.h linux-2.4.19-pre5-mjc/sound/ppc/awacs.h --- linux/sound/ppc/awacs.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/awacs.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,182 @@ +/* + * Driver for PowerMac AWACS onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __AWACS_H +#define __AWACS_H + +/*******************************/ +/* AWACs Audio Register Layout */ +/*******************************/ + +struct awacs_regs { + unsigned control; /* Audio control register */ + unsigned pad0[3]; + unsigned codec_ctrl; /* Codec control register */ + unsigned pad1[3]; + unsigned codec_stat; /* Codec status register */ + unsigned pad2[3]; + unsigned clip_count; /* Clipping count register */ + unsigned pad3[3]; + unsigned byteswap; /* Data is little-endian if 1 */ +}; + +/*******************/ +/* Audio Bit Masks */ +/*******************/ + +/* Audio Control Reg Bit Masks */ +/* ----- ------- --- --- ----- */ +#define MASK_ISFSEL (0xf) /* Input SubFrame Select */ +#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */ +#define MASK_RATE (0x7 << 8) /* Sound Rate */ +#define MASK_CNTLERR (0x1 << 11) /* Error */ +#define MASK_PORTCHG (0x1 << 12) /* Port Change */ +#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */ +#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */ +#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */ + +/* Audio Codec Control Reg Bit Masks */ +/* ----- ----- ------- --- --- ----- */ +#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */ +#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */ +#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */ +#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */ + +/* Audio Codec Control Address Values / Masks */ +/* ----- ----- ------- ------- ------ - ----- */ +#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */ +#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */ +#define MASK_ADDR_GAIN MASK_ADDR0 + +#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */ +#define MASK_ADDR_MUTE MASK_ADDR1 +#define MASK_ADDR_RATE MASK_ADDR1 + +#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */ +#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */ +#define MASK_ADDR_VOLHD MASK_ADDR2 + +#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */ +#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */ +#define MASK_ADDR_VOLSPK MASK_ADDR4 + +/* additional registers of screamer */ +#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */ +#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */ +#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */ + +/* Address 0 Bit Masks & Macros */ +/* ------- - --- ----- - ------ */ +#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */ +#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */ +#define MASK_GAINLINE (0x1 << 8) /* Disable Mic preamp */ +#define MASK_GAINMIC (0x0 << 8) /* Enable Mic preamp */ + +#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */ +#define MASK_MUX_MIC (0x1 << 10) /* Select Mic in MUX */ +#define MASK_MUX_AUDIN (0x1 << 11) /* Select Audio In in MUX */ +#define MASK_MUX_LINE MASK_MUX_AUDIN + +#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT) +#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT) + +/* Address 1 Bit Masks */ +/* ------- - --- ----- */ +#define MASK_ADDR1RES1 (0x3) /* Reserved */ +#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */ +#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */ +#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */ +#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ +#define MASK_SPKMUTE MASK_CMUTE +#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ +#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ +#define MASK_HDMUTE MASK_AMUTE +#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ + +#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ +#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ +#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */ +#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */ +#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */ +#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */ +#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */ +#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */ + +/* Address 2 & 4 Bit Masks & Macros */ +/* ------- - - - --- ----- - ------ */ +#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */ +#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */ +#define MASK_ADDR4RES1 MASK_ADDR2RES1 +#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */ +#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */ +#define MASK_ADDR4RES2 MASK_ADDR2RES2 + +#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT)) +#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) + +/* Audio Codec Status Reg Bit Masks */ +/* ----- ----- ------ --- --- ----- */ +#define MASK_EXTEND (0x1 << 23) /* Extend */ +#define MASK_VALID (0x1 << 22) /* Valid Data? */ +#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */ +#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */ +#define MASK_ERRCODE (0xf << 16) /* Error Code */ +#define MASK_REVISION (0xf << 12) /* Revision Number */ +#define MASK_MFGID (0xf << 8) /* Mfg. ID */ +#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ +#define MASK_INPPORT (0xf) /* Input Port */ +#define MASK_HDPCONN 8 /* headphone plugged in */ + +/* Clipping Count Reg Bit Masks */ +/* -------- ----- --- --- ----- */ +#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */ +#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */ + +/* DBDMA ChannelStatus Bit Masks */ +/* ----- ------------- --- ----- */ +#define MASK_CSERR (0x1 << 7) /* Error */ +#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ +#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ +#define MASK_WAIT (0x1) /* Wait */ + +/* Various Rates */ +/* ------- ----- */ +#define RATE_48000 (0x0 << 8) /* 48 kHz */ +#define RATE_44100 (0x0 << 8) /* 44.1 kHz */ +#define RATE_32000 (0x1 << 8) /* 32 kHz */ +#define RATE_29400 (0x1 << 8) /* 29.4 kHz */ +#define RATE_24000 (0x2 << 8) /* 24 kHz */ +#define RATE_22050 (0x2 << 8) /* 22.05 kHz */ +#define RATE_19200 (0x3 << 8) /* 19.2 kHz */ +#define RATE_17640 (0x3 << 8) /* 17.64 kHz */ +#define RATE_16000 (0x4 << 8) /* 16 kHz */ +#define RATE_14700 (0x4 << 8) /* 14.7 kHz */ +#define RATE_12000 (0x5 << 8) /* 12 kHz */ +#define RATE_11025 (0x5 << 8) /* 11.025 kHz */ +#define RATE_9600 (0x6 << 8) /* 9.6 kHz */ +#define RATE_8820 (0x6 << 8) /* 8.82 kHz */ +#define RATE_8000 (0x7 << 8) /* 8 kHz */ +#define RATE_7350 (0x7 << 8) /* 7.35 kHz */ + +#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ + + +#endif /* __AWACS_H */ diff -Nru linux/sound/ppc/burgundy.c linux-2.4.19-pre5-mjc/sound/ppc/burgundy.c --- linux/sound/ppc/burgundy.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/burgundy.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,378 @@ +/* + * PMac Burgundy lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pmac.h" +#include "burgundy.h" + +#define chip_t pmac_t + + +/* Waits for busy flag to clear */ +inline static void +snd_pmac_burgundy_busy_wait(pmac_t *chip) +{ + while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) + ; +} + +inline static void +snd_pmac_burgundy_extend_wait(pmac_t *chip) +{ + while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND)) + ; + while (in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) + ; +} + +static void +snd_pmac_burgundy_wcw(pmac_t *chip, unsigned addr, unsigned val) +{ + out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); + out_le32(&chip->awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); + snd_pmac_burgundy_busy_wait(chip); +} + +static unsigned +snd_pmac_burgundy_rcw(pmac_t *chip, unsigned addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + spin_lock_irqsave(&chip->reg_lock, flags); + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100100); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<8; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100200); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<16; + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100300); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24; + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return val; +} + +static void +snd_pmac_burgundy_wcb(pmac_t *chip, unsigned int addr, unsigned int val) +{ + out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); + snd_pmac_burgundy_busy_wait(chip); +} + +static unsigned +snd_pmac_burgundy_rcb(pmac_t *chip, unsigned int addr) +{ + unsigned val = 0; + unsigned long flags; + + /* should have timeouts here */ + spin_lock_irqsave(&chip->reg_lock, flags); + + out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); + snd_pmac_burgundy_busy_wait(chip); + snd_pmac_burgundy_extend_wait(chip); + val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; + + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return val; +} + +/* + * Burgundy volume: 0 - 100, stereo + */ +static void +snd_pmac_burgundy_write_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +{ + int hardvolume, lvolume, rvolume; + + lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; + rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; + + hardvolume = lvolume + (rvolume << shift); + if (shift == 8) + hardvolume |= hardvolume << 16; + + snd_pmac_burgundy_wcw(chip, address, hardvolume); +} + +static void +snd_pmac_burgundy_read_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +{ + int wvolume; + + wvolume = snd_pmac_burgundy_rcw(chip, address); + + volume[0] = wvolume & 0xff; + if (volume[0] >= BURGUNDY_VOLUME_OFFSET) + volume[0] -= BURGUNDY_VOLUME_OFFSET; + else + volume[0] = 0; + volume[1] = (wvolume >> shift) & 0xff; + if (volume[1] >= BURGUNDY_VOLUME_OFFSET) + volume[1] -= BURGUNDY_VOLUME_OFFSET; + else + volume[1] = 0; +} + + +/* + */ + +#define BASE2ADDR(base) ((base) << 12) +#define ADDR2BASE(addr) ((addr) >> 12) + +static int snd_pmac_burgundy_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_burgundy_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int shift = (kcontrol->private_value >> 8) & 0xff; + snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value, shift); + return 0; +} + +static int snd_pmac_burgundy_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int shift = (kcontrol->private_value >> 8) & 0xff; + long nvoices[2]; + + snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value, shift); + snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift); + return (nvoices[0] != ucontrol->value.integer.value[0] || + nvoices[1] != ucontrol->value.integer.value[1]); +} + +#define BURGUNDY_VOLUME(xname, xindex, addr, shift) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ + info: snd_pmac_burgundy_info_volume,\ + get: snd_pmac_burgundy_get_volume,\ + put: snd_pmac_burgundy_put_volume,\ + private_value: ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) } + +/* lineout/speaker */ + +static int snd_pmac_burgundy_info_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_pmac_burgundy_get_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; + if (stereo) + ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; + return 0; +} + +static int snd_pmac_burgundy_put_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val, oval; + oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + val = oval & ~(lmask | rmask); + if (ucontrol->value.integer.value[0]) + val |= lmask; + if (stereo && ucontrol->value.integer.value[1]) + val |= rmask; + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val); + return val != oval; +} + +#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ + info: snd_pmac_burgundy_info_switch_out,\ + get: snd_pmac_burgundy_get_switch_out,\ + put: snd_pmac_burgundy_put_switch_out,\ + private_value: ((lmask) | ((rmask) << 8) | ((stereo) << 24)) } + +/* line/speaker output volume */ +static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +static int snd_pmac_burgundy_get_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int oval; + + oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + ucontrol->value.integer.value[0] = oval & 0xf; + if (stereo) + ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; + return 0; +} + +static int snd_pmac_burgundy_put_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int oval, val; + + oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + val = ucontrol->value.integer.value[0]; + if (stereo) + val |= ucontrol->value.integer.value[1] << 4; + else + val |= ucontrol->value.integer.value[0] << 4; + val = ~val & 0xff; + snd_pmac_burgundy_wcb(chip, addr, val); + return val != oval; +} + +#define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \ +{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\ + info: snd_pmac_burgundy_info_volume_out,\ + get: snd_pmac_burgundy_get_volume_out,\ + put: snd_pmac_burgundy_put_volume_out,\ + private_value: (ADDR2BASE(addr) | ((stereo) << 24)) } + +static snd_kcontrol_new_t snd_pmac_burgundy_mixers[] = { + BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), + BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16), + BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16), + BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16), + BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0), + /*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/ + BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1), + BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0), + BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1), +}; + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + + +/* + * initialize burgundy + */ +int __init snd_pmac_burgundy_init(pmac_t *chip) +{ + int i, err; + + /* Checks to see the chip is alive and kicking */ + if ((in_le32(&chip->awacs->codec_ctrl) & MASK_ERRCODE) == 0xf0000) { + printk(KERN_WARNING "pmac burgundy: disabled by MacOS :-(\n"); + return 1; + } + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, + DEF_BURGUNDY_OUTPUTENABLES); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + DEF_BURGUNDY_MORE_OUTPUTENABLES); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTSELECTS, + DEF_BURGUNDY_OUTPUTSELECTS); + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21, + DEF_BURGUNDY_INPSEL21); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3, + DEF_BURGUNDY_INPSEL3); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD, + DEF_BURGUNDY_GAINCD); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE, + DEF_BURGUNDY_GAINLINE); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMIC, + DEF_BURGUNDY_GAINMIC); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMODEM, + DEF_BURGUNDY_GAINMODEM); + + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENSPEAKER, + DEF_BURGUNDY_ATTENSPEAKER); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENLINEOUT, + DEF_BURGUNDY_ATTENLINEOUT); + snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENHP, + DEF_BURGUNDY_ATTENHP); + + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_MASTER_VOLUME, + DEF_BURGUNDY_MASTER_VOLUME); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLCD, + DEF_BURGUNDY_VOLCD); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLLINE, + DEF_BURGUNDY_VOLLINE); + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC, + DEF_BURGUNDY_VOLMIC); + + /* + * build burgundy mixers + */ + strcpy(chip->card->mixername, "PowerMac Burgundy"); + + for (i = 0; i < num_controls(snd_pmac_burgundy_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0) + return err; + } + return 0; +} diff -Nru linux/sound/ppc/burgundy.h linux-2.4.19-pre5-mjc/sound/ppc/burgundy.h --- linux/sound/ppc/burgundy.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/burgundy.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,95 @@ +/* + * Driver for PowerMac Burgundy onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __BURGUNDY_H +#define __BURGUNDY_H + +#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) +#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) + +#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) +#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) +#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) + +#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) + +#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) + +#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) + +#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) +#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) +#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) + +#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) +#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) +#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) +#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + +#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) +#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) +#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) +#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) + + +/* These are all default values for the burgundy */ +#define DEF_BURGUNDY_INPSEL21 (0xAA) +#define DEF_BURGUNDY_INPSEL3 (0x0A) + +#define DEF_BURGUNDY_GAINCD (0x33) +#define DEF_BURGUNDY_GAINLINE (0x44) +#define DEF_BURGUNDY_GAINMIC (0x44) +#define DEF_BURGUNDY_GAINMODEM (0x06) + +/* Remember: lowest volume here is 0x9b */ +#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) +#define DEF_BURGUNDY_VOLLINE (0x00000000) +#define DEF_BURGUNDY_VOLMIC (0x00000000) +#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) + +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) + +/* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */ +#define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD) + +#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) + +#define DEF_BURGUNDY_ATTENSPEAKER (0x44) +#define DEF_BURGUNDY_ATTENLINEOUT (0xCC) +#define DEF_BURGUNDY_ATTENHP (0xCC) + +/* OUTPUTENABLES bits */ +#define BURGUNDY_OUTPUT_LEFT 0x02 +#define BURGUNDY_OUTPUT_RIGHT 0x04 +#define BURGUNDY_OUTPUT_INTERN 0x80 + +/* volume offset */ +#define BURGUNDY_VOLUME_OFFSET 155 + +#endif /* __BURGUNDY_H */ diff -Nru linux/sound/ppc/daca.c linux-2.4.19-pre5-mjc/sound/ppc/daca.c --- linux/sound/ppc/daca.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/daca.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,274 @@ +/* + * PMac DACA lowlevel functions + * + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include "pmac.h" + +#define chip_t pmac_t + +/* i2c address */ +#define DACA_I2C_ADDR 0x4d + +/* registers */ +#define DACA_REG_SR 0x01 +#define DACA_REG_AVOL 0x02 +#define DACA_REG_GCFG 0x03 + +/* maximum volume value */ +#define DACA_VOL_MAX 0x38 + + +typedef struct pmac_daca_t { + pmac_keywest_t i2c; + int left_vol, right_vol; + unsigned int deemphasis : 1; + unsigned int amp_on : 1; +} pmac_daca_t; + + +/* + * initialize / detect DACA + */ +static int daca_init_client(pmac_t *chip, pmac_keywest_t *i2c) +{ + unsigned short wdata = 0x00; + /* SR: no swap, 1bit delay, 32-48kHz */ + /* GCFG: power amp inverted, DAC on */ + if (snd_pmac_keywest_write_byte(i2c, DACA_REG_SR, 0x08) < 0 || + snd_pmac_keywest_write_byte(i2c, DACA_REG_GCFG, 0x05) < 0) + return -EINVAL; + return snd_pmac_keywest_write(i2c, DACA_REG_AVOL, 2, (unsigned char*)&wdata); +} + +/* + * update volume + */ +static int daca_set_volume(pmac_daca_t *mix) +{ + unsigned char data[2]; + + if (! mix->i2c.base) + return -ENODEV; + + if (mix->right_vol > DACA_VOL_MAX) + data[0] = DACA_VOL_MAX; + else + data[0] = mix->right_vol; + if (mix->left_vol > DACA_VOL_MAX) + data[1] = DACA_VOL_MAX; + else + data[1] = mix->left_vol; + data[1] |= mix->deemphasis ? 0x40 : 0; + if (snd_pmac_keywest_write(&mix->i2c, DACA_REG_AVOL, 2, data) < 0) { + snd_printk("failed to set volume \n"); + return -EINVAL; + } + return 0; +} + + +/* deemphasis switch */ +static int daca_info_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int daca_get_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0; + return 0; +} + +static int daca_put_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->deemphasis != ucontrol->value.integer.value[0]; + if (change) { + mix->deemphasis = ucontrol->value.integer.value[0]; + daca_set_volume(mix); + } + return change; +} + +/* output volume */ +static int daca_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = DACA_VOL_MAX; + return 0; +} + +static int daca_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->left_vol; + ucontrol->value.integer.value[1] = mix->right_vol; + return 0; +} + +static int daca_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->left_vol != ucontrol->value.integer.value[0] || + mix->right_vol != ucontrol->value.integer.value[1]; + if (change) { + mix->left_vol = ucontrol->value.integer.value[0]; + mix->right_vol = ucontrol->value.integer.value[1]; + daca_set_volume(mix); + } + return change; +} + +/* amplifier switch */ +#define daca_info_amp daca_info_deemphasis + +static int daca_get_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0; + return 0; +} + +static int daca_put_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_daca_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->amp_on != ucontrol->value.integer.value[0]; + if (change) { + mix->amp_on = ucontrol->value.integer.value[0]; + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG, + mix->amp_on ? 0x05 : 0x04); + } + return change; +} + +static snd_kcontrol_new_t daca_mixers[] = { + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Deemphasis Switch", + info: daca_info_deemphasis, + get: daca_get_deemphasis, + put: daca_put_deemphasis + }, + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Master Playback Volume", + info: daca_info_volume, + get: daca_get_volume, + put: daca_put_volume + }, + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Power Amplifier Switch", + info: daca_info_amp, + get: daca_get_amp, + put: daca_put_amp + }, +}; + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + + +#ifdef CONFIG_PMAC_PBOOK +static void daca_resume(pmac_t *chip) +{ + pmac_daca_t *mix = chip->mixer_data; + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_SR, 0x08); + snd_pmac_keywest_write_byte(&mix->i2c, DACA_REG_GCFG, + mix->amp_on ? 0x05 : 0x04); + daca_set_volume(mix); +} +#endif /* CONFIG_PMAC_PBOOK */ + + +static void daca_cleanup(pmac_t *chip) +{ + pmac_daca_t *mix = chip->mixer_data; + if (! mix) + return; + snd_pmac_keywest_cleanup(&mix->i2c); + kfree(mix); + chip->mixer_data = NULL; +} + +/* exported */ +int __init snd_pmac_daca_init(pmac_t *chip) +{ + int i, err; + pmac_daca_t *mix; + + mix = kmalloc(sizeof(*mix), GFP_KERNEL); + if (! mix) + return -ENOMEM; + memset(mix, 0, sizeof(*mix)); + chip->mixer_data = mix; + chip->mixer_free = daca_cleanup; + mix->amp_on = 1; /* default on */ + + if ((err = snd_pmac_keywest_find(chip, &mix->i2c, DACA_I2C_ADDR, daca_init_client)) < 0) + return err; + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac DACA"); + + for (i = 0; i < num_controls(daca_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0) + return err; + } + +#ifdef CONFIG_PMAC_PBOOK + chip->resume = daca_resume; +#endif + + return 0; +} diff -Nru linux/sound/ppc/keywest.c linux-2.4.19-pre5-mjc/sound/ppc/keywest.c --- linux/sound/ppc/keywest.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/keywest.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,228 @@ +/* + * Keywest i2c code + * + * Copyright (c) by Takashi Iwai + * + * + * based on i2c-keywest.c from lm_sensors. + * Copyright (c) 2000 Philip Edelbrock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include "pmac.h" + +/* The Tumbler audio equalizer can be really slow sometimes */ +#define KW_POLL_SANITY 10000 + +/* address indices */ +#define KW_ADDR_MODE 0 +#define KW_ADDR_CONTROL 1 +#define KW_ADDR_STATUS 2 +#define KW_ADDR_ISR 3 +#define KW_ADDR_IER 4 +#define KW_ADDR_ADDR 5 +#define KW_ADDR_SUBADDR 6 +#define KW_ADDR_DATA 7 + +#define KW_I2C_ADDR(i2c,type) ((i2c)->base + (type) * (i2c)->steps) + +/* keywest needs a small delay to defuddle itself after changing a setting */ +inline static void keywest_writeb_wait(pmac_keywest_t *i2c, int addr, int value) +{ + writeb(value, KW_I2C_ADDR(i2c, addr)); + udelay(10); +} + +inline static void keywest_writeb(pmac_keywest_t *i2c, int addr, int value) +{ + writeb(value, KW_I2C_ADDR(i2c, addr)); +} + +inline unsigned char keywest_readb(pmac_keywest_t *i2c, int addr) +{ + return readb(KW_I2C_ADDR(i2c, addr)); +} + +static int keywest_poll_interrupt(pmac_keywest_t *i2c) +{ + int i, res; + for (i = 0; i < KW_POLL_SANITY; i++) { + udelay(100); + res = keywest_readb(i2c, KW_ADDR_ISR) & 0x0f; + if (res > 0) + return res; + } + + //snd_printd("Sanity check failed! Expected interrupt never happened.\n"); + return -ENODEV; +} + + +static void keywest_reset(pmac_keywest_t *i2c) +{ + int interrupt_state; + + /* Clear all past interrupts */ + interrupt_state = keywest_readb(i2c, KW_ADDR_ISR) & 0x0f; + if (interrupt_state > 0) + keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state); +} + +static int keywest_start(pmac_keywest_t *i2c, unsigned char cmd, int is_read) +{ + int interrupt_state; + int ack; + + keywest_reset(i2c); + + /* Set up address and r/w bit */ + keywest_writeb_wait(i2c, KW_ADDR_ADDR, (i2c->addr << 1) | (is_read ? 1 : 0)); + + /* Set up 'sub address' which I'm guessing is the command field? */ + keywest_writeb_wait(i2c, KW_ADDR_SUBADDR, cmd); + + /* Start sending address */ + keywest_writeb_wait(i2c, KW_ADDR_CONTROL, keywest_readb(i2c, KW_ADDR_CONTROL) | 2); + interrupt_state = keywest_poll_interrupt(i2c); + if (interrupt_state < 0) + return interrupt_state; + + ack = keywest_readb(i2c, KW_ADDR_STATUS) & 0x0f; + if ((ack & 0x02) == 0) { + snd_printd("Ack Status on addr expected but got: 0x%02x on addr: 0x%02x\n", ack, i2c->addr); + return -EINVAL; + } + return interrupt_state; +} + +/* exported */ +int snd_pmac_keywest_write(pmac_keywest_t *i2c, unsigned char cmd, int len, unsigned char *data) +{ + int interrupt_state; + int error_state = 0; + int i; + + snd_assert(len >= 1 && len <= 32, return -EINVAL); + + if ((interrupt_state = keywest_start(i2c, cmd, 0)) < 0) + return -EINVAL; + + for(i = 0; i < len; i++) { + keywest_writeb_wait(i2c, KW_ADDR_DATA, data[i]); + + /* Clear interrupt and go */ + keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state); + + interrupt_state = keywest_poll_interrupt(i2c); + if (interrupt_state < 0) { + error_state = -EINVAL; + interrupt_state = 0; + } + if ((keywest_readb(i2c, KW_ADDR_STATUS) & 0x02) == 0) { + snd_printd("Ack Expected by not received(block)!\n"); + error_state = -EINVAL; + } + } + + /* Send stop */ + keywest_writeb_wait(i2c, KW_ADDR_CONTROL, + keywest_readb(i2c, KW_ADDR_CONTROL) | 4); + + keywest_writeb_wait(i2c, KW_ADDR_CONTROL, interrupt_state); + + interrupt_state = keywest_poll_interrupt(i2c); + if (interrupt_state < 0) { + error_state = -EINVAL; + interrupt_state = 0; + } + keywest_writeb_wait(i2c, KW_ADDR_ISR, interrupt_state); + + return error_state; +} + +/* exported */ +void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c) +{ + if (i2c->base) { + iounmap((void*)i2c->base); + i2c->base = 0; + } +} + +/* exported */ +int snd_pmac_keywest_find(pmac_t *chip, pmac_keywest_t *i2c, int addr, + int (*init_client)(pmac_t *, pmac_keywest_t *)) +{ + struct device_node *i2c_device; + void **temp; + void *base = NULL; + u32 steps = 0; + + i2c_device = find_compatible_devices("i2c", "keywest"); + + if (i2c_device == 0) { + printk(KERN_ERR "pmac: No Keywest i2c devices found.\n"); + return -ENODEV; + } + + for (; i2c_device; i2c_device = i2c_device->next) { + snd_printd("Keywest device found: %s\n", i2c_device->full_name); + temp = (void **) get_property(i2c_device, "AAPL,address", NULL); + if (temp != NULL) { + base = *temp; + } else { + snd_printd("pmac: no 'address' prop!\n"); + continue; + } + + temp = (void **) get_property(i2c_device, "AAPL,address-step", NULL); + if (temp != NULL) { + steps = *(uint *)temp; + } else { + snd_printd("pmac: no 'address-step' prop!\n"); + continue; + } + + i2c->base = (unsigned long)ioremap((unsigned long)base, steps * 8); + i2c->steps = steps; + + /* Select standard sub mode + * + * ie for
... style transactions + */ + keywest_writeb_wait(i2c, KW_ADDR_MODE, 0x08); + + /* Enable interrupts */ + keywest_writeb_wait(i2c, KW_ADDR_IER, 1 + 2 + 4 + 8); + + keywest_reset(i2c); + + i2c->addr = addr; + if (init_client(chip, i2c) < 0) { + snd_pmac_keywest_cleanup(i2c); + continue; + } + + return 0; /* ok */ + } + + return -ENODEV; +} diff -Nru linux/sound/ppc/pmac.c linux-2.4.19-pre5-mjc/sound/ppc/pmac.c --- linux/sound/ppc/pmac.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/pmac.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1431 @@ +/* + * PMac DBDMA lowlevel functions + * + * Copyright (c) by Takashi Iwai + * code based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include "pmac.h" +#include +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS +#include +#else +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define pmu_suspend() /**/ +#define pmu_resume() /**/ +#endif + + +#define chip_t pmac_t + + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +static int snd_pmac_register_sleep_notifier(pmac_t *chip); +static int snd_pmac_unregister_sleep_notifier(pmac_t *chip); +static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state); +#endif + + +/* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */ +static int awacs_freqs[8] = { + 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 +}; +/* fixed frequency table for tumbler */ +static int tumbler_freqs[2] = { + 48000, 44100 +}; + +/* + * allocate DBDMA command arrays + */ +static int snd_pmac_dbdma_alloc(pmac_dbdma_t *rec, int size) +{ + rec->space = kmalloc(sizeof(struct dbdma_cmd) * (size + 1), GFP_KERNEL); + if (rec->space == NULL) + return -ENOMEM; + rec->size = size; + memset(rec->space, 0, sizeof(struct dbdma_cmd) * (size + 1)); + rec->cmds = (void*)DBDMA_ALIGN(rec->space); + rec->addr = virt_to_bus(rec->cmds); + return 0; +} + +static void snd_pmac_dbdma_free(pmac_dbdma_t *rec) +{ + if (rec && rec->space) + kfree(rec->space); +} + + +/* + * pcm stuff + */ + +/* + * look up frequency table + */ + +static unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate) +{ + int i, ok, found; + + ok = rec->cur_freqs; + if (rate > chip->freq_table[0]) + return 0; + found = 0; + for (i = 0; i < chip->num_freqs; i++, ok >>= 1) { + if (! (ok & 1)) continue; + found = i; + if (rate >= chip->freq_table[i]) + break; + } + return found; +} + +/* + * check whether another stream is active + */ +static inline int another_stream(int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; +} + +/* + * allocate buffers + */ +static int snd_pmac_pcm_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw_params) +{ + return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params)); +} + +/* + * release buffers + */ +static int snd_pmac_pcm_hw_free(snd_pcm_substream_t *subs) +{ + snd_pcm_lib_free_pages(subs); + return 0; +} + +/* + * get a stream of the opposite direction + */ +static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream) +{ + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + return &chip->playback; + case SNDRV_PCM_STREAM_CAPTURE: + return &chip->capture; + default: + snd_BUG(); + return NULL; + } +} + +/* + * wait while run status is on + */ +inline static void +snd_pmac_wait_ack(pmac_stream_t *rec) +{ + int timeout = 50000; + while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0) + udelay(1); +} + +/* + * set the format and rate to the chip. + * call the lowlevel function if defined (e.g. for AWACS). + */ +static void snd_pmac_pcm_set_format(pmac_t *chip) +{ + /* set up frequency and format */ + out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8)); + out_le32(&chip->awacs->byteswap, chip->format == SNDRV_PCM_FORMAT_S16_LE); + if (chip->set_format) + chip->set_format(chip); +} + +/* + * stop the DMA transfer + */ +inline static void snd_pmac_dma_stop(pmac_stream_t *rec) +{ + out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); + snd_pmac_wait_ack(rec); +} + +/* + * set the command pointer address + */ +inline static void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd) +{ + out_le32(&rec->dma->cmdptr, cmd->addr); +} + +/* + * start the DMA + */ +inline static void snd_pmac_dma_run(pmac_stream_t *rec, int status) +{ + out_le32(&rec->dma->control, status | (status << 16)); +} + + +/* + * prepare playback/capture stream + */ +static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + int i; + volatile struct dbdma_cmd *cp; + unsigned long flags; + snd_pcm_runtime_t *runtime = subs->runtime; + int rate_index; + long offset; + pmac_stream_t *astr; + + rec->dma_size = snd_pcm_lib_buffer_bytes(subs); + rec->period_size = snd_pcm_lib_period_bytes(subs); + rec->nperiods = rec->dma_size / rec->period_size; + rec->cur_period = 0; + rate_index = snd_pmac_rate_index(chip, rec, runtime->rate); + + /* set up constraints */ + astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); + snd_runtime_check(astr, return -EINVAL); + astr->cur_freqs = 1 << rate_index; + astr->cur_formats = 1 << runtime->format; + chip->rate_index = rate_index; + chip->format = runtime->format; + + /* We really want to execute a DMA stop command, after the AWACS + * is initialized. + * For reasons I don't understand, it stops the hissing noise + * common to many PowerBook G3 systems (like mine :-). + */ + spin_lock_irqsave(&chip->reg_lock, flags); + snd_pmac_dma_stop(rec); + if (rec->stream == SNDRV_PCM_STREAM_PLAYBACK) { + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); + } + offset = runtime->dma_addr; + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) { + st_le32(&cp->phy_addr, offset); + st_le16(&cp->req_count, rec->period_size); + /*st_le16(&cp->res_count, 0);*/ + st_le16(&cp->xfer_status, 0); + offset += rec->period_size; + } + /* make loop */ + st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); + st_le32(&cp->cmd_dep, rec->cmd.addr); + + snd_pmac_dma_stop(rec); + snd_pmac_dma_set_command(rec, &rec->cmd); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + + +/* + * stop beep if running (no spinlock!) + */ +static void snd_pmac_beep_stop(pmac_t *chip) +{ + pmac_beep_t *beep = chip->beep; + + if (beep && beep->running) { + beep->running = 0; + del_timer(&beep->timer); + snd_pmac_dma_stop(&chip->playback); + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + } +} + +/* + * PCM trigger/stop + */ +static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, + snd_pcm_substream_t *subs, int cmd) +{ + unsigned long flags; + volatile struct dbdma_cmd *cp; + int i, command; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (rec->running) + return -EBUSY; + command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ? + OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_pmac_beep_stop(chip); + snd_pmac_pcm_set_format(chip); + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) + out_le16(&cp->command, command); + snd_pmac_dma_set_command(rec, &rec->cmd); + (void)in_le32(&rec->dma->status); + snd_pmac_dma_run(rec, RUN|WAKE); + rec->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + spin_lock_irqsave(&chip->reg_lock, flags); + rec->running = 0; + /*printk("stopped!!\n");*/ + snd_pmac_dma_stop(rec); + for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) + out_le16(&cp->command, DBDMA_STOP); + spin_unlock_irqrestore(&chip->reg_lock, flags); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * return the current pointer + */ +inline +static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec, + snd_pcm_substream_t *subs) +{ + int count; + +#if 0 /* hmm.. how can we get the current dma pointer?? */ + if (! rec->cmd.cmds[rec->cur_period].xfer_status) { + count = in_le16(&rec->cmd.cmds[rec->cur_period].res_count); + count = rec->period_size - count; + } else +#endif + count = 0; + count += rec->cur_period * rec->period_size; + /*printk("pointer=%d\n", count);*/ + return bytes_to_frames(subs->runtime, count); +} + +/* + * playback + */ + +static int snd_pmac_playback_prepare(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_prepare(chip, &chip->playback, subs); +} + +static int snd_pmac_playback_trigger(snd_pcm_substream_t *subs, + int cmd) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd); +} + +static snd_pcm_uframes_t snd_pmac_playback_pointer(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_pointer(chip, &chip->playback, subs); +} + + +/* + * capture + */ + +static int snd_pmac_capture_prepare(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_prepare(chip, &chip->capture, subs); +} + +static int snd_pmac_capture_trigger(snd_pcm_substream_t *subs, + int cmd) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd); +} + +static snd_pcm_uframes_t snd_pmac_capture_pointer(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + return snd_pmac_pcm_pointer(chip, &chip->capture, subs); +} + + +/* + * update playback/capture pointer from interrupts + */ +static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec) +{ + volatile struct dbdma_cmd *cp; + int c; + int stat; + + spin_lock(&chip->reg_lock); + if (rec->running) { + cp = &rec->cmd.cmds[rec->cur_period]; + for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ + stat = ld_le16(&cp->xfer_status); + if (! (stat & ACTIVE)) + break; + /*printk("update frag %d\n", rec->cur_period);*/ + st_le16(&cp->xfer_status, 0); + st_le16(&cp->req_count, rec->period_size); + /*st_le16(&cp->res_count, 0);*/ + rec->cur_period++; + if (rec->cur_period >= rec->nperiods) { + rec->cur_period = 0; + cp = rec->cmd.cmds; + } else + cp++; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(rec->substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + + +/* + * hw info + */ + +static snd_pcm_hardware_t snd_pmac_playback = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_8000_44100, + rate_min: 7350, + rate_max: 44100, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 32768, + period_bytes_min: 256, + period_bytes_max: 16384, + periods_min: 1, + periods_max: PMAC_MAX_FRAGS, +}; + +static snd_pcm_hardware_t snd_pmac_capture = +{ + info: (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME), + formats: SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, + rates: SNDRV_PCM_RATE_8000_44100, + rate_min: 7350, + rate_max: 44100, + channels_min: 2, + channels_max: 2, + buffer_bytes_max: 32768, + period_bytes_min: 256, + period_bytes_max: 16384, + periods_min: 1, + periods_max: PMAC_MAX_FRAGS, +}; + + +#if 0 // NYI +static int snd_pmac_hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + pmac_t *chip = rule->private; + pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + int i, freq_table[8], num_freqs; + + snd_runtime_check(rec, return -EINVAL); + num_freqs = 0; + for (i = chip->num_freqs - 1; i >= 0; i--) { + if (rec->cur_freqs & (1 << i)) + freq_table[num_freqs++] = chip->freq_table[i]; + } + + return snd_interval_list(hw_param_interval(params, rule->var), + num_freqs, freq_table, 0); +} + +static int snd_pmac_hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + pmac_t *chip = rule->private; + pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + + snd_runtime_check(rec, return -EINVAL); + return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + rec->cur_formats); +} +#endif // NYI + +static int snd_pmac_pcm_open(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + int i, j, fflags; + static int typical_freqs[] = { + 48000, + 44100, + 22050, + 11025, + 0, + }; + static int typical_freq_flags[] = { + SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_44100, + SNDRV_PCM_RATE_22050, + SNDRV_PCM_RATE_11025, + 0, + }; + + /* look up frequency table and fill bit mask */ + runtime->hw.rates = 0; + fflags = chip->freqs_ok; + for (i = 0; typical_freqs[i]; i++) { + for (j = 0; j < chip->num_freqs; j++) { + if ((chip->freqs_ok & (1 << j)) && + chip->freq_table[j] == typical_freqs[i]) { + runtime->hw.rates |= typical_freq_flags[i]; + fflags &= ~(1 << j); + break; + } + } + } + if (fflags) /* rest */ + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + + /* check for minimum and maximum rates */ + for (i = 0; i < chip->num_freqs; i++) { + if (chip->freqs_ok & (1 << i)) { + runtime->hw.rate_max = chip->freq_table[i]; + break; + } + } + for (i = chip->num_freqs - 1; i >= 0; i--) { + if (chip->freqs_ok & (1 << i)) { + runtime->hw.rate_min = chip->freq_table[i]; + break; + } + } + runtime->hw.formats = chip->formats_ok; + if (chip->can_duplex) + runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + runtime->private_data = rec; + rec->substream = subs; + +#if 0 /* FIXME: still under development.. */ + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pmac_hw_rule_rate, chip, rec->stream, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pmac_hw_rule_format, chip, rec->stream, -1); +#endif + + runtime->hw.periods_max = rec->cmd.size - 1; + + if (chip->can_duplex) + snd_pcm_set_sync(subs); + + return 0; +} + +static int snd_pmac_pcm_close(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +{ + pmac_stream_t *astr; + + snd_pmac_dma_stop(rec); + + astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); + snd_runtime_check(astr, return -EINVAL); + + /* reset constraints */ + astr->cur_freqs = chip->freqs_ok; + astr->cur_formats = chip->formats_ok; + + return 0; +} + +static int snd_pmac_playback_open(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + subs->runtime->hw = snd_pmac_playback; + return snd_pmac_pcm_open(chip, &chip->playback, subs); +} + +static int snd_pmac_capture_open(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + subs->runtime->hw = snd_pmac_capture; + return snd_pmac_pcm_open(chip, &chip->capture, subs); +} + +static int snd_pmac_playback_close(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + return snd_pmac_pcm_close(chip, &chip->playback, subs); +} + +static int snd_pmac_capture_close(snd_pcm_substream_t *subs) +{ + pmac_t *chip = snd_pcm_substream_chip(subs); + + return snd_pmac_pcm_close(chip, &chip->capture, subs); +} + +/* + */ + +static snd_pcm_ops_t snd_pmac_playback_ops = { + open: snd_pmac_playback_open, + close: snd_pmac_playback_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_pmac_pcm_hw_params, + hw_free: snd_pmac_pcm_hw_free, + prepare: snd_pmac_playback_prepare, + trigger: snd_pmac_playback_trigger, + pointer: snd_pmac_playback_pointer, +}; + +static snd_pcm_ops_t snd_pmac_capture_ops = { + open: snd_pmac_capture_open, + close: snd_pmac_capture_close, + ioctl: snd_pcm_lib_ioctl, + hw_params: snd_pmac_pcm_hw_params, + hw_free: snd_pmac_pcm_hw_free, + prepare: snd_pmac_capture_prepare, + trigger: snd_pmac_capture_trigger, + pointer: snd_pmac_capture_pointer, +}; + +int __init snd_pmac_pcm_new(pmac_t *chip) +{ + snd_pcm_t *pcm; + int err; + int num_captures = 1; + + if (! chip->can_capture) + num_captures = 0; + err = snd_pcm_new(chip->card, chip->card->driver, 0, 1, num_captures, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pmac_playback_ops); + if (chip->can_capture) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; + if (chip->can_byte_swap) + chip->formats_ok |= SNDRV_PCM_FMTBIT_S16_LE; + + chip->playback.cur_formats = chip->formats_ok; + chip->capture.cur_formats = chip->formats_ok; + chip->playback.cur_freqs = chip->freqs_ok; + chip->capture.cur_freqs = chip->freqs_ok; + + return 0; +} + + + +/* + * beep stuff + */ + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static void snd_pmac_beep_stop_callback(unsigned long data) +{ + pmac_t *chip = snd_magic_cast(pmac_t, (void*)data,); + + spin_lock(&chip->reg_lock); + snd_pmac_beep_stop(chip); + snd_pmac_pcm_set_format(chip); + spin_unlock(&chip->reg_lock); +} + +/* because mksound callback takes no private argument, we must keep + the chip pointer here as static variable. + This means that only one chip can beep. Well, it's ok - + anyway we don't like hearing loud beeps from every chip + at the same time :) +*/ + +static pmac_t *beeping_chip = NULL; + +static void snd_pmac_mksound(unsigned int hz, unsigned int ticks) +{ + pmac_t *chip; + pmac_stream_t *rec; + pmac_beep_t *beep; + unsigned long flags; + int beep_speed = 0; + int srate; + int period, ncycles, nsamples; + int i, j, f; + short *p; + + if ((chip = beeping_chip) == NULL || (beep = chip->beep) == NULL) + return; + rec = &chip->playback; + + beep_speed = snd_pmac_rate_index(chip, rec, BEEP_SRATE); + srate = chip->freq_table[beep_speed]; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) { + /* this is a hack for broken X server code */ + hz = 750; + ticks = 12; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->playback.running || chip->capture.running || beep->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return; + } + beep->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + if (hz == beep->hz && beep->volume == beep->volume_play) { + nsamples = beep->nsamples; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep->buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep->volume; + j = (j + f) & 0xffff; + } + beep->hz = hz; + beep->volume_play = beep->volume; + beep->nsamples = nsamples; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + if (beep->running) { + if (ticks <= 0) + ticks = 1; + del_timer(&beep->timer); + beep->timer.expires = jiffies + ticks; + beep->timer.function = snd_pmac_beep_stop_callback; + beep->timer.data = (unsigned long)chip; + add_timer(&beep->timer); + snd_pmac_dma_stop(rec); + st_le16(&chip->extra_dma.cmds->req_count, nsamples * 4); + st_le16(&chip->extra_dma.cmds->xfer_status, 0); + st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr); + st_le32(&chip->extra_dma.cmds->phy_addr, beep->addr); + st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS); + out_le32(&chip->awacs->control, + (in_le32(&chip->awacs->control) & ~0x1f00) + | (beep_speed << 8)); + out_le32(&chip->awacs->byteswap, 0); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); + } + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * beep volume mixer + */ +static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_get_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + snd_runtime_check(chip->beep, return -ENXIO); + ucontrol->value.integer.value[0] = chip->beep->volume; + return 0; +} + +static int snd_pmac_put_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int oval; + snd_runtime_check(chip->beep, return -ENXIO); + oval = chip->beep->volume; + chip->beep->volume = ucontrol->value.integer.value[0]; + return oval != chip->beep->volume; +} + +static snd_kcontrol_new_t snd_pmac_beep_mixer = { + iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Beep Playback Volume", + index: 0, + info: snd_pmac_info_beep, + get: snd_pmac_get_beep, + put: snd_pmac_put_beep, +}; + +static void snd_pmac_beep_free(snd_kcontrol_t *control) +{ + pmac_t *chip = snd_magic_cast(pmac_t, _snd_kcontrol_chip(control),); + if (chip->beep) { + /* restore */ + kd_mksound = chip->beep->orig_mksound; + kfree(chip->beep->buf); + kfree(chip->beep); + chip->beep = NULL; + } +} + +/* Initialize beep stuff */ +int __init snd_pmac_attach_beep(pmac_t *chip) +{ + pmac_beep_t *beep; + int err; + + beep = kmalloc(sizeof(*beep), GFP_KERNEL); + if (! beep) + return -ENOMEM; + + beep->buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (! beep->buf) { + kfree(beep); + return -ENOMEM; + } + beep->addr = virt_to_bus(beep->buf); + init_timer(&beep->timer); + beep->timer.function = snd_pmac_beep_stop_callback; + beep->timer.data = (unsigned long) chip; + beep->orig_mksound = kd_mksound; + beep->volume = BEEP_VOLUME; + beep->running = 0; + beep->control = snd_ctl_new1(&snd_pmac_beep_mixer, chip); + if (beep->control == NULL) { + kfree(beep); + return -ENOMEM; + } + beep->control->private_free = snd_pmac_beep_free; + if ((err = snd_ctl_add(chip->card, beep->control)) < 0) { + kfree(beep); + return err; + } + + /* hook */ + beeping_chip = chip; + chip->beep = beep; + kd_mksound = snd_pmac_mksound; + + return 0; +} + +static void snd_pmac_dbdma_reset(pmac_t *chip) +{ + out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); + snd_pmac_wait_ack(&chip->playback); + out_le32(&chip->capture.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); + snd_pmac_wait_ack(&chip->capture); +} + + +/* + * interrupt handlers + */ +static void +snd_pmac_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + snd_pmac_pcm_update(chip, &chip->playback); +} + + +static void +snd_pmac_rx_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + snd_pmac_pcm_update(chip, &chip->capture); +} + + +static void +snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + int ctrl = in_le32(&chip->awacs->control); + + printk("pmac: control interrupt.. 0x%x\n", ctrl); + if (ctrl & MASK_PORTCHG) { + /* do something when headphone is plugged/unplugged? */ + if (chip->port_change) + chip->port_change(chip); + } + if (ctrl & MASK_CNTLERR) { + int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16; + if (err && chip->model <= PMAC_SCREAMER) + snd_printk("error %x\n", err); + } + /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ + out_le32(&chip->awacs->control, ctrl); +} + + +/* + * feature + */ +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) +static void snd_pmac_sound_feature(pmac_t *chip, int enable) +{ +#ifdef CONFIG_PPC_HAS_FEATURE_CALLS + ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); +#else + if (chip->is_pbook_G3) { + pmu_suspend(); + feature_clear(chip->node, FEATURE_Sound_power); + feature_clear(chip->node, FEATURE_Sound_CLK_enable); + mdelay(1000); /* XXX */ + pmu_resume(); + } + if (chip->is_pbook_3400) { + feature_set(chip->node, FEATURE_IOBUS_enable); + udelay(10); + } +#endif +} +#else /* CONFIG_PM && CONFIG_PMAC_PBOOK */ +#define snd_pmac_sound_feature(chip,enable) /**/ +#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ + +/* + * release resources + */ + +static int snd_pmac_free(pmac_t *chip) +{ + /* stop sounds */ + if (chip->initialized) { + snd_pmac_dbdma_reset(chip); + /* disable interrupts from awacs interface */ + out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff); + } + + snd_pmac_sound_feature(chip, 0); +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + snd_pmac_unregister_sleep_notifier(chip); +#endif + + /* clean up mixer if any */ + if (chip->mixer_free) + chip->mixer_free(chip); + + /* release resources */ + if (chip->irq >= 0) + free_irq(chip->irq, (void*)chip); + if (chip->tx_irq >= 0) + free_irq(chip->tx_irq, (void*)chip); + if (chip->rx_irq >= 0) + free_irq(chip->rx_irq, (void*)chip); + snd_pmac_dbdma_free(&chip->playback.cmd); + snd_pmac_dbdma_free(&chip->capture.cmd); + snd_pmac_dbdma_free(&chip->extra_dma); + if (chip->macio_base) + iounmap(chip->macio_base); + if (chip->latch_base) + iounmap(chip->latch_base); + if (chip->awacs) + iounmap((void*)chip->awacs); + if (chip->playback.dma) + iounmap((void*)chip->playback.dma); + if (chip->capture.dma) + iounmap((void*)chip->capture.dma); + snd_magic_kfree(chip); + return 0; +} + + +/* + * free the device + */ +static int snd_pmac_dev_free(snd_device_t *device) +{ + pmac_t *chip = snd_magic_cast(pmac_t, device->device_data, return -ENXIO); + return snd_pmac_free(chip); +} + + +/* + * detect a sound chip + */ +static int __init snd_pmac_detect(pmac_t *chip) +{ + struct device_node *sound; + unsigned int *prop, l; + + if (_machine != _MACH_Pmac) + return -ENODEV; + + chip->subframe = 0; + chip->revision = 0; + chip->freqs_ok = 0xff; /* all ok */ + chip->model = PMAC_AWACS; + chip->can_byte_swap = 1; + chip->can_duplex = 1; + chip->can_capture = 1; + chip->num_freqs = 8; + chip->freq_table = awacs_freqs; + + chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ + + /* it seems the Pismo & iBook can't byte-swap in hardware. */ + if (machine_is_compatible("PowerBook3,1") || + machine_is_compatible("PowerBook2,1")) + chip->can_byte_swap = 0 ; + if (machine_is_compatible("PowerBook2,1")) + chip->can_duplex = 0; + + /* check machine type */ + if (machine_is_compatible("AAPL,3400/2400") + || machine_is_compatible("AAPL,3500")) + chip->is_pbook_3400 = 1; + else if (machine_is_compatible("PowerBook1,1") + || machine_is_compatible("AAPL,PowerBook1998")) + chip->is_pbook_G3 = 1; + chip->node = find_devices("awacs"); + if (chip->node) + return 0; /* ok */ + + /* + * powermac G3 models have a node called "davbus" + * with a child called "sound". + */ + chip->node = find_devices("davbus"); + /* + * if we didn't find a davbus device, try 'i2s-a' since + * this seems to be what iBooks have + */ + if (! chip->node) + chip->node = find_devices("i2s-a"); + if (! chip->node) + return -ENODEV; + sound = find_devices("sound"); + while (sound && sound->parent != chip->node) + sound = sound->next; + if (! sound) + return -ENODEV; + prop = (unsigned int *) get_property(sound, "sub-frame", 0); + if (prop && *prop < 16) + chip->subframe = *prop; + /* This should be verified on older screamers */ + if (device_is_compatible(sound, "screamer")) { + chip->model = PMAC_SCREAMER; + chip->can_byte_swap = 0; + } + if (device_is_compatible(sound, "burgundy")) { + chip->model = PMAC_BURGUNDY; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + if (device_is_compatible(sound, "daca")) { + chip->model = PMAC_DACA; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + chip->can_byte_swap = 0; /* no le support */ + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + if (device_is_compatible(sound, "tumbler")) { + chip->model = PMAC_TUMBLER; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + chip->can_byte_swap = 0; /* no le support */ + chip->num_freqs = 2; + chip->freq_table = tumbler_freqs; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } + prop = (unsigned int *)get_property(sound, "device-id", 0); + if (prop) + chip->device_id = *prop; + chip->has_iic = (find_devices("perch") != NULL); + + /* look for a property saying what sample rates + are available */ + prop = (unsigned int *) get_property(sound, "sample-rates", &l); + if (! prop) + prop = (unsigned int *) get_property(sound, "output-frame-rates", &l); + if (prop) { + int i; + chip->freqs_ok = 0; + for (l /= sizeof(int); l > 0; --l) { + unsigned int r = *prop++; + /* Apple 'Fixed' format */ + if (r >= 0x10000) + r >>= 16; + for (i = 0; i < chip->num_freqs; ++i) { + if (r == chip->freq_table[i]) { + chip->freqs_ok |= (1 << i); + break; + } + } + } + } else { + /* assume only 44.1khz */ + chip->freqs_ok = 1; + } + return 0; +} + + +/* + * create and detect a pmac chip record + */ +int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) +{ + pmac_t *chip; + struct device_node *np; + int err; + static snd_device_ops_t ops = { + dev_free: snd_pmac_dev_free, + }; + + snd_runtime_check(chip_return, return -EINVAL); + *chip_return = NULL; + + chip = snd_magic_kcalloc(pmac_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->card = card; + + spin_lock_init(&chip->reg_lock); + chip->irq = chip->tx_irq = chip->rx_irq = -1; + + chip->playback.stream = SNDRV_PCM_STREAM_PLAYBACK; + chip->capture.stream = SNDRV_PCM_STREAM_CAPTURE; + + if ((err = snd_pmac_detect(chip)) < 0) + goto __error; + + if (snd_pmac_dbdma_alloc(&chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || + snd_pmac_dbdma_alloc(&chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || + snd_pmac_dbdma_alloc(&chip->extra_dma, 2) < 0) { + err = -ENOMEM; + goto __error; + } + + np = chip->node; + if (np->n_addrs < 3 || np->n_intrs < 3) { + err = -ENODEV; + goto __error; + } + + chip->awacs = (volatile struct awacs_regs *) ioremap(np->addrs[0].address, 0x1000); + chip->playback.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[1].address, 0x100); + chip->capture.dma = (volatile struct dbdma_regs *) ioremap(np->addrs[2].address, 0x100); + if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0, + "PMac", (void*)chip)) { + snd_printk("unable to grab IRQ %d\n", np->intrs[0].line); + err = -EBUSY; + goto __error; + } + chip->irq = np->intrs[0].line; + if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0, + "PMac Output", (void*)chip)) { + snd_printk("unable to grab IRQ %d\n", np->intrs[1].line); + err = -EBUSY; + goto __error; + } + chip->tx_irq = np->intrs[1].line; + if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0, + "PMac Input", (void*)chip)) { + snd_printk("unable to grab IRQ %d\n", np->intrs[2].line); + err = -EBUSY; + goto __error; + } + chip->rx_irq = np->intrs[2].line; + + snd_pmac_sound_feature(chip, 1); + + /* reset */ + out_le32(&chip->awacs->control, 0x11); + + /* Powerbooks have odd ways of enabling inputs such as + an expansion-bay CD or sound from an internal modem + or a PC-card modem. */ + if (chip->is_pbook_3400) { + /* Enable CD and PC-card sound inputs. */ + /* This is done by reading from address + * f301a000, + 0x10 to enable the expansion-bay + * CD sound input, + 0x80 to enable the PC-card + * sound input. The 0x100 enables the SCSI bus + * terminator power. + */ + chip->latch_base = (unsigned char *) ioremap (0xf301a000, 0x1000); + in_8(chip->latch_base + 0x190); + } else if (chip->is_pbook_G3) { + struct device_node* mio; + for (mio = chip->node->parent; mio; mio = mio->parent) { + if (strcmp(mio->name, "mac-io") == 0 + && mio->n_addrs > 0) { + chip->macio_base = (unsigned char *) ioremap + (mio->addrs[0].address, 0x40); + break; + } + } + /* Enable CD sound input. */ + /* The relevant bits for writing to this byte are 0x8f. + * I haven't found out what the 0x80 bit does. + * For the 0xf bits, writing 3 or 7 enables the CD + * input, any other value disables it. Values + * 1, 3, 5, 7 enable the microphone. Values 0, 2, + * 4, 6, 8 - f enable the input from the modem. + */ + if (chip->macio_base) + out_8(chip->macio_base + 0x37, 3); + } + + /* Reset dbdma channels */ + snd_pmac_dbdma_reset(chip); + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + /* add sleep notifier */ + snd_pmac_register_sleep_notifier(chip); + card->set_power_state = snd_pmac_set_power_state; + card->power_state_private_data = chip; +#endif + + chip->initialized = 1; + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __error; + + *chip_return = chip; + return 0; + + __error: + snd_pmac_free(chip); + return err; +} + + +/* + * sleep notify for powerbook + */ + +#if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) + +/* + * Save state when going to sleep, restore it afterwards. + */ + +static void snd_pmac_suspend(pmac_t *chip, int can_schedule) +{ + unsigned long flags; + snd_card_t *card = chip->card; + + snd_power_lock(card, can_schedule); + if (card->power_state == SNDRV_CTL_POWER_D3hot) + goto __skip; + + if (chip->suspend) + chip->suspend(chip); + snd_pcm_suspend_all(chip->pcm); + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->beep && chip->beep->running) + snd_pmac_beep_stop(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + disable_irq(chip->irq); + disable_irq(chip->tx_irq); + disable_irq(chip->rx_irq); + snd_pmac_sound_feature(chip, 0); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + __skip: + snd_power_unlock(card); +} + +static void snd_pmac_resume(pmac_t *chip, int can_schedule) +{ + snd_card_t *card = chip->card; + + snd_power_lock(card, can_schedule); + if (card->power_state == SNDRV_CTL_POWER_D0) + goto __skip; + + snd_pmac_sound_feature(chip, 1); + if (chip->resume) + chip->resume(chip); + /* enable CD sound input */ + if (chip->macio_base && chip->is_pbook_G3) { + out_8(chip->macio_base + 0x37, 3); + } else if (chip->is_pbook_3400) { + in_8(chip->latch_base + 0x190); + } + + snd_pmac_pcm_set_format(chip); + + enable_irq(chip->irq); + enable_irq(chip->tx_irq); + enable_irq(chip->rx_irq); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + __skip: + snd_power_unlock(card); +} + +/* the chip is stored statically by snd_pmac_register_sleep_notifier + * because we can't have any private data for notify callback. + */ +static pmac_t *sleeping_pmac = NULL; + +static int snd_pmac_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + pmac_t *chip; + + chip = sleeping_pmac; + snd_runtime_check(chip, return 0); + + switch (when) { + case PBOOK_SLEEP_NOW: + snd_pmac_suspend(chip, 0); + break; + case PBOOK_WAKE: + snd_pmac_resume(chip, 0); + break; + } + return PBOOK_SLEEP_OK; +} + +static struct pmu_sleep_notifier snd_pmac_sleep_notifier = { + snd_pmac_sleep_notify, SLEEP_LEVEL_SOUND, +}; + +static int __init snd_pmac_register_sleep_notifier(pmac_t *chip) +{ + /* should be protected here.. */ + if (sleeping_pmac) { + snd_printd("sleep notifier already reigistered\n"); + return -EBUSY; + } + sleeping_pmac = chip; + pmu_register_sleep_notifier(&snd_pmac_sleep_notifier); + chip->sleep_registered = 1; + return 0; +} + +static int snd_pmac_unregister_sleep_notifier(pmac_t *chip) +{ + if (! chip->sleep_registered) + return 0; + /* should be protected here.. */ + if (sleeping_pmac != chip) + return -ENODEV; + pmu_unregister_sleep_notifier(&snd_pmac_sleep_notifier); + sleeping_pmac = NULL; + return 0; +} + +/* callback */ +static int snd_pmac_set_power_state(snd_card_t *card, unsigned int power_state) +{ + pmac_t *chip = snd_magic_cast(pmac_t, card->power_state_private_data, return -ENXIO); + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_pmac_resume(chip, 1); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_pmac_suspend(chip, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ diff -Nru linux/sound/ppc/pmac.h linux-2.4.19-pre5-mjc/sound/ppc/pmac.h --- linux/sound/ppc/pmac.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/pmac.h Mon Apr 8 22:31:23 2002 @@ -0,0 +1,220 @@ +/* + * Driver for PowerMac onboard soundchips + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __PMAC_H +#define __PMAC_H + +#include +#include +#include "awacs.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#include +#include +#include +#else /* 2.4.0 kernel */ +#include +#ifdef CONFIG_ADB_CUDA +#include +#endif +#ifdef CONFIG_ADB_PMU +#include +#endif +#endif +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) || defined(CONFIG_ADB_CUDA) +#define PMAC_AMP_AVAIL +#endif + +/* maximum number of fragments */ +#define PMAC_MAX_FRAGS 32 + + +/* + * typedefs + */ +typedef struct snd_pmac pmac_t; +typedef struct snd_pmac_stream pmac_stream_t; +typedef struct snd_pmac_beep pmac_beep_t; +typedef struct snd_pmac_dbdma pmac_dbdma_t; + + +/* + * DBDMA space + */ +struct snd_pmac_dbdma { + unsigned long addr; + struct dbdma_cmd *cmds; + void *space; + int size; +}; + +/* + * playback/capture stream + */ +struct snd_pmac_stream { + int running; /* boolean */ + + int stream; /* PLAYBACK/CAPTURE */ + + int dma_size; /* in bytes */ + int period_size; /* in bytes */ + int buffer_size; /* in kbytes */ + int nperiods, cur_period; + + pmac_dbdma_t cmd; + volatile struct dbdma_regs *dma; + + snd_pcm_substream_t *substream; + + unsigned int cur_freqs; /* currently available frequences */ + unsigned int cur_formats; /* currently available formats */ +}; + + +/* + * beep using pcm + */ +struct snd_pmac_beep { + int running; /* boolean */ + int volume; /* mixer volume: 0-100 */ + int volume_play; /* currently playing volume */ + int hz; + int nsamples; + short *buf; /* allocated wave buffer */ + unsigned long addr; /* physical address of buffer */ + struct timer_list timer; /* timer list for stopping beep */ + void (*orig_mksound)(unsigned int, unsigned int); + /* pointer to restore */ + snd_kcontrol_t *control; /* mixer element */ +}; + + +/* + */ + +enum snd_pmac_model { + PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER +}; + +struct snd_pmac { + snd_card_t *card; + + /* h/w info */ + struct device_node *node; + unsigned int revision; + unsigned int subframe; + unsigned int device_id; + enum snd_pmac_model model; + + unsigned int has_iic : 1; + unsigned int is_pbook_3400 : 1; + unsigned int is_pbook_G3 : 1; + + unsigned int can_byte_swap : 1; + unsigned int can_duplex : 1; + unsigned int can_capture : 1; + +#ifdef PMAC_AMP_AVAIL + unsigned int amp_only; + int amp_vol[2]; +#endif + + unsigned int initialized : 1; + unsigned int feature_is_set : 1; + + int num_freqs; + int *freq_table; + unsigned int freqs_ok; /* bit flags */ + unsigned int formats_ok; /* pcm hwinfo */ + int active; + int rate_index; + int format; /* current format */ + + spinlock_t reg_lock; + volatile struct awacs_regs *awacs; + int awacs_reg[8]; /* register cache */ + + unsigned char *latch_base; + unsigned char *macio_base; + + pmac_stream_t playback; + pmac_stream_t capture; + + pmac_dbdma_t extra_dma; + + int irq, tx_irq, rx_irq; + + snd_pcm_t *pcm; + + pmac_beep_t *beep; + + unsigned int control_mask; /* control mask */ + + /* mixer stuffs */ + void *mixer_data; + void (*mixer_free)(pmac_t *); + + /* lowlevel callbacks */ + void (*set_format)(pmac_t *chip); + void (*port_change)(pmac_t *chip); +#ifdef CONFIG_PMAC_PBOOK + unsigned int sleep_registered : 1; + void (*suspend)(pmac_t *chip); + void (*resume)(pmac_t *chip); +#endif + +}; + + +/* exported functions */ +int snd_pmac_new(snd_card_t *card, pmac_t **chip_return); +int snd_pmac_pcm_new(pmac_t *chip); +int snd_pmac_attach_beep(pmac_t *chip); + +/* initialize mixer */ +int snd_pmac_awacs_init(pmac_t *chip); +int snd_pmac_burgundy_init(pmac_t *chip); +int snd_pmac_daca_init(pmac_t *chip); +int snd_pmac_tumbler_init(pmac_t *chip); + +/* i2c functions */ +typedef struct snd_pmac_keywest { + unsigned long base; + int addr; + int steps; +} pmac_keywest_t; + +int snd_pmac_keywest_find(pmac_t *chip, pmac_keywest_t *i2c, int addr, int (*init_client)(pmac_t *, pmac_keywest_t *)); +void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c); +int snd_pmac_keywest_write(pmac_keywest_t *i2c, unsigned char cmd, int len, unsigned char *data); +inline static int snd_pmac_keywest_write_byte(pmac_keywest_t *i2c, unsigned char cmd, unsigned char data) +{ + return snd_pmac_keywest_write(i2c, cmd, 1, &data); +} + + +#endif /* __PMAC_H */ diff -Nru linux/sound/ppc/powermac.c linux-2.4.19-pre5-mjc/sound/ppc/powermac.c --- linux/sound/ppc/powermac.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/powermac.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,187 @@ +/* + * Driver for PowerMac AWACS + * Copyright (c) 2001 by Takashi Iwai + * based on dmasound.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#define SNDRV_GET_ID +#include +#include "pmac.h" +#include "awacs.h" +#include "burgundy.h" + +#define CHIP_NAME "PMac" + +EXPORT_NO_SYMBOLS; +MODULE_DESCRIPTION("PowerMac"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Apple,PowerMac}}"); +MODULE_LICENSE("GPL"); + +static int snd_index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *snd_id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static int snd_enable = 1; +static int snd_enable_beep = 1; + +MODULE_PARM(snd_index, "i"); +MODULE_PARM_DESC(snd_index, "Index value for " CHIP_NAME " soundchip."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "s"); +MODULE_PARM_DESC(snd_id, "ID string for " CHIP_NAME " soundchip."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "i"); +MODULE_PARM_DESC(snd_enable, "Enable this soundchip."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_enable_beep, "i"); +MODULE_PARM_DESC(snd_enable_beep, "Enable beep using PCM."); +MODULE_PARM_SYNTAX(snd_enable_beep, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); + + +/* + * card entry + */ + +static snd_card_t *snd_pmac_card = NULL; + +/* + */ + +static int __init snd_pmac_probe(void) +{ + snd_card_t *card; + pmac_t *chip; + char *name_ext; + int err; + + card = snd_card_new(snd_index, snd_id, THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_pmac_new(card, &chip)) < 0) + goto __error; + + switch (chip->model) { + case PMAC_BURGUNDY: + strcpy(card->driver, "PMac Burgundy"); + strcpy(card->shortname, "PowerMac Burgundy"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_burgundy_init(chip)) < 0) + goto __error; + break; + case PMAC_DACA: + strcpy(card->driver, "PMac DACA"); + strcpy(card->shortname, "PowerMac DACA"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_daca_init(chip)) < 0) + goto __error; + break; + case PMAC_TUMBLER: + strcpy(card->driver, "PMac Tumbler"); + strcpy(card->shortname, "PowerMac Tumbler"); + sprintf(card->longname, "%s (Dev %d) Sub-frame %d", + card->shortname, chip->device_id, chip->subframe); + if ((err = snd_pmac_tumbler_init(chip)) < 0) + goto __error; + break; + case PMAC_AWACS: + case PMAC_SCREAMER: + name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS"; + sprintf(card->driver, "PMac %s", name_ext); + sprintf(card->shortname, "PowerMac %s", name_ext); + if (chip->is_pbook_3400) + name_ext = " [PB3400]"; + else if (chip->is_pbook_G3) + name_ext = " [PBG3]"; + else + name_ext = ""; + sprintf(card->longname, "%s%s Rev %d", + card->shortname, name_ext, chip->revision); + if ((err = snd_pmac_awacs_init(chip)) < 0) + goto __error; + break; + default: + snd_printk("unsupported hardware %d\n", chip->model); + err = -EINVAL; + goto __error; + } + + if ((err = snd_pmac_pcm_new(chip)) < 0) + goto __error; + if (snd_enable_beep) + snd_pmac_attach_beep(chip); + + if ((err = snd_card_register(card)) < 0) + goto __error; + + snd_pmac_card = card; + return 0; + +__error: + snd_card_free(card); + return err; +} + + +/* + * MODULE sutff + */ + +static int __init alsa_card_pmac_init(void) +{ + int err; + if ((err = snd_pmac_probe() < 0)) { +#ifdef MODULE + printk(KERN_ERR "no PMac soundchip found\n"); +#endif + return err; + } + return 0; + +} + +static void __exit alsa_card_pmac_exit(void) +{ + if (snd_pmac_card) + snd_card_free(snd_pmac_card); +} + +module_init(alsa_card_pmac_init) +module_exit(alsa_card_pmac_exit) + +#ifndef MODULE + +/* format is: snd-pmac=snd_enable,snd_index,snd_id,snd_enable_beep + */ + +static int __init alsa_card_pmac_setup(char *str) +{ + (void)(get_option(&str,&snd_enable) == 2 && + get_option(&str,&snd_index) == 2 && + get_id(&str,&snd_id) == 2 && + get_option(&str,&snd_enable_beep) == 2 + ); + return 1; +} + +__setup("snd-pmac=", alsa_card_pmac_setup); + +#endif /* ifndef MODULE */ diff -Nru linux/sound/ppc/tumbler.c linux-2.4.19-pre5-mjc/sound/ppc/tumbler.c --- linux/sound/ppc/tumbler.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/ppc/tumbler.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,479 @@ +/* + * PMac Tumbler lowlevel functions + * + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define __NO_VERSION__ +#include +#include +#include +#include +#include "pmac.h" + +// #define TUMBLER_TONE_CONTROL_SUPPORT + +#define chip_t pmac_t + +/* i2c address for tumbler */ +#define TAS_I2C_ADDR 0x34 + +/* registers */ +#define TAS_REG_MCS 0x01 +#define TAS_REG_VOL 0x04 +#define TAS_VOL_MAX ((1<<20) - 1) + +#define TAS_REG_TREBLE 0x05 +#define TAS_VOL_MAX_TREBLE 0x96 /* 1 = max, 0x96 = min */ +#define TAS_REG_BASS 0x06 +#define TAS_VOL_MAX_BASS 0x86 /* 1 = max, 0x86 = min */ + +#define TAS_MIXER_VOL_MAX 500 + +typedef struct pmac_tumber_t { + pmac_keywest_t i2c; + void *amp_mute; + void *headphone_mute; + void *headphone_status; + int headphone_irq; + int left_vol, right_vol; + int bass_vol, treble_vol; +} pmac_tumbler_t; + + +/* + * initialize / detect tumbler + */ +static int tumbler_init_client(pmac_t *chip, pmac_keywest_t *i2c) +{ + /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */ + return snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS, + (1<<6)+(2<<4)+(2<<2)+0); +} + +/* + * update volume + */ +static int tumbler_set_volume(pmac_tumbler_t *mix) +{ + unsigned char block[6]; + unsigned int left_vol, right_vol; + + if (! mix->i2c.base) + return -ENODEV; + + left_vol = mix->left_vol << 6; + right_vol = mix->right_vol << 6; + + if (left_vol > TAS_VOL_MAX) + left_vol = TAS_VOL_MAX; + if (right_vol > TAS_VOL_MAX) + right_vol = TAS_VOL_MAX; + + block[0] = (left_vol >> 16) & 0xff; + block[1] = (left_vol >> 8) & 0xff; + block[2] = (left_vol >> 0) & 0xff; + + block[3] = (right_vol >> 16) & 0xff; + block[4] = (right_vol >> 8) & 0xff; + block[5] = (right_vol >> 0) & 0xff; + + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_VOL, 6, block) < 0) { + snd_printk("failed to set volume \n"); + return -EINVAL; + } + return 0; +} + + +/* output volume */ +static int tumbler_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TAS_MIXER_VOL_MAX; + return 0; +} + +static int tumbler_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->left_vol; + ucontrol->value.integer.value[1] = mix->right_vol; + return 0; +} + +static int tumbler_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->left_vol != ucontrol->value.integer.value[0] || + mix->right_vol != ucontrol->value.integer.value[1]; + if (change) { + mix->left_vol = ucontrol->value.integer.value[0]; + mix->right_vol = ucontrol->value.integer.value[1]; + tumbler_set_volume(mix); + } + return change; +} + + +#ifdef TUMBLER_TONE_CONTROL_SUPPORT +static int tumbler_set_bass(pmac_tumbler_t *mix) +{ + unsigned char data; + int val; + + if (! mix->i2c.base) + return -ENODEV; + + val = TAS_VOL_MAX_BASS - mix->bass_vol + 1; + if (val < 1) + data = 1; + else if (val > TAS_VOL_MAX_BASS) + data = TAS_VOL_MAX_BASS; + else + data = val; + if (snd_pmac_keywest_write(&mix->i2c TAS_REG_BASS, 1, &data) < 0) { + snd_printk("failed to set bass volume\n"); + return -EINVAL; + } + return 0; +} + +static int tumbler_set_treble(pmac_tumbler_t *mix) +{ + unsigned char data; + int val; + + if (! mix->i2c.base) + return -ENODEV; + + val = TAS_VOL_MAX_TREBLE - mix->treble_vol + 1; + if (val < 1) + data = 1; + else if (val > TAS_VOL_MAX_BASS) + data = TAS_VOL_MAX_BASS; + else + data = val; + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_TREBLE, 1, &data) < 0) { + snd_printk("failed to set treble volume\n"); + return -EINVAL; + } + return 0; +} + +/* bass volume */ +static int tumbler_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TAS_VOL_MAX_BASS - 1; + return 0; +} + +static int tumbler_get_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->bass_vol; + return 0; +} + +static int tumbler_put_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->bass_vol != ucontrol->value.integer.value[0]; + if (change) { + mix->bass_vol = ucontrol->value.integer.value[0]; + tumbler_set_bass(mix); + } + return change; +} + +static int tumbler_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TAS_VOL_MAX_TREBLE - 1; + return 0; +} + +static int tumbler_get_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->treble_vol; + return 0; +} + +static int tumbler_put_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->treble_vol != ucontrol->value.integer.value[0]; + if (change) { + mix->treble_vol = ucontrol->value.integer.value[0]; + tumbler_set_treble(mix); + } + return change; +} + +#endif + + +static snd_kcontrol_new_t tumbler_mixers[] = { + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Master Playback Volume", + info: tumbler_info_volume, + get: tumbler_get_volume, + put: tumbler_put_volume + }, +#ifdef TUMBLER_TONE_CONTROL_SUPPORT + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Tone Control - Bass", + info: tumbler_info_bass, + get: tumbler_get_bass, + put: tumbler_put_bass + }, + { iface: SNDRV_CTL_ELEM_IFACE_MIXER, + name: "Tone Control - Treble", + info: tumbler_info_treble, + get: tumbler_get_treble, + put: tumbler_put_treble + }, +#endif +}; + +#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) + +/* mute either amp or headphone according to the plug status */ +static void tumbler_update_headphone(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + + if (! mix) + return; + + if (readb(mix->headphone_status) & 2) { + writeb(4 + 1, mix->amp_mute); + writeb(4 + 0, mix->headphone_mute); + } else { + writeb(4 + 0, mix->amp_mute); + writeb(4 + 1, mix->headphone_mute); + } +} + +/* interrupt - headphone plug changed */ +static void headphone_intr(int irq, void *devid, struct pt_regs *regs) +{ + pmac_t *chip = snd_magic_cast(pmac_t, devid, return); + tumbler_update_headphone(chip); +} + +/* look for audio-gpio device */ +static struct device_node *find_audio_device(const char *name) +{ + struct device_node *np; + + if (! (np = find_devices("gpio"))) + return NULL; + + for (np = np->child; np; np = np->sibling) { + char *property = get_property(np, "audio-gpio", NULL); + if (property && strcmp(property, name) == 0) + return np; + } + return NULL; +} + +/* find an audio device and get its address */ +static unsigned long tumbler_find_device(const char *device) +{ + struct device_node *node; + void *base; + + node = find_audio_device(device); + if (! node) { + snd_printd("cannot find device %s\n", device); + return 0; + } + + base = (void *)get_property(node, "AAPL,address", NULL); + if (! base) { + snd_printd("cannot find address for device %s\n", device); + return 0; + } + + return *(unsigned long *)base; +} + +/* reset audio */ +static int tumbler_reset_audio(pmac_t *chip) +{ + unsigned long base; + void *map; + + if (! (base = tumbler_find_device("audio-hw-reset"))) + return -ENODEV; + + map = ioremap(base, 1); + writeb(5, map); + mdelay(100); + writeb(4, map); + mdelay(1); + writeb(5, map); + mdelay(1); + iounmap(map); + return 0; +} + +#ifdef CONFIG_PMAC_PBOOK +/* resume mixer */ +static void tumbler_resume(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + tumbler_reset_audio(chip); + snd_pmac_keywest_write_byte(&mix->i2c, TAS_REG_MCS, + (1<<6)+(2<<4)+(2<<2)+0); + tumbler_set_volume(mix); + tumbler_update_headphone(chip); /* update mute */ +} +#endif + +/* initialize tumbler */ +static int __init tumbler_init(pmac_t *chip) +{ + unsigned long base; + struct device_node *node; + int err; + pmac_tumbler_t *mix = chip->mixer_data; + + snd_assert(mix, return -EINVAL); + + /* reset audio */ + if (tumbler_reset_audio(chip) < 0) + return -ENODEV; + + /* get amp-mute */ + if (! (base = tumbler_find_device("amp-mute"))) + return -ENODEV; + mix->amp_mute = ioremap(base, 1); + if (! (base = tumbler_find_device("headphone-mute"))) + return -ENODEV; + mix->headphone_mute = ioremap(base, 1); + if (! (base = tumbler_find_device("headphone-detect"))) + return -ENODEV; + mix->headphone_status = ioremap(base, 1); + + /* activate headphone status interrupts */ + writeb(readb(mix->headphone_status) | (1<<7), mix->headphone_status); + + if (! (node = find_audio_device("headphone-detect"))) + return -ENODEV; + if (node->n_intrs == 0) + return -ENODEV; + + if ((err = request_irq(node->intrs[0].line, headphone_intr, 0, + "Tumbler Headphone Detection", chip)) < 0) + return err; + mix->headphone_irq = node->intrs[0].line; + + tumbler_update_headphone(chip); + return 0; +} + +static void tumbler_cleanup(pmac_t *chip) +{ + pmac_tumbler_t *mix = chip->mixer_data; + if (! mix) + return; + + if (mix->headphone_irq >= 0) + free_irq(mix->headphone_irq, chip); + if (mix->amp_mute) + iounmap(mix->amp_mute); + if (mix->headphone_mute) + iounmap(mix->headphone_mute); + if (mix->headphone_status) + iounmap(mix->headphone_status); + snd_pmac_keywest_cleanup(&mix->i2c); + kfree(mix); + chip->mixer_data = NULL; +} + +/* exported */ +int __init snd_pmac_tumbler_init(pmac_t *chip) +{ + int i, err; + pmac_tumbler_t *mix; + + mix = kmalloc(sizeof(*mix), GFP_KERNEL); + if (! mix) + return -ENOMEM; + memset(mix, 0, sizeof(*mix)); + mix->headphone_irq = -1; + + chip->mixer_data = mix; + chip->mixer_free = tumbler_cleanup; + + if ((err = tumbler_init(chip)) < 0) + return err; + + if ((err = snd_pmac_keywest_find(chip, &mix->i2c, TAS_I2C_ADDR, tumbler_init_client)) < 0) + return err; + + /* + * build mixers + */ + strcpy(chip->card->mixername, "PowerMac Tumbler"); + + for (i = 0; i < num_controls(tumbler_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0) + return err; + } + +#ifdef CONFIG_PMAC_PBOOK + chip->resume = tumbler_resume; +#endif + + return 0; +} diff -Nru linux/sound/sound_core.c linux-2.4.19-pre5-mjc/sound/sound_core.c --- linux/sound/sound_core.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/sound_core.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,573 @@ +/* + * Sound core handling. Breaks out sound functions to submodules + * + * Author: Alan Cox + * + * Fixes: + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * -------------------- + * + * Top level handler for the sound subsystem. Various devices can + * plug into this. The fact they dont all go via OSS doesn't mean + * they don't have to implement the OSS API. There is a lot of logic + * to keeping much of the OSS weight out of the code in a compatibility + * module, but it's up to the driver to rember to load it... + * + * The code provides a set of functions for registration of devices + * by type. This is done rather than providing a single call so that + * we can hide any future changes in the internals (eg when we go to + * 32bit dev_t) from the modules and their interface. + * + * Secondly we need to allocate the dsp, dsp16 and audio devices as + * one. Thus we misuse the chains a bit to simplify this. + * + * Thirdly to make it more fun and for 2.3.x and above we do all + * of this using fine grained locking. + * + * FIXME: we have to resolve modules and fine grained load/unload + * locking at some point in 2.3.x. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOUND_STEP 16 + + +struct sound_unit +{ + int unit_minor; + struct file_operations *unit_fops; + struct sound_unit *next; + devfs_handle_t de; +}; + +#ifdef CONFIG_SOUND_ALSA_MSNDCLAS +extern int msnd_classic_init(void); +#endif +#ifdef CONFIG_SOUND_ALSA_MSNDPIN +extern int msnd_pinnacle_init(void); +#endif + +/* + * Low level list operator. Scan the ordered list, find a hole and + * join into it. Called with the lock asserted + */ + +static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int index, int low, int top) +{ + int n=low; + + if (index < 0) { /* first free */ + + while (*list && (*list)->unit_minornext); + + while(nunit_minor>n) + break; + list=&((*list)->next); + n+=SOUND_STEP; + } + + if(n>=top) + return -ENOENT; + } else { + n = low+(index*16); + while (*list) { + if ((*list)->unit_minor==n) + return -EBUSY; + if ((*list)->unit_minor>n) + break; + list=&((*list)->next); + } + } + + /* + * Fill it in + */ + + s->unit_minor=n; + s->unit_fops=fops; + + /* + * Link it + */ + + s->next=*list; + *list=s; + + + return n; +} + +/* + * Remove a node from the chain. Called with the lock asserted + */ + +static void __sound_remove_unit(struct sound_unit **list, int unit) +{ + while(*list) + { + struct sound_unit *p=*list; + if(p->unit_minor==unit) + { + *list=p->next; + devfs_unregister (p->de); + kfree(p); + return; + } + list=&(p->next); + } + printk(KERN_ERR "Sound device %d went missing!\n", unit); +} + +/* + * This lock guards the sound loader list. + */ + +static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED; + +/* + * Allocate the controlling structure and add it to the sound driver + * list. Acquires locks as needed + */ + +static devfs_handle_t devfs_handle; + +static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode) +{ + int r; + struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL); + char name_buf[16]; + + if(s==NULL) + return -ENOMEM; + + spin_lock(&sound_loader_lock); + r=__sound_insert_unit(s,list,fops,index,low,top); + spin_unlock(&sound_loader_lock); + + if(r<0) + { + kfree(s); + return r; + } + + if (r < SOUND_STEP) + sprintf (name_buf, "%s", name); + else + sprintf (name_buf, "%s%d", name, r / SOUND_STEP); + s->de = devfs_register (devfs_handle, name_buf, + DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, + S_IFCHR | mode, fops, NULL); + return r; +} + +/* + * Remove a unit. Acquires locks as needed. The drivers MUST have + * completed the removal before their file operations become + * invalid. + */ + +static void sound_remove_unit(struct sound_unit **list, int unit) +{ + spin_lock(&sound_loader_lock); + __sound_remove_unit(list, unit); + spin_unlock(&sound_loader_lock); +} + +/* + * Allocations + * + * 0 *16 Mixers + * 1 *8 Sequencers + * 2 *16 Midi + * 3 *16 DSP + * 4 *16 SunDSP + * 5 *16 DSP16 + * 6 -- sndstat (obsolete) + * 7 *16 unused + * 8 -- alternate sequencer (see above) + * 9 *16 raw synthesizer access + * 10 *16 unused + * 11 *16 unused + * 12 *16 unused + * 13 *16 unused + * 14 *16 unused + * 15 *16 unused + */ + +static struct sound_unit *chains[SOUND_STEP]; + +/** + * register_sound_special - register a special sound node + * @fops: File operations for the driver + * @unit: Unit number to allocate + * + * Allocate a special sound device by minor number from the sound + * subsystem. The allocated number is returned on succes. On failure + * a negative error code is returned. + */ + +int register_sound_special(struct file_operations *fops, int unit) +{ + const int chain = unit % (SOUND_STEP-1); + int max_unit = 128 + chain; + const char *name; + char _name[16]; + + switch (chain) { + case 0: + name = "mixer"; + break; + case 1: + name = "sequencer"; + if (unit >= SOUND_STEP) + goto __unknown; + max_unit = unit + 1; + case 2: + name = "midi"; + break; + case 3: + name = "dsp"; + break; + case 4: + name = "audio"; + break; + case 8: + name = "sequencer2"; + if (unit >= SOUND_STEP) + goto __unknown; + max_unit = unit + 1; + break; + case 9: + name = "dmmidi"; + break; + case 10: + name = "dmfm"; + break; + case 12: + name = "adsp"; + break; + case 13: + name = "amidi"; + break; + case 14: + name = "admmidi"; + break; + default: + { + __unknown: + sprintf(_name, "unknown%d", chain); + if (unit >= SOUND_STEP) + strcat(_name, "-"); + name = _name; + } + break; + } + return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, + name, S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_special); + +/** + * register_sound_mixer - register a mixer device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a mixer device. Unit is the number of the mixer requested. + * Pass -1 to request the next free mixer unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + +int register_sound_mixer(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[0], fops, dev, 0, 128, + "mixer", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_mixer); + +/** + * register_sound_midi - register a midi device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a midi device. Unit is the number of the midi device requested. + * Pass -1 to request the next free midi unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + +int register_sound_midi(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[2], fops, dev, 2, 130, + "midi", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_midi); + +/* + * DSP's are registered as a triple. Register only one and cheat + * in open - see below. + */ + +/** + * register_sound_dsp - register a DSP device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a DSP device. Unit is the number of the DSP requested. + * Pass -1 to request the next free DSP unit. On success the allocated + * number is returned, on failure a negative error code is returned. + * + * This function allocates both the audio and dsp device entries together + * and will always allocate them as a matching pair - eg dsp3/audio3 + */ + +int register_sound_dsp(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[3], fops, dev, 3, 131, + "dsp", S_IWUSR | S_IRUSR); +} + +EXPORT_SYMBOL(register_sound_dsp); + +/** + * register_sound_synth - register a synth device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a synth device. Unit is the number of the synth device requested. + * Pass -1 to request the next free synth unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + + +int register_sound_synth(struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[9], fops, dev, 9, 137, + "synth", S_IRUSR | S_IWUSR); +} + +EXPORT_SYMBOL(register_sound_synth); + +/** + * unregister_sound_special - unregister a special sound device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with + * register_sound_special(). The unit passed is the return value from + * the register function. + */ + + +void unregister_sound_special(int unit) +{ + sound_remove_unit(&chains[unit&15], unit); +} + +EXPORT_SYMBOL(unregister_sound_special); + +/** + * unregister_sound_mixer - unregister a mixer + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_mixer(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_mixer(int unit) +{ + sound_remove_unit(&chains[0], unit); +} + +EXPORT_SYMBOL(unregister_sound_mixer); + +/** + * unregister_sound_midi - unregister a midi device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_midi(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_midi(int unit) +{ + return sound_remove_unit(&chains[2], unit); +} + +EXPORT_SYMBOL(unregister_sound_midi); + +/** + * unregister_sound_dsp - unregister a DSP device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_dsp(). + * The unit passed is the return value from the register function. + * + * Both of the allocated units are released together automatically. + */ + +void unregister_sound_dsp(int unit) +{ + return sound_remove_unit(&chains[3], unit); +} + + +EXPORT_SYMBOL(unregister_sound_dsp); + +/** + * unregister_sound_synth - unregister a synth device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_synth(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_synth(int unit) +{ + return sound_remove_unit(&chains[9], unit); +} + +EXPORT_SYMBOL(unregister_sound_synth); + +/* + * Now our file operations + */ + +static int soundcore_open(struct inode *, struct file *); + +static struct file_operations soundcore_fops= +{ + /* We must have an owner or the module locking fails */ + owner: THIS_MODULE, + open: soundcore_open, +}; + +static struct sound_unit *__look_for_unit(int chain, int unit) +{ + struct sound_unit *s; + + s=chains[chain]; + while(s && s->unit_minor <= unit) + { + if(s->unit_minor==unit) + return s; + s=s->next; + } + return NULL; +} + +int soundcore_open(struct inode *inode, struct file *file) +{ + int chain; + int unit = minor(inode->i_rdev); + struct sound_unit *s; + struct file_operations *new_fops = NULL; + + chain=unit&0x0F; + if(chain==4 || chain==5) /* dsp/audio/dsp16 */ + { + unit&=0xF0; + unit|=3; + chain=3; + } + + spin_lock(&sound_loader_lock); + s = __look_for_unit(chain, unit); + if (s) + new_fops = fops_get(s->unit_fops); + if (!new_fops) { + char mod[32]; + + spin_unlock(&sound_loader_lock); + /* + * Please, don't change this order or code. + * For ALSA slot means soundcard and OSS emulation code + * comes as add-on modules which aren't depend on + * ALSA toplevel modules for soundcards, thus we need + * load them at first. [Jaroslav Kysela ] + */ + sprintf(mod, "sound-slot-%i", unit>>4); + request_module(mod); + sprintf(mod, "sound-service-%i-%i", unit>>4, chain); + request_module(mod); + spin_lock(&sound_loader_lock); + s = __look_for_unit(chain, unit); + if (s) + new_fops = fops_get(s->unit_fops); + } + if (new_fops) { + /* + * We rely upon the fact that we can't be unloaded while the + * subdriver is there, so if ->open() is successful we can + * safely drop the reference counter and if it is not we can + * revert to old ->f_op. Ugly, indeed, but that's the cost of + * switching ->f_op in the first place. + */ + int err = 0; + struct file_operations *old_fops = file->f_op; + file->f_op = new_fops; + spin_unlock(&sound_loader_lock); + if(file->f_op->open) + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; + } + spin_unlock(&sound_loader_lock); + return -ENODEV; +} + +extern int mod_firmware_load(const char *, char **); +EXPORT_SYMBOL(mod_firmware_load); + + +MODULE_DESCRIPTION("Core sound module"); +MODULE_AUTHOR("Alan Cox"); +MODULE_LICENSE("GPL"); + +static void __exit cleanup_soundcore(void) +{ + /* We have nothing to really do here - we know the lists must be + empty */ + devfs_unregister_chrdev(SOUND_MAJOR, "sound"); + devfs_unregister (devfs_handle); +} + +static int __init init_soundcore(void) +{ + if(devfs_register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) + { + printk(KERN_ERR "soundcore: sound device already in use.\n"); + return -EBUSY; + } + devfs_handle = devfs_mk_dir (NULL, "sound", NULL); + + return 0; +} + +module_init(init_soundcore); +module_exit(cleanup_soundcore); diff -Nru linux/sound/sound_firmware.c linux-2.4.19-pre5-mjc/sound/sound_firmware.c --- linux/sound/sound_firmware.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/sound_firmware.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,78 @@ +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include + +static int errno; +static int do_mod_firmware_load(const char *fn, char **fp) +{ + int fd; + long l; + char *dp; + + fd = open(fn, 0, 0); + if (fd == -1) + { + printk(KERN_INFO "Unable to load '%s'.\n", fn); + return 0; + } + l = lseek(fd, 0L, 2); + if (l <= 0 || l > 131072) + { + printk(KERN_INFO "Invalid firmware '%s'\n", fn); + sys_close(fd); + return 0; + } + lseek(fd, 0L, 0); + dp = vmalloc(l); + if (dp == NULL) + { + printk(KERN_INFO "Out of memory loading '%s'.\n", fn); + sys_close(fd); + return 0; + } + if (read(fd, dp, l) != l) + { + printk(KERN_INFO "Failed to read '%s'.\n", fn); + vfree(dp); + sys_close(fd); + return 0; + } + close(fd); + *fp = dp; + return (int) l; +} + +/** + * mod_firmware_load - load sound driver firmware + * @fn: filename + * @fp: return for the buffer. + * + * Load the firmware for a sound module (up to 128K) into a buffer. + * The buffer is returned in *fp. It is allocated with vmalloc so is + * virtually linear and not DMAable. The caller should free it with + * vfree when finished. + * + * The length of the buffer is returned on a successful load, the + * value zero on a failure. + * + * Caution: This API is not recommended. Firmware should be loaded via + * an ioctl call and a setup application. This function may disappear + * in future. + */ + +int mod_firmware_load(const char *fn, char **fp) +{ + int r; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + r = do_mod_firmware_load(fn, fp); + set_fs(fs); + return r; +} + diff -Nru linux/sound/synth/Makefile linux-2.4.19-pre5-mjc/sound/synth/Makefile --- linux/sound/synth/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/Makefile Mon Apr 8 22:31:24 2002 @@ -0,0 +1,27 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := synth.o + +subdir-y := emux +subdir-m := $(subdir-y) + +list-multi := snd-util-mem.o + +export-objs := util_mem.o + +snd-util-mem-objs := util_mem.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o +obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o +endif + +include $(TOPDIR)/Rules.make + +snd-util-mem.o: $(snd-util-mem-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-util-mem-objs) diff -Nru linux/sound/synth/emux/Makefile linux-2.4.19-pre5-mjc/sound/synth/emux/Makefile --- linux/sound/synth/emux/Makefile Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/Makefile Mon Apr 8 22:31:23 2002 @@ -0,0 +1,24 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +O_TARGET := _emux.o + +list-multi := snd-emux-synth.o + +export-objs := emux.o + +snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ + emux_effect.o emux_proc.o emux_oss.o soundfont.o + +# Toplevel Module Dependency +ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) + obj-$(CONFIG_SND_SBAWE) += snd-emux-synth.o + obj-$(CONFIG_SND_EMU10K1) += snd-emux-synth.o +endif + +include $(TOPDIR)/Rules.make + +snd-emux-synth.o: $(snd-emux-synth-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(snd-emux-synth-objs) diff -Nru linux/sound/synth/emux/emux.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux.c --- linux/sound/synth/emux/emux.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Routines for control of EMU WaveTable chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "emux_voice.h" + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Routines for control of EMU WaveTable chip"); +MODULE_LICENSE("GPL"); + +/* + * create a new hardware dependent device for Emu8000/Emu10k1 + */ +int snd_emux_new(snd_emux_t **remu) +{ + snd_emux_t *emu; + + *remu = NULL; + emu = snd_magic_kcalloc(snd_emux_t, 0, GFP_KERNEL); + if (emu == NULL) + return -ENOMEM; + + spin_lock_init(&emu->voice_lock); + init_MUTEX(&emu->register_mutex); + + emu->client = -1; +#ifdef CONFIG_SND_OSSEMUL + emu->oss_synth = NULL; +#endif + emu->max_voices = 0; + emu->use_time = 0; + + emu->tlist.function = snd_emux_timer_callback; + emu->tlist.data = (unsigned long)emu; + emu->timer_active = 0; + + *remu = emu; + return 0; +} + + +/* + */ +int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name) +{ + snd_sf_callback_t sf_cb; + + snd_assert(emu->hw != NULL, return -EINVAL); + snd_assert(emu->max_voices > 0, return -EINVAL); + snd_assert(card != NULL, return -EINVAL); + snd_assert(name != NULL, return -EINVAL); + + emu->card = card; + emu->name = snd_kmalloc_strdup(name, GFP_KERNEL); + emu->voices = snd_kcalloc(sizeof(snd_emux_voice_t) * emu->max_voices, GFP_KERNEL); + if (emu->voices == NULL) + return -ENOMEM; + + /* create soundfont list */ + memset(&sf_cb, 0, sizeof(sf_cb)); + sf_cb.private_data = emu; + sf_cb.sample_new = (snd_sf_sample_new_t)emu->ops.sample_new; + sf_cb.sample_free = (snd_sf_sample_free_t)emu->ops.sample_free; + sf_cb.sample_reset = (snd_sf_sample_reset_t)emu->ops.sample_reset; + emu->sflist = snd_sf_new(&sf_cb, emu->memhdr); + if (emu->sflist == NULL) + return -ENOMEM; + + snd_emux_init_voices(emu); + + snd_emux_init_seq(emu, card, index); +#ifdef CONFIG_SND_OSSEMUL + snd_emux_init_seq_oss(emu); +#endif + snd_emux_init_virmidi(emu, card); + +#ifdef CONFIG_PROC_FS + snd_emux_proc_init(emu, card, index); +#endif + return 0; +} + + +/* + */ +int snd_emux_free(snd_emux_t *emu) +{ + unsigned long flags; + + if (! emu) + return -EINVAL; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->timer_active) + del_timer(&emu->tlist); + spin_unlock_irqrestore(&emu->voice_lock, flags); + +#ifdef CONFIG_PROC_FS + snd_emux_proc_free(emu); +#endif + snd_emux_delete_virmidi(emu); +#ifdef CONFIG_SND_OSSEMUL + snd_emux_detach_seq_oss(emu); +#endif + snd_emux_detach_seq(emu); + + if (emu->sflist) + snd_sf_free(emu->sflist); + + if (emu->voices) + kfree(emu->voices); + + if (emu->name) + kfree(emu->name); + + snd_magic_kfree(emu); + return 0; +} + + +EXPORT_SYMBOL(snd_emux_new); +EXPORT_SYMBOL(snd_emux_register); +EXPORT_SYMBOL(snd_emux_free); + +EXPORT_SYMBOL(snd_emux_terminate_all); +EXPORT_SYMBOL(snd_emux_lock_voice); +EXPORT_SYMBOL(snd_emux_unlock_voice); + + +/* + * INIT part + */ + +static int __init alsa_emux_init(void) +{ + return 0; +} + +static void __exit alsa_emux_exit(void) +{ +} + +module_init(alsa_emux_init) +module_exit(alsa_emux_exit) diff -Nru linux/sound/synth/emux/emux_effect.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_effect.c --- linux/sound/synth/emux/emux_effect.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_effect.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,309 @@ +/* + * Midi synth routines for the Emu8k/Emu10k1 + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * Contains code based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +/* + * effects table + */ + +#define xoffsetof(type,tag) ((long)(&((type)NULL)->tag) - (long)(NULL)) + +#define parm_offset(tag) xoffsetof(soundfont_voice_parm_t*, tag) + +#define PARM_IS_BYTE (1 << 0) +#define PARM_IS_WORD (1 << 1) +#define PARM_IS_ALIGNED (3 << 2) +#define PARM_IS_ALIGN_HI (1 << 2) +#define PARM_IS_ALIGN_LO (2 << 2) +#define PARM_IS_SIGNED (1 << 4) + +#define PARM_WORD (PARM_IS_WORD) +#define PARM_BYTE_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO) +#define PARM_BYTE_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI) +#define PARM_BYTE (PARM_IS_BYTE) +#define PARM_SIGN_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED) +#define PARM_SIGN_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED) + +static struct emux_parm_defs { + int type; /* byte or word */ + int low, high; /* value range */ + long offset; /* offset in parameter record (-1 = not written) */ + int update; /* flgas for real-time update */ +} parm_defs[EMUX_NUM_EFFECTS] = { + {PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0}, /* env1 delay */ + {PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0}, /* env1 attack */ + {PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0}, /* env1 hold */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0}, /* env1 decay */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0}, /* env1 release */ + {PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0}, /* env1 sustain */ + {PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0}, /* env1 pitch */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0}, /* env1 fc */ + + {PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0}, /* env2 delay */ + {PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0}, /* env2 attack */ + {PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0}, /* env2 hold */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0}, /* env2 decay */ + {PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0}, /* env2 release */ + {PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0}, /* env2 sustain */ + + {PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0}, /* lfo1 delay */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 freq */ + {PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 vol */ + {PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 pitch */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 cutoff */ + + {PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0}, /* lfo2 delay */ + {PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 freq */ + {PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 pitch */ + + {PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH}, /* initial pitch */ + {PARM_BYTE, 0, 0xff, parm_offset(chorus), 0}, /* chorus */ + {PARM_BYTE, 0, 0xff, parm_offset(reverb), 0}, /* reverb */ + {PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME}, /* cutoff */ + {PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q}, /* resonance */ + + {PARM_WORD, 0, 0xffff, -1, 0}, /* sample start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* loop start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* loop end */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse sample start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop start */ + {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop end */ + {PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME}, /* initial attenuation */ +}; + +/* set byte effect value */ +static void +effect_set_byte(unsigned char *valp, snd_midi_channel_t *chan, int type) +{ + short effect; + snd_emux_effect_table_t *fx = chan->private; + + effect = fx->val[type]; + if (fx->flag[type] == EMUX_FX_FLAG_ADD) { + if (parm_defs[type].type & PARM_IS_SIGNED) + effect += *(char*)valp; + else + effect += *valp; + } + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + *valp = (unsigned char)effect; +} + +/* set word effect value */ +static void +effect_set_word(unsigned short *valp, snd_midi_channel_t *chan, int type) +{ + int effect; + snd_emux_effect_table_t *fx = chan->private; + + effect = *(unsigned short*)&fx->val[type]; + if (fx->flag[type] == EMUX_FX_FLAG_ADD) + effect += *valp; + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + *valp = (unsigned short)effect; +} + +/* address offset */ +static int +effect_get_offset(snd_midi_channel_t *chan, int lo, int hi, int mode) +{ + int addr = 0; + snd_emux_effect_table_t *fx = chan->private; + + if (fx->flag[hi]) + addr = (short)fx->val[hi]; + addr = addr << 15; + if (fx->flag[lo]) + addr += (short)fx->val[lo]; + if (!(mode & SNDRV_SFNT_SAMPLE_8BITS)) + addr /= 2; + return addr; +} + +#ifdef CONFIG_SND_OSSEMUL +/* change effects - for OSS sequencer compatibility */ +void +snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val) +{ + int mode; + + if (type & 0x40) + mode = EMUX_FX_FLAG_OFF; + else if (type & 0x80) + mode = EMUX_FX_FLAG_ADD; + else + mode = EMUX_FX_FLAG_SET; + type &= 0x3f; + + snd_emux_send_effect(port, chan, type, val, mode); +} +#endif + +/* Modify the effect value. + * if update is necessary, call emu8000_control + */ +void +snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode) +{ + int i; + int offset; + unsigned char *srcp, *origp; + snd_emux_t *emu; + snd_emux_effect_table_t *fx; + unsigned long flags; + + emu = port->emu; + fx = chan->private; + if (emu == NULL || fx == NULL) + return; + if (type < 0 || type >= EMUX_NUM_EFFECTS) + return; + + fx->val[type] = val; + fx->flag[type] = mode; + + /* do we need to modify the register in realtime ? */ + if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0) + return; + +#ifdef SNDRV_LITTLE_ENDIAN + if (parm_defs[type].type & PARM_IS_ALIGN_HI) + offset++; +#else + if (parm_defs[type].type & PARM_IS_ALIGN_LO) + offset++; +#endif + /* modify the register values */ + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + snd_emux_voice_t *vp = &emu->voices[i]; + if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan) + continue; + srcp = (unsigned char*)&vp->reg.parm + offset; + origp = (unsigned char*)&vp->zone->v.parm + offset; + if (parm_defs[i].type & PARM_IS_BYTE) { + *srcp = *origp; + effect_set_byte(srcp, chan, type); + } else { + *(unsigned short*)srcp = *(unsigned short*)origp; + effect_set_word((unsigned short*)srcp, chan, type); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + + /* activate them */ + snd_emux_update_channel(port, chan, parm_defs[type].update); +} + + +/* copy wavetable registers to voice table */ +void +snd_emux_setup_effect(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + snd_emux_effect_table_t *fx; + unsigned char *srcp; + int i; + + if (! (fx = chan->private)) + return; + + /* modify the register values via effect table */ + for (i = 0; i < EMUX_FX_END; i++) { + int offset; + if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0) + continue; +#ifdef SNDRV_LITTLE_ENDIAN + if (parm_defs[i].type & PARM_IS_ALIGN_HI) + offset++; +#else + if (parm_defs[i].type & PARM_IS_ALIGN_LO) + offset++; +#endif + srcp = (unsigned char*)&vp->reg.parm + offset; + if (parm_defs[i].type & PARM_IS_BYTE) + effect_set_byte(srcp, chan, i); + else + effect_set_word((unsigned short*)srcp, chan, i); + } + + /* correct sample and loop points */ + vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START, + EMUX_FX_COARSE_SAMPLE_START, + vp->reg.sample_mode); + + vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START, + EMUX_FX_COARSE_LOOP_START, + vp->reg.sample_mode); + + vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END, + EMUX_FX_COARSE_LOOP_END, + vp->reg.sample_mode); +} + +/* + * effect table + */ +void +snd_emux_create_effect(snd_emux_port_t *p) +{ + int i; + p->effect = snd_kcalloc(sizeof(snd_emux_effect_table_t) * p->chset.max_channels, GFP_KERNEL); + if (p->effect) { + for (i = 0; i < p->chset.max_channels; i++) + p->chset.channels[i].private = p->effect + i; + } else { + for (i = 0; i < p->chset.max_channels; i++) + p->chset.channels[i].private = NULL; + } +} + +void +snd_emux_delete_effect(snd_emux_port_t *p) +{ + if (p->effect) { + kfree(p->effect); + p->effect = NULL; + } +} + +void +snd_emux_clear_effect(snd_emux_port_t *p) +{ + if (p->effect) { + memset(p->effect, 0, sizeof(snd_emux_effect_table_t) * p->chset.max_channels); + } +} + +#endif /* SNDRV_EMUX_USE_RAW_EFFECT */ diff -Nru linux/sound/synth/emux/emux_nrpn.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_nrpn.c --- linux/sound/synth/emux/emux_nrpn.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_nrpn.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,401 @@ +/* + * NRPN / SYSEX callbacks for Emu8k/Emu10k1 + * + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + +#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) + +/* + * conversion from NRPN/control parameters to Emu8000 raw parameters + */ + +/* NRPN / CC -> Emu8000 parameter converter */ +typedef struct { + int control; + int effect; + int (*convert)(int val); +} nrpn_conv_table; + +/* effect sensitivity */ + +#define FX_CUTOFF 0 +#define FX_RESONANCE 1 +#define FX_ATTACK 2 +#define FX_RELEASE 3 +#define FX_VIBRATE 4 +#define FX_VIBDEPTH 5 +#define FX_VIBDELAY 6 +#define FX_NUMS 7 + +/* + * convert NRPN/control values + */ + +static int send_converted_effect(nrpn_conv_table *table, int num_tables, + snd_emux_port_t *port, snd_midi_channel_t *chan, + int type, int val, int mode) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + snd_emux_send_effect(port, chan, table[i].effect, + cval, mode); + return 1; + } + } + return 0; +} + +#define DEF_FX_CUTOFF 170 +#define DEF_FX_RESONANCE 6 +#define DEF_FX_ATTACK 50 +#define DEF_FX_RELEASE 50 +#define DEF_FX_VIBRATE 30 +#define DEF_FX_VIBDEPTH 4 +#define DEF_FX_VIBDELAY 1500 + +/* effect sensitivities for GS NRPN: + * adjusted for chaos 8MB soundfonts + */ +static int gs_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + +/* effect sensitivies for XG controls: + * adjusted for chaos 8MB soundfonts + */ +static int xg_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + + +/* + * AWE32 NRPN effects + */ + +static int fx_delay(int val); +static int fx_attack(int val); +static int fx_hold(int val); +static int fx_decay(int val); +static int fx_the_value(int val); +static int fx_twice_value(int val); +static int fx_conv_pitch(int val); +static int fx_conv_Q(int val); + +/* function for each NRPN */ /* [range] units */ +#define fx_env1_delay fx_delay /* [0,5900] 4msec */ +#define fx_env1_attack fx_attack /* [0,5940] 1msec */ +#define fx_env1_hold fx_hold /* [0,8191] 1msec */ +#define fx_env1_decay fx_decay /* [0,5940] 4msec */ +#define fx_env1_release fx_decay /* [0,5940] 4msec */ +#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ +#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ + +#define fx_env2_delay fx_delay /* [0,5900] 4msec */ +#define fx_env2_attack fx_attack /* [0,5940] 1msec */ +#define fx_env2_hold fx_hold /* [0,8191] 1msec */ +#define fx_env2_decay fx_decay /* [0,5940] 4msec */ +#define fx_env2_release fx_decay /* [0,5940] 4msec */ +#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ + +#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ +#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ + +#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ + +#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ +#define fx_chorus fx_the_value /* [0,255] -- */ +#define fx_reverb fx_the_value /* [0,255] -- */ +#define fx_cutoff fx_twice_value /* [0,127] 62Hz */ +#define fx_filterQ fx_conv_Q /* [0,127] -- */ + +static int fx_delay(int val) +{ + return (unsigned short)snd_sf_calc_parm_delay(val); +} + +static int fx_attack(int val) +{ + return (unsigned short)snd_sf_calc_parm_attack(val); +} + +static int fx_hold(int val) +{ + return (unsigned short)snd_sf_calc_parm_hold(val); +} + +static int fx_decay(int val) +{ + return (unsigned short)snd_sf_calc_parm_decay(val); +} + +static int fx_the_value(int val) +{ + return (unsigned short)(val & 0xff); +} + +static int fx_twice_value(int val) +{ + return (unsigned short)((val * 2) & 0xff); +} + +static int fx_conv_pitch(int val) +{ + return (short)(val * 4096 / 1200); +} + +static int fx_conv_Q(int val) +{ + return (unsigned short)((val / 8) & 0xff); +} + + +static nrpn_conv_table awe_effects[] = +{ + { 0, EMUX_FX_LFO1_DELAY, fx_lfo1_delay}, + { 1, EMUX_FX_LFO1_FREQ, fx_lfo1_freq}, + { 2, EMUX_FX_LFO2_DELAY, fx_lfo2_delay}, + { 3, EMUX_FX_LFO2_FREQ, fx_lfo2_freq}, + + { 4, EMUX_FX_ENV1_DELAY, fx_env1_delay}, + { 5, EMUX_FX_ENV1_ATTACK,fx_env1_attack}, + { 6, EMUX_FX_ENV1_HOLD, fx_env1_hold}, + { 7, EMUX_FX_ENV1_DECAY, fx_env1_decay}, + { 8, EMUX_FX_ENV1_SUSTAIN, fx_env1_sustain}, + { 9, EMUX_FX_ENV1_RELEASE, fx_env1_release}, + + {10, EMUX_FX_ENV2_DELAY, fx_env2_delay}, + {11, EMUX_FX_ENV2_ATTACK, fx_env2_attack}, + {12, EMUX_FX_ENV2_HOLD, fx_env2_hold}, + {13, EMUX_FX_ENV2_DECAY, fx_env2_decay}, + {14, EMUX_FX_ENV2_SUSTAIN, fx_env2_sustain}, + {15, EMUX_FX_ENV2_RELEASE, fx_env2_release}, + + {16, EMUX_FX_INIT_PITCH, fx_init_pitch}, + {17, EMUX_FX_LFO1_PITCH, fx_lfo1_pitch}, + {18, EMUX_FX_LFO2_PITCH, fx_lfo2_pitch}, + {19, EMUX_FX_ENV1_PITCH, fx_env1_pitch}, + {20, EMUX_FX_LFO1_VOLUME, fx_lfo1_volume}, + {21, EMUX_FX_CUTOFF, fx_cutoff}, + {22, EMUX_FX_FILTERQ, fx_filterQ}, + {23, EMUX_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, + {24, EMUX_FX_ENV1_CUTOFF, fx_env1_cutoff}, + {25, EMUX_FX_CHORUS, fx_chorus}, + {26, EMUX_FX_REVERB, fx_reverb}, +}; + +static int num_awe_effects = NELEM(awe_effects); + + +/* + * GS(SC88) NRPN effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static int gs_cutoff(int val) +{ + return (val - 64) * gs_sense[FX_CUTOFF] / 50; +} + +/* resonance: 0 to 15(max) */ +static int gs_filterQ(int val) +{ + return (val - 64) * gs_sense[FX_RESONANCE] / 50; +} + +/* attack: */ +static int gs_attack(int val) +{ + return -(val - 64) * gs_sense[FX_ATTACK] / 50; +} + +/* decay: */ +static int gs_decay(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* release: */ +static int gs_release(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* vibrato freq: 0.042Hz step, max=255 */ +static int gs_vib_rate(int val) +{ + return (val - 64) * gs_sense[FX_VIBRATE] / 50; +} + +/* vibrato depth: max=127, 1 octave */ +static int gs_vib_depth(int val) +{ + return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; +} + +/* vibrato delay: -0.725msec step */ +static int gs_vib_delay(int val) +{ + return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; +} + +static nrpn_conv_table gs_effects[] = +{ + {32, EMUX_FX_CUTOFF, gs_cutoff}, + {33, EMUX_FX_FILTERQ, gs_filterQ}, + {99, EMUX_FX_ENV2_ATTACK, gs_attack}, + {100, EMUX_FX_ENV2_DECAY, gs_decay}, + {102, EMUX_FX_ENV2_RELEASE, gs_release}, + {8, EMUX_FX_LFO1_FREQ, gs_vib_rate}, + {9, EMUX_FX_LFO1_VOLUME, gs_vib_depth}, + {10, EMUX_FX_LFO1_DELAY, gs_vib_delay}, +}; + +static int num_gs_effects = NELEM(gs_effects); + + +/* + * NRPN events + */ +void +snd_emux_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL, return); + snd_assert(chan != NULL, return); + + if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 && + chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) { + int val; + /* Win/DOS AWE32 specific NRPNs */ + /* both MSB/LSB necessary */ + val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) | + chan->control[MIDI_CTL_LSB_DATA_ENTRY]; + val -= 8192; + send_converted_effect + (awe_effects, num_awe_effects, + port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], + val, EMUX_FX_FLAG_SET); + return; + } + + if (port->chset.midi_mode == SNDRV_MIDI_MODE_GS && + chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 1) { + int val; + /* GS specific NRPNs */ + /* only MSB is valid */ + val = chan->control[MIDI_CTL_MSB_DATA_ENTRY]; + send_converted_effect + (gs_effects, num_gs_effects, + port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], + val, EMUX_FX_FLAG_ADD); + return; + } +} + + +/* + * XG control effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static int xg_cutoff(int val) +{ + return (val - 64) * xg_sense[FX_CUTOFF] / 64; +} + +/* resonance: 0(open) to 15(most nasal) */ +static int xg_filterQ(int val) +{ + return (val - 64) * xg_sense[FX_RESONANCE] / 64; +} + +/* attack: */ +static int xg_attack(int val) +{ + return -(val - 64) * xg_sense[FX_ATTACK] / 64; +} + +/* release: */ +static int xg_release(int val) +{ + return -(val - 64) * xg_sense[FX_RELEASE] / 64; +} + +static nrpn_conv_table xg_effects[] = +{ + {71, EMUX_FX_CUTOFF, xg_cutoff}, + {74, EMUX_FX_FILTERQ, xg_filterQ}, + {72, EMUX_FX_ENV2_RELEASE, xg_release}, + {73, EMUX_FX_ENV2_ATTACK, xg_attack}, +}; + +static int num_xg_effects = NELEM(xg_effects); + +int +snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param) +{ + return send_converted_effect(xg_effects, num_xg_effects, + port, chan, param, + chan->control[param], + EMUX_FX_FLAG_ADD); +} + +/* + * receive sysex + */ +void +snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset) +{ + snd_emux_port_t *port; + snd_emux_t *emu; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL, return); + snd_assert(chset != NULL, return); + emu = port->emu; + + switch (parsed) { + case SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME: + snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); + break; + default: + if (emu->ops.sysex) + emu->ops.sysex(emu, buf, len, parsed, chset); + break; + } +} + diff -Nru linux/sound/synth/emux/emux_oss.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_oss.c --- linux/sound/synth/emux/emux_oss.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_oss.c Mon Apr 8 22:31:23 2002 @@ -0,0 +1,496 @@ +/* + * Interface for OSS sequencer emulation + * + * Copyright (C) 1999 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Changes + * 19990227 Steve Ratcliffe Made separate file and merged in latest + * midi emulation. + */ + +#define __NO_VERSION__ +#include + +#ifdef CONFIG_SND_OSSEMUL + +#include +#include +#include "emux_voice.h" +#include + +static int snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); +static int snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); +static int snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char *buf, int offs, int count); +static int snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg); +static int snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +static void reset_port_mode(snd_emux_port_t *port, int midi_mode); +static void emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); +static void gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); +static void fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop); + +/* operators */ +static snd_seq_oss_callback_t oss_callback = { + owner: THIS_MODULE, + open: snd_emux_open_seq_oss, + close: snd_emux_close_seq_oss, + ioctl: snd_emux_ioctl_seq_oss, + load_patch: snd_emux_load_patch_seq_oss, + reset: snd_emux_reset_seq_oss, +}; + + +/* + * register OSS synth + */ + +void +snd_emux_init_seq_oss(snd_emux_t *emu) +{ + snd_seq_oss_reg_t *arg; + snd_seq_device_t *dev; + + if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS, + sizeof(snd_seq_oss_reg_t), &dev) < 0) + return; + + emu->oss_synth = dev; + strcpy(dev->name, emu->name); + arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); + arg->type = SYNTH_TYPE_SAMPLE; + arg->subtype = SAMPLE_TYPE_AWE32; + arg->nvoices = emu->max_voices; + arg->oper = oss_callback; + arg->private_data = emu; + + /* register to OSS synth table */ + snd_device_register(emu->card, dev); +} + + +/* + * unregister + */ +void +snd_emux_detach_seq_oss(snd_emux_t *emu) +{ + if (emu->oss_synth) { + snd_device_free(emu->card, emu->oss_synth); + emu->oss_synth = NULL; + } +} + + +/* use port number as a unique soundfont client number */ +#define SF_CLIENT_NO(p) ((p) + 0x1000) + +/* + * open port for OSS sequencer + */ +static int +snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + snd_seq_port_callback_t callback; + char tmpname[64]; + + emu = snd_magic_cast(snd_emux_t, closure, return -EINVAL); + snd_assert(arg != NULL && emu != NULL, return -ENXIO); + + down(&emu->register_mutex); + + if (!snd_emux_inc_count(emu)) { + up(&emu->register_mutex); + return -EFAULT; + } + + memset(&callback, 0, sizeof(callback)); + callback.owner = THIS_MODULE; + callback.event_input = snd_emux_event_oss_input; + + sprintf(tmpname, "%s OSS Port", emu->name); + p = snd_emux_create_port(emu, tmpname, 32, + 1, &callback); + if (p == NULL) { + snd_printk("can't create port\n"); + snd_emux_dec_count(emu); + up(&emu->register_mutex); + return -ENOMEM; + } + + /* fill the argument data */ + arg->private_data = p; + arg->addr.client = p->chset.client; + arg->addr.port = p->chset.port; + p->oss_arg = arg; + + reset_port_mode(p, arg->seq_mode); + + snd_emux_reset_port(p); + + up(&emu->register_mutex); + return 0; +} + + +#define DEFAULT_DRUM_FLAGS ((1<<9) | (1<<25)) + +/* + * reset port mode + */ +static void +reset_port_mode(snd_emux_port_t *port, int midi_mode) +{ + if (midi_mode) { + port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI; + port->drum_flags = DEFAULT_DRUM_FLAGS; + port->volume_atten = 0; + port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS; + } else { + port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH; + port->drum_flags = 0; + port->volume_atten = 32; + port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; + } +} + + +/* + * close port + */ +static int +snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + down(&emu->register_mutex); + snd_emux_sounds_off_all(p); + snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port)); + snd_seq_event_port_detach(p->chset.client, p->chset.port); + snd_emux_dec_count(emu); + + up(&emu->register_mutex); + return 0; +} + + +/* + * load patch + */ +static int +snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, + const char *buf, int offs, int count) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + int rc; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + if (format == GUS_PATCH) + rc = snd_soundfont_load_guspatch(emu->sflist, buf, count, + SF_CLIENT_NO(p->chset.port)); + else if (format == SNDRV_OSS_SOUNDFONT_PATCH) { + soundfont_patch_info_t patch; + if (count < sizeof(patch)) + rc = -EINVAL; + if (copy_from_user(&patch, buf, sizeof(patch))) + rc = -EFAULT; + if (patch.type >= SNDRV_SFNT_LOAD_INFO && + patch.type <= SNDRV_SFNT_PROBE_DATA) + rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port)); + else { + if (emu->ops.load_fx) + rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count); + else + rc = -EINVAL; + } + } else + rc = 0; + return rc; +} + + +/* + * ioctl + */ +static int +snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + + emu = p->emu; + snd_assert(emu != NULL, return -ENXIO); + + switch (cmd) { + case SNDCTL_SEQ_RESETSAMPLES: + snd_soundfont_remove_samples(emu->sflist); + return 0; + + case SNDCTL_SYNTH_MEMAVL: + if (emu->memhdr) + return snd_util_mem_avail(emu->memhdr); + return 0; + } + + return 0; +} + + +/* + * reset device + */ +static int +snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg) +{ + snd_emux_port_t *p; + + snd_assert(arg != NULL, return -ENXIO); + p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + snd_assert(p != NULL, return -ENXIO); + snd_emux_reset_port(p); + return 0; +} + + +/* + * receive raw events: only SEQ_PRIVATE is accepted. + */ +static int +snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + snd_emux_t *emu; + snd_emux_port_t *p; + unsigned char cmd, *data; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + if (ev->type != SNDRV_SEQ_EVENT_OSS) + return snd_emux_event_input(ev, direct, private_data, atomic, hop); + + data = ev->data.raw8.d; + /* only SEQ_PRIVATE is accepted */ + if (data[0] != 0xfe) + return 0; + cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK; + if (data[2] & _EMUX_OSS_MODE_FLAG) + emuspec_control(emu, p, cmd, data, atomic, hop); + else + gusspec_control(emu, p, cmd, data, atomic, hop); + return 0; +} + + +/* + * OSS/AWE driver specific h/w controls + */ +static void +emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, + unsigned char *event, int atomic, int hop) +{ + int voice; + unsigned short p1; + short p2; + int i; + snd_midi_channel_t *chan; + + voice = event[3]; + if (voice < 0 || voice >= port->chset.max_channels) + chan = NULL; + else + chan = &port->chset.channels[voice]; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + + switch (cmd) { + case _EMUX_OSS_REMOVE_LAST_SAMPLES: + snd_soundfont_remove_unlocked(emu->sflist); + break; + case _EMUX_OSS_SEND_EFFECT: + if (chan) + snd_emux_send_effect_oss(port, chan, p1, p2); + break; + + case _EMUX_OSS_TERMINATE_ALL: + snd_emux_terminate_all(emu); + break; + + case _EMUX_OSS_TERMINATE_CHANNEL: + /*snd_emux_mute_channel(emu, chan);*/ + break; + case _EMUX_OSS_RESET_CHANNEL: + /*snd_emux_channel_init(chset, chan);*/ + break; + + case _EMUX_OSS_RELEASE_ALL: + fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop); + break; + case _EMUX_OSS_NOTEOFF_ALL: + fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop); + break; + + case _EMUX_OSS_INITIAL_VOLUME: + if (p2) { + port->volume_atten = (short)p1; + snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); + } + break; + + case _EMUX_OSS_CHN_PRESSURE: + if (chan) { + chan->midi_pressure = p1; + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2); + } + break; + + case _EMUX_OSS_CHANNEL_MODE: + reset_port_mode(port, p1); + snd_emux_reset_port(port); + break; + + case _EMUX_OSS_DRUM_CHANNELS: + port->drum_flags = *(unsigned int*)&event[4]; + for (i = 0; i < port->chset.max_channels; i++) { + chan = &port->chset.channels[i]; + chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; + } + break; + + case _EMUX_OSS_MISC_MODE: + if (p1 < EMUX_MD_END) + port->ctrls[p1] = p2; + break; + case _EMUX_OSS_DEBUG_MODE: + break; + + default: + if (emu->ops.oss_ioctl) + emu->ops.oss_ioctl(emu, cmd, p1, p2); + break; + } +} + +/* + * GUS specific h/w controls + */ + +#include + +static void +gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, + unsigned char *event, int atomic, int hop) +{ + int voice; + unsigned short p1; + short p2; + int plong; + snd_midi_channel_t *chan; + + if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH) + return; + if (cmd == _GUS_NUMVOICES) + return; + voice = event[3]; + if (voice < 0 || voice >= port->chset.max_channels) + return; + + chan = &port->chset.channels[voice]; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + plong = *(int*) &event[4]; + + switch (cmd) { + case _GUS_VOICESAMPLE: + chan->midi_program = p1; + return; + + case _GUS_VOICEBALA: + /* 0 to 15 --> 0 to 127 */ + chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3; + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); + return; + + case _GUS_VOICEVOL: + case _GUS_VOICEVOL2: + /* not supported yet */ + return; + + case _GUS_RAMPRANGE: + case _GUS_RAMPRATE: + case _GUS_RAMPMODE: + case _GUS_RAMPON: + case _GUS_RAMPOFF: + /* volume ramping not supported */ + return; + + case _GUS_VOLUME_SCALE: + return; + + case _GUS_VOICE_POS: +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START, + (short)(plong & 0x7fff), + EMUX_FX_FLAG_SET); + snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff, + EMUX_FX_FLAG_SET); +#endif + return; + } +} + + +/* + * send an event to midi emulation + */ +static void +fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop) +{ + snd_seq_event_t ev; + memset(&ev, 0, sizeof(ev)); + ev.type = SNDRV_SEQ_EVENT_CONTROLLER; + ev.data.control.channel = ch; + ev.data.control.param = param; + ev.data.control.value = val; + snd_emux_event_input(&ev, 0, port, atomic, hop); +} + +#endif /* CONFIG_SND_OSSEMUL */ diff -Nru linux/sound/synth/emux/emux_proc.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_proc.c --- linux/sound/synth/emux/emux_proc.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_proc.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Proc interface for Emu8k/Emu10k1 WaveTable synth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include "emux_voice.h" + +#ifdef CONFIG_PROC_FS + +static void +snd_emux_proc_info_read(snd_info_entry_t *entry, + snd_info_buffer_t *buf) +{ + snd_emux_t *emu; + int i; + + emu = snd_magic_cast(snd_emux_t, entry->private_data, return); + down(&emu->register_mutex); + if (emu->name) + snd_iprintf(buf, "Device: %s\n", emu->name); + snd_iprintf(buf, "Ports: %d\n", emu->num_ports); + snd_iprintf(buf, "Addresses:"); + for (i = 0; i < emu->num_ports; i++) + snd_iprintf(buf, " %d:%d", emu->client, emu->ports[i]); + snd_iprintf(buf, "\n"); + snd_iprintf(buf, "Use Counter: %d\n", emu->used); + snd_iprintf(buf, "Max Voices: %d\n", emu->max_voices); + snd_iprintf(buf, "Allocated Voices: %d\n", emu->num_voices); + if (emu->memhdr) { + snd_iprintf(buf, "Memory Size: %d\n", emu->memhdr->size); + snd_iprintf(buf, "Memory Available: %d\n", snd_util_mem_avail(emu->memhdr)); + snd_iprintf(buf, "Allocated Blocks: %d\n", emu->memhdr->nblocks); + } else { + snd_iprintf(buf, "Memory Size: 0\n"); + } + if (emu->sflist) { + down(&emu->sflist->presets_mutex); + snd_iprintf(buf, "SoundFonts: %d\n", emu->sflist->fonts_size); + snd_iprintf(buf, "Instruments: %d\n", emu->sflist->zone_counter); + snd_iprintf(buf, "Samples: %d\n", emu->sflist->sample_counter); + snd_iprintf(buf, "Locked Instruments: %d\n", emu->sflist->zone_locked); + snd_iprintf(buf, "Locked Samples: %d\n", emu->sflist->sample_locked); + up(&emu->sflist->presets_mutex); + } +#if 0 /* debug */ + if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) { + snd_emux_voice_t *vp = &emu->voices[0]; + snd_iprintf(buf, "voice 0: on\n"); + snd_iprintf(buf, "mod delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", + vp->reg.parm.moddelay, + vp->reg.parm.modatkhld, + vp->reg.parm.moddcysus, + vp->reg.parm.modrelease); + snd_iprintf(buf, "vol delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", + vp->reg.parm.voldelay, + vp->reg.parm.volatkhld, + vp->reg.parm.voldcysus, + vp->reg.parm.volrelease); + snd_iprintf(buf, "lfo1 delay=%x, lfo2 delay=%x, pefe=%x\n", + vp->reg.parm.lfo1delay, + vp->reg.parm.lfo2delay, + vp->reg.parm.pefe); + snd_iprintf(buf, "fmmod=%x, tremfrq=%x, fm2frq2=%x\n", + vp->reg.parm.fmmod, + vp->reg.parm.tremfrq, + vp->reg.parm.fm2frq2); + snd_iprintf(buf, "cutoff=%x, filterQ=%x, chorus=%x, reverb=%x\n", + vp->reg.parm.cutoff, + vp->reg.parm.filterQ, + vp->reg.parm.chorus, + vp->reg.parm.reverb); + snd_iprintf(buf, "avol=%x, acutoff=%x, apitch=%x\n", + vp->avol, vp->acutoff, vp->apitch); + snd_iprintf(buf, "apan=%x, aaux=%x, ptarget=%x, vtarget=%x, ftarget=%x\n", + vp->apan, vp->aaux, + vp->ptarget, + vp->vtarget, + vp->ftarget); + snd_iprintf(buf, "start=%x, end=%x, loopstart=%x, loopend=%x\n", + vp->reg.start, vp->reg.end, vp->reg.loopstart, vp->reg.loopend); + snd_iprintf(buf, "sample_mode=%x, rate=%x\n", vp->reg.sample_mode, vp->reg.rate_offset); + } +#endif + up(&emu->register_mutex); +} + + +void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device) +{ + snd_info_entry_t *entry; + char name[64]; + + sprintf(name, "wavetableD%d", device); + entry = snd_info_create_card_entry(card, name, card->proc_root); + if (entry == NULL) + return; + + entry->content = SNDRV_INFO_CONTENT_TEXT; + entry->private_data = emu; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_emux_proc_info_read; + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); + else + emu->proc = entry; +} + +void snd_emux_proc_free(snd_emux_t *emu) +{ + if (emu->proc) { + snd_info_unregister(emu->proc); + emu->proc = NULL; + } +} + +#endif /* CONFIG_PROC_FS */ diff -Nru linux/sound/synth/emux/emux_seq.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_seq.c --- linux/sound/synth/emux/emux_seq.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_seq.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,437 @@ +/* + * Midi Sequencer interface routines. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + + +/* Prototypes for static functions */ +static void free_port(void *private); +static void snd_emux_init_port(snd_emux_port_t *p); +static int snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info); +static int snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info); +static int get_client(snd_card_t *card, int index, char *name); + +/* + * MIDI emulation operators + */ +static snd_midi_op_t emux_ops = { + snd_emux_note_on, + snd_emux_note_off, + snd_emux_key_press, + snd_emux_terminate_note, + snd_emux_control, + snd_emux_nrpn, + snd_emux_sysex, +}; + + +/* + * number of MIDI channels + */ +#define MIDI_CHANNELS 16 + +/* + * type flags for MIDI sequencer port + */ +#define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\ + SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ + SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ + SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE) + +/* + */ + +static inline void dec_mod_count(struct module *module) +{ + if (module) + __MOD_DEC_USE_COUNT(module); +} + +/* + * Initialise the EMUX Synth by creating a client and registering + * a series of ports. + * Each of the ports will contain the 16 midi channels. Applications + * can connect to these ports to play midi data. + */ +int +snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index) +{ + int i; + snd_seq_port_callback_t pinfo; + char tmpname[64]; + + sprintf(tmpname, "%s WaveTable", emu->name); + emu->client = get_client(card, index, tmpname); + if (emu->client < 0) { + snd_printk("can't create client\n"); + return -ENODEV; + } + + if (emu->num_ports < 0) { + snd_printk("seqports must be greater than zero\n"); + emu->num_ports = 1; + } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { + snd_printk("too many ports." + "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); + emu->num_ports = SNDRV_EMUX_MAX_PORTS; + } + + memset(&pinfo, 0, sizeof(pinfo)); + pinfo.owner = THIS_MODULE; + pinfo.use = snd_emux_use; + pinfo.unuse = snd_emux_unuse; + pinfo.event_input = snd_emux_event_input; + + for (i = 0; i < emu->num_ports; i++) { + snd_emux_port_t *p; + + sprintf(tmpname, "%s Port %d", emu->name, i); + p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS, + 0, &pinfo); + if (p == NULL) { + snd_printk("can't create port\n"); + return -ENOMEM; + } + + p->port_mode = SNDRV_EMUX_PORT_MODE_MIDI; + snd_emux_init_port(p); + emu->ports[i] = p->chset.port; + } + + return 0; +} + + +/* + * Detach from the ports that were set up for this synthesizer and + * destroy the kernel client. + */ +void +snd_emux_detach_seq(snd_emux_t *emu) +{ + if (emu->voices) + snd_emux_terminate_all(emu); + + down(&emu->register_mutex); + if (emu->client >= 0) { + snd_seq_delete_kernel_client(emu->client); + emu->client = -1; + } + up(&emu->register_mutex); +} + + +/* + * create a sequencer port and channel_set + */ + +snd_emux_port_t * +snd_emux_create_port(snd_emux_t *emu, char *name, + int max_channels, int oss_port, + snd_seq_port_callback_t *callback) +{ + snd_emux_port_t *p; + int i, type, cap; + + /* Allocate structures for this channel */ + if ((p = snd_magic_kcalloc(snd_emux_port_t, 0, GFP_KERNEL)) == NULL) { + snd_printk("no memory\n"); + return NULL; + } + p->chset.channels = snd_kcalloc(max_channels * sizeof(snd_midi_channel_t), GFP_KERNEL); + if (p->chset.channels == NULL) { + snd_printk("no memory\n"); + snd_magic_kfree(p); + return NULL; + } + for (i = 0; i < max_channels; i++) + p->chset.channels[i].number = i; + p->chset.private_data = p; + p->chset.max_channels = max_channels; + p->emu = emu; + p->chset.client = emu->client; +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_create_effect(p); +#endif + callback->private_free = free_port; + callback->private_data = p; + + cap = SNDRV_SEQ_PORT_CAP_WRITE; + if (oss_port) { + type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; + } else { + type = DEFAULT_MIDI_TYPE; + cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE; + } + + p->chset.port = snd_seq_event_port_attach(emu->client, callback, + cap, type, name); + + return p; +} + + +/* + * release memory block for port + */ +static void +free_port(void *private_data) +{ + snd_emux_port_t *p; + + p = snd_magic_cast(snd_emux_port_t, private_data, return); + if (p) { +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_delete_effect(p); +#endif + if (p->chset.channels) + kfree(p->chset.channels); + snd_magic_kfree(p); + } +} + + +#define DEFAULT_DRUM_FLAGS (1<<9) + +/* + * initialize the port specific parameters + */ +static void +snd_emux_init_port(snd_emux_port_t *p) +{ + p->drum_flags = DEFAULT_DRUM_FLAGS; + p->volume_atten = 0; + + snd_emux_reset_port(p); +} + + +/* + * reset port + */ +void +snd_emux_reset_port(snd_emux_port_t *port) +{ + int i; + + /* stop all sounds */ + snd_emux_sounds_off_all(port); + + snd_midi_channel_set_clear(&port->chset); + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_clear_effect(port); +#endif + + /* set port specific control parameters */ + port->ctrls[EMUX_MD_DEF_BANK] = 0; + port->ctrls[EMUX_MD_DEF_DRUM] = 0; + port->ctrls[EMUX_MD_REALTIME_PAN] = 1; + + for (i = 0; i < port->chset.max_channels; i++) { + snd_midi_channel_t *chan = port->chset.channels + i; + chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; + } +} + + +/* + * input sequencer event + */ +int +snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private_data, + int atomic, int hop) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(port != NULL && ev != NULL, return -EINVAL); + + snd_midi_process_event(&emux_ops, ev, &port->chset); + + return 0; +} + + +/* + * increment usage count + */ +int +snd_emux_inc_count(snd_emux_t *emu) +{ + emu->used++; + if (!try_inc_mod_count(emu->ops.owner)) + goto __error; + if (!try_inc_mod_count(emu->card->module)) { + dec_mod_count(emu->ops.owner); + __error: + emu->used--; + return 0; + } + return 1; +} + + +/* + * decrease usage count + */ +void +snd_emux_dec_count(snd_emux_t *emu) +{ + dec_mod_count(emu->ops.owner); + emu->used--; + if (emu->used <= 0) + snd_emux_terminate_all(emu); + dec_mod_count(emu->card->module); +} + + +/* + * Routine that is called upon a first use of a particular port + */ +static int +snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + + down(&emu->register_mutex); + snd_emux_init_port(p); + snd_emux_inc_count(emu); + up(&emu->register_mutex); + return 0; +} + +/* + * Routine that is called upon the last unuse() of a particular port. + */ +static int +snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info) +{ + snd_emux_port_t *p; + snd_emux_t *emu; + + p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + snd_assert(p != NULL, return -EINVAL); + emu = p->emu; + snd_assert(emu != NULL, return -EINVAL); + + down(&emu->register_mutex); + snd_emux_sounds_off_all(p); + snd_emux_dec_count(emu); + up(&emu->register_mutex); + return 0; +} + + +/* + * Create a sequencer client + */ +static int +get_client(snd_card_t *card, int index, char *name) +{ + snd_seq_client_callback_t callbacks; + snd_seq_client_info_t cinfo; + int client; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.private_data = NULL; + callbacks.allow_input = 1; + callbacks.allow_output = 1; + + /* Find a free client, start from 1 as the MPU expects to use 0 */ + client = snd_seq_create_kernel_client(card, index, &callbacks); + if (client < 0) + return client; + + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.client = client; + cinfo.type = KERNEL_CLIENT; + strcpy(cinfo.name, name); + snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); + + return client; +} + + +/* + * attach virtual rawmidi devices + */ +int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card) +{ + int i; + + emu->vmidi = NULL; + if (emu->midi_ports <= 0) + return 0; + + emu->vmidi = snd_kcalloc(sizeof(snd_rawmidi_t*) * emu->midi_ports, GFP_KERNEL); + if (emu->vmidi == NULL) + return -ENOMEM; + + for (i = 0; i < emu->midi_ports; i++) { + snd_rawmidi_t *rmidi; + snd_virmidi_dev_t *rdev; + if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) + goto __error; + rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + sprintf(rmidi->name, "%s Synth MIDI", emu->name); + rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; + rdev->client = emu->client; + rdev->port = emu->ports[i]; + if (snd_device_register(card, rmidi) < 0) { + snd_device_free(card, rmidi); + goto __error; + } + emu->vmidi[i] = rmidi; + //snd_printk("virmidi %d ok\n", i); + } + return 0; + +__error: + //snd_printk("error init..\n"); + snd_emux_delete_virmidi(emu); + return -ENOMEM; +} + +int snd_emux_delete_virmidi(snd_emux_t *emu) +{ + int i; + + if (emu->vmidi == NULL) + return 0; + + for (i = 0; i < emu->midi_ports; i++) { + if (emu->vmidi[i]) + snd_device_free(emu->card, emu->vmidi[i]); + } + kfree(emu->vmidi); + emu->vmidi = NULL; + return 0; +} diff -Nru linux/sound/synth/emux/emux_synth.c linux-2.4.19-pre5-mjc/sound/synth/emux/emux_synth.c --- linux/sound/synth/emux/emux_synth.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_synth.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,974 @@ +/* + * Midi synth routines for the Emu8k/Emu10k1 + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * Contains code based on awe_wave.c by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define __NO_VERSION__ +#include "emux_voice.h" +#include + +/* + * Prototypes + */ + +/* + * Ensure a value is between two points + * macro evaluates its args more than once, so changed to upper-case. + */ +#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) +#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) + +static int get_zone(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table); +static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan); +static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free); +static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass); +static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free); +static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update); +static void setup_voice(snd_emux_voice_t *vp); +static int calc_pan(snd_emux_voice_t *vp); +static int calc_volume(snd_emux_voice_t *vp); +static int calc_pitch(snd_emux_voice_t *vp); + + +/* + * Start a note. + */ +void +snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + snd_emux_t *emu; + int i, key, nvoices; + snd_emux_voice_t *vp; + snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES]; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.get_voice != NULL, return); + snd_assert(emu->ops.trigger != NULL, return); + + key = note; /* remember the original note */ + nvoices = get_zone(emu, port, ¬e, vel, chan, table); + if (! nvoices) + return; + + /* exclusive note off */ + for (i = 0; i < nvoices; i++) { + snd_sf_zone_t *zp = table[i]; + if (zp && zp->v.exclusiveClass) + exclusive_note_off(emu, port, zp->v.exclusiveClass); + } + +#if 0 // seems not necessary + /* Turn off the same note on the same channel. */ + terminate_note1(emu, key, chan, 0); +#endif + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < nvoices; i++) { + + /* set up each voice parameter */ + /* at this stage, we don't trigger the voice yet. */ + + if (table[i] == NULL) + continue; + + vp = emu->ops.get_voice(emu, port); + if (vp == NULL || vp->ch < 0) + continue; + snd_assert(vp->emu != NULL && vp->hw != NULL, return); + if (STATE_IS_PLAYING(vp->state)) + emu->ops.terminate(vp); + + vp->time = emu->use_time++; + vp->chan = chan; + vp->port = port; + vp->key = key; + vp->note = note; + vp->velocity = vel; + vp->zone = table[i]; + if (vp->zone->sample) + vp->block = vp->zone->sample->block; + else + vp->block = NULL; + + setup_voice(vp); + + vp->state = SNDRV_EMUX_ST_STANDBY; + if (emu->ops.prepare) { + vp->state = SNDRV_EMUX_ST_OFF; + if (emu->ops.prepare(vp) >= 0) + vp->state = SNDRV_EMUX_ST_STANDBY; + } + } + + /* start envelope now */ + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->state == SNDRV_EMUX_ST_STANDBY && + vp->chan == chan) { + emu->ops.trigger(vp); + vp->state = SNDRV_EMUX_ST_ON; + vp->ontime = jiffies; /* remember the trigger timing */ + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { + /* clear voice position for the next note on this channel */ + snd_emux_effect_table_t *fx = chan->private; + if (fx) { + fx->flag[EMUX_FX_SAMPLE_START] = 0; + fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0; + } + } +#endif +} + +/* + * Release a note in response to a midi note off. + */ +void +snd_emux_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + int ch; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.release != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (STATE_IS_PLAYING(vp->state) && + vp->chan == chan && vp->key == note) { + vp->time = emu->use_time++; + vp->state = SNDRV_EMUX_ST_RELEASED; + if (vp->ontime == jiffies) { + /* if note-off is sent too shortly after + * note-on, emuX engine cannot produce the sound + * correctly. so we'll release this note + * a bit later via timer callback. + */ + vp->state = SNDRV_EMUX_ST_PENDING; + if (! emu->timer_active) { + emu->tlist.expires = jiffies + 1; + add_timer(&emu->tlist); + emu->timer_active = 1; + } + } else + /* ok now release the note */ + emu->ops.release(vp); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * timer callback + * + * release the pending note-offs + */ +void snd_emux_timer_callback(unsigned long data) +{ + snd_emux_t *emu = snd_magic_cast(snd_emux_t, (void*)data, return); + snd_emux_voice_t *vp; + int ch, do_again = 0; + + spin_lock(&emu->voice_lock); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (vp->state == SNDRV_EMUX_ST_PENDING) { + if (vp->ontime == jiffies) + do_again++; /* release this at the next interrupt */ + else { + emu->ops.release(vp); + vp->state = SNDRV_EMUX_ST_RELEASED; + } + } + } + if (do_again) { + emu->tlist.expires = jiffies + 1; + add_timer(&emu->tlist); + emu->timer_active = 1; + } else + emu->timer_active = 0; + spin_unlock(&emu->voice_lock); +} + +/* + * key pressure change + */ +void +snd_emux_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) +{ + int ch; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (ch = 0; ch < emu->max_voices; ch++) { + vp = &emu->voices[ch]; + if (vp->state == SNDRV_EMUX_ST_ON && + vp->chan == chan && vp->key == note) { + vp->velocity = vel; + update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Modulate the voices which belong to the channel + */ +void +snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + if (! update) + return; + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->chan == chan) + update_voice(emu, vp, update); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * Modulate all the voices which belong to the port. + */ +void +snd_emux_update_port(snd_emux_port_t *port, int update) +{ + snd_emux_t *emu; + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + if (! update) + return; + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.update != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (vp->port == port) + update_voice(emu, vp, update); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Deal with a controler type event. This includes all types of + * control events, not just the midi controllers + */ +void +snd_emux_control(void *p, int type, snd_midi_channel_t *chan) +{ + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + switch (type) { + case MIDI_CTL_MSB_MAIN_VOLUME: + case MIDI_CTL_MSB_EXPRESSION: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME); + break; + + case MIDI_CTL_MSB_PAN: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); + break; + + case MIDI_CTL_SOFT_PEDAL: +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + /* FIXME: this is an emulation */ + snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160, + EMUX_FX_FLAG_ADD); +#endif + break; + + case MIDI_CTL_PITCHBEND: + snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH); + break; + + case MIDI_CTL_MSB_MODWHEEL: + case MIDI_CTL_CHAN_PRESSURE: + snd_emux_update_channel(port, chan, + SNDRV_EMUX_UPDATE_FMMOD | + SNDRV_EMUX_UPDATE_FM2FRQ2); + break; + + } + + if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) { + snd_emux_xg_control(port, chan, type); + } +} + + +/* + * for Emu10k1 - release at least 1 voice currently using + */ +int +snd_emux_release_voice(snd_emux_t *emu) +{ + return 0; +} + + +/* + * terminate note - if free flag is true, free the terminated voice + */ +static void +terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free) +{ + int i; + snd_emux_voice_t *vp; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && vp->chan == chan && + vp->key == note) + terminate_voice(emu, vp, free); + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * terminate note - exported for midi emulation + */ +void +snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan) +{ + snd_emux_t *emu; + snd_emux_port_t *port; + + port = snd_magic_cast(snd_emux_port_t, p, return); + snd_assert(port != NULL && chan != NULL, return); + + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.terminate != NULL, return); + + terminate_note1(emu, note, chan, 1); +} + + +/* + * Terminate all the notes + */ +void +snd_emux_terminate_all(snd_emux_t *emu) +{ + int i; + snd_emux_voice_t *vp; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state)) + terminate_voice(emu, vp, 0); + if (vp->state == SNDRV_EMUX_ST_OFF) { + if (emu->ops.free_voice) + emu->ops.free_voice(vp); + if (emu->ops.reset) + emu->ops.reset(emu, i); + } + vp->time = 0; + } + /* initialize allocation time */ + emu->use_time = 0; + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Terminate all voices associated with the given port + */ +void +snd_emux_sounds_off_all(snd_emux_port_t *port) +{ + int i; + snd_emux_t *emu; + snd_emux_voice_t *vp; + unsigned long flags; + + snd_assert(port != NULL, return); + emu = port->emu; + snd_assert(emu != NULL, return); + snd_assert(emu->ops.terminate != NULL, return); + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && + vp->port == port) + terminate_voice(emu, vp, 0); + if (vp->state == SNDRV_EMUX_ST_OFF) { + if (emu->ops.free_voice) + emu->ops.free_voice(vp); + if (emu->ops.reset) + emu->ops.reset(emu, i); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + + +/* + * Terminate all voices that have the same exclusive class. This + * is mainly for drums. + */ +static void +exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass) +{ + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + if (STATE_IS_PLAYING(vp->state) && vp->port == port && + vp->reg.exclusiveClass == exclass) { + terminate_voice(emu, vp, 0); + } + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + * terminate a voice + * if free flag is true, call free_voice after termination + */ +static void +terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free) +{ + emu->ops.terminate(vp); + vp->time = emu->use_time++; + vp->chan = NULL; + vp->port = NULL; + vp->zone = NULL; + vp->block = NULL; + vp->state = SNDRV_EMUX_ST_OFF; + if (free && emu->ops.free_voice) + emu->ops.free_voice(vp); +} + + +/* + * Modulate the voice + */ +static void +update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update) +{ + if (!STATE_IS_PLAYING(vp->state)) + return; + + if (vp->chan == NULL || vp->port == NULL) + return; + if (update & SNDRV_EMUX_UPDATE_VOLUME) + calc_volume(vp); + if (update & SNDRV_EMUX_UPDATE_PITCH) + calc_pitch(vp); + if (update & SNDRV_EMUX_UPDATE_PAN) { + if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN)) + return; + } + emu->ops.update(vp, update); +} + + +#if 0 // not used +/* table for volume target calculation */ +static unsigned short voltarget[16] = { + 0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58, + 0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90 +}; +#endif + +#define LO_BYTE(v) ((v) & 0xff) +#define HI_BYTE(v) (((v) >> 8) & 0xff) + +/* + * Sets up the voice structure by calculating some values that + * will be needed later. + */ +static void +setup_voice(snd_emux_voice_t *vp) +{ + soundfont_voice_parm_t *parm; + int pitch; + + /* copy the original register values */ + vp->reg = vp->zone->v; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + snd_emux_setup_effect(vp); +#endif + + /* reset status */ + vp->apan = -1; + vp->avol = -1; + vp->apitch = -1; + + calc_volume(vp); + calc_pitch(vp); + calc_pan(vp); + + parm = &vp->reg.parm; + + /* compute filter target and correct modulation parameters */ + if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) { + parm->moddelay = 0xbfff; + pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch; + if (pitch > 0xffff) + pitch = 0xffff; + /* calculate filter target */ + vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe); + LIMITVALUE(vp->ftarget, 0, 255); + vp->ftarget <<= 8; + } else { + vp->ftarget = parm->cutoff; + vp->ftarget <<= 8; + pitch = vp->apitch; + } + + /* compute pitch target */ + if (pitch != 0xffff) { + vp->ptarget = 1 << (pitch >> 12); + if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710; + if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710; + if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710; + vp->ptarget += (vp->ptarget >> 1); + if (vp->ptarget > 0xffff) vp->ptarget = 0xffff; + } else + vp->ptarget = 0xffff; + + if (LO_BYTE(parm->modatkhld) >= 0x80) { + parm->modatkhld &= ~0xff; + parm->modatkhld |= 0x7f; + } + + /* compute volume target and correct volume parameters */ + vp->vtarget = 0; +#if 0 /* FIXME: this leads to some clicks.. */ + if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) { + parm->voldelay = 0xbfff; + vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4); + } +#endif + + if (LO_BYTE(parm->volatkhld) >= 0x80) { + parm->volatkhld &= ~0xff; + parm->volatkhld |= 0x7f; + } +} + +/* + * calculate pitch parameter + */ +static unsigned char pan_volumes[256] = { +0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a, +0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52, +0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75, +0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92, +0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab, +0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0, +0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1, +0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf, +0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9, +0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1, +0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7, +0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb, +0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, +0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +}; + +static int +calc_pan(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + int pan; + + /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ + if (vp->reg.fixpan > 0) /* 0-127 */ + pan = 255 - (int)vp->reg.fixpan * 2; + else { + pan = chan->control[MIDI_CTL_MSB_PAN] - 64; + if (vp->reg.pan >= 0) /* 0-127 */ + pan += vp->reg.pan - 64; + pan = 127 - (int)pan * 2; + } + LIMITVALUE(pan, 0, 255); + +#if 1 + /* using volume table */ + if (vp->apan != (int)pan_volumes[pan]) { + vp->apan = pan_volumes[pan]; + vp->aaux = pan_volumes[255 - pan]; + return 1; + } + return 0; +#else + /* assuming linear volume */ + if (pan != vp->apan) { + vp->apan = pan; + if (pan == 0) + vp->aaux = 0xff; + else + vp->aaux = (-pan) & 0xff; + return 1; + } else + return 0; +#endif +} + + +/* + * calculate volume attenuation + * + * Voice volume is controlled by volume attenuation parameter. + * So volume becomes maximum when avol is 0 (no attenuation), and + * minimum when 255 (-96dB or silence). + */ + +/* tables for volume->attenuation calculation */ +static unsigned char voltab1[128] = { + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, + 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, + 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char voltab2[128] = { + 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, + 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, + 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char expressiontab[128] = { + 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, + 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, + 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, + 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, + 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, + 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * Magic to calculate the volume (actually attenuation) from all the + * voice and channels parameters. + */ +static int +calc_volume(snd_emux_voice_t *vp) +{ + int vol; + int main_vol, expression_vol, master_vol; + snd_midi_channel_t *chan = vp->chan; + snd_emux_port_t *port = vp->port; + + expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION]; + LIMITMAX(vp->velocity, 127); + LIMITVALUE(expression_vol, 0, 127); + if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { + /* 0 - 127 */ + main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME]; + vol = (vp->velocity * main_vol * expression_vol) / (127*127); + vol = vol * vp->reg.amplitude / 127; + + LIMITVALUE(vol, 0, 127); + + /* calc to attenuation */ + vol = snd_sf_vol_table[vol]; + + } else { + main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127; + LIMITVALUE(main_vol, 0, 127); + + vol = voltab1[main_vol] + voltab2[vp->velocity]; + vol = (vol * 8) / 3; + vol += vp->reg.attenuation; + vol += ((0x100 - vol) * expressiontab[expression_vol])/128; + } + + master_vol = port->chset.gs_master_volume; + LIMITVALUE(master_vol, 0, 127); + vol += snd_sf_vol_table[master_vol]; + vol += port->volume_atten; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + if (chan->private) { + snd_emux_effect_table_t *fx = chan->private; + vol += fx->val[EMUX_FX_ATTEN]; + } +#endif + + LIMITVALUE(vol, 0, 255); + if (vp->avol == vol) + return 0; /* value unchanged */ + + vp->avol = vol; + if (!SF_IS_DRUM_BANK(get_bank(port, chan)) + && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) { + int atten; + if (vp->velocity < 70) + atten = 70; + else + atten = vp->velocity; + vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7; + } else { + vp->acutoff = vp->reg.parm.cutoff; + } + + return 1; /* value changed */ +} + +/* + * calculate pitch offset + * + * 0xE000 is no pitch offset at 44100Hz sample. + * Every 4096 is one octave. + */ + +static int +calc_pitch(snd_emux_voice_t *vp) +{ + snd_midi_channel_t *chan = vp->chan; + int offset; + + /* calculate offset */ + if (vp->reg.fixkey >= 0) { + offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12; + } else { + offset = (vp->note - vp->reg.root) * 4096 / 12; + } + offset = (offset * vp->reg.scaleTuning) / 100; + offset += vp->reg.tune * 4096 / 1200; + if (chan->midi_pitchbend != 0) { + /* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */ + offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072; + } + + /* tuning via RPN: + * coarse = -8192 to 8192 (100 cent per 128) + * fine = -8192 to 8192 (max=100cent) + */ + /* 4096 = 1200 cents in emu8000 parameter */ + offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128); + offset += chan->gm_rpn_fine_tuning / 24; + +#ifdef SNDRV_EMUX_USE_RAW_EFFECT + /* add initial pitch correction */ + if (chan->private) { + snd_emux_effect_table_t *fx = chan->private; + if (fx->flag[EMUX_FX_INIT_PITCH]) + offset += fx->val[EMUX_FX_INIT_PITCH]; + } +#endif + + /* 0xe000: root pitch */ + offset += 0xe000 + vp->reg.rate_offset; + offset += vp->emu->pitch_shift; + LIMITVALUE(offset, 0, 0xffff); + if (offset == vp->apitch) + return 0; /* unchanged */ + vp->apitch = offset; + return 1; /* value changed */ +} + +/* + * Get the bank number assigned to the channel + */ +static int +get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan) +{ + int val; + + switch (port->chset.midi_mode) { + case SNDRV_MIDI_MODE_XG: + val = chan->control[MIDI_CTL_MSB_BANK]; + if (val == 127) + return 128; /* return drum bank */ + return chan->control[MIDI_CTL_LSB_BANK]; + + case SNDRV_MIDI_MODE_GS: + if (chan->drum_channel) + return 128; + /* ignore LSB (bank map) */ + return chan->control[MIDI_CTL_MSB_BANK]; + + default: + if (chan->drum_channel) + return 128; + return chan->control[MIDI_CTL_MSB_BANK]; + } +} + + +/* Look for the zones matching with the given note and velocity. + * The resultant zones are stored on table. + */ +static int +get_zone(snd_emux_t *emu, snd_emux_port_t *port, + int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table) +{ + int preset, bank, def_preset, def_bank; + + bank = get_bank(port, chan); + preset = chan->midi_program; + + if (SF_IS_DRUM_BANK(bank)) { + def_preset = port->ctrls[EMUX_MD_DEF_DRUM]; + def_bank = bank; + } else { + def_preset = preset; + def_bank = port->ctrls[EMUX_MD_DEF_BANK]; + } + + return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank, + def_preset, def_bank, + table, SNDRV_EMUX_MAX_MULTI_VOICES); +} + +/* + */ +void +snd_emux_init_voices(snd_emux_t *emu) +{ + snd_emux_voice_t *vp; + int i; + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + for (i = 0; i < emu->max_voices; i++) { + vp = &emu->voices[i]; + vp->ch = -1; /* not used */ + vp->state = SNDRV_EMUX_ST_OFF; + vp->chan = NULL; + vp->port = NULL; + vp->time = 0; + vp->emu = emu; + vp->hw = emu->hw; + } + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + */ +void snd_emux_lock_voice(snd_emux_t *emu, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF) + emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED; + else + snd_printk("invalid voice for lock %d (state = %x)\n", + voice, emu->voices[voice].state); + spin_unlock_irqrestore(&emu->voice_lock, flags); +} + +/* + */ +void snd_emux_unlock_voice(snd_emux_t *emu, int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&emu->voice_lock, flags); + if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED) + emu->voices[voice].state = SNDRV_EMUX_ST_OFF; + else + snd_printk("invalid voice for unlock %d (state = %x)\n", + voice, emu->voices[voice].state); + spin_unlock_irqrestore(&emu->voice_lock, flags); +} diff -Nru linux/sound/synth/emux/emux_voice.h linux-2.4.19-pre5-mjc/sound/synth/emux/emux_voice.h --- linux/sound/synth/emux/emux_voice.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/emux_voice.h Mon Apr 8 22:31:24 2002 @@ -0,0 +1,84 @@ +#ifndef __EMUX_VOICE_H +#define __EMUX_VOICE_H + +/* + * A structure to keep track of each hardware voice + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +/* Prototypes for emux_seq.c */ +int snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index); +void snd_emux_detach_seq(snd_emux_t *emu); +snd_emux_port_t *snd_emux_create_port(snd_emux_t *emu, char *name, int max_channels, int type, snd_seq_port_callback_t *callback); +void snd_emux_reset_port(snd_emux_port_t *port); +int snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); +int snd_emux_inc_count(snd_emux_t *emu); +void snd_emux_dec_count(snd_emux_t *emu); +int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card); +int snd_emux_delete_virmidi(snd_emux_t *emu); + +/* Prototypes for emux_synth.c */ +void snd_emux_init_voices(snd_emux_t *emu); + +void snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); +void snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan); +void snd_emux_control(void *p, int type, struct snd_midi_channel *chan); + +void snd_emux_sounds_off_all(snd_emux_port_t *port); +void snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update); +void snd_emux_update_port(snd_emux_port_t *port, int update); + +void snd_emux_timer_callback(unsigned long data); + +/* emux_effect.c */ +#ifdef SNDRV_EMUX_USE_RAW_EFFECT +void snd_emux_create_effect(snd_emux_port_t *p); +void snd_emux_delete_effect(snd_emux_port_t *p); +void snd_emux_clear_effect(snd_emux_port_t *p); +void snd_emux_setup_effect(snd_emux_voice_t *vp); +void snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val); +void snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode); +#endif + +/* emux_nrpn.c */ +void snd_emux_sysex(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); +int snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param); +void snd_emux_nrpn(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); + +/* emux_oss.c */ +void snd_emux_init_seq_oss(snd_emux_t *emu); +void snd_emux_detach_seq_oss(snd_emux_t *emu); + +/* emux_proc.c */ +#ifdef CONFIG_PROC_FS +void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device); +void snd_emux_proc_free(snd_emux_t *emu); +#endif + +#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON) + +#endif diff -Nru linux/sound/synth/emux/soundfont.c linux-2.4.19-pre5-mjc/sound/synth/emux/soundfont.c --- linux/sound/synth/emux/soundfont.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/emux/soundfont.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,1462 @@ +/* + * Soundfont generic routines. + * It is intended that these should be used by any driver that is willing + * to accept soundfont patches. + * + * Copyright (C) 1999 Steve Ratcliffe + * Copyright (c) 1999-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Deal with reading in of a soundfont. Code follows the OSS way + * of doing things so that the old sfxload utility can be used. + * Everything may change when there is an alsa way of doing things. + */ +#define __NO_VERSION__ +#include +#include +#include +#include +#include + +/* Prototypes for static functions */ + +static int open_patch(snd_sf_list_t *sflist, const char *data, int count, int client); +static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name); +static int is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name); +static int close_patch(snd_sf_list_t *sflist); +static int probe_data(snd_sf_list_t *sflist, int sample_id); +static void set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp); +static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); +static void set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); +static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); +static void sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); +static int load_map(snd_sf_list_t *sflist, const void *data, int count); +static int load_info(snd_sf_list_t *sflist, const void *data, long count); +static int remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr); +static void init_voice_info(soundfont_voice_info_t *avp); +static void init_voice_parm(soundfont_voice_parm_t *pp); +static snd_sf_sample_t *set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp); +static snd_sf_sample_t *find_sample(snd_soundfont_t *sf, int sample_id); +static int load_data(snd_sf_list_t *sflist, const void *data, long count); +static void rebuild_presets(snd_sf_list_t *sflist); +static void add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur); +static void delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp); +static snd_sf_zone_t *search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key); +static int search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level); +static int get_index(int bank, int instr, int key); +static void snd_sf_init(snd_sf_list_t *sflist); +static void snd_sf_clear(snd_sf_list_t *sflist); + +/* + * lock access to sflist + */ +static int +lock_preset(snd_sf_list_t *sflist, int nonblock) +{ + unsigned long flags; + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->sf_locked && nonblock) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + down(&sflist->presets_mutex); + sflist->sf_locked = 1; + return 0; +} + + +/* + * remove lock + */ +static void +unlock_preset(snd_sf_list_t *sflist) +{ + up(&sflist->presets_mutex); + sflist->sf_locked = 0; +} + + +/* + * close the patch if the patch was opened by this client. + */ +int +snd_soundfont_close_check(snd_sf_list_t *sflist, int client) +{ + unsigned long flags; + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client == client) { + spin_unlock_irqrestore(&sflist->lock, flags); + return close_patch(sflist); + } + spin_unlock_irqrestore(&sflist->lock, flags); + return 0; +} + + +/* + * Deal with a soundfont patch. Any driver could use these routines + * although it was designed for the AWE64. + * + * The sample_write and callargs pararameters allow a callback into + * the actual driver to write sample data to the board or whatever + * it wants to do with it. + */ +int +snd_soundfont_load(snd_sf_list_t *sflist, const void *data, long count, int client) +{ + soundfont_patch_info_t patch; + unsigned long flags; + int rc; + + if (count < sizeof(patch)) { + snd_printk("patch record too small %ld\n", count); + return -EINVAL; + } + if (copy_from_user(&patch, data, sizeof(patch))) + return -EFAULT; + + count -= sizeof(patch); + data += sizeof(patch); + + if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) { + snd_printk("'The wrong kind of patch' %x\n", patch.key); + return -EINVAL; + } + if (count < patch.len) { + snd_printk("Patch too short %ld, need %d\n", count, patch.len); + return -EINVAL; + } + if (patch.len < 0) { + snd_printk("poor length %d\n", patch.len); + return -EINVAL; + } + + if (patch.type == SNDRV_SFNT_OPEN_PATCH) { + /* grab sflist to open */ + lock_preset(sflist, 0); + rc = open_patch(sflist, data, count, client); + unlock_preset(sflist); + return rc; + } + + /* check if other client already opened patch */ + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client != client) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + + lock_preset(sflist, 0); + rc = -EINVAL; + switch (patch.type) { + case SNDRV_SFNT_LOAD_INFO: + rc = load_info(sflist, data, count); + break; + case SNDRV_SFNT_LOAD_DATA: + rc = load_data(sflist, data, count); + break; + case SNDRV_SFNT_CLOSE_PATCH: + rc = close_patch(sflist); + break; + case SNDRV_SFNT_REPLACE_DATA: + /*rc = replace_data(&patch, data, count);*/ + break; + case SNDRV_SFNT_MAP_PRESET: + rc = load_map(sflist, data, count); + break; + case SNDRV_SFNT_PROBE_DATA: + rc = probe_data(sflist, patch.optarg); + break; + case SNDRV_SFNT_REMOVE_INFO: + /* patch must be opened */ + if (sflist->currsf) { + snd_printk("soundfont: remove_info: patch not opened\n"); + rc = -EINVAL; + } else { + int bank, instr; + bank = ((unsigned short)patch.optarg >> 8) & 0xff; + instr = (unsigned short)patch.optarg & 0xff; + if (! remove_info(sflist, sflist->currsf, bank, instr)) + rc = -EINVAL; + else + rc = 0; + } + break; + } + unlock_preset(sflist); + + return rc; +} + + +/* check if specified type is special font (GUS or preset-alias) */ +static inline int +is_special_type(int type) +{ + type &= 0x0f; + return (type == SNDRV_SFNT_PAT_TYPE_GUS || + type == SNDRV_SFNT_PAT_TYPE_MAP); +} + + +/* open patch; create sf list */ +static int +open_patch(snd_sf_list_t *sflist, const char *data, int count, int client) +{ + soundfont_open_parm_t parm; + snd_soundfont_t *sf; + unsigned long flags; + + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->open_client >= 0 || sflist->currsf) { + spin_unlock_irqrestore(&sflist->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&sflist->lock, flags); + + if (copy_from_user(&parm, data, sizeof(parm))) + return -EFAULT; + + if (is_special_type(parm.type)) { + parm.type |= SNDRV_SFNT_PAT_SHARED; + sf = newsf(sflist, parm.type, NULL); + } else + sf = newsf(sflist, parm.type, parm.name); + if (sf == NULL) { + return -ENOMEM; + } + + sflist->open_client = client; + sflist->currsf = sf; + + return 0; +} + +/* + * Allocate a new soundfont structure. + */ +static snd_soundfont_t * +newsf(snd_sf_list_t *sflist, int type, char *name) +{ + snd_soundfont_t *sf; + + /* check the shared fonts */ + if (type & SNDRV_SFNT_PAT_SHARED) { + for (sf = sflist->fonts; sf; sf = sf->next) { + if (is_identical_font(sf, type, name)) { + return sf; + } + } + } + + /* not found -- create a new one */ + sf = (snd_soundfont_t*)snd_kcalloc(sizeof(*sf), GFP_KERNEL); + if (sf == NULL) + return NULL; + sf->id = sflist->fonts_size; + sflist->fonts_size++; + + /* prepend this record */ + sf->next = sflist->fonts; + sflist->fonts = sf; + + sf->type = type; + sf->zones = NULL; + sf->samples = NULL; + if (name) + memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN); + + return sf; +} + +/* check if the given name matches to the existing list */ +static int +is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name) +{ + return ((sf->type & SNDRV_SFNT_PAT_SHARED) && + (sf->type & 0x0f) == (type & 0x0f) && + (name == NULL || + memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0)); +} + +/* + * Close the current patch. + */ +static int +close_patch(snd_sf_list_t *sflist) +{ + sflist->currsf = NULL; + sflist->open_client = -1; + + rebuild_presets(sflist); + + return 0; + +} + +/* probe sample in the current list -- nothing to be loaded */ +static int +probe_data(snd_sf_list_t *sflist, int sample_id) +{ + /* patch must be opened */ + if (sflist->currsf) { + /* search the specified sample by optarg */ + if (find_sample(sflist->currsf, sample_id)) + return 0; + } + return -EINVAL; +} + +/* + * increment zone counter + */ +static void +set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp) +{ + zp->counter = sflist->zone_counter++; + if (sf->type & SNDRV_SFNT_PAT_LOCKED) + sflist->zone_locked = sflist->zone_counter; +} + +/* + * allocate a new zone record + */ +static snd_sf_zone_t * +sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) +{ + snd_sf_zone_t *zp; + + if ((zp = snd_kcalloc(sizeof(*zp), GFP_KERNEL)) == NULL) + return NULL; + zp->next = sf->zones; + sf->zones = zp; + + init_voice_info(&zp->v); + + set_zone_counter(sflist, sf, zp); + return zp; +} + + +/* + * increment sample couter + */ +static void +set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) +{ + sp->counter = sflist->sample_counter++; + if (sf->type & SNDRV_SFNT_PAT_LOCKED) + sflist->sample_locked = sflist->sample_counter; +} + +/* + * allocate a new sample list record + */ +static snd_sf_sample_t * +sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) +{ + snd_sf_sample_t *sp; + + if ((sp = snd_kcalloc(sizeof(*sp), GFP_KERNEL)) == NULL) + return NULL; + + sp->next = sf->samples; + sf->samples = sp; + + set_sample_counter(sflist, sf, sp); + return sp; +} + +/* + * delete sample list -- this is an exceptional job. + * only the last allocated sample can be deleted. + */ +static void +sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) +{ + /* only last sample is accepted */ + if (sp == sf->samples) { + sf->samples = sp->next; + kfree(sp); + } +} + + +/* load voice map */ +static int +load_map(snd_sf_list_t *sflist, const void *data, int count) +{ + snd_sf_zone_t *zp, *prevp; + snd_soundfont_t *sf; + soundfont_voice_map_t map; + + /* get the link info */ + if (count < sizeof(map)) + return -EINVAL; + if (copy_from_user(&map, data, sizeof(map))) + return -EFAULT; + + if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS) + return -EINVAL; + + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL); + if (sf == NULL) + return -ENOMEM; + + prevp = NULL; + for (zp = sf->zones; zp; prevp = zp, zp = zp->next) { + if (zp->mapped && + zp->instr == map.map_instr && + zp->bank == map.map_bank && + zp->v.low == map.map_key && + zp->v.start == map.src_instr && + zp->v.end == map.src_bank && + zp->v.fixkey == map.src_key) { + /* the same mapping is already present */ + /* relink this record to the link head */ + if (prevp) { + prevp->next = zp->next; + zp->next = sf->zones; + sf->zones = zp; + } + /* update the counter */ + set_zone_counter(sflist, sf, zp); + return 0; + } + } + + /* create a new zone */ + if ((zp = sf_zone_new(sflist, sf)) == NULL) + return -ENOMEM; + + zp->bank = map.map_bank; + zp->instr = map.map_instr; + zp->mapped = 1; + if (map.map_key >= 0) { + zp->v.low = map.map_key; + zp->v.high = map.map_key; + } + zp->v.start = map.src_instr; + zp->v.end = map.src_bank; + zp->v.fixkey = map.src_key; + zp->v.sf_id = sf->id; + + add_preset(sflist, zp); + + return 0; +} + + +/* remove the present instrument layers */ +static int +remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr) +{ + snd_sf_zone_t *prev, *next, *p; + int removed = 0; + + prev = NULL; + for (p = sf->zones; p; p = next) { + next = p->next; + if (! p->mapped && + p->bank == bank && p->instr == instr) { + /* remove this layer */ + if (prev) + prev->next = next; + else + sf->zones = next; + removed++; + kfree(p); + } else + prev = p; + } + if (removed) + rebuild_presets(sflist); + return removed; +} + + +/* + * Read an info record from the user buffer and save it on the current + * open soundfont. + */ +static int +load_info(snd_sf_list_t *sflist, const void *data, long count) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *zone; + soundfont_voice_rec_hdr_t hdr; + int i; + + /* patch must be opened */ + if ((sf = sflist->currsf) == NULL) + return -EINVAL; + + if (is_special_type(sf->type)) + return -EINVAL; + + if (count < sizeof(hdr)) { + printk("Soundfont error: invalid patch zone length\n"); + return -EINVAL; + } + if (copy_from_user((char*)&hdr, data, sizeof(hdr))) + return -EFAULT; + + data += sizeof(hdr); + count -= sizeof(hdr); + + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices); + return -EINVAL; + } + + if (count < sizeof(soundfont_voice_info_t)*hdr.nvoices) { + printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return -EINVAL; + } + + switch (hdr.write_mode) { + case SNDRV_SFNT_WR_EXCLUSIVE: + /* exclusive mode - if the instrument already exists, + return error */ + for (zone = sf->zones; zone; zone = zone->next) { + if (!zone->mapped && + zone->bank == hdr.bank && + zone->instr == hdr.instr) + return -EINVAL; + } + break; + case SNDRV_SFNT_WR_REPLACE: + /* replace mode - remove the instrument if it already exists */ + remove_info(sflist, sf, hdr.bank, hdr.instr); + break; + } + + for (i = 0; i < hdr.nvoices; i++) { + snd_sf_zone_t tmpzone; + + /* copy awe_voice_info parameters */ + if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) { + return -EFAULT; + } + + data += sizeof(tmpzone.v); + count -= sizeof(tmpzone.v); + + tmpzone.bank = hdr.bank; + tmpzone.instr = hdr.instr; + tmpzone.mapped = 0; + tmpzone.v.sf_id = sf->id; + if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM) + init_voice_parm(&tmpzone.v.parm); + + /* create a new zone */ + if ((zone = sf_zone_new(sflist, sf)) == NULL) { + return -ENOMEM; + } + + /* copy the temporary data */ + zone->bank = tmpzone.bank; + zone->instr = tmpzone.instr; + zone->v = tmpzone.v; + + /* look up the sample */ + zone->sample = set_sample(sf, &zone->v); + } + + return 0; +} + + +/* initialize voice_info record */ +static void +init_voice_info(soundfont_voice_info_t *avp) +{ + memset(avp, 0, sizeof(*avp)); + + avp->root = 60; + avp->high = 127; + avp->velhigh = 127; + avp->fixkey = -1; + avp->fixvel = -1; + avp->fixpan = -1; + avp->pan = -1; + avp->amplitude = 127; + avp->scaleTuning = 100; + + init_voice_parm(&avp->parm); +} + +/* initialize voice_parm record: + * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. + * Vibrato and Tremolo effects are zero. + * Cutoff is maximum. + * Chorus and Reverb effects are zero. + */ +static void +init_voice_parm(soundfont_voice_parm_t *pp) +{ + memset(pp, 0, sizeof(*pp)); + + pp->moddelay = 0x8000; + pp->modatkhld = 0x7f7f; + pp->moddcysus = 0x7f7f; + pp->modrelease = 0x807f; + + pp->voldelay = 0x8000; + pp->volatkhld = 0x7f7f; + pp->voldcysus = 0x7f7f; + pp->volrelease = 0x807f; + + pp->lfo1delay = 0x8000; + pp->lfo2delay = 0x8000; + + pp->cutoff = 0xff; +} + +/* search the specified sample */ +static snd_sf_sample_t * +set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp) +{ + snd_sf_sample_t *sample; + + sample = find_sample(sf, avp->sample); + if (sample == NULL) + return NULL; + + /* add in the actual sample offsets: + * The voice_info addresses define only the relative offset + * from sample pointers. Here we calculate the actual DRAM + * offset from sample pointers. + */ + avp->start += sample->v.start; + avp->end += sample->v.end; + avp->loopstart += sample->v.loopstart; + avp->loopend += sample->v.loopend; + + /* copy mode flags */ + avp->sample_mode = sample->v.mode_flags; + + return sample; +} + +/* find the sample pointer with the given id in the soundfont */ +static snd_sf_sample_t * +find_sample(snd_soundfont_t *sf, int sample_id) +{ + snd_sf_sample_t *p; + + if (sf == NULL) + return NULL; + + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample_id) + return p; + } + return NULL; +} + + +/* + * Load sample information, this can include data to be loaded onto + * the soundcard. It can also just be a pointer into soundcard ROM. + * If there is data it will be written to the soundcard via the callback + * routine. + */ +static int +load_data(snd_sf_list_t *sflist, const void *data, long count) +{ + snd_soundfont_t *sf; + soundfont_sample_info_t sample_info; + snd_sf_sample_t *sp; + long off; + + /* patch must be opened */ + if ((sf = sflist->currsf) == NULL) + return -EINVAL; + + if (is_special_type(sf->type)) + return -EINVAL; + + if (copy_from_user(&sample_info, data, sizeof(sample_info))) + return -EFAULT; + + off = sizeof(sample_info); + + if (sample_info.size != (count-off)/2) + return -EINVAL; + + /* Check for dup */ + if (find_sample(sf, sample_info.sample)) { + /* if shared sample, skip this data */ + if (sf->type & SNDRV_SFNT_PAT_SHARED) + return 0; + return -EINVAL; + } + + /* Allocate a new sample structure */ + if ((sp = sf_sample_new(sflist, sf)) == NULL) + return -ENOMEM; + + sp->v = sample_info; + sp->v.sf_id = sf->id; + sp->v.dummy = 0; + sp->v.truesize = sp->v.size; + + /* + * If there is wave data then load it. + */ + if (sp->v.size > 0) { + int rc; + rc = sflist->callback.sample_new + (sflist->callback.private_data, sp, sflist->memhdr, + data + off, count - off); + if (rc < 0) { + sf_sample_delete(sflist, sf, sp); + return rc; + } + sflist->mem_used += sp->v.truesize; + } + + return count; +} + + +/* log2_tbl[i] = log2(i+128) * 0x10000 */ +static int log_tbl[129] = { + 0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa, + 0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed, + 0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08, + 0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019, + 0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a, + 0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382, + 0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404, + 0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2, + 0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9, + 0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188, + 0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89, + 0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07, + 0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c, + 0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f, + 0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8, + 0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d, + 0x80000, +}; + +/* convert from linear to log value + * + * conversion: value = log2(amount / base) * ratio + * + * argument: + * amount = linear value (unsigned, 32bit max) + * offset = base offset (:= log2(base) * 0x10000) + * ratio = division ratio + * + */ +int +snd_sf_linear_to_log(unsigned int amount, int offset, int ratio) +{ + int v; + int s, low, bit; + + if (amount < 2) + return 0; + for (bit = 0; ! (amount & 0x80000000L); bit++) + amount <<= 1; + s = (amount >> 24) & 0x7f; + low = (amount >> 16) & 0xff; + /* linear approxmimation by lower 8 bit */ + v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8; + v -= offset; + v = (v * ratio) >> 16; + v += (24 - bit) * ratio; + return v; +} + +#define OFFSET_MSEC 653117 /* base = 1000 */ +#define OFFSET_ABSCENT 851781 /* base = 8176 */ +#define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ + +#define ABSCENT_RATIO 1200 +#define TIMECENT_RATIO 1200 +#define SAMPLERATE_RATIO 4096 + +/* + * mHz to abscent + * conversion: abscent = log2(MHz / 8176) * 1200 + */ +static int +freq_to_note(int mhz) +{ + return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO); +} + +/* convert Hz to AWE32 rate offset: + * sample pitch offset for the specified sample rate + * rate=44100 is no offset, each 4096 is 1 octave (twice). + * eg, when rate is 22050, this offset becomes -4096. + * + * conversion: offset = log2(Hz / 44100) * 4096 + */ +static int +calc_rate_offset(int hz) +{ + return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); +} + + +/* calculate GUS envelope time */ +static int +calc_gus_envelope_time(int rate, int start, int end) +{ + int r, p, t; + r = (3 - ((rate >> 6) & 3)) * 3; + p = rate & 0x3f; + t = end - start; + if (t < 0) t = -t; + if (13 > r) + t = t << (13 - r); + else + t = t >> (r - 13); + return (t * 10) / (p * 441); +} + +/* convert envelope time parameter to soundfont parameters */ + +/* attack & decay/release time table (msec) */ +static short attack_time_tbl[128] = { +32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, +707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, +361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, +180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, +90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, +45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, +22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, +11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, +}; + +static short decay_time_tbl[128] = { +32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, +2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, +1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, +691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, +345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, +172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, +86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, +43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, +}; + +/* delay time = 0x8000 - msec/92 */ +int +snd_sf_calc_parm_hold(int msec) +{ + int val = (0x7f * 92 - msec) / 92; + if (val < 1) val = 1; + if (val >= 126) val = 126; + return val; +} + +/* search an index for specified time from given time table */ +static int +calc_parm_search(int msec, short *table) +{ + int left = 1, right = 127, mid; + while (left < right) { + mid = (left + right) / 2; + if (msec < (int)table[mid]) + left = mid + 1; + else + right = mid; + } + return left; +} + +/* attack time: search from time table */ +int +snd_sf_calc_parm_attack(int msec) +{ + return calc_parm_search(msec, attack_time_tbl); +} + +/* decay/release time: search from time table */ +int +snd_sf_calc_parm_decay(int msec) +{ + return calc_parm_search(msec, decay_time_tbl); +} + +int snd_sf_vol_table[128] = { + 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, + 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, + 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, + 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, + 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, + 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, + 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, + 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, +}; + + +#define calc_gus_sustain(val) (0x7f - snd_sf_vol_table[(val)/2]) +#define calc_gus_attenuation(val) snd_sf_vol_table[(val)/2] + +/* load GUS patch */ +static int +load_guspatch(snd_sf_list_t *sflist, const char *data, long count, int client) +{ + struct patch_info patch; + snd_soundfont_t *sf; + snd_sf_zone_t *zone; + snd_sf_sample_t *smp; + int note, sample_id; + int rc; + + if (count < sizeof(patch)) { + snd_printk("patch record too small %ld\n", count); + return -EINVAL; + } + if (copy_from_user(&patch, data, sizeof(patch))) + return -EFAULT; + + count -= sizeof(patch); + data += sizeof(patch); + + sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); + if (sf == NULL) + return -ENOMEM; + if ((smp = sf_sample_new(sflist, sf)) == NULL) + return -ENOMEM; + sample_id = sflist->sample_counter; + smp->v.sample = sample_id; + smp->v.start = 0; + smp->v.end = patch.len; + smp->v.loopstart = patch.loop_start; + smp->v.loopend = patch.loop_end; + smp->v.size = patch.len; + + /* set up mode flags */ + smp->v.mode_flags = 0; + if (!(patch.mode & WAVE_16_BITS)) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS; + if (patch.mode & WAVE_UNSIGNED) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED; + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK; + if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT; + if (patch.mode & WAVE_BIDIR_LOOP) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP; + if (patch.mode & WAVE_LOOP_BACK) + smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP; + + if (patch.mode & WAVE_16_BITS) { + /* convert to word offsets */ + smp->v.size /= 2; + smp->v.end /= 2; + smp->v.loopstart /= 2; + smp->v.loopend /= 2; + } + /*smp->v.loopend++;*/ + + smp->v.dummy = 0; + smp->v.truesize = 0; + smp->v.sf_id = sf->id; + + /* set up voice info */ + if ((zone = sf_zone_new(sflist, sf)) == NULL) { + sf_sample_delete(sflist, sf, smp); + return -ENOMEM; + } + + /* + * load wave data + */ + if (sflist->callback.sample_new) { + rc = sflist->callback.sample_new + (sflist->callback.private_data, smp, sflist->memhdr, data, count); + if (rc < 0) { + sf_sample_delete(sflist, sf, smp); + return rc; + } + /* memory offset is updated after */ + } + + /* update the memory offset here */ + sflist->mem_used += smp->v.truesize; + + zone->v.sample = sample_id; /* the last sample */ + zone->v.rate_offset = calc_rate_offset(patch.base_freq); + note = freq_to_note(patch.base_note); + zone->v.root = note / 100; + zone->v.tune = -(note % 100); + zone->v.low = (freq_to_note(patch.low_note) + 99) / 100; + zone->v.high = freq_to_note(patch.high_note) / 100; + /* panning position; -128 - 127 => 0-127 */ + zone->v.pan = (patch.panning + 128) / 2; +#if 0 + snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n", + (int)patch.base_freq, zone->v.rate_offset, + zone->v.root, zone->v.tune, zone->v.low, zone->v.high); +#endif + + /* detuning is ignored */ + /* 6points volume envelope */ + if (patch.mode & WAVE_ENVELOPES) { + int attack, hold, decay, release; + attack = calc_gus_envelope_time + (patch.env_rate[0], 0, patch.env_offset[0]); + hold = calc_gus_envelope_time + (patch.env_rate[1], patch.env_offset[0], + patch.env_offset[1]); + decay = calc_gus_envelope_time + (patch.env_rate[2], patch.env_offset[1], + patch.env_offset[2]); + release = calc_gus_envelope_time + (patch.env_rate[3], patch.env_offset[1], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[4], patch.env_offset[3], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[5], patch.env_offset[4], + patch.env_offset[5]); + zone->v.parm.volatkhld = + (snd_sf_calc_parm_hold(hold) << 8) | + snd_sf_calc_parm_attack(attack); + zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + snd_sf_calc_parm_decay(decay); + zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release); + zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]); +#if 0 + snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n", + zone->v.parm.volatkhld, + zone->v.parm.voldcysus, + zone->v.parm.volrelease, + zone->v.attenuation); +#endif + } + + /* fast release */ + if (patch.mode & WAVE_FAST_RELEASE) { + zone->v.parm.volrelease = 0x807f; + } + + /* tremolo effect */ + if (patch.mode & WAVE_TREMOLO) { + int rate = (patch.tremolo_rate * 1000 / 38) / 42; + zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + } + /* vibrato effect */ + if (patch.mode & WAVE_VIBRATO) { + int rate = (patch.vibrato_rate * 1000 / 38) / 42; + zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + } + + /* scale_freq, scale_factor, volume, and fractions not implemented */ + + if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT)) + zone->v.mode = SNDRV_SFNT_MODE_LOOPING; + else + zone->v.mode = 0; + + /* append to the tail of the list */ + /*zone->bank = ctrls[AWE_MD_GUS_BANK];*/ + zone->bank = 0; + zone->instr = patch.instr_no; + zone->mapped = 0; + zone->v.sf_id = sf->id; + + zone->sample = set_sample(sf, &zone->v); + + /* rebuild preset now */ + add_preset(sflist, zone); + + return 0; +} + +/* load GUS patch */ +int +snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char *data, + long count, int client) +{ + int rc; + lock_preset(sflist, 0); + rc = load_guspatch(sflist, data, count, client); + unlock_preset(sflist); + return rc; +} + + +/* + * Rebuild the preset table. This is like a hash table in that it allows + * quick access to the zone information. For each preset there are zone + * structures linked by next_instr and by next_zone. Former is the whole + * link for this preset, and latter is the link for zone (i.e. instrument/ + * bank/key combination). + */ +static void +rebuild_presets(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *cur; + + /* clear preset table */ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + /* search all fonts and insert each font */ + for (sf = sflist->fonts; sf; sf = sf->next) { + for (cur = sf->zones; cur; cur = cur->next) { + if (! cur->mapped && cur->sample == NULL) { + /* try again to search the corresponding sample */ + cur->sample = set_sample(sf, &cur->v); + if (cur->sample == NULL) + continue; + } + + add_preset(sflist, cur); + } + } +} + + +/* + * add the given zone to preset table + */ +static void +add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur) +{ + snd_sf_zone_t *zone; + int index; + + zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low); + if (zone && zone->v.sf_id != cur->v.sf_id) { + /* different instrument was already defined */ + snd_sf_zone_t *p; + /* compare the allocated time */ + for (p = zone; p; p = p->next_zone) { + if (p->counter > cur->counter) + /* the current is older.. skipped */ + return; + } + /* remove old zones */ + delete_preset(sflist, zone); + zone = NULL; /* do not forget to clear this! */ + } + + /* prepend this zone */ + if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0) + return; + cur->next_zone = zone; /* zone link */ + cur->next_instr = sflist->presets[index]; /* preset table link */ + sflist->presets[index] = cur; +} + +/* + * delete the given zones from preset_table + */ +static void +delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp) +{ + int index; + snd_sf_zone_t *p; + + if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0) + return; + for (p = sflist->presets[index]; p; p = p->next_instr) { + while (p->next_instr == zp) { + p->next_instr = zp->next_instr; + zp = zp->next_zone; + if (zp == NULL) + return; + } + } +} + + +/* + * Search matching zones from preset table. + * The note can be rewritten by preset mapping (alias). + * The found zones are stored on 'table' array. max_layers defines + * the maximum number of elements in this array. + * This function returns the number of found zones. 0 if not found. + */ +int +snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel, + int preset, int bank, + int def_preset, int def_bank, + snd_sf_zone_t **table, int max_layers) +{ + int nvoices; + + if (lock_preset(sflist, 1)) + return 0; + + nvoices = search_zones(sflist, notep, vel, preset, bank, table, max_layers, 0); + if (! nvoices) { + if (preset != def_preset || bank != def_bank) + nvoices = search_zones(sflist, notep, vel, def_preset, def_bank, table, max_layers, 0); + } + unlock_preset(sflist); + + return nvoices; +} + + +/* + * search the first matching zone + */ +static snd_sf_zone_t * +search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key) +{ + int index; + snd_sf_zone_t *zp; + + if ((index = get_index(bank, preset, key)) < 0) + return NULL; + for (zp = sflist->presets[index]; zp; zp = zp->next_instr) { + if (zp->instr == preset && zp->bank == bank) + return zp; + } + return NULL; +} + + +/* + * search matching zones from sflist. can be called recursively. + */ +static int +search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level) +{ + snd_sf_zone_t *zp; + int nvoices; + + zp = search_first_zone(sflist, bank, preset, *notep); + nvoices = 0; + for (; zp; zp = zp->next_zone) { + if (*notep >= zp->v.low && *notep <= zp->v.high && + vel >= zp->v.vellow && vel <= zp->v.velhigh) { + if (zp->mapped) { + /* search preset mapping (aliasing) */ + int key = zp->v.fixkey; + preset = zp->v.start; + bank = zp->v.end; + + if (level > 5) /* too deep alias level */ + return 0; + if (key < 0) + key = *notep; + nvoices = search_zones(sflist, &key, vel, + preset, bank, table, + max_layers, level + 1); + if (nvoices > 0) + *notep = key; + break; + } + table[nvoices++] = zp; + if (nvoices >= max_layers) + break; + } + } + + return nvoices; +} + + +/* calculate the index of preset table: + * drums are mapped from 128 to 255 according to its note key. + * other instruments are mapped from 0 to 127. + * if the index is out of range, return -1. + */ +static int +get_index(int bank, int instr, int key) +{ + int index; + if (SF_IS_DRUM_BANK(bank)) + index = key + SF_MAX_INSTRUMENTS; + else + index = instr; + index = index % SF_MAX_PRESETS; + if (index < 0) + return -1; + return index; +} + +/* + * Initialise the sflist structure. + */ +static void +snd_sf_init(snd_sf_list_t *sflist) +{ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + sflist->mem_used = 0; + sflist->currsf = NULL; + sflist->open_client = -1; + sflist->fonts = NULL; + sflist->fonts_size = 0; + sflist->zone_counter = 0; + sflist->sample_counter = 0; + sflist->zone_locked = 0; + sflist->sample_locked = 0; +} + +/* + * Release all list records + */ +static void +snd_sf_clear(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf, *nextsf; + snd_sf_zone_t *zp, *nextzp; + snd_sf_sample_t *sp, *nextsp; + + for (sf = sflist->fonts; sf; sf = nextsf) { + nextsf = sf->next; + for (zp = sf->zones; zp; zp = nextzp) { + nextzp = zp->next; + kfree(zp); + } + for (sp = sf->samples; sp; sp = nextsp) { + nextsp = sp->next; + if (sflist->callback.sample_free) + sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); + kfree(sp); + } + kfree(sf); + } + + snd_sf_init(sflist); +} + + +/* + * Create a new sflist structure + */ +snd_sf_list_t * +snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr) +{ + snd_sf_list_t *sflist; + + if ((sflist = snd_kcalloc(sizeof(snd_sf_list_t), GFP_KERNEL)) == NULL) + return NULL; + + init_MUTEX(&sflist->presets_mutex); + spin_lock_init(&sflist->lock); + sflist->sf_locked = 0; + sflist->memhdr = hdr; + + if (callback) + sflist->callback = *callback; + + snd_sf_init(sflist); + return sflist; +} + + +/* + * Free everything allocated off the sflist structure. + */ +void +snd_sf_free(snd_sf_list_t *sflist) +{ + if (sflist == NULL) + return; + + lock_preset(sflist, 0); + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + snd_sf_clear(sflist); + unlock_preset(sflist); + + kfree(sflist); +} + +/* + * Remove all samples + * The soundcard should be silet before calling this function. + */ +int +snd_soundfont_remove_samples(snd_sf_list_t *sflist) +{ + lock_preset(sflist, 0); + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + snd_sf_clear(sflist); + unlock_preset(sflist); + + return 0; +} + +/* + * Remove unlocked samples. + * The soundcard should be silet before calling this function. + */ +int +snd_soundfont_remove_unlocked(snd_sf_list_t *sflist) +{ + snd_soundfont_t *sf; + snd_sf_zone_t *zp, *nextzp; + snd_sf_sample_t *sp, *nextsp; + + if (lock_preset(sflist, 1)) + return -EBUSY; + + if (sflist->callback.sample_reset) + sflist->callback.sample_reset(sflist->callback.private_data); + + /* to be sure */ + memset(sflist->presets, 0, sizeof(sflist->presets)); + + for (sf = sflist->fonts; sf; sf = sf->next) { + for (zp = sf->zones; zp; zp = nextzp) { + if (zp->counter < sflist->zone_locked) + break; + nextzp = zp->next; + sf->zones = nextzp; + kfree(zp); + } + + for (sp = sf->samples; sp; sp = nextsp) { + if (sp->counter < sflist->sample_locked) + break; + nextsp = sp->next; + sf->samples = nextsp; + sflist->mem_used -= sp->v.truesize; + if (sflist->callback.sample_free) + sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); + kfree(sp); + } + } + + sflist->zone_counter = sflist->zone_locked; + sflist->sample_counter = sflist->sample_locked; + + rebuild_presets(sflist); + + unlock_preset(sflist); + return 0; +} + +/* + * Return the used memory size (in words) + */ +int +snd_soundfont_mem_used(snd_sf_list_t *sflist) +{ + return sflist->mem_used; +} diff -Nru linux/sound/synth/util_mem.c linux-2.4.19-pre5-mjc/sound/synth/util_mem.c --- linux/sound/synth/util_mem.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/sound/synth/util_mem.c Mon Apr 8 22:31:24 2002 @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2000 Takashi Iwai + * + * Generic memory management routines for soundcard memory allocation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai"); +MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); +MODULE_LICENSE("GPL"); + +#define get_memblk(p) list_entry(p, snd_util_memblk_t, list) + +/* + * create a new memory manager + */ +snd_util_memhdr_t * +snd_util_memhdr_new(int memsize) +{ + snd_util_memhdr_t *hdr; + + hdr = snd_kcalloc(sizeof(*hdr), GFP_KERNEL); + if (hdr == NULL) + return NULL; + hdr->size = memsize; + init_MUTEX(&hdr->block_mutex); + INIT_LIST_HEAD(&hdr->block); + + return hdr; +} + +/* + * free a memory manager + */ +void snd_util_memhdr_free(snd_util_memhdr_t *hdr) +{ + struct list_head *p; + + snd_assert(hdr != NULL, return); + /* release all blocks */ + while ((p = hdr->block.next) != &hdr->block) { + list_del(p); + kfree(get_memblk(p)); + } + kfree(hdr); +} + +/* + * allocate a memory block (without mutex) + */ +snd_util_memblk_t * +__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk; + snd_util_unit_t units, prev_offset; + struct list_head *p; + + snd_assert(hdr != NULL, return NULL); + snd_assert(size > 0, return NULL); + + /* word alignment */ + units = size; + if (units & 1) + units++; + if (units > hdr->size) + return NULL; + + /* look for empty block */ + prev_offset = 0; + list_for_each(p, &hdr->block) { + blk = get_memblk(p); + if (blk->offset - prev_offset >= units) + goto __found; + prev_offset = blk->offset + blk->size; + } + if (hdr->size - prev_offset < units) + return NULL; + +__found: + return __snd_util_memblk_new(hdr, units, p->prev); +} + + +/* + * create a new memory block with the given size + * the block is linked next to prev + */ +snd_util_memblk_t * +__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, + struct list_head *prev) +{ + snd_util_memblk_t *blk; + + blk = kmalloc(sizeof(snd_util_memblk_t) + hdr->block_extra_size, GFP_KERNEL); + if (blk == NULL) + return NULL; + + if (! prev || prev == &hdr->block) + blk->offset = 0; + else { + snd_util_memblk_t *p = get_memblk(prev); + blk->offset = p->offset + p->size; + } + blk->size = units; + list_add(&blk->list, prev); + hdr->nblocks++; + hdr->used += units; + return blk; +} + + +/* + * allocate a memory block (with mutex) + */ +snd_util_memblk_t * +snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) +{ + snd_util_memblk_t *blk; + down(&hdr->block_mutex); + blk = __snd_util_mem_alloc(hdr, size); + up(&hdr->block_mutex); + return blk; +} + + +/* + * remove the block from linked-list and free resource + * (without mutex) + */ +void +__snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) +{ + list_del(&blk->list); + hdr->nblocks--; + hdr->used -= blk->size; + kfree(blk); +} + +/* + * free a memory block (with mutex) + */ +int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) +{ + snd_assert(hdr && blk, return -EINVAL); + + down(&hdr->block_mutex); + __snd_util_mem_free(hdr, blk); + up(&hdr->block_mutex); + return 0; +} + +/* + * return available memory size + */ +int snd_util_mem_avail(snd_util_memhdr_t *hdr) +{ + unsigned int size; + down(&hdr->block_mutex); + size = hdr->size - hdr->used; + up(&hdr->block_mutex); + return size; +} + + +EXPORT_SYMBOL(snd_util_memhdr_new); +EXPORT_SYMBOL(snd_util_memhdr_free); +EXPORT_SYMBOL(snd_util_mem_alloc); +EXPORT_SYMBOL(snd_util_mem_free); +EXPORT_SYMBOL(snd_util_mem_avail); +EXPORT_SYMBOL(__snd_util_mem_alloc); +EXPORT_SYMBOL(__snd_util_mem_free); +EXPORT_SYMBOL(__snd_util_memblk_new); + +/* + * INIT part + */ + +static int __init alsa_util_mem_init(void) +{ + return 0; +} + +static void __exit alsa_util_mem_exit(void) +{ +} + +module_init(alsa_util_mem_init) +module_exit(alsa_util_mem_exit)